ketekny-ui-kit 1.0.104 → 1.0.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ketekny-ui-kit",
3
3
  "type": "module",
4
- "version": "1.0.104",
4
+ "version": "1.0.106",
5
5
  "description": "A Vue 3 UI component library with Tailwind CSS styling",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -10,7 +10,10 @@
10
10
  </div>
11
11
  </div>
12
12
 
13
- <section class="bg-white px-4 pb-4 pb-0 pt-8 sm:px-6 lg:px-8">
13
+ <section
14
+ v-if="hasTopSection"
15
+ class="bg-white px-4 pb-4 pb-0 pt-8 sm:px-6 lg:px-8"
16
+ >
14
17
  <div class="mx-auto flex w-full max-w-none flex-col gap-5">
15
18
  <div
16
19
  v-if="pageTitle || pageDescription"
@@ -93,6 +96,14 @@ export default {
93
96
  },
94
97
  },
95
98
  computed: {
99
+ hasTopSection() {
100
+ return Boolean(
101
+ this.pageTitle ||
102
+ this.pageDescription ||
103
+ this.externalUrl ||
104
+ this.$slots['header-actions']
105
+ )
106
+ },
96
107
  showHeaderExtraBeforeTitle() {
97
108
  return this.headerExtraBeforeTitle && Boolean(this.$slots['header-extra'])
98
109
  },
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <main class="flex h-screen w-full max-w-none flex-col overflow-hidden bg-slate-100">
3
- <section class="shrink-0 bg-white px-4 pb-4 pb-0 pt-8 sm:px-6 lg:px-8">
3
+ <section
4
+ v-if="hasTopSection"
5
+ class="shrink-0 bg-white px-4 pb-4 pb-0 pt-8 sm:px-6 lg:px-8"
6
+ >
4
7
  <div class="mx-auto flex w-full max-w-none flex-col gap-5">
5
8
  <div
6
9
  v-if="pageTitle || pageDescription"
@@ -31,7 +34,10 @@
31
34
  </div>
32
35
  </section>
33
36
 
34
- <div class="shrink-0 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur">
37
+ <div
38
+ v-if="$slots['header-extra']"
39
+ class="shrink-0 border-b border-t border-slate-200 bg-slate-50 px-4 backdrop-blur"
40
+ >
35
41
  <div class="mx-auto flex w-full max-w-none items-center">
36
42
  <slot name="header-extra" />
37
43
  </div>
@@ -80,6 +86,14 @@ export default {
80
86
  },
81
87
  },
