@adamarant/designsystem 0.11.2

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.
Files changed (84) hide show
  1. package/README.md +101 -0
  2. package/dist/designsystem.css +13494 -0
  3. package/dist/designsystem.js +67 -0
  4. package/dist/designsystem.min.css +2 -0
  5. package/package.json +111 -0
  6. package/src/base/index.css +2 -0
  7. package/src/base/reset.css +119 -0
  8. package/src/base/typography.css +172 -0
  9. package/src/components/accordion.css +166 -0
  10. package/src/components/admin-layout.css +371 -0
  11. package/src/components/alert.css +159 -0
  12. package/src/components/avatar.css +109 -0
  13. package/src/components/badge.css +80 -0
  14. package/src/components/bottom-nav.css +125 -0
  15. package/src/components/bottom-sheet.css +146 -0
  16. package/src/components/breadcrumb.css +102 -0
  17. package/src/components/button.css +250 -0
  18. package/src/components/card.css +117 -0
  19. package/src/components/chip.css +79 -0
  20. package/src/components/collapsible.css +112 -0
  21. package/src/components/color-picker.css +82 -0
  22. package/src/components/combobox.css +420 -0
  23. package/src/components/command.css +210 -0
  24. package/src/components/context-menu.css +162 -0
  25. package/src/components/copy-button.css +106 -0
  26. package/src/components/custom-select.css +446 -0
  27. package/src/components/datepicker.css +301 -0
  28. package/src/components/description-list.css +100 -0
  29. package/src/components/divider.css +66 -0
  30. package/src/components/drawer.css +234 -0
  31. package/src/components/drop-zone.css +166 -0
  32. package/src/components/dropdown.css +169 -0
  33. package/src/components/empty-state.css +75 -0
  34. package/src/components/field.css +112 -0
  35. package/src/components/gallery.css +257 -0
  36. package/src/components/hero.css +111 -0
  37. package/src/components/icon-btn.css +103 -0
  38. package/src/components/index.css +74 -0
  39. package/src/components/input.css +311 -0
  40. package/src/components/kbd.css +54 -0
  41. package/src/components/media-library.css +230 -0
  42. package/src/components/modal.css +136 -0
  43. package/src/components/nav.css +198 -0
  44. package/src/components/number-input.css +163 -0
  45. package/src/components/pagination.css +175 -0
  46. package/src/components/pin-input.css +136 -0
  47. package/src/components/popover.css +111 -0
  48. package/src/components/progress.css +217 -0
  49. package/src/components/prose.css +337 -0
  50. package/src/components/result.css +80 -0
  51. package/src/components/scroll-area.css +73 -0
  52. package/src/components/search.css +311 -0
  53. package/src/components/segmented-control.css +94 -0
  54. package/src/components/skeleton.css +100 -0
  55. package/src/components/slider.css +133 -0
  56. package/src/components/sortable.css +70 -0
  57. package/src/components/spinner.css +60 -0
  58. package/src/components/star-rating.css +121 -0
  59. package/src/components/stat-card.css +44 -0
  60. package/src/components/table.css +359 -0
  61. package/src/components/tabs.css +215 -0
  62. package/src/components/tag.css +188 -0
  63. package/src/components/timeline.css +130 -0
  64. package/src/components/toast.css +90 -0
  65. package/src/components/toggle.css +173 -0
  66. package/src/components/toolbar.css +206 -0
  67. package/src/components/tooltip.css +167 -0
  68. package/src/components/truncated-text.css +75 -0
  69. package/src/index.css +21 -0
  70. package/src/js/theme.js +67 -0
  71. package/src/tokens/colors.css +256 -0
  72. package/src/tokens/index.css +11 -0
  73. package/src/tokens/shadows.css +55 -0
  74. package/src/tokens/spacing.css +82 -0
  75. package/src/tokens/tokens.json +413 -0
  76. package/src/tokens/typography.css +90 -0
  77. package/src/utilities/a11y.css +102 -0
  78. package/src/utilities/index.css +7 -0
  79. package/src/utilities/interactive.css +121 -0
  80. package/src/utilities/layout.css +273 -0
  81. package/src/utilities/sizing.css +85 -0
  82. package/src/utilities/spacing.css +204 -0
  83. package/src/utilities/states.css +182 -0
  84. package/src/utilities/text.css +381 -0
@@ -0,0 +1,311 @@
1
+ /* ==========================================================================
2
+ Component: Search
3
+ Inline search bar with dropdown results, keyboard navigation, and
4
+ responsive mobile expansion. Distinct from ds-command (modal overlay).
5
+
6
+ ARIA requirements (consumer responsibility):
7
+ - Container: role="search" on wrapper or parent <form>
8
+ - Input: role="combobox", aria-expanded="true|false", aria-controls="[list-id]"
9
+ - Dropdown: role="listbox" with id matching aria-controls
10
+ - Results: role="option", aria-selected="true" on active result
11
+ - Clear button: aria-label="Clear search"
12
+ - Keyboard: ArrowUp/Down to navigate, Enter to select, Escape to close
13
+
14
+ Usage:
15
+ <div class="ds-search" role="search">
16
+ <span class="ds-search__icon">...</span>
17
+ <input class="ds-search__input" placeholder="Search..." />
18
+ <kbd class="ds-search__shortcut">Ctrl+K</kbd>
19
+ <button class="ds-search__clear" aria-label="Clear search">...</button>
20
+ </div>
21
+
22
+ Modifiers:
23
+ .ds-search--mobile-expanded — Fullscreen bar on mobile
24
+ .ds-search__dropdown--mobile — Fullscreen dropdown on mobile
25
+
26
+ Mobile:
27
+ .ds-search-mobile-trigger — Icon button shown on mobile (hidden desktop)
28
+ .ds-search__close — Close button inside expanded mobile bar
29
+ ========================================================================== */
30
+
31
+ .ds-search {
32
+ position: relative;
33
+ display: flex;
34
+ align-items: center;
35
+ gap: var(--ds-space-1);
36
+ width: 100%;
37
+ padding: var(--ds-space-2) var(--ds-space-2-5);
38
+ background: var(--ds-color-surface);
39
+ border: 1px solid var(--ds-color-border);
40
+ border-radius: var(--ds-radius-lg);
41
+ transition:
42
+ border-color var(--ds-duration-fast) var(--ds-ease-default),
43
+ box-shadow var(--ds-duration-fast) var(--ds-ease-default);
44
+ container-type: inline-size;
45
+ }
46
+
47
+ /* Container query: hide shortcut hint when search is narrow */
48
+ @container (max-width: 240px) {
49
+ .ds-search__shortcut { display: none; }
50
+ }
51
+
52
+ .ds-search:focus-within {
53
+ border-color: var(--ds-color-border-active);
54
+ box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
55
+ }
56
+
57
+ @media (hover: hover) {
58
+ .ds-search:hover:not(:focus-within) {
59
+ background: var(--ds-color-surface-hover);
60
+ border-color: var(--ds-color-border-hover);
61
+ }
62
+ }
63
+
64
+ /* Icon */
65
+ .ds-search__icon {
66
+ color: var(--ds-color-text-tertiary);
67
+ flex-shrink: 0;
68
+ }
69
+
70
+ /* Input */
71
+ .ds-search__input {
72
+ flex: 1;
73
+ min-width: 0;
74
+ border: none;
75
+ outline: none;
76
+ background: transparent;
77
+ color: var(--ds-color-text);
78
+ font-size: var(--ds-text-base);
79
+ font-family: inherit;
80
+ line-height: 1.25;
81
+ }
82
+
83
+ .ds-search__input::placeholder {
84
+ color: var(--ds-color-text-tertiary);
85
+ }
86
+
87
+ /* Keyboard shortcut hint */
88
+ .ds-search__shortcut {
89
+ flex-shrink: 0;
90
+ padding: 0.125rem 0.375rem;
91
+ font-size: var(--ds-text-2xs);
92
+ font-family: inherit;
93
+ line-height: 1;
94
+ color: var(--ds-color-text-tertiary);
95
+ background: var(--ds-color-surface-muted);
96
+ border: 1px solid var(--ds-color-border);
97
+ border-radius: var(--ds-radius-sm);
98
+ pointer-events: none;
99
+ user-select: none;
100
+ }
101
+
102
+ /* Clear button */
103
+ .ds-search__clear {
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ flex-shrink: 0;
108
+ width: 1.25rem;
109
+ height: 1.25rem;
110
+ min-width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
111
+ min-height: 1.5rem;
112
+ border: none;
113
+ border-radius: var(--ds-radius-sm);
114
+ background: transparent;
115
+ color: var(--ds-color-text-tertiary);
116
+ cursor: pointer;
117
+ transition:
118
+ color var(--ds-duration-fast) var(--ds-ease-default),
119
+ background-color var(--ds-duration-fast) var(--ds-ease-default);
120
+ }
121
+
122
+ .ds-search__clear:hover {
123
+ background: var(--ds-color-surface-hover);
124
+ color: var(--ds-color-text-secondary);
125
+ }
126
+
127
+ /* Dropdown results panel */
128
+ .ds-search__dropdown {
129
+ position: fixed;
130
+ max-height: min(22rem, calc(100dvh - 5rem));
131
+ overflow-y: auto;
132
+ background: var(--ds-color-surface-muted);
133
+ border: 1px solid var(--ds-color-border);
134
+ border-radius: var(--ds-radius-lg);
135
+ box-shadow: var(--ds-shadow-lg);
136
+ z-index: var(--ds-z-tooltip);
137
+ padding: var(--ds-space-1) 0;
138
+ }
139
+
140
+ /* Result groups */
141
+ .ds-search__group {
142
+ padding: var(--ds-space-1) 0;
143
+ }
144
+
145
+ .ds-search__group + .ds-search__group {
146
+ border-block-start: 1px solid var(--ds-color-border);
147
+ }
148
+
149
+ .ds-search__group-label {
150
+ display: block;
151
+ padding: var(--ds-space-1) var(--ds-space-3);
152
+ font-size: var(--ds-text-2xs);
153
+ font-weight: var(--ds-weight-medium);
154
+ color: var(--ds-color-text-tertiary);
155
+ text-transform: uppercase;
156
+ letter-spacing: 0.04em;
157
+ }
158
+
159
+ /* Individual result */
160
+ .ds-search__result {
161
+ display: flex;
162
+ align-items: center;
163
+ gap: var(--ds-space-2);
164
+ width: 100%;
165
+ padding: var(--ds-space-2-5) var(--ds-space-3);
166
+ border: none;
167
+ background: transparent;
168
+ color: var(--ds-color-text);
169
+ font-size: var(--ds-text-sm);
170
+ font-family: inherit;
171
+ text-align: start;
172
+ cursor: pointer;
173
+ transition: background-color var(--ds-duration-fast) var(--ds-ease-default);
174
+ }
175
+
176
+ .ds-search__result:hover {
177
+ background: var(--ds-color-surface-hover);
178
+ }
179
+
180
+ .ds-search__result--active,
181
+ .ds-search__result--active:hover {
182
+ background: var(--ds-color-surface-elevated);
183
+ }
184
+
185
+ .ds-search__result:focus-visible {
186
+ outline: none;
187
+ box-shadow: inset 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
188
+ scroll-margin-block: var(--ds-space-16, 4rem);
189
+ }
190
+
191
+ .ds-search__result-icon {
192
+ color: var(--ds-color-text-tertiary);
193
+ flex-shrink: 0;
194
+ }
195
+
196
+ .ds-search__result-db-icon {
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: center;
200
+ width: 1.5rem;
201
+ height: 1.5rem;
202
+ border-radius: var(--ds-radius-md);
203
+ flex-shrink: 0;
204
+ }
205
+
206
+ .ds-search__result-content {
207
+ display: flex;
208
+ flex-direction: column;
209
+ min-width: 0;
210
+ }
211
+
212
+ .ds-search__result-title {
213
+ white-space: nowrap;
214
+ overflow: hidden;
215
+ text-overflow: ellipsis;
216
+ min-width: 0;
217
+ line-height: var(--ds-leading-snug);
218
+ }
219
+
220
+ .ds-search__result-meta {
221
+ font-size: var(--ds-text-2xs);
222
+ line-height: var(--ds-leading-snug);
223
+ color: var(--ds-color-text-tertiary);
224
+ white-space: nowrap;
225
+ overflow: hidden;
226
+ text-overflow: ellipsis;
227
+ }
228
+
229
+ /* Empty state */
230
+ .ds-search__empty {
231
+ display: flex;
232
+ align-items: center;
233
+ justify-content: center;
234
+ padding: var(--ds-space-4) var(--ds-space-2);
235
+ color: var(--ds-color-text-tertiary);
236
+ font-size: var(--ds-text-sm);
237
+ }
238
+
239
+ /* Mobile trigger (hidden on desktop) */
240
+ .ds-search-mobile-trigger {
241
+ display: none;
242
+ align-items: center;
243
+ justify-content: center;
244
+ width: 2.25rem;
245
+ height: 2.25rem;
246
+ border: none;
247
+ border-radius: var(--ds-radius-md);
248
+ background: transparent;
249
+ color: var(--ds-color-text);
250
+ cursor: pointer;
251
+ flex-shrink: 0;
252
+ transition: background-color var(--ds-duration-fast) var(--ds-ease-default);
253
+ }
254
+
255
+ @media (hover: hover) {
256
+ .ds-search-mobile-trigger:hover {
257
+ background-color: var(--ds-color-surface-hover);
258
+ }
259
+ }
260
+
261
+ /* Close button (mobile expanded) */
262
+ .ds-search__close {
263
+ display: flex;
264
+ align-items: center;
265
+ justify-content: center;
266
+ width: 2.25rem;
267
+ height: 2.25rem;
268
+ border: none;
269
+ border-radius: var(--ds-radius-md);
270
+ background: transparent;
271
+ color: var(--ds-color-text);
272
+ cursor: pointer;
273
+ flex-shrink: 0;
274
+ }
275
+
276
+ /* Responsive: mobile */
277
+ @media (max-width: 1023px) /* below --ds-breakpoint-lg */ {
278
+ .ds-search__shortcut { display: none; }
279
+ .ds-search-mobile-trigger { display: flex; }
280
+ .ds-search { display: none; }
281
+
282
+ .ds-search--mobile-expanded {
283
+ display: flex;
284
+ position: fixed;
285
+ inset-block-start: 0;
286
+ inset-inline-start: 0;
287
+ inset-inline-end: 0;
288
+ height: var(--ds-search-bar-height, 3.5rem);
289
+ z-index: var(--ds-z-dropdown);
290
+ border: none;
291
+ border-block-end: 1px solid var(--ds-color-border);
292
+ border-radius: 0;
293
+ padding: 0 var(--ds-space-3);
294
+ background: var(--ds-color-nav-bg);
295
+ backdrop-filter: blur(var(--ds-blur-lg)) saturate(1.5);
296
+ -webkit-backdrop-filter: blur(var(--ds-blur-lg)) saturate(1.5);
297
+ }
298
+
299
+ .ds-search__dropdown--mobile {
300
+ inset-inline-start: 0 !important;
301
+ inset-inline-end: 0;
302
+ width: auto !important;
303
+ border-radius: 0;
304
+ border-inline-start: none;
305
+ border-inline-end: none;
306
+ max-height: calc(100dvh - var(--ds-search-bar-height, 3.5rem));
307
+ background: var(--ds-color-surface-muted);
308
+ backdrop-filter: blur(var(--ds-blur-lg)) saturate(1.5);
309
+ -webkit-backdrop-filter: blur(var(--ds-blur-lg)) saturate(1.5);
310
+ }
311
+ }
@@ -0,0 +1,94 @@
1
+ /* ==========================================================================
2
+ Component: Segmented Control
3
+ Toggle between 2-5 mutually exclusive options. Compact alternative to tabs.
4
+
5
+ ARIA requirements (consumer responsibility):
6
+ - Container: role="radiogroup", aria-label="[description]"
7
+ - Each segment: role="radio", aria-checked="true|false"
8
+ - Keyboard: ArrowLeft/Right to navigate, Space/Enter to select
9
+
10
+ Usage:
11
+ <div class="ds-segmented" role="radiogroup" aria-label="View">
12
+ <button class="ds-segmented__item ds-segmented__item--active" role="radio" aria-checked="true">Grid</button>
13
+ <button class="ds-segmented__item" role="radio" aria-checked="false">List</button>
14
+ <button class="ds-segmented__item" role="radio" aria-checked="false">Board</button>
15
+ </div>
16
+
17
+ Modifiers:
18
+ .ds-segmented--sm — Smaller size
19
+ .ds-segmented--full — Full width (items stretch equally)
20
+ ========================================================================== */
21
+
22
+ .ds-segmented {
23
+ display: inline-flex;
24
+ align-items: center;
25
+ gap: var(--ds-space-0-5);
26
+ padding: var(--ds-space-0-5);
27
+ background-color: var(--ds-color-surface-muted);
28
+ border-radius: var(--ds-radius-lg);
29
+ border: 1px solid var(--ds-color-border-subtle);
30
+
31
+ &__item {
32
+ display: inline-flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+ gap: var(--ds-space-1-5);
36
+ padding: var(--ds-space-1-5) var(--ds-space-3);
37
+ font-family: var(--ds-font-sans);
38
+ font-size: var(--ds-text-sm);
39
+ font-weight: var(--ds-weight-medium);
40
+ line-height: var(--ds-leading-none);
41
+ color: var(--ds-color-text-secondary);
42
+ background: transparent;
43
+ border: none;
44
+ border-radius: var(--ds-radius-md);
45
+ cursor: pointer;
46
+ white-space: nowrap;
47
+ transition: all var(--ds-duration-fast) var(--ds-ease-default);
48
+
49
+ &:hover {
50
+ color: var(--ds-color-text);
51
+ background-color: var(--ds-color-surface-hover);
52
+ }
53
+
54
+ &:focus-visible {
55
+ outline: none;
56
+ box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
57
+ scroll-margin-block: var(--ds-space-16, 4rem);
58
+ }
59
+
60
+ &--active,
61
+ &--active:hover {
62
+ background-color: var(--ds-color-surface-elevated);
63
+ color: var(--ds-color-text);
64
+ box-shadow: var(--ds-shadow-sm);
65
+ }
66
+
67
+ &:disabled,
68
+ &[aria-disabled="true"] {
69
+ opacity: var(--ds-opacity-disabled);
70
+ cursor: not-allowed;
71
+ pointer-events: none;
72
+ }
73
+ }
74
+
75
+ /* --- Small variant --- */
76
+ &--sm {
77
+ padding: 2px;
78
+
79
+ & .ds-segmented__item {
80
+ padding: var(--ds-space-1) var(--ds-space-2);
81
+ font-size: var(--ds-text-xs);
82
+ }
83
+ }
84
+
85
+ /* --- Full width --- */
86
+ &--full {
87
+ display: flex;
88
+ width: 100%;
89
+
90
+ & .ds-segmented__item {
91
+ flex: 1;
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,100 @@
1
+ /* ==========================================================================
2
+ Component: Skeleton
3
+ Loading placeholders with a subtle pulse animation.
4
+ ========================================================================== */
5
+
6
+ .ds-skeleton {
7
+ display: block;
8
+ background-color: var(--ds-color-surface-muted);
9
+ border-radius: var(--ds-radius-md);
10
+ animation: ds-skeleton-pulse var(--ds-duration-slower) var(--ds-ease-default) infinite;
11
+
12
+ /* --- Text variants --- */
13
+
14
+ &--text {
15
+ height: 1rem;
16
+ width: 100%;
17
+ border-radius: var(--ds-radius-sm);
18
+ margin-block-end: var(--ds-space-2);
19
+ }
20
+
21
+ &--text-sm {
22
+ height: 0.75rem;
23
+ width: 100%;
24
+ border-radius: var(--ds-radius-sm);
25
+ margin-block-end: var(--ds-space-2);
26
+ }
27
+
28
+ &--text-lg {
29
+ height: 1.5rem;
30
+ width: 100%;
31
+ border-radius: var(--ds-radius-sm);
32
+ margin-block-end: var(--ds-space-2);
33
+ }
34
+
35
+ &--heading {
36
+ height: 2rem;
37
+ width: 60%;
38
+ border-radius: var(--ds-radius-md);
39
+ margin-block-end: var(--ds-space-3);
40
+ }
41
+
42
+ /* --- Shape variants --- */
43
+
44
+ &--circle,
45
+ &--avatar {
46
+ width: 2.5rem;
47
+ height: 2.5rem;
48
+ border-radius: var(--ds-radius-full);
49
+ }
50
+
51
+ &--card {
52
+ height: 12rem;
53
+ width: 100%;
54
+ border-radius: var(--ds-radius-xl);
55
+ }
56
+
57
+ /* --- UI element variants --- */
58
+
59
+ &--btn {
60
+ height: 2.25rem;
61
+ width: 6rem;
62
+ border-radius: var(--ds-radius-lg);
63
+ }
64
+
65
+ &--input {
66
+ height: 2.5rem;
67
+ width: 100%;
68
+ border-radius: var(--ds-radius-lg);
69
+ }
70
+
71
+ /* --- Width modifiers --- */
72
+
73
+ &--w-1\/2 {
74
+ width: 50%;
75
+ }
76
+
77
+ &--w-3\/4 {
78
+ width: 75%;
79
+ }
80
+
81
+ &--w-1\/3 {
82
+ width: 33%;
83
+ }
84
+ }
85
+
86
+ /* --- Pulse animation --- */
87
+
88
+ @keyframes ds-skeleton-pulse {
89
+ 0%,
90
+ 100% {
91
+ opacity: 1;
92
+ }
93
+ 50% {
94
+ opacity: 0.4;
95
+ }
96
+ }
97
+
98
+ @media (prefers-reduced-motion: reduce) {
99
+ .ds-skeleton { animation: none; }
100
+ }
@@ -0,0 +1,133 @@
1
+ /* ==========================================================================
2
+ Slider
3
+ ==========================================================================
4
+ Range slider input component with customizable thumb and track styling.
5
+ Supports labels, value display, size variants, and disabled state.
6
+
7
+ ARIA requirements (consumer responsibility):
8
+ - Use native <input type="range"> (inherits slider role)
9
+ - Add aria-label or aria-labelledby for context
10
+ - Add aria-valuemin, aria-valuemax, aria-valuenow if using custom slider
11
+ - Add aria-valuetext for human-readable value (e.g., "50%")
12
+
13
+ Usage:
14
+ <div class="ds-slider">
15
+ <input type="range" min="0" max="100" value="50" aria-label="Volume" />
16
+ <div class="ds-slider__labels">
17
+ <span>0</span>
18
+ <span>100</span>
19
+ </div>
20
+ </div>
21
+
22
+ Modifiers:
23
+ .ds-slider--sm — Smaller track and thumb
24
+ .ds-slider--disabled — Reduced opacity, no interaction
25
+ ========================================================================== */
26
+
27
+ .ds-slider {
28
+ width: 100%;
29
+ position: relative;
30
+
31
+ & input[type="range"] {
32
+ -webkit-appearance: none;
33
+ -moz-appearance: none;
34
+ appearance: none;
35
+ width: 100%;
36
+ height: 0.375rem;
37
+ background-color: var(--ds-color-surface-muted);
38
+ border-radius: var(--ds-radius-full);
39
+ outline: none;
40
+ cursor: pointer;
41
+
42
+ /* Webkit thumb */
43
+ &::-webkit-slider-thumb {
44
+ -webkit-appearance: none;
45
+ appearance: none;
46
+ width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
47
+ height: 1.5rem;
48
+ border-radius: var(--ds-radius-full);
49
+ background-color: var(--ds-color-inverted);
50
+ border: 2px solid var(--ds-color-surface);
51
+ box-shadow: var(--ds-shadow-sm);
52
+ cursor: pointer;
53
+ transition: transform var(--ds-duration-fast) var(--ds-ease-default);
54
+ }
55
+
56
+ /* Firefox thumb */
57
+ &::-moz-range-thumb {
58
+ width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
59
+ height: 1.5rem;
60
+ border-radius: var(--ds-radius-full);
61
+ background-color: var(--ds-color-inverted);
62
+ border: 2px solid var(--ds-color-surface);
63
+ box-shadow: var(--ds-shadow-sm);
64
+ cursor: pointer;
65
+ transition: transform var(--ds-duration-fast) var(--ds-ease-default);
66
+ }
67
+
68
+ /* Hover: scale up thumb */
69
+ &:hover::-webkit-slider-thumb {
70
+ transform: scale(1.1);
71
+ }
72
+
73
+ &:hover::-moz-range-thumb {
74
+ transform: scale(1.1);
75
+ }
76
+
77
+ /* Focus ring */
78
+ &:focus-visible::-webkit-slider-thumb {
79
+ outline: var(--ds-ring-width) solid var(--ds-ring-color);
80
+ outline-offset: 0;
81
+ scroll-margin-block: var(--ds-space-16, 4rem);
82
+ }
83
+
84
+ &:focus-visible::-moz-range-thumb {
85
+ outline: var(--ds-ring-width) solid var(--ds-ring-color);
86
+ outline-offset: 0;
87
+ }
88
+ }
89
+
90
+ /* Labels row */
91
+ &__labels {
92
+ display: flex;
93
+ justify-content: space-between;
94
+ font-size: var(--ds-text-xs);
95
+ color: var(--ds-color-text-tertiary);
96
+ margin-block-start: var(--ds-space-1);
97
+ }
98
+
99
+ /* Current value display */
100
+ &__value {
101
+ font-size: var(--ds-text-sm);
102
+ font-weight: var(--ds-weight-medium);
103
+ color: var(--ds-color-text);
104
+ }
105
+
106
+ /* Size: small */
107
+ &--sm {
108
+ & input[type="range"] {
109
+ height: 0.25rem;
110
+
111
+ &::-webkit-slider-thumb {
112
+ width: 1rem;
113
+ height: 1rem;
114
+ }
115
+
116
+ &::-moz-range-thumb {
117
+ width: 1rem;
118
+ height: 1rem;
119
+ }
120
+ }
121
+ }
122
+
123
+ /* Disabled state */
124
+ &--disabled {
125
+ opacity: var(--ds-opacity-disabled);
126
+ cursor: not-allowed;
127
+
128
+ & input[type="range"] {
129
+ cursor: not-allowed;
130
+ pointer-events: none;
131
+ }
132
+ }
133
+ }
@@ -0,0 +1,70 @@
1
+ /* ==========================================================================
2
+ Component: Sortable
3
+ Optional drag-to-reorder styles for table rows or list items.
4
+ Provides grip handle, dragging states, and drop zone indicators.
5
+ Works with native HTML5 Drag API or any JS drag library.
6
+
7
+ ARIA requirements (consumer responsibility):
8
+ - Handle: aria-label="Reorder [item]", role="button"
9
+ - Draggable row: aria-grabbed="true|false" (when using HTML5 DnD)
10
+ - Drop target: aria-dropeffect="move"
11
+ - Keyboard: handle should support ArrowUp/Down + Enter to move items
12
+ ========================================================================== */
13
+
14
+ .ds-sortable {
15
+
16
+ /* --- Dragging state: applied to the row being dragged --- */
17
+ &--dragging {
18
+ opacity: 0.5;
19
+ background-color: var(--ds-color-surface);
20
+ }
21
+
22
+ /* --- Drop target: applied to the row being hovered over --- */
23
+ &--over {
24
+ box-shadow: inset 0 -2px 0 var(--ds-color-interactive);
25
+ }
26
+
27
+ /* --- Grip handle: visible on row hover, grab cursor --- */
28
+ &__handle {
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ min-width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
33
+ min-height: 1.5rem;
34
+ padding: var(--ds-space-1);
35
+ color: var(--ds-color-text-tertiary);
36
+ cursor: grab;
37
+ border-radius: var(--ds-radius-sm);
38
+ opacity: 0;
39
+ transition:
40
+ opacity var(--ds-duration-fast) var(--ds-ease-default),
41
+ color var(--ds-duration-fast) var(--ds-ease-default);
42
+
43
+ &:active {
44
+ cursor: grabbing;
45
+ }
46
+
47
+ &:focus-visible {
48
+ opacity: 1;
49
+ outline: none;
50
+ box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
51
+ border-radius: var(--ds-radius-sm);
52
+ scroll-margin-block: var(--ds-space-16, 4rem);
53
+ }
54
+
55
+ /* --- Always-visible handle variant --- */
56
+ &--visible {
57
+ opacity: 1;
58
+ }
59
+ }
60
+ }
61
+
62
+ /* Show handle on row hover or any hovered interactive parent */
63
+ tr:hover .ds-sortable__handle,
64
+ .ds-sortable-row:hover .ds-sortable__handle,
65
+ button:hover .ds-sortable__handle,
66
+ a:hover .ds-sortable__handle,
67
+ [role="button"]:hover .ds-sortable__handle {
68
+ opacity: 1;
69
+ color: var(--ds-color-text-secondary);
70
+ }