82
88
  computed: {
89
+ hasTopSection() {
90
+ return Boolean(
91
+ this.pageTitle ||
92
+ this.pageDescription ||
93
+ this.externalUrl ||
94
+ this.$slots['header-actions']
95
+ )
96
+ },
83
97
  externalUrlLabel() {
84
98
  const target = String(this.externalUrl || '').trim()
85
99
  if (!target) return ''
@@ -20,4 +20,13 @@ body {
20
20
  html {
21
21
  font-size: 14px;
22
22
  }
23
+ }
24
+
25
+ @media (prefers-reduced-motion: reduce) {
26
+ *, *::before, *::after {
27
+ animation-duration: 0.01ms !important;
28
+ animation-iteration-count: 1 !important;
29
+ transition-duration: 0.01ms !important;
30
+ scroll-behavior: auto !important;
31
+ }
23
32
  }
@@ -470,6 +470,10 @@ export default {
470
470
  </div>
471
471
  </div>
472
472
 
473
+ <div v-if="$slots['sidebar-header']" :class="collapsed ? 'px-2 pb-2' : 'px-4 pb-3'">
474
+ <slot name="sidebar-header" />
475
+ </div>
476
+
473
477
  <nav
474
478
  class="flex-1 px-3 py-2"
475
479
  :class="collapsed ? 'overflow-visible' : 'overflow-y-auto'"
@@ -20,7 +20,11 @@
20
20
  @edit-profile="handleEditProfile"
21
21
  @item-click="handleItemClick"
22
22
  @select="handleSelect"
23
- />
23
+ >
24
+ <template v-if="$slots['sidebar-header']" #sidebar-header>
25
+ <slot name="sidebar-header" />
26
+ </template>
27
+ </MultilevelSidebar>
24
28
 
25
29
  <main class="flex min-h-0 flex-1 flex-col overflow-y-auto">
26
30
  <div
@@ -3,10 +3,13 @@
3
3
  <div class="flex items-center justify-between px-4 py-3 border-b cp-border cp-muted-50">
4
4
  <h2 class="text-[15px] font-semibold cp-text">{{ componentName }} Description</h2>
5
5
 
6
- <div class="flex items-center gap-1">
6
+ <div class="flex items-center gap-1" role="tablist">
7
7
  <button
8
8
  v-for="tab in tabs"
9
9
  :key="tab.id"
10
+ type="button"
11
+ role="tab"
12
+ :aria-selected="activeTab === tab.id"
10
13
  @click="activeTab = tab.id"
11
14
  :class="['px-3 py-1.5 text-[13px] font-medium rounded-md transition-colors', activeTab === tab.id ? 'cp-tab-active' : 'cp-tab']">
12
15
  {{ tab.label }}
@@ -15,13 +18,14 @@
15
18
  </div>
16
19
 
17
20
  <div class="p-4">
18
- <div v-show="activeTab === 'props'">
21
+ <div v-show="activeTab === 'props'" role="tabpanel">
19
22
  <div v-if="props.length > 0" class="overflow-x-auto">
20
23
  <table class="w-full text-[13px]">
21
24
  <thead>
22
25
  <tr class="border-b cp-border">
23
26
  <th class="px-4 py-3 font-semibold text-left cp-text">Name</th>
24
27
  <th class="px-4 py-3 font-semibold text-left cp-text">Type</th>
28
+ <th class="px-4 py-3 font-semibold text-left cp-text">Values</th>
25
29
  <th class="px-4 py-3 font-semibold text-left cp-text">Default</th>
26
30
  <th class="px-4 py-3 font-semibold text-left cp-text">Description</th>
27
31
  </tr>
@@ -33,6 +37,12 @@
33
37
  <code class="text-[13px] font-mono px-1.5 py-0.5 rounded cp-chip cp-chip-text">{{ prop.name }}</code>
34
38
  </td>
35
39
  <td class="px-4 py-3"><span class="font-mono text-[13px] text-blue-600">{{ prop.type }}</span></td>
40
+ <td class="px-4 py-3">
41
+ <div v-if="prop.values && prop.values.length" class="flex flex-wrap gap-1">
42
+ <code v-for="v in prop.values" :key="v" class="text-[12px] font-mono px-1.5 py-0.5 rounded cp-chip cp-chip-text">{{ v }}</code>
43
+ </div>
44
+ <span v-else class="cp-text-muted">-</span>
45
+ </td>
36
46
  <td class="px-4 py-3">
37
47
  <code v-if="prop.default !== undefined" class="font-mono text-[13px] text-slate-700">{{ prop.default }}</code>
38
48
  <span v-else class="cp-text-muted">-</span>
@@ -48,7 +58,7 @@
48
58
  </div>
49
59
  </div>
50
60
 
51
- <div v-show="activeTab === 'slots'">
61
+ <div v-show="activeTab === 'slots'" role="tabpanel">
52
62
  <div v-if="slots.length > 0" class="overflow-x-auto">
53
63
  <table class="w-full text-[13px]">
54
64
  <thead>
@@ -3,10 +3,13 @@
3
3
  <div class="flex items-center justify-between px-4 py-3 border-b cp-border cp-muted-50">
4
4
  <h2 class="text-[15px] font-semibold cp-text">{{ componentName }} Preview</h2>
5
5
 
6
- <div class="flex items-center gap-1">
6
+ <div class="flex items-center gap-1" role="tablist">
7
7
  <button
8
8
  v-for="tab in tabs"
9
9
  :key="tab.id"
10
+ type="button"
11
+ role="tab"
12
+ :aria-selected="activeTab === tab.id"
10
13
  @click="activeTab = tab.id"
11
14
  :class="['px-3 py-1.5 text-[13px] font-medium rounded-md transition-colors', activeTab === tab.id ? 'cp-tab-active' : 'cp-tab']">
12
15
  {{ tab.label }}
@@ -15,13 +18,14 @@
15
18
  </div>
16
19
 
17
20
  <div class="p-4">
18
- <div v-show="activeTab === 'preview'" class="min-h-[120px] rounded-lg p-6 border cp-muted-30 cp-border-50">
21
+ <div v-show="activeTab === 'preview'" role="tabpanel" class="min-h-[120px] rounded-lg p-6 border cp-muted-30 cp-border-50">
19
22
  <div class="flex justify-end mb-4">
20
23
  <div class="inline-flex p-1 rounded-md cp-muted-50">
21
24
  <button
22
25
  v-for="vp in previewViewports"
23
26
  :key="vp.id"
24
27
  @click="activeViewport = vp.id"
28
+ :aria-pressed="activeViewport === vp.id"
25
29
  :class="['px-3 py-1.5 text-[13px] font-semibold rounded-md transition-colors', activeViewport === vp.id ? 'cp-tab-active' : 'cp-tab']">
26
30
  {{ vp.label }}
27
31
  </button>
@@ -35,9 +39,9 @@
35
39
  </div>
36
40
  </div>
37
41
 
38
- <div v-show="activeTab === 'code'" class="relative">
42
+ <div v-show="activeTab === 'code'" role="tabpanel" class="relative">
39
43
  <div class="absolute z-10 top-3 right-3">
40
- <button @click="copyCode" class="flex items-center gap-1.5 px-3 py-1.5 text-[13px] font-medium rounded-md transition-colors cp-chip cp-chip-hover cp-chip-text">
44
+ <button @click="copyCode" type="button" aria-label="Copy code" class="flex items-center gap-1.5 px-3 py-1.5 text-[13px] font-medium rounded-md transition-colors cp-chip cp-chip-hover cp-chip-text">
41
45
  <svg
42
46
  v-if="!copied"
43
47
  xmlns="http://www.w3.org/2000/svg"
@@ -68,7 +72,7 @@
68
72
  <polyline points="20 6 9 17 4 12" />
69
73
  </svg>
70
74
 
71
- {{ copied ? "Copied!" : "Copy" }}
75
+ <span aria-live="polite">{{ copied ? "Copied!" : "Copy" }}</span>
72
76
  </button>
73
77
  </div>
74
78
 
@@ -13,7 +13,7 @@
13
13
  :disabled="disabled"
14
14
  :class="[
15
15
  'block w-full p-3 transition border border-gray-300 shadow-sm outline-none rounded-xl dark:border-slate-600 dark:text-slate-100 dark:placeholder-slate-500',
16
- disabled ? 'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-slate-900 dark:text-slate-500' : 'bg-white focus:border-primary focus:ring-2 focus:ring-primary/20 dark:bg-slate-800',
16
+ disabled ? 'bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-slate-900 dark:text-slate-500' : 'bg-white focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/20 dark:bg-slate-800',
17
17
  ]"
18
18
  @focus="isFocused = true"
19
19
  @blur="isFocused = false"
@@ -1,6 +1,8 @@
1
1
  <template>
2
2
  <button
3
3
  type="button"
4
+ role="checkbox"
5
+ :aria-checked="isActive"
4
6
  class="admin-checkbox flex w-full items-center gap-2.5 rounded-2xl px-3 py-2 transition"
5
7
  :class="[themeTextClass, isActive ? activeTextClass : inactiveTextClass, combinedContainerClass]"
6
8
  :title="hoverCaption"
@@ -114,7 +116,7 @@ export default {
114
116
  return this.theme === 'dark' ? 'text-slate-300' : 'text-slate-500'
115
117
  },
116
118
  themeActiveClass() {
117
- return this.theme === 'dark' ? 'bg-cyan-400 text-cyan-950' : 'bg-sky-600 text-white'
119
+ return this.theme === 'dark' ? 'bg-primary/90 text-white' : 'bg-primary text-white'
118
120
  },
119
121
  themeInactiveClass() {
120
122
  return this.theme === 'dark' ? 'bg-white/10 text-transparent' : 'bg-slate-200 text-transparent'
@@ -8,7 +8,7 @@
8
8
  <transition name="dialog">
9
9
  <div
10
10
  ref="dialogPanel"
11
- class="relative bg-white dark:bg-zinc-900 shadow-lg overflow-hidden rounded-2xl flex flex-col max-h-[calc(100dvh-1rem)] sm:max-h-[90vh] m-2 sm:m-4"
11
+ class="relative bg-white dark:bg-slate-900 shadow-lg overflow-hidden rounded-2xl flex flex-col max-h-[calc(100dvh-1rem)] sm:max-h-[90vh] m-2 sm:m-4"
12
12
  :class="dialogClasses"
13
13
  :role="'dialog'"
14
14
  :aria-modal="'true'"
@@ -22,7 +22,7 @@
22
22
  <div class="flex-1" />
23
23
  <button
24
24
  type="button"
25
- class="p-1.5 rounded-md text-zinc-400 hover:text-primary dark:text-zinc-500 dark:hover:text-primary hover:bg-primary/10 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
25
+ class="p-1.5 rounded-md text-slate-400 hover:text-primary dark:text-slate-500 dark:hover:text-primary hover:bg-primary/10 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50"
26
26
  aria-label="Close dialog"
27
27
  @click="close"
28
28
  >
@@ -43,7 +43,7 @@
43
43
  <!-- Footer -->
44
44
  <div
45
45
  v-if="$slots.footer"
46
- class="flex flex-col-reverse gap-2 p-3 border-t shrink-0 border-zinc-200 dark:border-zinc-800 sm:flex-row sm:justify-end sm:p-4"
46
+ class="flex flex-col-reverse gap-2 p-3 border-t shrink-0 border-slate-200 dark:border-slate-800 sm:flex-row sm:justify-end sm:p-4"
47
47
  >
48
48
  <slot name="footer"></slot>
49
49
  </div>
@@ -130,10 +130,41 @@ export default {
130
130
  this.close()
131
131
  },
132
132
  handleKeydown(event) {
133
- if (!this.visible || !this.closeOnEsc) return
133
+ if (!this.visible) return
134
134
  if (event.key === 'Escape') {
135
+ if (!this.closeOnEsc) return
135
136
  event.preventDefault()
136
137
  this.close()
138
+ return
139
+ }
140
+ if (event.key === 'Tab') {
141
+ this.trapFocus(event)
142
+ }
143
+ },
144
+ trapFocus(event) {
145
+ const panel = this.$refs.dialogPanel
146
+ if (!panel) return
147
+ const focusable = panel.querySelectorAll(
148
+ 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
149
+ )
150
+ if (focusable.length === 0) {
151
+ event.preventDefault()
152
+ panel.focus()
153
+ return
154
+ }
155
+ const first = focusable[0]
156
+ const last = focusable[focusable.length - 1]
157
+ const active = document.activeElement
158
+ if (event.shiftKey) {
159
+ if (active === first || active === panel || !panel.contains(active)) {
160
+ event.preventDefault()
161
+ last.focus()
162
+ }
163
+ } else {
164
+ if (active === last) {
165
+ event.preventDefault()
166
+ first.focus()
167
+ }
137
168
  }
138
169
  },
139
170
  lockBodyScroll() {
@@ -66,8 +66,8 @@ export default {
66
66
  data() {
67
67
  return {
68
68
  defaultStyle:
69
- "w-full px-3 py-2 border rounded-b-lg transition shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/20 min-h-[150px] focus:border-primary bg-white placeholder-gray-400 !list-disc !list-inside prose dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500",
70
- errorStyle: "border-red-500 focus:ring focus:ring-red-300 dark:border-rose-500 dark:focus:ring-rose-500/20",
69
+ "w-full px-3 py-2 border rounded-b-lg transition shadow-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/20 min-h-[150px] focus-visible:border-primary bg-white placeholder-gray-400 !list-disc !list-inside prose dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500",
70
+ errorStyle: "border-red-500 focus-visible:ring focus-visible:ring-red-300 dark:border-rose-500 dark:focus-visible:ring-rose-500/20",
71
71
  disabledStyle: "!bg-gray-100 !text-gray-400 !cursor-not-allowed editor pointer-events-none select-none dark:!bg-slate-900 dark:!text-slate-500",
72
72
  isFocused: false,
73
73
  pendingModelValue: null,
@@ -14,7 +14,7 @@
14
14
  :type="inputType"
15
15
  v-model="value"
16
16
  :placeholder="placeholder"
17
- class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-slate-700 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500 dark:focus:ring-indigo-400"
17
+ class="w-full px-3 py-2 border border-gray-300 rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 dark:bg-slate-700 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500 dark:focus-visible:ring-indigo-400"
18
18
  :disabled="loading"
19
19
  ref="inputEl"
20
20
  @keyup.enter="confirm"
package/src/ui/kList.vue CHANGED
@@ -122,7 +122,7 @@ export default {
122
122
  },
123
123
  entryClass(stat) {
124
124
  return stat.action
125
- ? 'cursor-pointer transition hover:border-primary/40 hover:bg-slate-50 focus:outline-none focus:ring-2 focus:ring-primary/20 dark:hover:bg-slate-700/70'
125
+ ? 'cursor-pointer transition hover:border-primary/40 hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/20 dark:hover:bg-slate-700/70'
126
126
  : ''
127
127
  },
128
128
  handleAction(stat, index) {
@@ -39,7 +39,7 @@ export default {
39
39
  data() {
40
40
  return {
41
41
  inputId: `ksearch-${Math.random().toString(36).slice(2, 10)}`,
42
- defaultStyle: "w-full px-3 py-2 border rounded-lg transition shadow-sm focus:outline-none text-gray-700 focus:ring-2 focus:ring-primary/20 focus:border-primary bg-white placeholder-gray-400 dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500",
42
+ defaultStyle: "w-full px-3 py-2 border rounded-lg transition shadow-sm focus-visible:outline-none text-gray-700 focus-visible:ring-2 focus-visible:ring-primary/20 focus-visible:border-primary bg-white placeholder-gray-400 dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500",
43
43
  disabledStyle: "bg-gray-100 text-gray-400 cursor-not-allowed dark:bg-slate-900 dark:text-slate-500",
44
44
 
45
45
  localValue: this.modelValue,
@@ -48,7 +48,7 @@
48
48
  type="text"
49
49
  v-model="searchQuery"
50
50
  placeholder="Αναζήτηση..."
51
- class="w-full pl-9 pr-3 py-2 text-base border border-gray-200 rounded-lg focus:outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 transition-colors placeholder-gray-400 dark:bg-slate-700 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500"
51
+ class="w-full pl-9 pr-3 py-2 text-base border border-gray-200 rounded-lg focus-visible:outline-none focus-visible:border-primary focus-visible:ring-2 focus-visible:ring-primary/20 transition-colors placeholder-gray-400 dark:bg-slate-700 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500"
52
52
  role="searchbox"
53
53
  aria-label="Search options"
54
54
  @keydown="handleSearchKeydown"
@@ -137,8 +137,8 @@ const searchQuery = ref("");
137
137
  const searchInput = ref(null);
138
138
  const viewportRef = ref(null);
139
139
  const generatedId = `select-${Math.random().toString(36).substr(2, 9)}`;
140
- const defaultStyle = "w-full px-3 py-2 border rounded-lg transition shadow-sm focus:outline-none text-gray-700 focus:ring-2 focus:ring-primary/20 focus:border-primary bg-white placeholder-gray-400 dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500";
141
- const errorStyle = "border-red-500 focus:ring focus:ring-red-300 dark:border-rose-500 dark:focus:ring-rose-500/20";
140
+ const defaultStyle = "w-full px-3 py-2 border rounded-lg transition shadow-sm focus-visible:outline-none text-gray-700 focus-visible:ring-2 focus-visible:ring-primary/20 focus-visible:border-primary bg-white placeholder-gray-400 dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500";
141
+ const errorStyle = "border-red-500 focus-visible:ring focus-visible:ring-red-300 dark:border-rose-500 dark:focus-visible:ring-rose-500/20";
142
142
  const disabledStyle = "!bg-gray-100 !text-gray-400 !cursor-not-allowed dark:!bg-slate-900 dark:!text-slate-500";
143
143
 
144
144
  function normalizeDropdownHeight(value) {
@@ -34,7 +34,7 @@ export default {
34
34
  emits: ["update:modelValue"],
35
35
  computed: {
36
36
  baseClasses() {
37
- return "shrink-0 whitespace-nowrap px-3 py-2.5 text-sm font-medium border-none focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-inset sm:px-4 sm:py-2";
37
+ return "shrink-0 whitespace-nowrap px-3 py-2.5 text-sm font-medium border-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-inset sm:px-4 sm:py-2";
38
38
  },
39
39
  activeClasses() {
40
40
  return "bg-primary text-white";
package/src/ui/kTable.vue CHANGED
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div ref="tableWrapper" :class="['w-full', { 'k-table-mobile': isMobileLayout }]">
3
- <table ref="tableRef" :class="['table-custom', `table-custom--${variant}`]">
3
+ <table ref="tableRef" role="table" :class="['table-custom', `table-custom--${variant}`]">
4
4
  <template v-if="hasNamedSlots">
5
5
  <thead ref="theadRef">
6
6
  <slot name="head"></slot>
@@ -74,15 +74,22 @@ export default {
74
74
  const thead = this.getTheadRef();
75
75
  if (!thead) return;
76
76
 
77
- const headers = Array.from(thead.querySelectorAll("th")).map((th) => th.textContent.trim());
77
+ const headerCells = Array.from(thead.querySelectorAll("th"));
78
+ headerCells.forEach((th) => {
79
+ th.setAttribute("role", "columnheader");
80
+ th.setAttribute("scope", "col");
81
+ });
82
+ const headers = headerCells.map((th) => th.textContent.trim());
78
83
 
79
84
  const tbody = this.getTbodyRef();
80
85
  if (!tbody) return;
81
86
  const rows = tbody.querySelectorAll("tr");
82
87
 
83
88
  rows.forEach((row) => {
89
+ row.setAttribute("role", "row");
84
90
  const cells = row.querySelectorAll("td");
85
91
  cells.forEach((cell, index) => {
92
+ cell.setAttribute("role", "cell");
86
93
  cell.setAttribute("data-label", headers[index] || "");
87
94
  });
88
95
  });
@@ -97,7 +104,7 @@ export default {
97
104
  };
98
105
  </script>
99
106
 
100
- <style css>
107
+ <style>
101
108
  .table-custom {
102
109
  @apply table-auto w-full border-collapse;
103
110
  }
package/src/ui/kTags.vue CHANGED
@@ -27,7 +27,7 @@
27
27
  :placeholder="placeholder"
28
28
  :aria-label="!label ? placeholder || 'Add tag' : null"
29
29
  :disabled="disabled"
30
- :class="['flex-1 p-1 bg-transparent border-none focus:outline-none dark:text-slate-100 dark:placeholder-slate-500', disabled ? 'text-gray-400 cursor-not-allowed dark:text-slate-500' : '']"
30
+ :class="['flex-1 p-1 bg-transparent border-none focus-visible:outline-none dark:text-slate-100 dark:placeholder-slate-500', disabled ? 'text-gray-400 cursor-not-allowed dark:text-slate-500' : '']"
31
31
  />
32
32
  </div>
33
33
  <!-- Error message -->
@@ -74,8 +74,8 @@ export default {
74
74
  },
75
75
  textareaClass() {
76
76
  return [
77
- 'w-full px-3 py-2 border rounded-lg transition shadow-sm focus:outline-none text-gray-700 focus:ring-2 focus:ring-primary/20 focus:border-primary bg-white placeholder-gray-400 dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500',
78
- this.hasError ? 'border-red-500 focus:ring focus:ring-red-300 dark:border-rose-500 dark:focus:ring-rose-500/20' : '',
77
+ 'w-full px-3 py-2 border rounded-lg transition shadow-sm focus-visible:outline-none text-gray-700 focus-visible:ring-2 focus-visible:ring-primary/20 focus-visible:border-primary bg-white placeholder-gray-400 dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500',
78
+ this.hasError ? 'border-red-500 focus-visible:ring focus-visible:ring-red-300 dark:border-rose-500 dark:focus-visible:ring-rose-500/20' : '',
79
79
  this.disabled ? '!bg-gray-100 !text-gray-400 !cursor-not-allowed dark:!bg-slate-900 dark:!text-slate-500 dark:!border-slate-700' : '',
80
80
  ]
81
81
  },
@@ -7,7 +7,6 @@
7
7
  'block mb-1 text-sm font-bold',
8
8
  disabled ? 'text-slate-500' : 'text-primary/90',
9
9
  ]"
10
- for="toggle"
11
10
  >
12
11
  {{ label }}
13
12
  </div>
@@ -15,7 +14,9 @@
15
14
  <!-- Toggle button -->
16
15
  <button
17
16
  @click="toggle"
18
- id="toggle"
17
+ role="switch"
18
+ :aria-checked="modelValue"
19
+ :id="computedId"
19
20
  :aria-label="label || 'Toggle'"
20
21
  :aria-disabled="disabled.toString()"
21
22
  :disabled="disabled"
@@ -100,12 +101,12 @@ export default {
100
101
 
101
102
  .k-toggle-track:focus-visible {
102
103
  outline: none;
103
- box-shadow: 0 0 0 2px rgb(37 99 235 / 0.35);
104
+ box-shadow: 0 0 0 2px rgb(3 105 161 / 0.35);
104
105
  }
105
106
 
106
107
  .k-toggle-track--on {
107
- background: #1d4ed8;
108
- border-color: #1d4ed8;
108
+ background: #0369A1;
109
+ border-color: #0369A1;
109
110
  box-shadow: 0 2px 8px rgb(2 6 23 / 0.25);
110
111
  }
111
112
 
@@ -148,8 +149,8 @@ export default {
148
149
  }
149
150
 
150
151
  :global(html.dark) .k-toggle-track--on {
151
- background: #1d4ed8;
152
- border-color: #1d4ed8;
152
+ background: #0369A1;
153
+ border-color: #0369A1;
153
154
  box-shadow: 0 2px 8px rgb(2 6 23 / 0.45);
154
155
  }
155
156
 
@@ -16,7 +16,7 @@
16
16
  Σύρετε το αρχείο εδώ, ή
17
17
  <label
18
18
  for="file-input"
19
- class="underline cursor-pointer text-primary hover:text-primary/80 focus:outline-none"
19
+ class="underline cursor-pointer text-primary hover:text-primary/80 focus-visible:outline-none"
20
20
  >
21
21
  αναζητήστε
22
22
  </label>
@@ -63,7 +63,7 @@
63
63
  <CheckCircleIcon class="w-5 h-5 text-green-500" />
64
64
  <button
65
65
  @click="removeFile(file)"
66
- class="text-gray-400 hover:text-red-500 focus:outline-none dark:text-slate-500 dark:hover:text-red-400"
66
+ class="text-gray-400 hover:text-red-500 focus-visible:outline-none dark:text-slate-500 dark:hover:text-red-400"
67
67
  :disabled="disabled"
68
68
  aria-label="Remove file"
69
69
  >
@@ -1,6 +1,6 @@
1
1
  import { INTENT_VARIANT_STYLES } from "./intentColors";
2
2
 
3
- export const K_BUTTON_FOCUS_RING = "focus-visible:ring-slate-900";
3
+ export const K_BUTTON_FOCUS_RING = "focus-visible:ring-slate-900 dark:focus-visible:ring-primary";
4
4
  export const K_BUTTON_BASE = "inline-flex flex-row items-center justify-center font-medium transition-all rounded-lg border border-transparent";
5
5
 
6
6
  export const K_BUTTON_SIZE_STYLES = {
@@ -3,11 +3,11 @@ export const K_INPUT_THEME = {
3
3
  labelDisabled: "text-slate-500 dark:text-slate-400",
4
4
  labelError: "text-rose-800 dark:text-rose-400",
5
5
  baseInput:
6
- "w-full px-3 py-2 border rounded-lg transition shadow-sm focus:outline-none text-slate-900 bg-white placeholder-gray-400 focus:ring-2 focus:ring-primary/25 focus:border-primary dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500",
6
+ "w-full px-3 py-2 border rounded-lg transition shadow-sm focus-visible:outline-none text-slate-900 bg-white placeholder-gray-400 focus-visible:ring-2 focus-visible:ring-primary/25 focus-visible:border-primary dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500",
7
7
  withRightAdornment: "pr-10",
8
8
  withPasswordToggle: "pr-14",
9
9
  disabled: "!bg-slate-50 !text-slate-400 !border-slate-200 !shadow-none cursor-not-allowed placeholder-slate-400 dark:!bg-slate-900 dark:!text-slate-500 dark:!border-slate-700",
10
- errorInput: "border-rose-500 bg-rose-50/40 focus:border-rose-600 focus:ring-rose-500/20 dark:bg-rose-950/40 dark:border-rose-500 dark:focus:ring-rose-500/20",
10
+ errorInput: "border-rose-500 bg-rose-50/40 focus-visible:border-rose-600 focus-visible:ring-rose-500/20 dark:bg-rose-950/40 dark:border-rose-500 dark:focus-visible:ring-rose-500/20",
11
11
  infoText: "text-sm text-slate-600 dark:text-slate-400",
12
12
  errorText: "text-sm text-rose-700 dark:text-rose-400",
13
13
  trailingIcon: "absolute w-4 h-4 text-primary/70 -translate-y-1/2 pointer-events-none right-3 top-1/2",