@digiko-npm/designsystem 0.4.0 → 0.7.1

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 (57) hide show
  1. package/README.md +7 -7
  2. package/dist/designsystem.css +2559 -777
  3. package/dist/designsystem.min.css +2 -2
  4. package/package.json +12 -1
  5. package/src/base/typography.css +8 -8
  6. package/src/components/accordion.css +21 -14
  7. package/src/components/alert.css +25 -19
  8. package/src/components/avatar.css +16 -16
  9. package/src/components/bottom-nav.css +125 -0
  10. package/src/components/breadcrumb.css +7 -0
  11. package/src/components/button.css +10 -2
  12. package/src/components/card.css +6 -42
  13. package/src/components/chip.css +79 -0
  14. package/src/components/collapsible.css +116 -0
  15. package/src/components/command.css +17 -7
  16. package/src/components/custom-select.css +123 -7
  17. package/src/components/datepicker.css +9 -9
  18. package/src/components/description-list.css +98 -0
  19. package/src/components/divider.css +1 -1
  20. package/src/components/drawer.css +28 -19
  21. package/src/components/drop-zone.css +10 -3
  22. package/src/components/dropdown.css +17 -8
  23. package/src/components/empty-state.css +3 -3
  24. package/src/components/field.css +77 -0
  25. package/src/components/icon-btn.css +102 -0
  26. package/src/components/index.css +11 -0
  27. package/src/components/input.css +26 -16
  28. package/src/components/kbd.css +1 -1
  29. package/src/components/modal.css +30 -3
  30. package/src/components/nav.css +20 -10
  31. package/src/components/pagination.css +9 -0
  32. package/src/components/popover.css +10 -10
  33. package/src/components/progress.css +2 -2
  34. package/src/components/result.css +84 -0
  35. package/src/components/search.css +294 -0
  36. package/src/components/skeleton.css +4 -4
  37. package/src/components/slider.css +13 -6
  38. package/src/components/sortable.css +9 -0
  39. package/src/components/spinner.css +60 -0
  40. package/src/components/stat-card.css +41 -0
  41. package/src/components/table.css +3 -3
  42. package/src/components/tabs.css +26 -18
  43. package/src/components/tag.css +11 -3
  44. package/src/components/timeline.css +14 -14
  45. package/src/components/toast.css +19 -7
  46. package/src/components/toggle.css +10 -2
  47. package/src/components/toolbar.css +189 -0
  48. package/src/components/tooltip.css +34 -28
  49. package/src/index.css +6 -4
  50. package/src/tokens/colors.css +18 -6
  51. package/src/tokens/spacing.css +7 -0
  52. package/src/utilities/a11y.css +102 -0
  53. package/src/utilities/index.css +2 -0
  54. package/src/utilities/interactive.css +116 -0
  55. package/src/utilities/layout.css +26 -26
  56. package/src/utilities/spacing.css +52 -52
  57. package/src/utilities/text.css +8 -8
@@ -1,4 +1,4 @@
1
- /* @ds/designsystem v0.4.0 */
1
+ /* @ds/designsystem v0.7.0 */
2
2
  /* ==========================================================================
3
3
  @digiko-npm/designsystem
4
4
 
@@ -14,6 +14,9 @@
14
14
  Your values always win. Nothing breaks.
15
15
  ========================================================================== */
16
16
 
17
+ @layer tokens, base, components, utilities;
18
+
19
+ @layer tokens {
17
20
  /* ==========================================================================
18
21
  Design Tokens — All tokens in one import
19
22
 
@@ -40,7 +43,13 @@
40
43
  --ds-color-surface-hover: #fafafa;
41
44
  --ds-color-surface-active: #f4f4f5;
42
45
 
43
- /* --- Text — zinc hierarchy --- */
46
+ /* --- Text — zinc hierarchy ---
47
+ Contrast ratios on --ds-color-bg (#fafafa):
48
+ --text: #09090b → ~19.3:1 ✅ WCAG AAA
49
+ --text-secondary: #52525b → ~7.6:1 ✅ WCAG AAA
50
+ --text-tertiary: #a1a1aa → ~2.5:1 ⚠ Decorative/non-essential only (does not meet 4.5:1)
51
+ --text-disabled: #d4d4d8 → ~1.5:1 ⚠ Disabled state (exempt per WCAG 1.4.3)
52
+ --- */
44
53
  --ds-color-text: #09090b;
45
54
  --ds-color-text-secondary: #52525b;
46
55
  --ds-color-text-tertiary: #a1a1aa;
@@ -87,18 +96,24 @@
87
96
  }
88
97
 
89
98
  /* Dark theme */
90
- [data-theme="dark"], .dark {
99
+ [data-theme="dark"] {
91
100
  /* --- Backgrounds — deep, refined zinc-900 palette --- */
92
101
  --ds-color-bg: #09090b;
93
102
  --ds-color-bg-subtle: #0f0f11;
94
- --ds-color-bg-muted: #27272a;
95
- --ds-color-bg-elevated: #18181b;
103
+ --ds-color-bg-muted: #18181b;
104
+ --ds-color-bg-elevated: #27272a;
96
105
 
97
106
  --ds-color-surface: #0f0f11;
98
107
  --ds-color-surface-hover: #18181b;
99
108
  --ds-color-surface-active: #27272a;
100
109
 
101
- /* --- Text --- */
110
+ /* --- Text ---
111
+ Contrast ratios on --ds-color-bg (#09090b):
112
+ --text: #fafafa → ~19.3:1 ✅ WCAG AAA
113
+ --text-secondary: #a1a1aa → ~7.6:1 ✅ WCAG AAA
114
+ --text-tertiary: #71717a → ~3.8:1 ⚠ Decorative/non-essential only (does not meet 4.5:1)
115
+ --text-disabled: #3f3f46 → ~2.0:1 ⚠ Disabled state (exempt per WCAG 1.4.3)
116
+ --- */
102
117
  --ds-color-text: #fafafa;
103
118
  --ds-color-text-secondary: #a1a1aa;
104
119
  --ds-color-text-tertiary: #71717a;
@@ -185,7 +200,7 @@
185
200
  }
186
201
 
187
202
  /* Dark overrides for status/accent (brighter for contrast) */
188
- [data-theme="dark"], .dark {
203
+ [data-theme="dark"] {
189
204
  --ds-color-success: #4ade80;
190
205
  --ds-color-success-subtle: rgba(74, 222, 128, 0.1);
191
206
  --ds-color-success-border: rgba(74, 222, 128, 0.2);
@@ -275,6 +290,13 @@
275
290
  Generous whitespace, responsive sections.
276
291
  ========================================================================== */
277
292
 
293
+ /* Breakpoints (reference only — CSS cannot use vars in @media)
294
+ --ds-breakpoint-sm: 640px
295
+ --ds-breakpoint-md: 768px
296
+ --ds-breakpoint-lg: 1024px
297
+ --ds-breakpoint-xl: 1280px
298
+ */
299
+
278
300
  :root {
279
301
  /* --- Spacing Scale --- */
280
302
  --ds-space-0: 0;
@@ -395,6 +417,8 @@
395
417
  }
396
418
 
397
419
 
420
+ }
421
+ @layer base {
398
422
  /* ==========================================================================
399
423
  Base: Reset
400
424
  Clean, modern, antialiased.
@@ -538,7 +562,7 @@ p {
538
562
  }
539
563
 
540
564
  .ds-prose p + p {
541
- margin-top: var(--ds-space-4);
565
+ margin-block-start: var(--ds-space-4);
542
566
  }
543
567
 
544
568
  small {
@@ -595,7 +619,7 @@ strong, b {
595
619
  @media (min-width: 768px) {
596
620
  .ds-hero-title { font-size: var(--ds-text-6xl); }
597
621
  }
598
- @media (min-width: 1024px) {
622
+ @media (min-width: 1024px) /* --ds-breakpoint-lg */ {
599
623
  .ds-hero-title { font-size: var(--ds-text-7xl); }
600
624
  }
601
625
 
@@ -624,8 +648,8 @@ pre code {
624
648
  }
625
649
 
626
650
  blockquote {
627
- padding-left: var(--ds-space-4);
628
- border-left: 2px solid var(--ds-color-border-hover);
651
+ padding-inline-start: var(--ds-space-4);
652
+ border-inline-start: 2px solid var(--ds-color-border-hover);
629
653
  color: var(--ds-color-text-secondary);
630
654
  }
631
655
 
@@ -648,9 +672,9 @@ hr {
648
672
  }
649
673
 
650
674
  .ds-prose ul, .ds-prose ol {
651
- padding-left: var(--ds-space-6);
652
- margin-top: var(--ds-space-2);
653
- margin-bottom: var(--ds-space-2);
675
+ padding-inline-start: var(--ds-space-6);
676
+ margin-block-start: var(--ds-space-2);
677
+ margin-block-end: var(--ds-space-2);
654
678
  }
655
679
  .ds-prose ul { list-style-type: disc; }
656
680
  .ds-prose ol { list-style-type: decimal; }
@@ -660,7 +684,7 @@ hr {
660
684
  color: var(--ds-color-text-secondary);
661
685
  }
662
686
  .ds-prose li + li {
663
- margin-top: var(--ds-space-1);
687
+ margin-block-start: var(--ds-space-1);
664
688
  }
665
689
 
666
690
  /* Tabular figures for numbers */
@@ -678,10 +702,19 @@ hr {
678
702
  }
679
703
 
680
704
 
705
+ }
706
+ @layer components {
681
707
  /* === Core === */
682
708
  /* ==========================================================================
683
709
  Component: Button
684
710
  Inverted primary, rounded-full CTAs, refined sizing.
711
+
712
+ ARIA requirements (consumer responsibility):
713
+ - Use <button> or <a role="button"> elements
714
+ - Disabled: add aria-disabled="true" (supported by DS styles)
715
+ - Loading: add aria-busy="true" when --loading is applied
716
+ - Icon-only: add aria-label="[action]" for screen readers
717
+ - Button groups: wrap in role="group" with aria-label
685
718
  ========================================================================== */
686
719
 
687
720
  .ds-btn {
@@ -713,6 +746,7 @@ hr {
713
746
  .ds-btn:focus-visible {
714
747
  outline: none;
715
748
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
749
+ scroll-margin-block: var(--ds-space-16, 4rem);
716
750
  }
717
751
 
718
752
  .ds-btn:disabled,
@@ -845,7 +879,7 @@ hr {
845
879
  .ds-btn-group .ds-btn { border-radius: 0; }
846
880
  .ds-btn-group .ds-btn:first-child { border-radius: var(--ds-radius-lg) 0 0 var(--ds-radius-lg); }
847
881
  .ds-btn-group .ds-btn:last-child { border-radius: 0 var(--ds-radius-lg) var(--ds-radius-lg) 0; }
848
- .ds-btn-group .ds-btn + .ds-btn { margin-left: -1px; }
882
+ .ds-btn-group .ds-btn + .ds-btn { margin-inline-start: -1px; }
849
883
 
850
884
  /* --- Loading spinner --- */
851
885
  .ds-btn--loading {
@@ -859,7 +893,7 @@ hr {
859
893
  width: 1em;
860
894
  height: 1em;
861
895
  border: 2px solid currentColor;
862
- border-right-color: transparent;
896
+ border-inline-end-color: transparent;
863
897
  border-radius: var(--ds-radius-full);
864
898
  animation: ds-btn-spin 0.6s linear infinite;
865
899
  color: var(--ds-color-on-inverted);
@@ -915,7 +949,7 @@ hr {
915
949
 
916
950
  .ds-card__header {
917
951
  padding: var(--ds-space-5);
918
- border-bottom: 1px solid var(--ds-color-border);
952
+ border-block-end: 1px solid var(--ds-color-border);
919
953
  }
920
954
 
921
955
  .ds-card__header h3,
@@ -930,7 +964,7 @@ hr {
930
964
  .ds-card__description {
931
965
  font-size: var(--ds-text-sm);
932
966
  color: var(--ds-color-text-tertiary);
933
- margin-top: var(--ds-space-1);
967
+ margin-block-start: var(--ds-space-1);
934
968
  }
935
969
 
936
970
  .ds-card__body {
@@ -939,7 +973,7 @@ hr {
939
973
 
940
974
  .ds-card__footer {
941
975
  padding: var(--ds-space-4) var(--ds-space-5);
942
- border-top: 1px solid var(--ds-color-border);
976
+ border-block-start: 1px solid var(--ds-color-border);
943
977
  display: flex;
944
978
  align-items: center;
945
979
  gap: var(--ds-space-2);
@@ -963,10 +997,16 @@ hr {
963
997
  }
964
998
 
965
999
  /* Flush — no internal dividers */
966
- .ds-card--flush .ds-card__header { border-bottom: none; }
967
- .ds-card--flush .ds-card__footer { border-top: none; }
1000
+ .ds-card--flush .ds-card__header { border-block-end: none; }
1001
+ .ds-card--flush .ds-card__footer { border-block-start: none; }
1002
+
1003
+ /* Stat card — extracted to stat-card.css */
1004
+
1005
+ /* ==========================================================================
1006
+ Component: Stat Card
1007
+ Compact metric display with label, value, detail, and optional icon.
1008
+ ========================================================================== */
968
1009
 
969
- /* Stat card (like StatCard component) */
970
1010
  .ds-stat-card {
971
1011
  background-color: var(--ds-color-surface);
972
1012
  border: 1px solid var(--ds-color-border);
@@ -984,13 +1024,13 @@ hr {
984
1024
  font-weight: var(--ds-font-display-weight);
985
1025
  font-size: var(--ds-text-2xl);
986
1026
  color: var(--ds-color-text);
987
- margin-top: var(--ds-space-1);
1027
+ margin-block-start: var(--ds-space-1);
988
1028
  }
989
1029
 
990
1030
  .ds-stat-card__detail {
991
1031
  font-size: var(--ds-text-xs);
992
1032
  color: var(--ds-color-text-tertiary);
993
- margin-top: var(--ds-space-0-5);
1033
+ margin-block-start: var(--ds-space-0-5);
994
1034
  }
995
1035
 
996
1036
  .ds-stat-card__icon {
@@ -1007,6 +1047,13 @@ hr {
1007
1047
  /* ==========================================================================
1008
1048
  Component: Form Inputs
1009
1049
  Surface bg, clean borders, focus ring.
1050
+
1051
+ ARIA requirements (consumer responsibility):
1052
+ - Input: associate with <label for="id"> or aria-label
1053
+ - Error state: add aria-invalid="true" + aria-describedby pointing to error msg
1054
+ - Required: add aria-required="true" or HTML required attribute
1055
+ - Input group: icon is decorative (pointer-events: none), no ARIA needed
1056
+ - Checkbox/radio: use native <input type="checkbox|radio"> inside <label>
1010
1057
  ========================================================================== */
1011
1058
 
1012
1059
  .ds-label {
@@ -1014,7 +1061,7 @@ hr {
1014
1061
  font-size: var(--ds-text-sm);
1015
1062
  font-weight: var(--ds-weight-medium);
1016
1063
  color: var(--ds-color-text-secondary);
1017
- margin-bottom: var(--ds-space-1-5);
1064
+ margin-block-end: var(--ds-space-1-5);
1018
1065
  }
1019
1066
 
1020
1067
  .ds-input,
@@ -1061,6 +1108,7 @@ hr {
1061
1108
  .ds-select:focus-visible {
1062
1109
  border-color: var(--ds-ring-color);
1063
1110
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
1111
+ scroll-margin-block: var(--ds-space-16, 4rem);
1064
1112
  }
1065
1113
 
1066
1114
  .ds-input::placeholder,
@@ -1120,7 +1168,7 @@ hr {
1120
1168
  }
1121
1169
 
1122
1170
  .ds-select--lg {
1123
- padding-right: var(--ds-space-8);
1171
+ padding-inline-end: var(--ds-space-8);
1124
1172
  }
1125
1173
 
1126
1174
  /* Textarea */
@@ -1137,7 +1185,7 @@ hr {
1137
1185
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23a1a1aa' d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
1138
1186
  background-repeat: no-repeat;
1139
1187
  background-position: right var(--ds-space-3) center;
1140
- padding-right: var(--ds-space-8);
1188
+ padding-inline-end: var(--ds-space-8);
1141
1189
  }
1142
1190
 
1143
1191
  /* --- Layout Modifiers ---
@@ -1176,35 +1224,35 @@ hr {
1176
1224
  /* --- Input Group: icon-right ---
1177
1225
  Move icon to the right side, flip input padding. */
1178
1226
  .ds-input-group--icon-right .ds-input-group__icon {
1179
- left: auto;
1180
- right: var(--ds-space-3);
1227
+ inset-inline-start: auto;
1228
+ inset-inline-end: var(--ds-space-3);
1181
1229
  }
1182
1230
  .ds-input-group--icon-right .ds-input {
1183
- padding-left: var(--ds-space-4);
1184
- padding-right: calc(var(--ds-space-3) + 1rem + var(--ds-space-2));
1231
+ padding-inline-start: var(--ds-space-4);
1232
+ padding-inline-end: calc(var(--ds-space-3) + 1rem + var(--ds-space-2));
1185
1233
  }
1186
1234
 
1187
1235
  /* Help text */
1188
1236
  .ds-help {
1189
1237
  font-size: var(--ds-text-xs);
1190
1238
  color: var(--ds-color-text-tertiary);
1191
- margin-top: var(--ds-space-1-5);
1239
+ margin-block-start: var(--ds-space-1-5);
1192
1240
  }
1193
1241
  .ds-help--error { color: var(--ds-color-error); }
1194
1242
 
1195
1243
  /* Form Group (legacy — prefer ds-form) */
1196
- .ds-form-group { margin-bottom: var(--ds-space-4); }
1244
+ .ds-form-group { margin-block-end: var(--ds-space-4); }
1197
1245
 
1198
1246
  /* --- Form Stack ---
1199
1247
  Vertical form layout: comfortable spacing between fields (24px),
1200
1248
  extra breathing room before the action area (32px).
1201
1249
  Use ds-form__actions on the button/submit wrapper. */
1202
1250
  .ds-form > * + * {
1203
- margin-top: var(--ds-space-6);
1251
+ margin-block-start: var(--ds-space-6);
1204
1252
  }
1205
1253
 
1206
1254
  .ds-form__actions {
1207
- margin-top: var(--ds-space-8);
1255
+ margin-block-start: var(--ds-space-8);
1208
1256
  }
1209
1257
 
1210
1258
  /* --- Input Group ---
@@ -1216,8 +1264,8 @@ hr {
1216
1264
 
1217
1265
  .ds-input-group__icon {
1218
1266
  position: absolute;
1219
- left: var(--ds-space-3);
1220
- top: 50%;
1267
+ inset-inline-start: var(--ds-space-3);
1268
+ inset-block-start: 50%;
1221
1269
  transform: translateY(-50%);
1222
1270
  display: flex;
1223
1271
  align-items: center;
@@ -1227,7 +1275,7 @@ hr {
1227
1275
  }
1228
1276
 
1229
1277
  .ds-input-group .ds-input {
1230
- padding-left: calc(var(--ds-space-3) + 1rem + var(--ds-space-2));
1278
+ padding-inline-start: calc(var(--ds-space-3) + 1rem + var(--ds-space-2));
1231
1279
  }
1232
1280
 
1233
1281
  /* Checkbox / Radio */
@@ -1243,8 +1291,88 @@ hr {
1243
1291
  .ds-checkbox input,
1244
1292
  .ds-radio input {
1245
1293
  accent-color: var(--ds-color-interactive);
1246
- width: 0.875rem;
1247
- height: 0.875rem;
1294
+ width: 1rem;
1295
+ height: 1rem;
1296
+ min-width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
1297
+ min-height: 1.5rem;
1298
+ }
1299
+
1300
+ /* ==========================================================================
1301
+ Component: Field
1302
+ Unified wrapper for form fields — label + input + hint + error.
1303
+ ========================================================================== */
1304
+
1305
+ /* ---------------------------------------------------------------------------
1306
+ Container
1307
+ --------------------------------------------------------------------------- */
1308
+
1309
+ .ds-field {
1310
+ display: flex;
1311
+ flex-direction: column;
1312
+ gap: var(--ds-space-1-5);
1313
+ }
1314
+
1315
+ /* ---------------------------------------------------------------------------
1316
+ Label
1317
+ --------------------------------------------------------------------------- */
1318
+
1319
+ .ds-field__label {
1320
+ font-family: var(--ds-font-sans);
1321
+ font-size: var(--ds-text-sm);
1322
+ font-weight: var(--ds-weight-medium);
1323
+ color: var(--ds-color-text);
1324
+ line-height: var(--ds-leading-normal);
1325
+ }
1326
+
1327
+ /* Required asterisk */
1328
+ .ds-field--required .ds-field__label::after {
1329
+ content: " *";
1330
+ color: var(--ds-color-error);
1331
+ }
1332
+
1333
+ /* ---------------------------------------------------------------------------
1334
+ Hint (help text below input)
1335
+ --------------------------------------------------------------------------- */
1336
+
1337
+ .ds-field__hint {
1338
+ font-size: var(--ds-text-xs);
1339
+ color: var(--ds-color-text-tertiary);
1340
+ line-height: var(--ds-leading-normal);
1341
+ }
1342
+
1343
+ /* ---------------------------------------------------------------------------
1344
+ Error message
1345
+ --------------------------------------------------------------------------- */
1346
+
1347
+ .ds-field__error {
1348
+ font-size: var(--ds-text-xs);
1349
+ color: var(--ds-color-error);
1350
+ line-height: var(--ds-leading-normal);
1351
+ }
1352
+
1353
+ /* ---------------------------------------------------------------------------
1354
+ Modifier: Horizontal layout (label left, input right)
1355
+ --------------------------------------------------------------------------- */
1356
+
1357
+ .ds-field--horizontal {
1358
+ flex-direction: row;
1359
+ align-items: flex-start;
1360
+ gap: var(--ds-space-4);
1361
+ }
1362
+
1363
+ .ds-field--horizontal .ds-field__label {
1364
+ flex-shrink: 0;
1365
+ min-width: 8rem;
1366
+ padding-block-start: var(--ds-space-2);
1367
+ }
1368
+
1369
+ /* ---------------------------------------------------------------------------
1370
+ Modifier: Disabled
1371
+ --------------------------------------------------------------------------- */
1372
+
1373
+ .ds-field--disabled {
1374
+ opacity: var(--ds-opacity-disabled);
1375
+ pointer-events: none;
1248
1376
  }
1249
1377
 
1250
1378
  /* ==========================================================================
@@ -1331,20 +1459,27 @@ hr {
1331
1459
  /* ==========================================================================
1332
1460
  Component: Navigation
1333
1461
  Fixed glass header, backdrop-blur, h-16. Clean links.
1462
+
1463
+ ARIA requirements (consumer responsibility):
1464
+ - Nav: <nav aria-label="Main navigation"> (or aria-label="Sidebar")
1465
+ - Active link: aria-current="page" on the current page link
1466
+ - Mobile menu: toggle aria-expanded on hamburger button
1467
+ - Mobile menu button: aria-label="Open menu" / "Close menu"
1468
+ - Skip nav: add ds-skip-link before nav to skip to main content
1334
1469
  ========================================================================== */
1335
1470
 
1336
1471
  .ds-nav {
1337
1472
  position: fixed;
1338
- top: 0;
1339
- left: 0;
1340
- right: 0;
1473
+ inset-block-start: 0;
1474
+ inset-inline-start: 0;
1475
+ inset-inline-end: 0;
1341
1476
  z-index: var(--ds-z-sticky);
1342
1477
  display: flex;
1343
1478
  align-items: center;
1344
1479
  justify-content: space-between;
1345
1480
  height: 4rem;
1346
1481
  background-color: var(--ds-color-nav-bg);
1347
- border-bottom: 1px solid var(--ds-color-nav-border);
1482
+ border-block-end: 1px solid var(--ds-color-nav-border);
1348
1483
  backdrop-filter: blur(var(--ds-blur-lg)) saturate(1.5);
1349
1484
  -webkit-backdrop-filter: blur(var(--ds-blur-lg)) saturate(1.5);
1350
1485
  }
@@ -1392,6 +1527,7 @@ hr {
1392
1527
  .ds-nav__link:focus-visible {
1393
1528
  outline: none;
1394
1529
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
1530
+ scroll-margin-block: var(--ds-space-16, 4rem);
1395
1531
  }
1396
1532
 
1397
1533
  .ds-nav__link--active {
@@ -1426,13 +1562,14 @@ hr {
1426
1562
  .ds-nav__icon-btn:focus-visible {
1427
1563
  outline: none;
1428
1564
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
1565
+ scroll-margin-block: var(--ds-space-16, 4rem);
1429
1566
  }
1430
1567
 
1431
1568
  /* --- Mobile nav overlay --- */
1432
1569
  .ds-nav__mobile {
1433
1570
  overflow: hidden;
1434
1571
  max-height: 0;
1435
- border-bottom: 0 solid var(--ds-color-nav-border);
1572
+ border-block-end: 0 solid var(--ds-color-nav-border);
1436
1573
  background-color: var(--ds-color-nav-bg);
1437
1574
  backdrop-filter: blur(var(--ds-blur-lg));
1438
1575
  transition: all var(--ds-duration-slow) var(--ds-ease-out-expo);
@@ -1440,7 +1577,7 @@ hr {
1440
1577
 
1441
1578
  .ds-nav__mobile--open {
1442
1579
  max-height: 16rem;
1443
- border-bottom-width: 1px;
1580
+ border-block-end-width: 1px;
1444
1581
  }
1445
1582
 
1446
1583
  .ds-nav__mobile-links {
@@ -1463,12 +1600,12 @@ hr {
1463
1600
  width: 16rem;
1464
1601
  padding: var(--ds-space-5);
1465
1602
  background-color: var(--ds-color-surface);
1466
- border-right: 1px solid var(--ds-color-border);
1603
+ border-inline-end: 1px solid var(--ds-color-border);
1467
1604
  height: 100%;
1468
1605
  }
1469
1606
 
1470
1607
  .ds-sidebar__section {
1471
- margin-bottom: var(--ds-space-6);
1608
+ margin-block-end: var(--ds-space-6);
1472
1609
  }
1473
1610
 
1474
1611
  .ds-sidebar__title {
@@ -1477,7 +1614,7 @@ hr {
1477
1614
  text-transform: uppercase;
1478
1615
  letter-spacing: var(--ds-tracking-wide);
1479
1616
  color: var(--ds-color-text-tertiary);
1480
- margin-bottom: var(--ds-space-3);
1617
+ margin-block-end: var(--ds-space-3);
1481
1618
  }
1482
1619
 
1483
1620
  .ds-sidebar__link {
@@ -1492,7 +1629,7 @@ hr {
1492
1629
  transition: all var(--ds-duration-normal) var(--ds-ease-default);
1493
1630
  }
1494
1631
  .ds-sidebar__link + .ds-sidebar__link {
1495
- margin-top: var(--ds-space-1);
1632
+ margin-block-start: var(--ds-space-1);
1496
1633
  }
1497
1634
 
1498
1635
  @media (hover: hover) {
@@ -1505,6 +1642,7 @@ hr {
1505
1642
  .ds-sidebar__link:focus-visible {
1506
1643
  outline: none;
1507
1644
  box-shadow: inset 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
1645
+ scroll-margin-block: var(--ds-space-16, 4rem);
1508
1646
  }
1509
1647
 
1510
1648
  .ds-sidebar__link--active {
@@ -1515,6 +1653,14 @@ hr {
1515
1653
  /* ==========================================================================
1516
1654
  Component: Modal
1517
1655
  Backdrop blur, shadow-2xl, rounded-xl. Smooth scale transition.
1656
+
1657
+ ARIA requirements (consumer responsibility):
1658
+ - Container: role="dialog", aria-modal="true", aria-labelledby="[title-id]"
1659
+ - Close button: aria-label="Close"
1660
+ - Focus: trap focus inside modal when open
1661
+ - Keyboard: Escape closes modal
1662
+ - On open: move focus to first focusable element or close button
1663
+ - On close: return focus to the element that triggered the modal
1518
1664
  ========================================================================== */
1519
1665
 
1520
1666
  .ds-modal {
@@ -1566,7 +1712,7 @@ hr {
1566
1712
  align-items: flex-start;
1567
1713
  justify-content: space-between;
1568
1714
  padding: var(--ds-space-4) var(--ds-space-5);
1569
- border-bottom: 1px solid var(--ds-color-border);
1715
+ border-block-end: 1px solid var(--ds-color-border);
1570
1716
  }
1571
1717
 
1572
1718
  .ds-modal__header h3 {
@@ -1579,7 +1725,7 @@ hr {
1579
1725
  .ds-modal__header p {
1580
1726
  font-size: var(--ds-text-sm);
1581
1727
  color: var(--ds-color-text-tertiary);
1582
- margin-top: var(--ds-space-1);
1728
+ margin-block-start: var(--ds-space-1);
1583
1729
  }
1584
1730
 
1585
1731
  .ds-modal__close {
@@ -1599,6 +1745,7 @@ hr {
1599
1745
  .ds-modal__close:focus-visible {
1600
1746
  outline: none;
1601
1747
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
1748
+ scroll-margin-block: var(--ds-space-16, 4rem);
1602
1749
  }
1603
1750
 
1604
1751
  .ds-modal__body {
@@ -1613,15 +1760,39 @@ hr {
1613
1760
  justify-content: flex-end;
1614
1761
  gap: var(--ds-space-2);
1615
1762
  padding: var(--ds-space-4) var(--ds-space-5);
1616
- border-top: 1px solid var(--ds-color-border);
1763
+ border-block-start: 1px solid var(--ds-color-border);
1617
1764
  }
1618
1765
 
1619
1766
  /* Size variants */
1620
1767
  .ds-modal--md .ds-modal__content { max-width: var(--ds-container-md); }
1621
1768
  .ds-modal--lg .ds-modal__content { max-width: var(--ds-container-lg); }
1622
1769
 
1770
+ /* Fullscreen on mobile */
1771
+ @media (max-width: 1023px) /* below --ds-breakpoint-lg */ {
1772
+ .ds-modal--fullscreen-mobile .ds-modal__content {
1773
+ max-width: none;
1774
+ max-height: none;
1775
+ width: 100%;
1776
+ height: 100dvh;
1777
+ margin: 0;
1778
+ border-radius: 0;
1779
+ border: none;
1780
+ box-shadow: none;
1781
+ }
1782
+
1783
+ .ds-modal--fullscreen-mobile {
1784
+ padding: 0;
1785
+ }
1786
+ }
1787
+
1623
1788
  /* ==========================================================================
1624
1789
  Component: Toast
1790
+
1791
+ ARIA requirements (consumer responsibility):
1792
+ - Container: role="status", aria-live="polite" (or role="alert" for errors)
1793
+ - Close button: aria-label="Dismiss notification"
1794
+ - Auto-dismiss: provide enough time (minimum 5s), or let user control
1795
+ - Toast region: wrap container in aria-label="Notifications"
1625
1796
  ========================================================================== */
1626
1797
 
1627
1798
  .ds-toast-container {
@@ -1634,10 +1805,10 @@ hr {
1634
1805
  pointer-events: none;
1635
1806
  }
1636
1807
 
1637
- .ds-toast-container--top-right { top: 0; right: 0; }
1638
- .ds-toast-container--bottom-right { bottom: 0; right: 0; }
1808
+ .ds-toast-container--top-right { inset-block-start: 0; inset-inline-end: 0; }
1809
+ .ds-toast-container--bottom-right { inset-block-end: 0; inset-inline-end: 0; }
1639
1810
  .ds-toast-container--bottom-center {
1640
- bottom: 0; left: 50%;
1811
+ inset-block-end: 0; inset-inline-start: 50%;
1641
1812
  transform: translateX(-50%);
1642
1813
  align-items: center;
1643
1814
  }
@@ -1657,10 +1828,10 @@ hr {
1657
1828
  animation: ds-toast-in var(--ds-duration-slow) var(--ds-ease-out-expo) forwards;
1658
1829
  }
1659
1830
 
1660
- .ds-toast--info { border-left: var(--ds-accent-border-width) solid var(--ds-color-info); }
1661
- .ds-toast--success { border-left: var(--ds-accent-border-width) solid var(--ds-color-success); }
1662
- .ds-toast--warning { border-left: var(--ds-accent-border-width) solid var(--ds-color-warning); }
1663
- .ds-toast--error { border-left: var(--ds-accent-border-width) solid var(--ds-color-error); }
1831
+ .ds-toast--info { border-inline-start: var(--ds-accent-border-width) solid var(--ds-color-info); }
1832
+ .ds-toast--success { border-inline-start: var(--ds-accent-border-width) solid var(--ds-color-success); }
1833
+ .ds-toast--warning { border-inline-start: var(--ds-accent-border-width) solid var(--ds-color-warning); }
1834
+ .ds-toast--error { border-inline-start: var(--ds-accent-border-width) solid var(--ds-color-error); }
1664
1835
 
1665
1836
  .ds-toast__message {
1666
1837
  flex: 1;
@@ -1677,9 +1848,15 @@ hr {
1677
1848
  .ds-toast__close:hover {
1678
1849
  color: var(--ds-color-text);
1679
1850
  }
1851
+ .ds-toast__close {
1852
+ min-width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
1853
+ min-height: 1.5rem;
1854
+ }
1855
+
1680
1856
  .ds-toast__close:focus-visible {
1681
1857
  outline: none;
1682
1858
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
1859
+ scroll-margin-block: var(--ds-space-16, 4rem);
1683
1860
  }
1684
1861
 
1685
1862
  @keyframes ds-toast-in {
@@ -1706,7 +1883,7 @@ hr {
1706
1883
 
1707
1884
  .ds-table {
1708
1885
  width: 100%;
1709
- text-align: left;
1886
+ text-align: start;
1710
1887
  font-size: var(--ds-text-sm);
1711
1888
  }
1712
1889
 
@@ -1717,13 +1894,13 @@ hr {
1717
1894
  color: var(--ds-color-text-tertiary);
1718
1895
  text-transform: uppercase;
1719
1896
  letter-spacing: var(--ds-tracking-wide);
1720
- border-bottom: 1px solid var(--ds-color-border);
1897
+ border-block-end: 1px solid var(--ds-color-border);
1721
1898
  }
1722
1899
 
1723
1900
  .ds-table td {
1724
1901
  padding: var(--ds-space-3) var(--ds-space-4);
1725
1902
  color: var(--ds-color-text);
1726
- border-bottom: 1px solid var(--ds-color-border-subtle);
1903
+ border-block-end: 1px solid var(--ds-color-border-subtle);
1727
1904
  }
1728
1905
 
1729
1906
  .ds-table tbody tr {
@@ -1754,20 +1931,27 @@ hr {
1754
1931
  /* ==========================================================================
1755
1932
  Component: Tabs
1756
1933
  Horizontal/vertical tab navigation with pill, small, and full-width variants.
1934
+
1935
+ ARIA requirements (consumer responsibility):
1936
+ - Tab list: role="tablist", aria-label="[description]"
1937
+ - Tab: role="tab", aria-selected="true|false", aria-controls="[panel-id]"
1938
+ - Tab panel: role="tabpanel", id matching aria-controls, aria-labelledby="[tab-id]"
1939
+ - Keyboard: ArrowLeft/Right (horizontal) or ArrowUp/Down (vertical) to navigate
1940
+ - Active tab: only the active tab should be in tab order (tabindex="0"), others tabindex="-1"
1757
1941
  ========================================================================== */
1758
1942
 
1759
1943
  .ds-tabs {
1760
1944
  display: flex;
1761
1945
  flex-direction: row;
1762
1946
  align-items: stretch;
1763
- border-bottom: 1px solid var(--ds-color-border);
1947
+ border-block-end: 1px solid var(--ds-color-border);
1764
1948
  gap: var(--ds-space-0);
1765
1949
  }
1766
1950
 
1767
1951
  /* --- Pill variant --- */
1768
1952
 
1769
1953
  .ds-tabs--pills {
1770
- border-bottom: none;
1954
+ border-block-end: none;
1771
1955
  gap: var(--ds-space-1);
1772
1956
  background-color: var(--ds-color-bg-elevated);
1773
1957
  border-radius: var(--ds-radius-lg);
@@ -1775,7 +1959,7 @@ hr {
1775
1959
  }
1776
1960
 
1777
1961
  .ds-tabs--pills .ds-tab {
1778
- border-bottom: none;
1962
+ border-block-end: none;
1779
1963
  border-radius: var(--ds-radius-md);
1780
1964
  padding: var(--ds-space-1-5) var(--ds-space-3);
1781
1965
  }
@@ -1788,28 +1972,28 @@ hr {
1788
1972
  background-color: var(--ds-color-surface);
1789
1973
  color: var(--ds-color-text);
1790
1974
  box-shadow: var(--ds-shadow-sm);
1791
- border-bottom: none;
1975
+ border-block-end: none;
1792
1976
  }
1793
1977
 
1794
1978
  /* --- Vertical variant --- */
1795
1979
 
1796
1980
  .ds-tabs--vertical {
1797
1981
  flex-direction: column;
1798
- border-bottom: none;
1799
- border-right: 1px solid var(--ds-color-border);
1982
+ border-block-end: none;
1983
+ border-inline-end: 1px solid var(--ds-color-border);
1800
1984
  gap: var(--ds-space-0-5);
1801
1985
  }
1802
1986
 
1803
1987
  .ds-tabs--vertical .ds-tab {
1804
- border-bottom: none;
1805
- border-right: 2px solid transparent;
1988
+ border-block-end: none;
1989
+ border-inline-end: 2px solid transparent;
1806
1990
  padding: var(--ds-space-2) var(--ds-space-4);
1807
- text-align: left;
1991
+ text-align: start;
1808
1992
  }
1809
1993
 
1810
1994
  .ds-tabs--vertical .ds-tab--active {
1811
- border-bottom: none;
1812
- border-right-color: var(--ds-color-interactive);
1995
+ border-block-end: none;
1996
+ border-inline-end-color: var(--ds-color-interactive);
1813
1997
  color: var(--ds-color-text);
1814
1998
  }
1815
1999
 
@@ -1844,14 +2028,14 @@ hr {
1844
2028
  line-height: var(--ds-leading-snug);
1845
2029
  color: var(--ds-color-text-secondary);
1846
2030
  cursor: pointer;
1847
- border-bottom: 2px solid transparent;
2031
+ border-block-end: 2px solid transparent;
1848
2032
  background: none;
1849
- border-top: none;
1850
- border-left: none;
1851
- border-right: none;
2033
+ border-block-start: none;
2034
+ border-inline-start: none;
2035
+ border-inline-end: none;
1852
2036
  white-space: nowrap;
1853
2037
  transition: all var(--ds-duration-fast) var(--ds-ease-default);
1854
- margin-bottom: -1px;
2038
+ margin-block-end: -1px;
1855
2039
  }
1856
2040
 
1857
2041
  .ds-tab:hover {
@@ -1862,11 +2046,12 @@ hr {
1862
2046
  outline: none;
1863
2047
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
1864
2048
  border-radius: var(--ds-radius-sm);
2049
+ scroll-margin-block: var(--ds-space-16, 4rem);
1865
2050
  }
1866
2051
 
1867
2052
  .ds-tab--active {
1868
2053
  color: var(--ds-color-text);
1869
- border-bottom-color: var(--ds-color-interactive);
2054
+ border-block-end-color: var(--ds-color-interactive);
1870
2055
  }
1871
2056
 
1872
2057
  .ds-tab:disabled,
@@ -1912,12 +2097,17 @@ hr {
1912
2097
  /* --- Tab Panel --- */
1913
2098
 
1914
2099
  .ds-tab-panel {
1915
- padding-top: var(--ds-space-5);
2100
+ padding-block-start: var(--ds-space-5);
1916
2101
  }
1917
2102
 
1918
2103
  /* ==========================================================================
1919
2104
  Component: Alert
1920
2105
  Contextual feedback banners with semantic variants and dismissibility.
2106
+
2107
+ ARIA requirements (consumer responsibility):
2108
+ - Container: role="alert" (for important messages) or role="status"
2109
+ - Dismissible: close button needs aria-label="Dismiss"
2110
+ - For non-urgent info, use role="status" instead of role="alert"
1921
2111
  ========================================================================== */
1922
2112
 
1923
2113
  .ds-alert {
@@ -1929,7 +2119,7 @@ hr {
1929
2119
  border: 1px solid var(--ds-color-border);
1930
2120
  border-radius: var(--ds-radius-lg);
1931
2121
  background-color: var(--ds-color-surface);
1932
- border-left: var(--ds-accent-border-width) solid var(--ds-color-border);
2122
+ border-inline-start: var(--ds-accent-border-width) solid var(--ds-color-border);
1933
2123
  }
1934
2124
 
1935
2125
  /* --- Semantic Variants --- */
@@ -1937,25 +2127,25 @@ hr {
1937
2127
  .ds-alert--info {
1938
2128
  background-color: var(--ds-color-info-subtle);
1939
2129
  border-color: var(--ds-color-info-border);
1940
- border-left-color: var(--ds-color-info);
2130
+ border-inline-start-color: var(--ds-color-info);
1941
2131
  }
1942
2132
 
1943
2133
  .ds-alert--success {
1944
2134
  background-color: var(--ds-color-success-subtle);
1945
2135
  border-color: var(--ds-color-success-border);
1946
- border-left-color: var(--ds-color-success);
2136
+ border-inline-start-color: var(--ds-color-success);
1947
2137
  }
1948
2138
 
1949
2139
  .ds-alert--warning {
1950
2140
  background-color: var(--ds-color-warning-subtle);
1951
2141
  border-color: var(--ds-color-warning-border);
1952
- border-left-color: var(--ds-color-warning);
2142
+ border-inline-start-color: var(--ds-color-warning);
1953
2143
  }
1954
2144
 
1955
2145
  .ds-alert--error {
1956
2146
  background-color: var(--ds-color-error-subtle);
1957
2147
  border-color: var(--ds-color-error-border);
1958
- border-left-color: var(--ds-color-error);
2148
+ border-inline-start-color: var(--ds-color-error);
1959
2149
  }
1960
2150
 
1961
2151
  /* --- Icon --- */
@@ -1994,11 +2184,11 @@ hr {
1994
2184
  font-size: var(--ds-text-sm);
1995
2185
  line-height: var(--ds-leading-normal);
1996
2186
  color: var(--ds-color-text-secondary);
1997
- margin-top: var(--ds-space-1);
2187
+ margin-block-start: var(--ds-space-1);
1998
2188
  }
1999
2189
 
2000
2190
  .ds-alert__title + .ds-alert__description {
2001
- margin-top: var(--ds-space-1);
2191
+ margin-block-start: var(--ds-space-1);
2002
2192
  }
2003
2193
 
2004
2194
  /* --- Close Button --- */
@@ -2026,6 +2216,7 @@ hr {
2026
2216
  .ds-alert__close:focus-visible {
2027
2217
  outline: none;
2028
2218
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
2219
+ scroll-margin-block: var(--ds-space-16, 4rem);
2029
2220
  }
2030
2221
 
2031
2222
  /* --- Compact Variant --- */
@@ -2039,31 +2230,31 @@ hr {
2039
2230
 
2040
2231
  .ds-alert--banner {
2041
2232
  border-radius: var(--ds-radius-none);
2042
- border-left: none;
2043
- border-right: none;
2044
- border-top: 1px solid var(--ds-color-border);
2045
- border-bottom: 1px solid var(--ds-color-border);
2233
+ border-inline-start: none;
2234
+ border-inline-end: none;
2235
+ border-block-start: 1px solid var(--ds-color-border);
2236
+ border-block-end: 1px solid var(--ds-color-border);
2046
2237
  width: 100%;
2047
2238
  }
2048
2239
 
2049
2240
  .ds-alert--banner.ds-alert--info {
2050
- border-top-color: var(--ds-color-info-border);
2051
- border-bottom-color: var(--ds-color-info-border);
2241
+ border-block-start-color: var(--ds-color-info-border);
2242
+ border-block-end-color: var(--ds-color-info-border);
2052
2243
  }
2053
2244
 
2054
2245
  .ds-alert--banner.ds-alert--success {
2055
- border-top-color: var(--ds-color-success-border);
2056
- border-bottom-color: var(--ds-color-success-border);
2246
+ border-block-start-color: var(--ds-color-success-border);
2247
+ border-block-end-color: var(--ds-color-success-border);
2057
2248
  }
2058
2249
 
2059
2250
  .ds-alert--banner.ds-alert--warning {
2060
- border-top-color: var(--ds-color-warning-border);
2061
- border-bottom-color: var(--ds-color-warning-border);
2251
+ border-block-start-color: var(--ds-color-warning-border);
2252
+ border-block-end-color: var(--ds-color-warning-border);
2062
2253
  }
2063
2254
 
2064
2255
  .ds-alert--banner.ds-alert--error {
2065
- border-top-color: var(--ds-color-error-border);
2066
- border-bottom-color: var(--ds-color-error-border);
2256
+ border-block-start-color: var(--ds-color-error-border);
2257
+ border-block-end-color: var(--ds-color-error-border);
2067
2258
  }
2068
2259
 
2069
2260
  /* ==========================================================================
@@ -2082,7 +2273,7 @@ hr {
2082
2273
 
2083
2274
  .ds-divider--vertical {
2084
2275
  border-top: none;
2085
- border-left: 1px solid var(--ds-color-border);
2276
+ border-inline-start: 1px solid var(--ds-color-border);
2086
2277
  display: inline-block;
2087
2278
  width: auto;
2088
2279
  height: auto;
@@ -2136,6 +2327,14 @@ hr {
2136
2327
  /* ==========================================================================
2137
2328
  Component: Dropdown
2138
2329
  Floating menu with surface bg, scale transition, keyboard-friendly items.
2330
+
2331
+ ARIA requirements (consumer responsibility):
2332
+ - Trigger: aria-haspopup="true", aria-expanded="true|false"
2333
+ - Menu: role="menu"
2334
+ - Items: role="menuitem"
2335
+ - Keyboard: ArrowUp/Down to navigate, Enter to select, Escape to close
2336
+ - On open: move focus to first menu item
2337
+ - On close: return focus to trigger
2139
2338
  ========================================================================== */
2140
2339
 
2141
2340
  .ds-dropdown {
@@ -2152,8 +2351,8 @@ hr {
2152
2351
 
2153
2352
  .ds-dropdown__menu {
2154
2353
  position: absolute;
2155
- top: calc(100% + var(--ds-space-1));
2156
- left: 0;
2354
+ inset-block-start: calc(100% + var(--ds-space-1));
2355
+ inset-inline-start: 0;
2157
2356
  z-index: var(--ds-z-dropdown);
2158
2357
  min-width: 12rem;
2159
2358
  padding-block: var(--ds-space-1-5);
@@ -2183,14 +2382,14 @@ hr {
2183
2382
  /* --- Alignment variants --- */
2184
2383
 
2185
2384
  .ds-dropdown__menu--right {
2186
- left: auto;
2187
- right: 0;
2385
+ inset-inline-start: auto;
2386
+ inset-inline-end: 0;
2188
2387
  transform-origin: top right;
2189
2388
  }
2190
2389
 
2191
2390
  .ds-dropdown__menu--up {
2192
- top: auto;
2193
- bottom: calc(100% + var(--ds-space-1));
2391
+ inset-block-start: auto;
2392
+ inset-block-end: calc(100% + var(--ds-space-1));
2194
2393
  transform-origin: bottom left;
2195
2394
  }
2196
2395
 
@@ -2228,6 +2427,7 @@ hr {
2228
2427
  .ds-dropdown__item:focus-visible {
2229
2428
  outline: none;
2230
2429
  box-shadow: inset 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
2430
+ scroll-margin-block: var(--ds-space-16, 4rem);
2231
2431
  }
2232
2432
 
2233
2433
  .ds-dropdown__item--active {
@@ -2257,7 +2457,7 @@ hr {
2257
2457
  }
2258
2458
 
2259
2459
  .ds-dropdown__item-shortcut {
2260
- margin-left: auto;
2460
+ margin-inline-start: auto;
2261
2461
  font-size: var(--ds-text-xs);
2262
2462
  color: var(--ds-color-text-tertiary);
2263
2463
  }
@@ -2267,7 +2467,7 @@ hr {
2267
2467
  .ds-dropdown__divider {
2268
2468
  margin-block: var(--ds-space-1);
2269
2469
  border: 0;
2270
- border-top: 1px solid var(--ds-color-border);
2470
+ border-block-start: 1px solid var(--ds-color-border);
2271
2471
  }
2272
2472
 
2273
2473
  /* --- Section header --- */
@@ -2286,6 +2486,12 @@ hr {
2286
2486
  Component: Tooltip
2287
2487
  Inverted bubble with arrow, positioned via modifier classes.
2288
2488
  Default placement: top.
2489
+
2490
+ ARIA requirements (consumer responsibility):
2491
+ - Tooltip content: role="tooltip", id="[tooltip-id]"
2492
+ - Trigger element: aria-describedby="[tooltip-id]"
2493
+ - Keyboard: tooltip should show on focus as well as hover (CSS handles this via :hover)
2494
+ - For focus support: also add :focus-within show logic if trigger is focusable
2289
2495
  ========================================================================== */
2290
2496
 
2291
2497
  .ds-tooltip {
@@ -2346,8 +2552,8 @@ hr {
2346
2552
 
2347
2553
  .ds-tooltip .ds-tooltip__content,
2348
2554
  .ds-tooltip--top .ds-tooltip__content {
2349
- bottom: calc(100% + var(--ds-offset-md));
2350
- left: 50%;
2555
+ inset-block-end: calc(100% + var(--ds-offset-md));
2556
+ inset-inline-start: 50%;
2351
2557
  transform: translateX(-50%) translateY(4px);
2352
2558
  }
2353
2559
 
@@ -2359,8 +2565,8 @@ hr {
2359
2565
  /* Arrow — points down */
2360
2566
  .ds-tooltip .ds-tooltip__content::after,
2361
2567
  .ds-tooltip--top .ds-tooltip__content::after {
2362
- top: 100%;
2363
- left: 50%;
2568
+ inset-block-start: 100%;
2569
+ inset-inline-start: 50%;
2364
2570
  transform: translateX(-50%);
2365
2571
  border-top-color: var(--ds-color-inverted);
2366
2572
  }
@@ -2370,9 +2576,9 @@ hr {
2370
2576
  ============================================= */
2371
2577
 
2372
2578
  .ds-tooltip--bottom .ds-tooltip__content {
2373
- top: calc(100% + var(--ds-offset-md));
2374
- bottom: auto;
2375
- left: 50%;
2579
+ inset-block-start: calc(100% + var(--ds-offset-md));
2580
+ inset-block-end: auto;
2581
+ inset-inline-start: 50%;
2376
2582
  transform: translateX(-50%) translateY(-4px);
2377
2583
  }
2378
2584
 
@@ -2382,9 +2588,9 @@ hr {
2382
2588
 
2383
2589
  /* Arrow — points up */
2384
2590
  .ds-tooltip--bottom .ds-tooltip__content::after {
2385
- bottom: 100%;
2386
- top: auto;
2387
- left: 50%;
2591
+ inset-block-end: 100%;
2592
+ inset-block-start: auto;
2593
+ inset-inline-start: 50%;
2388
2594
  transform: translateX(-50%);
2389
2595
  border-top-color: transparent;
2390
2596
  border-bottom-color: var(--ds-color-inverted);
@@ -2395,10 +2601,10 @@ hr {
2395
2601
  ============================================= */
2396
2602
 
2397
2603
  .ds-tooltip--left .ds-tooltip__content {
2398
- right: calc(100% + var(--ds-offset-md));
2399
- left: auto;
2400
- bottom: auto;
2401
- top: 50%;
2604
+ inset-inline-end: calc(100% + var(--ds-offset-md));
2605
+ inset-inline-start: auto;
2606
+ inset-block-end: auto;
2607
+ inset-block-start: 50%;
2402
2608
  transform: translateY(-50%) translateX(4px);
2403
2609
  }
2404
2610
 
@@ -2408,13 +2614,13 @@ hr {
2408
2614
 
2409
2615
  /* Arrow — points right */
2410
2616
  .ds-tooltip--left .ds-tooltip__content::after {
2411
- left: 100%;
2412
- top: 50%;
2413
- bottom: auto;
2414
- right: auto;
2617
+ inset-inline-start: 100%;
2618
+ inset-block-start: 50%;
2619
+ inset-block-end: auto;
2620
+ inset-inline-end: auto;
2415
2621
  transform: translateY(-50%);
2416
2622
  border-top-color: transparent;
2417
- border-left-color: var(--ds-color-inverted);
2623
+ border-inline-start-color: var(--ds-color-inverted);
2418
2624
  }
2419
2625
 
2420
2626
  /* =============================================
@@ -2422,10 +2628,10 @@ hr {
2422
2628
  ============================================= */
2423
2629
 
2424
2630
  .ds-tooltip--right .ds-tooltip__content {
2425
- left: calc(100% + var(--ds-offset-md));
2426
- right: auto;
2427
- bottom: auto;
2428
- top: 50%;
2631
+ inset-inline-start: calc(100% + var(--ds-offset-md));
2632
+ inset-inline-end: auto;
2633
+ inset-block-end: auto;
2634
+ inset-block-start: 50%;
2429
2635
  transform: translateY(-50%) translateX(-4px);
2430
2636
  }
2431
2637
 
@@ -2435,13 +2641,13 @@ hr {
2435
2641
 
2436
2642
  /* Arrow — points left */
2437
2643
  .ds-tooltip--right .ds-tooltip__content::after {
2438
- right: 100%;
2439
- left: auto;
2440
- top: 50%;
2441
- bottom: auto;
2644
+ inset-inline-end: 100%;
2645
+ inset-inline-start: auto;
2646
+ inset-block-start: 50%;
2647
+ inset-block-end: auto;
2442
2648
  transform: translateY(-50%);
2443
2649
  border-top-color: transparent;
2444
- border-right-color: var(--ds-color-inverted);
2650
+ border-inline-end-color: var(--ds-color-inverted);
2445
2651
  }
2446
2652
 
2447
2653
  /* ==========================================================================
@@ -2453,8 +2659,8 @@ hr {
2453
2659
  display: inline-flex;
2454
2660
  align-items: center;
2455
2661
  justify-content: center;
2456
- width: 2.5rem;
2457
- height: 2.5rem;
2662
+ width: var(--ds-size-3);
2663
+ height: var(--ds-size-3);
2458
2664
  border-radius: var(--ds-radius-full);
2459
2665
  background-color: var(--ds-color-bg-elevated);
2460
2666
  color: var(--ds-color-text-secondary);
@@ -2476,32 +2682,32 @@ hr {
2476
2682
  /* --- Sizes --- */
2477
2683
 
2478
2684
  .ds-avatar--xs {
2479
- width: 1.5rem;
2480
- height: 1.5rem;
2685
+ width: var(--ds-size-1);
2686
+ height: var(--ds-size-1);
2481
2687
  font-size: var(--ds-text-xs);
2482
2688
  }
2483
2689
 
2484
2690
  .ds-avatar--sm {
2485
- width: 2rem;
2486
- height: 2rem;
2691
+ width: var(--ds-size-2);
2692
+ height: var(--ds-size-2);
2487
2693
  font-size: var(--ds-text-xs);
2488
2694
  }
2489
2695
 
2490
2696
  .ds-avatar--md {
2491
- width: 2.5rem;
2492
- height: 2.5rem;
2697
+ width: var(--ds-size-3);
2698
+ height: var(--ds-size-3);
2493
2699
  font-size: var(--ds-text-sm);
2494
2700
  }
2495
2701
 
2496
2702
  .ds-avatar--lg {
2497
- width: 3rem;
2498
- height: 3rem;
2703
+ width: var(--ds-size-4);
2704
+ height: var(--ds-size-4);
2499
2705
  font-size: var(--ds-text-base);
2500
2706
  }
2501
2707
 
2502
2708
  .ds-avatar--xl {
2503
- width: 4rem;
2504
- height: 4rem;
2709
+ width: var(--ds-space-16);
2710
+ height: var(--ds-space-16);
2505
2711
  font-size: var(--ds-text-lg);
2506
2712
  }
2507
2713
 
@@ -2523,19 +2729,19 @@ hr {
2523
2729
  }
2524
2730
 
2525
2731
  .ds-avatar-group > .ds-avatar + .ds-avatar {
2526
- margin-left: -0.5rem;
2732
+ margin-inline-start: -0.5rem;
2527
2733
  }
2528
2734
 
2529
2735
  .ds-avatar-group--sm > .ds-avatar + .ds-avatar {
2530
- margin-left: -0.375rem;
2736
+ margin-inline-start: -0.375rem;
2531
2737
  }
2532
2738
 
2533
2739
  /* --- Status indicator --- */
2534
2740
 
2535
2741
  .ds-avatar__status {
2536
2742
  position: absolute;
2537
- bottom: 0;
2538
- right: 0;
2743
+ inset-block-end: 0;
2744
+ inset-inline-end: 0;
2539
2745
  width: 0.625rem;
2540
2746
  height: 0.625rem;
2541
2747
  border-radius: var(--ds-radius-full);
@@ -2572,28 +2778,28 @@ hr {
2572
2778
  height: 1rem;
2573
2779
  width: 100%;
2574
2780
  border-radius: var(--ds-radius-sm);
2575
- margin-bottom: var(--ds-space-2);
2781
+ margin-block-end: var(--ds-space-2);
2576
2782
  }
2577
2783
 
2578
2784
  .ds-skeleton--text-sm {
2579
2785
  height: 0.75rem;
2580
2786
  width: 100%;
2581
2787
  border-radius: var(--ds-radius-sm);
2582
- margin-bottom: var(--ds-space-2);
2788
+ margin-block-end: var(--ds-space-2);
2583
2789
  }
2584
2790
 
2585
2791
  .ds-skeleton--text-lg {
2586
2792
  height: 1.5rem;
2587
2793
  width: 100%;
2588
2794
  border-radius: var(--ds-radius-sm);
2589
- margin-bottom: var(--ds-space-2);
2795
+ margin-block-end: var(--ds-space-2);
2590
2796
  }
2591
2797
 
2592
2798
  .ds-skeleton--heading {
2593
2799
  height: 2rem;
2594
2800
  width: 60%;
2595
2801
  border-radius: var(--ds-radius-md);
2596
- margin-bottom: var(--ds-space-3);
2802
+ margin-block-end: var(--ds-space-3);
2597
2803
  }
2598
2804
 
2599
2805
  /* --- Shape variants --- */
@@ -2674,7 +2880,7 @@ hr {
2674
2880
  width: 3rem;
2675
2881
  height: 3rem;
2676
2882
  color: var(--ds-color-text-tertiary);
2677
- margin-bottom: var(--ds-space-2);
2883
+ margin-block-end: var(--ds-space-2);
2678
2884
  }
2679
2885
 
2680
2886
  .ds-empty-state__title {
@@ -2699,7 +2905,7 @@ hr {
2699
2905
  align-items: center;
2700
2906
  justify-content: center;
2701
2907
  gap: var(--ds-space-3);
2702
- margin-top: var(--ds-space-2);
2908
+ margin-block-start: var(--ds-space-2);
2703
2909
  }
2704
2910
 
2705
2911
  /* --- Compact variant --- */
@@ -2728,7 +2934,7 @@ hr {
2728
2934
  /* --- Left-aligned variant --- */
2729
2935
  .ds-empty-state--left {
2730
2936
  align-items: flex-start;
2731
- text-align: left;
2937
+ text-align: start;
2732
2938
  }
2733
2939
 
2734
2940
 
@@ -2801,8 +3007,8 @@ hr {
2801
3007
  .ds-datepicker__panel {
2802
3008
  position: absolute;
2803
3009
  z-index: var(--ds-z-dropdown);
2804
- top: calc(100% + var(--ds-offset-sm));
2805
- left: 0;
3010
+ inset-block-start: calc(100% + var(--ds-offset-sm));
3011
+ inset-inline-start: 0;
2806
3012
  background-color: var(--ds-color-surface);
2807
3013
  border: 1px solid var(--ds-color-border);
2808
3014
  border-radius: var(--ds-radius-xl);
@@ -2830,7 +3036,7 @@ hr {
2830
3036
  display: flex;
2831
3037
  align-items: center;
2832
3038
  justify-content: space-between;
2833
- margin-bottom: var(--ds-space-2);
3039
+ margin-block-end: var(--ds-space-2);
2834
3040
  }
2835
3041
 
2836
3042
  .ds-datepicker__title {
@@ -2868,7 +3074,7 @@ hr {
2868
3074
  .ds-datepicker__weekdays {
2869
3075
  display: grid;
2870
3076
  grid-template-columns: repeat(7, 1fr);
2871
- margin-bottom: var(--ds-space-1);
3077
+ margin-block-end: var(--ds-space-1);
2872
3078
  }
2873
3079
 
2874
3080
  .ds-datepicker__weekday {
@@ -2941,9 +3147,9 @@ hr {
2941
3147
  .ds-datepicker__footer {
2942
3148
  display: flex;
2943
3149
  justify-content: center;
2944
- margin-top: var(--ds-space-2);
2945
- padding-top: var(--ds-space-2);
2946
- border-top: 1px solid var(--ds-color-border);
3150
+ margin-block-start: var(--ds-space-2);
3151
+ padding-block-start: var(--ds-space-2);
3152
+ border-block-start: 1px solid var(--ds-color-border);
2947
3153
  }
2948
3154
 
2949
3155
  .ds-datepicker__today {
@@ -3025,8 +3231,8 @@ hr {
3025
3231
  }
3026
3232
 
3027
3233
  .ds-datepicker--compact .ds-datepicker__footer {
3028
- margin-top: var(--ds-space-1);
3029
- padding-top: var(--ds-space-1);
3234
+ margin-block-start: var(--ds-space-1);
3235
+ padding-block-start: var(--ds-space-1);
3030
3236
  }
3031
3237
 
3032
3238
  /* ==========================================================================
@@ -3035,6 +3241,12 @@ hr {
3035
3241
  A toggle (switch) control for binary on/off states.
3036
3242
  Supports aria-checked attribute and modifier-class driven states.
3037
3243
 
3244
+ ARIA requirements (consumer responsibility):
3245
+ - Element: <button role="switch" aria-checked="false|true">
3246
+ - Label: pair with visible label via aria-labelledby or wrap in <label>
3247
+ - Keyboard: Space toggles state (native <button> handles this)
3248
+ - State: update aria-checked on toggle
3249
+
3038
3250
  Usage:
3039
3251
  <button class="ds-toggle" role="switch" aria-checked="false"></button>
3040
3252
 
@@ -3071,8 +3283,8 @@ hr {
3071
3283
  .ds-toggle::after {
3072
3284
  content: "";
3073
3285
  position: absolute;
3074
- left: 2px;
3075
- top: 50%;
3286
+ inset-inline-start: 2px;
3287
+ inset-block-start: 50%;
3076
3288
  width: 1.25rem;
3077
3289
  height: 1.25rem;
3078
3290
  border-radius: var(--ds-radius-full);
@@ -3116,6 +3328,7 @@ hr {
3116
3328
  .ds-toggle:focus-visible {
3117
3329
  outline: none;
3118
3330
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
3331
+ scroll-margin-block: var(--ds-space-16, 4rem);
3119
3332
  }
3120
3333
 
3121
3334
  /* ---------------------------------------------------------------------------
@@ -3136,6 +3349,7 @@ hr {
3136
3349
  .ds-toggle--sm {
3137
3350
  width: 2rem;
3138
3351
  height: 1.125rem;
3352
+ min-height: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
3139
3353
  }
3140
3354
 
3141
3355
  .ds-toggle--sm::after {
@@ -3170,6 +3384,12 @@ hr {
3170
3384
  ==========================================================================
3171
3385
  A horizontal breadcrumb trail for hierarchical navigation.
3172
3386
 
3387
+ ARIA requirements (consumer responsibility):
3388
+ - Container: <nav aria-label="Breadcrumb">
3389
+ - Current page: aria-current="page" on the last item
3390
+ - Links: use <a> elements for navigation
3391
+ - Separator: decorative (generated via CSS ::after, not in DOM)
3392
+
3173
3393
  Usage:
3174
3394
  <nav class="ds-breadcrumb" aria-label="Breadcrumb">
3175
3395
  <span class="ds-breadcrumb__item">
@@ -3237,6 +3457,7 @@ hr {
3237
3457
  outline: none;
3238
3458
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
3239
3459
  border-radius: var(--ds-radius-sm);
3460
+ scroll-margin-block: var(--ds-space-16, 4rem);
3240
3461
  }
3241
3462
 
3242
3463
  /* ---------------------------------------------------------------------------
@@ -3266,6 +3487,13 @@ hr {
3266
3487
  ==========================================================================
3267
3488
  A row of page-number controls for navigating multi-page content.
3268
3489
 
3490
+ ARIA requirements (consumer responsibility):
3491
+ - Container: <nav aria-label="Pagination">
3492
+ - Active page: aria-current="page"
3493
+ - Prev/Next: aria-label="Previous page" / "Next page"
3494
+ - Disabled: aria-disabled="true" on disabled prev/next buttons
3495
+ - Page items: aria-label="Page [N]" (optional, number is visible)
3496
+
3269
3497
  Usage:
3270
3498
  <nav class="ds-pagination" aria-label="Pagination">
3271
3499
  <button class="ds-pagination__prev" aria-label="Previous page">&lsaquo;</button>
@@ -3322,6 +3550,7 @@ hr {
3322
3550
  .ds-pagination__item:focus-visible {
3323
3551
  outline: none;
3324
3552
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
3553
+ scroll-margin-block: var(--ds-space-16, 4rem);
3325
3554
  }
3326
3555
 
3327
3556
  /* ---------------------------------------------------------------------------
@@ -3381,6 +3610,7 @@ hr {
3381
3610
  .ds-pagination__next:focus-visible {
3382
3611
  outline: none;
3383
3612
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
3613
+ scroll-margin-block: var(--ds-space-16, 4rem);
3384
3614
  }
3385
3615
 
3386
3616
  .ds-pagination__prev:disabled,
@@ -3433,6 +3663,11 @@ hr {
3433
3663
  ==========================================================================
3434
3664
  Interactive, optionally removable tag for categorisation and filtering.
3435
3665
 
3666
+ ARIA requirements (consumer responsibility):
3667
+ - Remove button: add aria-label="Remove [tag name]"
3668
+ - Selectable tags (as <button>): use aria-pressed="true|false"
3669
+ - Tag list: wrap in role="list" with aria-label
3670
+
3436
3671
  Usage:
3437
3672
  <span class="ds-tag">Default</span>
3438
3673
  <span class="ds-tag ds-tag--success">Approved</span>
@@ -3473,6 +3708,7 @@ hr {
3473
3708
  button.ds-tag:focus-visible {
3474
3709
  outline: none;
3475
3710
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
3711
+ scroll-margin-block: var(--ds-space-16, 4rem);
3476
3712
  }
3477
3713
 
3478
3714
  /* ---------------------------------------------------------------------------
@@ -3520,7 +3756,7 @@ button.ds-tag:focus-visible {
3520
3756
  --------------------------------------------------------------------------- */
3521
3757
 
3522
3758
  .ds-tag--removable {
3523
- padding-right: var(--ds-space-1);
3759
+ padding-inline-end: var(--ds-space-1);
3524
3760
  }
3525
3761
 
3526
3762
  /* ---------------------------------------------------------------------------
@@ -3533,6 +3769,8 @@ button.ds-tag:focus-visible {
3533
3769
  justify-content: center;
3534
3770
  width: 1rem;
3535
3771
  height: 1rem;
3772
+ min-width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
3773
+ min-height: 1.5rem;
3536
3774
  padding: 0;
3537
3775
  border: none;
3538
3776
  border-radius: var(--ds-radius-full);
@@ -3578,7 +3816,7 @@ button.ds-tag:focus-visible {
3578
3816
  }
3579
3817
 
3580
3818
  .ds-tag--sm.ds-tag--removable {
3581
- padding-right: var(--ds-space-0-5, 0.125rem);
3819
+ padding-inline-end: var(--ds-space-0-5, 0.125rem);
3582
3820
  }
3583
3821
 
3584
3822
  /* ---------------------------------------------------------------------------
@@ -3591,7 +3829,7 @@ button.ds-tag:focus-visible {
3591
3829
  }
3592
3830
 
3593
3831
  .ds-tag--lg.ds-tag--removable {
3594
- padding-right: var(--ds-space-1-5, 0.375rem);
3832
+ padding-inline-end: var(--ds-space-1-5, 0.375rem);
3595
3833
  }
3596
3834
 
3597
3835
  /* ==========================================================================
@@ -3600,6 +3838,12 @@ button.ds-tag:focus-visible {
3600
3838
  A vertically stacked set of collapsible sections. Supports CSS-only
3601
3839
  open/close via the `.ds-accordion__item--open` modifier.
3602
3840
 
3841
+ ARIA requirements (consumer responsibility):
3842
+ - Trigger: <button> with aria-expanded="true|false"
3843
+ - Trigger: aria-controls="[content-panel-id]"
3844
+ - Content panel: id matching aria-controls, role="region", aria-labelledby="[trigger-id]"
3845
+ - Keyboard: Enter/Space toggles section (native <button> handles this)
3846
+
3603
3847
  Variants:
3604
3848
  --flush – borderless, full-bleed style
3605
3849
  --separated – visually detached items with individual borders
@@ -3607,8 +3851,8 @@ button.ds-tag:focus-visible {
3607
3851
  Usage:
3608
3852
  <div class="ds-accordion">
3609
3853
  <div class="ds-accordion__item ds-accordion__item--open">
3610
- <button class="ds-accordion__trigger">Section</button>
3611
- <div class="ds-accordion__content">
3854
+ <button class="ds-accordion__trigger" aria-expanded="true" aria-controls="panel-1">Section</button>
3855
+ <div class="ds-accordion__content" id="panel-1" role="region">
3612
3856
  <div class="ds-accordion__body">…</div>
3613
3857
  </div>
3614
3858
  </div>
@@ -3630,11 +3874,11 @@ button.ds-tag:focus-visible {
3630
3874
  --------------------------------------------------------------------------- */
3631
3875
 
3632
3876
  .ds-accordion__item {
3633
- border-top: 1px solid var(--ds-color-border);
3877
+ border-block-start: 1px solid var(--ds-color-border);
3634
3878
  }
3635
3879
 
3636
3880
  .ds-accordion__item:first-child {
3637
- border-top: 0;
3881
+ border-block-start: 0;
3638
3882
  }
3639
3883
 
3640
3884
  /* ---------------------------------------------------------------------------
@@ -3648,7 +3892,7 @@ button.ds-tag:focus-visible {
3648
3892
  align-items: center;
3649
3893
  width: 100%;
3650
3894
  padding: var(--ds-space-4);
3651
- text-align: left;
3895
+ text-align: start;
3652
3896
  font-family: var(--ds-font-sans);
3653
3897
  font-size: var(--ds-text-sm);
3654
3898
  font-weight: var(--ds-weight-medium);
@@ -3665,11 +3909,11 @@ button.ds-tag:focus-visible {
3665
3909
  display: inline-block;
3666
3910
  width: 0.5rem;
3667
3911
  height: 0.5rem;
3668
- border-right: 2px solid var(--ds-color-text-secondary);
3669
- border-bottom: 2px solid var(--ds-color-text-secondary);
3912
+ border-inline-end: 2px solid var(--ds-color-text-secondary);
3913
+ border-block-end: 2px solid var(--ds-color-text-secondary);
3670
3914
  transform: rotate(45deg);
3671
3915
  flex-shrink: 0;
3672
- margin-left: var(--ds-space-3);
3916
+ margin-inline-start: var(--ds-space-3);
3673
3917
  transition: transform var(--ds-duration-fast) var(--ds-ease-default);
3674
3918
  }
3675
3919
 
@@ -3679,6 +3923,7 @@ button.ds-tag:focus-visible {
3679
3923
  .ds-accordion__trigger:focus-visible {
3680
3924
  outline: none;
3681
3925
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
3926
+ scroll-margin-block: var(--ds-space-16, 4rem);
3682
3927
  }
3683
3928
 
3684
3929
  /* Open state – rotate chevron */
@@ -3708,7 +3953,7 @@ button.ds-tag:focus-visible {
3708
3953
 
3709
3954
  .ds-accordion__body {
3710
3955
  padding: var(--ds-space-4);
3711
- padding-top: 0;
3956
+ padding-block-start: 0;
3712
3957
  font-size: var(--ds-text-sm);
3713
3958
  color: var(--ds-color-text-secondary);
3714
3959
  line-height: var(--ds-leading-relaxed);
@@ -3724,12 +3969,12 @@ button.ds-tag:focus-visible {
3724
3969
  }
3725
3970
 
3726
3971
  .ds-accordion--flush .ds-accordion__item {
3727
- border-top: 0;
3728
- border-bottom: 1px solid var(--ds-color-border);
3972
+ border-block-start: 0;
3973
+ border-block-end: 1px solid var(--ds-color-border);
3729
3974
  }
3730
3975
 
3731
3976
  .ds-accordion--flush .ds-accordion__item:last-child {
3732
- border-bottom: 0;
3977
+ border-block-end: 0;
3733
3978
  }
3734
3979
 
3735
3980
  /* ==========================================================================
@@ -3745,12 +3990,12 @@ button.ds-tag:focus-visible {
3745
3990
  .ds-accordion--separated .ds-accordion__item {
3746
3991
  border: 1px solid var(--ds-color-border);
3747
3992
  border-radius: var(--ds-radius-xl);
3748
- margin-bottom: var(--ds-space-3);
3993
+ margin-block-end: var(--ds-space-3);
3749
3994
  overflow: hidden;
3750
3995
  }
3751
3996
 
3752
3997
  .ds-accordion--separated .ds-accordion__item:last-child {
3753
- margin-bottom: 0;
3998
+ margin-block-end: 0;
3754
3999
  }
3755
4000
 
3756
4001
  /* ==========================================================================
@@ -3760,13 +4005,21 @@ button.ds-tag:focus-visible {
3760
4005
  Default direction is right; use `--left` or `--bottom` modifiers
3761
4006
  to change the slide origin.
3762
4007
 
4008
+ ARIA requirements (consumer responsibility):
4009
+ - Container: role="dialog", aria-modal="true", aria-labelledby="[title-id]"
4010
+ - Close button: aria-label="Close"
4011
+ - Focus: trap focus inside drawer when open
4012
+ - Keyboard: Escape closes drawer
4013
+ - On open: move focus to first focusable element or close button
4014
+ - On close: return focus to the element that triggered the drawer
4015
+
3763
4016
  Sizes:
3764
4017
  --sm 18 rem
3765
4018
  (default) 24 rem
3766
4019
  --lg 36 rem
3767
4020
 
3768
4021
  Usage:
3769
- <div class="ds-drawer ds-drawer--right ds-drawer--open">
4022
+ <div class="ds-drawer ds-drawer--right ds-drawer--open" role="dialog" aria-modal="true">
3770
4023
  <div class="ds-drawer__content">
3771
4024
  <div class="ds-drawer__header">
3772
4025
  <h3>Title</h3>
@@ -3819,12 +4072,12 @@ button.ds-tag:focus-visible {
3819
4072
 
3820
4073
  .ds-drawer__content,
3821
4074
  .ds-drawer--right .ds-drawer__content {
3822
- top: 0;
3823
- right: 0;
3824
- bottom: 0;
4075
+ inset-block-start: 0;
4076
+ inset-inline-end: 0;
4077
+ inset-block-end: 0;
3825
4078
  width: 24rem;
3826
4079
  max-width: 90vw;
3827
- border-left: 1px solid var(--ds-color-border);
4080
+ border-inline-start: 1px solid var(--ds-color-border);
3828
4081
  transform: translateX(100%);
3829
4082
  }
3830
4083
 
@@ -3838,14 +4091,14 @@ button.ds-tag:focus-visible {
3838
4091
  * ========================================================================== */
3839
4092
 
3840
4093
  .ds-drawer--left .ds-drawer__content {
3841
- left: 0;
3842
- top: 0;
3843
- bottom: 0;
3844
- right: auto;
4094
+ inset-inline-start: 0;
4095
+ inset-block-start: 0;
4096
+ inset-block-end: 0;
4097
+ inset-inline-end: auto;
3845
4098
  width: 24rem;
3846
4099
  max-width: 90vw;
3847
- border-left: 0;
3848
- border-right: 1px solid var(--ds-color-border);
4100
+ border-inline-start: 0;
4101
+ border-inline-end: 1px solid var(--ds-color-border);
3849
4102
  transform: translateX(-100%);
3850
4103
  }
3851
4104
 
@@ -3858,14 +4111,14 @@ button.ds-tag:focus-visible {
3858
4111
  * ========================================================================== */
3859
4112
 
3860
4113
  .ds-drawer--bottom .ds-drawer__content {
3861
- bottom: 0;
3862
- left: 0;
3863
- right: 0;
3864
- top: auto;
4114
+ inset-block-end: 0;
4115
+ inset-inline-start: 0;
4116
+ inset-inline-end: 0;
4117
+ inset-block-start: auto;
3865
4118
  width: auto;
3866
4119
  max-height: 90dvh;
3867
- border-left: 0;
3868
- border-top: 1px solid var(--ds-color-border);
4120
+ border-inline-start: 0;
4121
+ border-block-start: 1px solid var(--ds-color-border);
3869
4122
  border-radius: var(--ds-radius-xl) var(--ds-radius-xl) 0 0;
3870
4123
  transform: translateY(100%);
3871
4124
  }
@@ -3901,7 +4154,7 @@ button.ds-tag:focus-visible {
3901
4154
  justify-content: space-between;
3902
4155
  align-items: flex-start;
3903
4156
  padding: var(--ds-space-4) var(--ds-space-5);
3904
- border-bottom: 1px solid var(--ds-color-border);
4157
+ border-block-end: 1px solid var(--ds-color-border);
3905
4158
  }
3906
4159
 
3907
4160
  .ds-drawer__header h3 {
@@ -3943,6 +4196,7 @@ button.ds-tag:focus-visible {
3943
4196
  .ds-drawer__close:focus-visible {
3944
4197
  outline: none;
3945
4198
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
4199
+ scroll-margin-block: var(--ds-space-16, 4rem);
3946
4200
  }
3947
4201
 
3948
4202
  /* ==========================================================================
@@ -3961,7 +4215,7 @@ button.ds-tag:focus-visible {
3961
4215
 
3962
4216
  .ds-drawer__footer {
3963
4217
  padding: var(--ds-space-4) var(--ds-space-5);
3964
- border-top: 1px solid var(--ds-color-border);
4218
+ border-block-start: 1px solid var(--ds-color-border);
3965
4219
  display: flex;
3966
4220
  align-items: center;
3967
4221
  justify-content: flex-end;
@@ -4013,7 +4267,7 @@ button.ds-tag:focus-visible {
4013
4267
  justify-content: space-between;
4014
4268
  font-size: var(--ds-text-xs);
4015
4269
  color: var(--ds-color-text-tertiary);
4016
- margin-bottom: var(--ds-space-1-5);
4270
+ margin-block-end: var(--ds-space-1-5);
4017
4271
  }
4018
4272
 
4019
4273
  /* Track */
@@ -4178,7 +4432,7 @@ button.ds-tag:focus-visible {
4178
4432
  height: 2rem;
4179
4433
  min-width: auto;
4180
4434
  margin-inline: 0;
4181
- margin-left: calc(1rem - 1px); /* center under the 2 rem indicator */
4435
+ margin-inline-start: calc(1rem - 1px); /* center under the 2 rem indicator */
4182
4436
  margin-block: var(--ds-space-1);
4183
4437
  background-color: var(--ds-color-border-subtle);
4184
4438
  }
@@ -4190,6 +4444,12 @@ button.ds-tag:focus-visible {
4190
4444
  /* ==========================================================================
4191
4445
  Component: Drop Zone
4192
4446
  Dashed-border upload area with icon, label, hint, and progress states.
4447
+
4448
+ ARIA requirements (consumer responsibility):
4449
+ - Container: role="button" if clickable, tabindex="0" for keyboard access
4450
+ - Label: aria-label="Upload files" or visible label via aria-labelledby
4451
+ - Drag state: announce via aria-live="polite" region
4452
+ - Progress: use aria-busy="true" during upload, aria-valuenow for progress
4193
4453
  ==========================================================================
4194
4454
 
4195
4455
  Usage:
@@ -4231,6 +4491,7 @@ button.ds-tag:focus-visible {
4231
4491
  .ds-drop-zone:focus-visible {
4232
4492
  outline: none;
4233
4493
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
4494
+ scroll-margin-block: var(--ds-space-16, 4rem);
4234
4495
  }
4235
4496
 
4236
4497
  :root:not(.dark) .ds-drop-zone {
@@ -4267,7 +4528,7 @@ button.ds-tag:focus-visible {
4267
4528
  }
4268
4529
 
4269
4530
  .ds-drop-zone:hover .ds-drop-zone__icon {
4270
- color: var(--ds-color-text-primary);
4531
+ color: var(--ds-color-text);
4271
4532
  }
4272
4533
 
4273
4534
  /* ---------------------------------------------------------------------------
@@ -4277,13 +4538,13 @@ button.ds-tag:focus-visible {
4277
4538
  .ds-drop-zone__label {
4278
4539
  font-size: var(--ds-text-sm);
4279
4540
  font-weight: var(--ds-weight-medium);
4280
- color: var(--ds-color-text-primary);
4541
+ color: var(--ds-color-text);
4281
4542
  }
4282
4543
 
4283
4544
  .ds-drop-zone__hint {
4284
4545
  font-size: var(--ds-text-xs);
4285
4546
  color: var(--ds-color-text-tertiary);
4286
- margin-top: calc(-1 * var(--ds-space-1));
4547
+ margin-block-start: calc(-1 * var(--ds-space-1));
4287
4548
  }
4288
4549
 
4289
4550
  /* ---------------------------------------------------------------------------
@@ -4329,6 +4590,14 @@ button.ds-tag:focus-visible {
4329
4590
  Replaces native <select> with styled dropdown panel.
4330
4591
  Desktop: absolute positioned panel below trigger.
4331
4592
  Mobile: fullscreen overlay with header + search.
4593
+
4594
+ ARIA requirements (consumer responsibility):
4595
+ - Trigger: role="combobox", aria-expanded="true|false", aria-haspopup="listbox"
4596
+ - Trigger: aria-controls="[panel-list-id]", aria-labelledby="[label-id]"
4597
+ - Panel list: role="listbox", id matching aria-controls
4598
+ - Option: role="option", aria-selected="true|false"
4599
+ - Multi-select: add aria-multiselectable="true" on listbox
4600
+ - Keyboard: ArrowUp/Down to navigate, Enter/Space to select, Escape to close
4332
4601
  ========================================================================== */
4333
4602
 
4334
4603
  /* --- Wrapper --- */
@@ -4355,7 +4624,7 @@ button.ds-tag:focus-visible {
4355
4624
  border: 1px solid var(--ds-color-border);
4356
4625
  border-radius: var(--ds-radius-lg);
4357
4626
  cursor: pointer;
4358
- text-align: left;
4627
+ text-align: start;
4359
4628
  transition: all var(--ds-duration-fast) var(--ds-ease-default);
4360
4629
  }
4361
4630
 
@@ -4367,6 +4636,7 @@ button.ds-tag:focus-visible {
4367
4636
  border-color: var(--ds-color-border-active);
4368
4637
  outline: none;
4369
4638
  box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
4639
+ scroll-margin-block: var(--ds-space-16, 4rem);
4370
4640
  }
4371
4641
 
4372
4642
  .ds-custom-select__trigger--open {
@@ -4396,7 +4666,7 @@ button.ds-tag:focus-visible {
4396
4666
  flex-shrink: 0;
4397
4667
  width: 1rem;
4398
4668
  height: 1rem;
4399
- margin-left: var(--ds-space-2);
4669
+ margin-inline-start: var(--ds-space-2);
4400
4670
  color: var(--ds-color-text-tertiary);
4401
4671
  transition: transform var(--ds-duration-fast) var(--ds-ease-default);
4402
4672
  }
@@ -4471,13 +4741,13 @@ button.ds-tag:focus-visible {
4471
4741
  .ds-custom-select__search {
4472
4742
  position: relative;
4473
4743
  padding: var(--ds-space-2) var(--ds-space-3);
4474
- border-bottom: 1px solid var(--ds-color-border);
4744
+ border-block-end: 1px solid var(--ds-color-border);
4475
4745
  }
4476
4746
 
4477
4747
  .ds-custom-select__search-icon {
4478
4748
  position: absolute;
4479
- left: calc(var(--ds-space-3) + var(--ds-space-2-5));
4480
- top: 50%;
4749
+ inset-inline-start: calc(var(--ds-space-3) + var(--ds-space-2-5));
4750
+ inset-block-start: 50%;
4481
4751
  transform: translateY(-50%);
4482
4752
  width: 1rem;
4483
4753
  height: 1rem;
@@ -4581,7 +4851,7 @@ button.ds-tag:focus-visible {
4581
4851
  .ds-custom-select__panel {
4582
4852
  position: fixed;
4583
4853
  inset: 0;
4584
- top: auto;
4854
+ inset-block-start: auto;
4585
4855
  z-index: var(--ds-z-modal);
4586
4856
  max-height: 85dvh;
4587
4857
  border-radius: var(--ds-radius-xl) var(--ds-radius-xl) 0 0;
@@ -4605,7 +4875,7 @@ button.ds-tag:focus-visible {
4605
4875
  align-items: center;
4606
4876
  justify-content: space-between;
4607
4877
  padding: var(--ds-space-4) var(--ds-space-4);
4608
- border-bottom: 1px solid var(--ds-color-border);
4878
+ border-block-end: 1px solid var(--ds-color-border);
4609
4879
  }
4610
4880
 
4611
4881
  .ds-custom-select__header-title {
@@ -4645,690 +4915,1981 @@ button.ds-tag:focus-visible {
4645
4915
  }
4646
4916
  }
4647
4917
 
4648
- @media (prefers-reduced-motion: reduce) {
4649
- .ds-custom-select__panel {
4650
- animation: none;
4651
- opacity: 1;
4652
- transform: scale(1);
4653
- }
4654
- .ds-custom-select__backdrop { animation: none; opacity: 1; }
4655
- }
4656
-
4657
4918
  /* ==========================================================================
4658
- Component: Sortable
4659
- Optional drag-to-reorder styles for table rows or list items.
4660
- Provides grip handle, dragging states, and drop zone indicators.
4661
- Works with native HTML5 Drag API or any JS drag library.
4919
+ Multi-select mode
4662
4920
  ========================================================================== */
4663
4921
 
4664
- /* --- Grip handle: visible on row hover, grab cursor --- */
4665
- .ds-sortable__handle {
4922
+ /* --- Multi trigger: tags inside trigger --- */
4923
+
4924
+ .ds-custom-select--multi .ds-custom-select__trigger {
4925
+ height: auto;
4926
+ min-height: var(--ds-size-3);
4927
+ flex-wrap: wrap;
4928
+ padding: var(--ds-space-1) var(--ds-space-4) var(--ds-space-1) var(--ds-space-2);
4929
+ gap: var(--ds-space-1);
4930
+ }
4931
+
4932
+ /* --- Tags container --- */
4933
+
4934
+ .ds-custom-select__tags {
4666
4935
  display: flex;
4936
+ flex-wrap: wrap;
4937
+ gap: var(--ds-space-1);
4938
+ flex: 1;
4939
+ min-width: 0;
4940
+ }
4941
+
4942
+ /* --- Single tag (based on ds-tag styling) --- */
4943
+
4944
+ .ds-custom-select__tag {
4945
+ display: inline-flex;
4667
4946
  align-items: center;
4668
- justify-content: center;
4669
- padding: var(--ds-space-1);
4670
- color: var(--ds-color-text-tertiary);
4671
- cursor: grab;
4672
- border-radius: var(--ds-radius-sm);
4673
- opacity: 0;
4674
- transition:
4675
- opacity var(--ds-duration-fast) var(--ds-ease-default),
4676
- color var(--ds-duration-fast) var(--ds-ease-default);
4947
+ gap: var(--ds-space-1);
4948
+ padding: var(--ds-space-0-5) var(--ds-space-1) var(--ds-space-0-5) var(--ds-space-2);
4949
+ font-size: var(--ds-text-xs);
4950
+ font-weight: var(--ds-weight-medium);
4951
+ font-family: var(--ds-font-sans);
4952
+ line-height: var(--ds-leading-none);
4953
+ border-radius: var(--ds-radius-full);
4954
+ background-color: var(--ds-color-bg-elevated);
4955
+ border: 1px solid var(--ds-color-border);
4956
+ color: var(--ds-color-text-secondary);
4957
+ white-space: nowrap;
4677
4958
  }
4678
4959
 
4679
- .ds-sortable__handle:active {
4680
- cursor: grabbing;
4960
+ .ds-custom-select__tag-remove {
4961
+ display: inline-flex;
4962
+ align-items: center;
4963
+ justify-content: center;
4964
+ width: 1rem;
4965
+ height: 1rem;
4966
+ min-width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
4967
+ min-height: 1.5rem;
4968
+ padding: 0;
4969
+ border: none;
4970
+ border-radius: var(--ds-radius-full);
4971
+ background: transparent;
4972
+ color: currentColor;
4973
+ font-size: inherit;
4974
+ line-height: var(--ds-leading-none);
4975
+ opacity: 0.6;
4976
+ cursor: pointer;
4977
+ transition: opacity var(--ds-duration-fast) var(--ds-ease-out);
4978
+ -webkit-appearance: none;
4979
+ appearance: none;
4681
4980
  }
4682
4981
 
4683
- /* Show handle on row hover */
4684
- tr:hover .ds-sortable__handle,
4685
- .ds-sortable-row:hover .ds-sortable__handle,
4686
- .ds-sortable__handle:focus-visible {
4982
+ .ds-custom-select__tag-remove:hover {
4687
4983
  opacity: 1;
4688
4984
  }
4689
4985
 
4690
- .ds-sortable__handle:focus-visible {
4691
- outline: none;
4692
- box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
4986
+ /* --- Multi option: checkbox style indicator --- */
4987
+
4988
+ .ds-custom-select--multi .ds-custom-select__option-check {
4989
+ width: 1rem;
4990
+ height: 1rem;
4991
+ border: 1px solid var(--ds-color-border);
4693
4992
  border-radius: var(--ds-radius-sm);
4993
+ flex-shrink: 0;
4994
+ display: flex;
4995
+ align-items: center;
4996
+ justify-content: center;
4997
+ transition:
4998
+ background-color var(--ds-duration-fast) var(--ds-ease-default),
4999
+ border-color var(--ds-duration-fast) var(--ds-ease-default);
4694
5000
  }
4695
5001
 
4696
- tr:hover .ds-sortable__handle,
4697
- .ds-sortable-row:hover .ds-sortable__handle {
4698
- color: var(--ds-color-text-secondary);
5002
+ .ds-custom-select--multi .ds-custom-select__option--selected .ds-custom-select__option-check {
5003
+ background-color: var(--ds-color-interactive);
5004
+ border-color: var(--ds-color-interactive);
5005
+ color: var(--ds-color-on-inverted);
4699
5006
  }
4700
5007
 
4701
- /* --- Dragging state: applied to the row being dragged --- */
4702
- .ds-sortable--dragging {
4703
- opacity: 0.5;
4704
- background-color: var(--ds-color-surface);
5008
+ /* --- Multi size variants --- */
5009
+
5010
+ .ds-custom-select--xs.ds-custom-select--multi .ds-custom-select__trigger {
5011
+ min-height: var(--ds-size-1);
5012
+ padding: var(--ds-space-0-5) var(--ds-space-1-5) var(--ds-space-0-5) var(--ds-space-1);
4705
5013
  }
4706
5014
 
4707
- /* --- Drop target: applied to the row being hovered over --- */
4708
- .ds-sortable--over {
4709
- box-shadow: inset 0 -2px 0 var(--ds-color-interactive);
5015
+ .ds-custom-select--sm.ds-custom-select--multi .ds-custom-select__trigger {
5016
+ min-height: var(--ds-size-2);
5017
+ padding: var(--ds-space-0-5) var(--ds-space-2) var(--ds-space-0-5) var(--ds-space-1-5);
4710
5018
  }
4711
5019
 
4712
- /* --- Always-visible handle variant --- */
4713
- .ds-sortable__handle--visible {
4714
- opacity: 1;
5020
+ .ds-custom-select--lg.ds-custom-select--multi .ds-custom-select__trigger {
5021
+ min-height: var(--ds-size-4);
5022
+ padding: var(--ds-space-1) var(--ds-space-4) var(--ds-space-1) var(--ds-space-2);
4715
5023
  }
4716
5024
 
5025
+ @media (prefers-reduced-motion: reduce) {
5026
+ .ds-custom-select__panel {
5027
+ animation: none;
5028
+ opacity: 1;
5029
+ transform: scale(1);
5030
+ }
5031
+ .ds-custom-select__backdrop { animation: none; opacity: 1; }
5032
+ }
4717
5033
 
4718
- /* === Tier 3 — Advanced === */
4719
5034
  /* ==========================================================================
4720
- Popover
4721
- ==========================================================================
4722
- Flexible popover component for displaying complex content anchored to a
4723
- trigger element. Supports four placement directions (top, bottom, left,
4724
- right) and multiple size variants. Default placement is bottom.
5035
+ Component: Collapsible
5036
+ Single collapsible section — a simplified, standalone accordion item.
4725
5037
 
4726
- Usage:
4727
- <div class="ds-popover ds-popover--open">
4728
- <button>Trigger</button>
4729
- <div class="ds-popover__content">...</div>
4730
- </div>
5038
+ ARIA requirements (consumer responsibility):
5039
+ - Trigger: <button> with aria-expanded="true|false"
5040
+ - Trigger: aria-controls="[content-panel-id]"
5041
+ - Content: id matching aria-controls
5042
+ ========================================================================== */
4731
5043
 
4732
- Modifiers:
4733
- .ds-popover--open — Shows the popover content
4734
- .ds-popover--top — Places content above the trigger
4735
- .ds-popover--bottom — Places content below the trigger (default)
4736
- .ds-popover--left — Places content to the left of the trigger
4737
- .ds-popover--right — Places content to the right of the trigger
4738
- .ds-popover__content--sm — Smaller popover (12rem min-width)
5044
+ /* ---------------------------------------------------------------------------
5045
+ Container
5046
+ --------------------------------------------------------------------------- */
5047
+
5048
+ .ds-collapsible {
5049
+ border: 1px solid var(--ds-color-border);
5050
+ border-radius: var(--ds-radius-xl);
5051
+ overflow: hidden;
5052
+ }
5053
+
5054
+ /* ---------------------------------------------------------------------------
5055
+ Trigger (button)
5056
+ --------------------------------------------------------------------------- */
5057
+
5058
+ .ds-collapsible__trigger {
5059
+ display: flex;
5060
+ flex-direction: row;
5061
+ justify-content: space-between;
5062
+ align-items: center;
5063
+ width: 100%;
5064
+ padding: var(--ds-space-4);
5065
+ text-align: start;
5066
+ font-family: var(--ds-font-sans);
5067
+ font-size: var(--ds-text-sm);
5068
+ font-weight: var(--ds-weight-medium);
5069
+ color: var(--ds-color-text);
5070
+ background: transparent;
5071
+ border: 0;
5072
+ cursor: pointer;
5073
+ transition: background-color var(--ds-duration-fast) var(--ds-ease-default);
5074
+ }
5075
+
5076
+ .ds-collapsible__trigger:hover {
5077
+ background-color: var(--ds-color-overlay);
5078
+ }
5079
+
5080
+ .ds-collapsible__trigger:focus-visible {
5081
+ outline: none;
5082
+ box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
5083
+ scroll-margin-block: var(--ds-space-16, 4rem);
5084
+ }
5085
+
5086
+ /* ---------------------------------------------------------------------------
5087
+ Chevron icon (CSS border-arrow)
5088
+ --------------------------------------------------------------------------- */
5089
+
5090
+ .ds-collapsible__icon {
5091
+ flex-shrink: 0;
5092
+ width: 0.5rem;
5093
+ height: 0.5rem;
5094
+ border-inline-end: 2px solid var(--ds-color-text-secondary);
5095
+ border-block-end: 2px solid var(--ds-color-text-secondary);
5096
+ transform: rotate(45deg);
5097
+ margin-inline-start: var(--ds-space-3);
5098
+ transition: transform var(--ds-duration-fast) var(--ds-ease-default);
5099
+ }
5100
+
5101
+ .ds-collapsible--open .ds-collapsible__icon {
5102
+ transform: rotate(225deg);
5103
+ }
5104
+
5105
+ /* ---------------------------------------------------------------------------
5106
+ Collapsible content
5107
+ --------------------------------------------------------------------------- */
5108
+
5109
+ .ds-collapsible__content {
5110
+ max-height: 0;
5111
+ overflow: hidden;
5112
+ transition:
5113
+ max-height var(--ds-duration-normal) var(--ds-ease-default),
5114
+ padding var(--ds-duration-normal) var(--ds-ease-default);
5115
+ }
5116
+
5117
+ .ds-collapsible--open .ds-collapsible__content {
5118
+ max-height: 80rem;
5119
+ }
5120
+
5121
+ /* ---------------------------------------------------------------------------
5122
+ Inner body
5123
+ --------------------------------------------------------------------------- */
5124
+
5125
+ .ds-collapsible__body {
5126
+ padding: var(--ds-space-4);
5127
+ padding-block-start: 0;
5128
+ font-size: var(--ds-text-sm);
5129
+ color: var(--ds-color-text-secondary);
5130
+ line-height: var(--ds-leading-relaxed);
5131
+ }
5132
+
5133
+ /* ---------------------------------------------------------------------------
5134
+ Variant: Flush (no border, no radius)
5135
+ --------------------------------------------------------------------------- */
5136
+
5137
+ .ds-collapsible--flush {
5138
+ border: 0;
5139
+ border-radius: 0;
5140
+ }
5141
+
5142
+ /* ---------------------------------------------------------------------------
5143
+ Variant: Ghost (no border, transparent bg)
5144
+ --------------------------------------------------------------------------- */
5145
+
5146
+ .ds-collapsible--ghost {
5147
+ border: 0;
5148
+ background: transparent;
5149
+ }
5150
+
5151
+ /* ==========================================================================
5152
+ Component: Description List
5153
+ Key-value pairs display with horizontal and vertical layouts.
5154
+ ========================================================================== */
5155
+
5156
+ /* ---------------------------------------------------------------------------
5157
+ Container
5158
+ --------------------------------------------------------------------------- */
5159
+
5160
+ .ds-description-list {
5161
+ display: flex;
5162
+ flex-direction: column;
5163
+ gap: 0;
5164
+ }
5165
+
5166
+ /* ---------------------------------------------------------------------------
5167
+ Item
5168
+ --------------------------------------------------------------------------- */
5169
+
5170
+ .ds-description-list__item {
5171
+ display: flex;
5172
+ flex-direction: column;
5173
+ gap: var(--ds-space-1);
5174
+ padding: var(--ds-space-3) 0;
5175
+ }
5176
+
5177
+ .ds-description-list__item:first-child {
5178
+ padding-block-start: 0;
5179
+ }
5180
+
5181
+ .ds-description-list__item:last-child {
5182
+ padding-block-end: 0;
5183
+ }
5184
+
5185
+ /* ---------------------------------------------------------------------------
5186
+ Term (key)
5187
+ --------------------------------------------------------------------------- */
5188
+
5189
+ .ds-description-list__term {
5190
+ font-family: var(--ds-font-sans);
5191
+ font-size: var(--ds-text-sm);
5192
+ font-weight: var(--ds-weight-medium);
5193
+ color: var(--ds-color-text-secondary);
5194
+ line-height: var(--ds-leading-normal);
5195
+ }
5196
+
5197
+ /* ---------------------------------------------------------------------------
5198
+ Detail (value)
5199
+ --------------------------------------------------------------------------- */
5200
+
5201
+ .ds-description-list__detail {
5202
+ font-family: var(--ds-font-sans);
5203
+ font-size: var(--ds-text-sm);
5204
+ color: var(--ds-color-text);
5205
+ line-height: var(--ds-leading-normal);
5206
+ }
5207
+
5208
+ /* ---------------------------------------------------------------------------
5209
+ Variant: Horizontal (term and detail on same row)
5210
+ --------------------------------------------------------------------------- */
5211
+
5212
+ .ds-description-list--horizontal .ds-description-list__item {
5213
+ flex-direction: row;
5214
+ align-items: flex-start;
5215
+ gap: var(--ds-space-4);
5216
+ }
5217
+
5218
+ .ds-description-list--horizontal .ds-description-list__term {
5219
+ flex-shrink: 0;
5220
+ min-width: 10rem;
5221
+ }
5222
+
5223
+ .ds-description-list--horizontal .ds-description-list__detail {
5224
+ flex: 1;
5225
+ min-width: 0;
5226
+ }
5227
+
5228
+ /* ---------------------------------------------------------------------------
5229
+ Variant: Bordered (separator between items)
5230
+ --------------------------------------------------------------------------- */
5231
+
5232
+ .ds-description-list--bordered .ds-description-list__item {
5233
+ border-block-end: 1px solid var(--ds-color-border);
5234
+ }
5235
+
5236
+ .ds-description-list--bordered .ds-description-list__item:last-child {
5237
+ border-block-end: 0;
5238
+ }
5239
+
5240
+ /* ---------------------------------------------------------------------------
5241
+ Variant: Striped (alternating background)
5242
+ --------------------------------------------------------------------------- */
5243
+
5244
+ .ds-description-list--striped .ds-description-list__item:nth-child(odd) {
5245
+ background-color: var(--ds-color-bg-subtle);
5246
+ padding-inline: var(--ds-space-3);
5247
+ border-radius: var(--ds-radius-md);
5248
+ }
5249
+
5250
+ /* ==========================================================================
5251
+ Component: Result
5252
+ Feedback page for success, error, 404, empty, etc.
5253
+ ========================================================================== */
5254
+
5255
+ /* ---------------------------------------------------------------------------
5256
+ Container
5257
+ --------------------------------------------------------------------------- */
5258
+
5259
+ .ds-result {
5260
+ display: flex;
5261
+ flex-direction: column;
5262
+ align-items: center;
5263
+ text-align: center;
5264
+ padding: var(--ds-space-12) var(--ds-space-6);
5265
+ max-width: 28rem;
5266
+ margin-inline: auto;
5267
+ }
5268
+
5269
+ /* ---------------------------------------------------------------------------
5270
+ Icon
5271
+ --------------------------------------------------------------------------- */
5272
+
5273
+ .ds-result__icon {
5274
+ width: 3rem;
5275
+ height: 3rem;
5276
+ margin-block-end: var(--ds-space-4);
5277
+ color: var(--ds-color-text-secondary);
5278
+ }
5279
+
5280
+ /* ---------------------------------------------------------------------------
5281
+ Title
5282
+ --------------------------------------------------------------------------- */
5283
+
5284
+ .ds-result__title {
5285
+ font-family: var(--ds-font-display);
5286
+ font-weight: var(--ds-weight-semibold);
5287
+ font-size: var(--ds-text-xl);
5288
+ color: var(--ds-color-text);
5289
+ line-height: var(--ds-leading-tight);
5290
+ }
5291
+
5292
+ /* ---------------------------------------------------------------------------
5293
+ Description
5294
+ --------------------------------------------------------------------------- */
5295
+
5296
+ .ds-result__description {
5297
+ font-size: var(--ds-text-sm);
5298
+ color: var(--ds-color-text-secondary);
5299
+ line-height: var(--ds-leading-relaxed);
5300
+ margin-block-start: var(--ds-space-2);
5301
+ }
5302
+
5303
+ /* ---------------------------------------------------------------------------
5304
+ Actions
5305
+ --------------------------------------------------------------------------- */
5306
+
5307
+ .ds-result__actions {
5308
+ display: flex;
5309
+ align-items: center;
5310
+ justify-content: center;
5311
+ gap: var(--ds-space-3);
5312
+ margin-block-start: var(--ds-space-6);
5313
+ }
5314
+
5315
+ /* ---------------------------------------------------------------------------
5316
+ Semantic variants — icon color
5317
+ --------------------------------------------------------------------------- */
5318
+
5319
+ .ds-result--success .ds-result__icon {
5320
+ color: var(--ds-color-success);
5321
+ }
5322
+
5323
+ .ds-result--warning .ds-result__icon {
5324
+ color: var(--ds-color-warning);
5325
+ }
5326
+
5327
+ .ds-result--error .ds-result__icon {
5328
+ color: var(--ds-color-error);
5329
+ }
5330
+
5331
+ .ds-result--info .ds-result__icon {
5332
+ color: var(--ds-color-info);
5333
+ }
5334
+
5335
+ /* ==========================================================================
5336
+ Component: Sortable
5337
+ Optional drag-to-reorder styles for table rows or list items.
5338
+ Provides grip handle, dragging states, and drop zone indicators.
5339
+ Works with native HTML5 Drag API or any JS drag library.
5340
+
5341
+ ARIA requirements (consumer responsibility):
5342
+ - Handle: aria-label="Reorder [item]", role="button"
5343
+ - Draggable row: aria-grabbed="true|false" (when using HTML5 DnD)
5344
+ - Drop target: aria-dropeffect="move"
5345
+ - Keyboard: handle should support ArrowUp/Down + Enter to move items
5346
+ ========================================================================== */
5347
+
5348
+ /* --- Grip handle: visible on row hover, grab cursor --- */
5349
+ .ds-sortable__handle {
5350
+ display: flex;
5351
+ align-items: center;
5352
+ justify-content: center;
5353
+ min-width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
5354
+ min-height: 1.5rem;
5355
+ padding: var(--ds-space-1);
5356
+ color: var(--ds-color-text-tertiary);
5357
+ cursor: grab;
5358
+ border-radius: var(--ds-radius-sm);
5359
+ opacity: 0;
5360
+ transition:
5361
+ opacity var(--ds-duration-fast) var(--ds-ease-default),
5362
+ color var(--ds-duration-fast) var(--ds-ease-default);
5363
+ }
5364
+
5365
+ .ds-sortable__handle:active {
5366
+ cursor: grabbing;
5367
+ }
5368
+
5369
+ /* Show handle on row hover */
5370
+ tr:hover .ds-sortable__handle,
5371
+ .ds-sortable-row:hover .ds-sortable__handle,
5372
+ .ds-sortable__handle:focus-visible {
5373
+ opacity: 1;
5374
+ }
5375
+
5376
+ .ds-sortable__handle:focus-visible {
5377
+ outline: none;
5378
+ box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
5379
+ border-radius: var(--ds-radius-sm);
5380
+ scroll-margin-block: var(--ds-space-16, 4rem);
5381
+ }
5382
+
5383
+ tr:hover .ds-sortable__handle,
5384
+ .ds-sortable-row:hover .ds-sortable__handle {
5385
+ color: var(--ds-color-text-secondary);
5386
+ }
5387
+
5388
+ /* --- Dragging state: applied to the row being dragged --- */
5389
+ .ds-sortable--dragging {
5390
+ opacity: 0.5;
5391
+ background-color: var(--ds-color-surface);
5392
+ }
5393
+
5394
+ /* --- Drop target: applied to the row being hovered over --- */
5395
+ .ds-sortable--over {
5396
+ box-shadow: inset 0 -2px 0 var(--ds-color-interactive);
5397
+ }
5398
+
5399
+ /* --- Always-visible handle variant --- */
5400
+ .ds-sortable__handle--visible {
5401
+ opacity: 1;
5402
+ }
5403
+
5404
+
5405
+ /* === Tier 3 — Advanced === */
5406
+ /* ==========================================================================
5407
+ Popover
5408
+ ==========================================================================
5409
+ Flexible popover component for displaying complex content anchored to a
5410
+ trigger element. Supports four placement directions (top, bottom, left,
5411
+ right) and multiple size variants. Default placement is bottom.
5412
+
5413
+ Usage:
5414
+ <div class="ds-popover ds-popover--open">
5415
+ <button>Trigger</button>
5416
+ <div class="ds-popover__content">...</div>
5417
+ </div>
5418
+
5419
+ Modifiers:
5420
+ .ds-popover--open — Shows the popover content
5421
+ .ds-popover--top — Places content above the trigger
5422
+ .ds-popover--bottom — Places content below the trigger (default)
5423
+ .ds-popover--left — Places content to the left of the trigger
5424
+ .ds-popover--right — Places content to the right of the trigger
5425
+ .ds-popover__content--sm — Smaller popover (12rem min-width)
4739
5426
  .ds-popover__content--lg — Larger popover (24rem min-width)
4740
5427
  ========================================================================== */
4741
5428
 
4742
- .ds-popover {
4743
- position: relative;
4744
- display: inline-flex;
5429
+ .ds-popover {
5430
+ position: relative;
5431
+ display: inline-flex;
5432
+ }
5433
+
5434
+ .ds-popover__content {
5435
+ position: absolute;
5436
+ z-index: var(--ds-z-dropdown);
5437
+ background-color: var(--ds-color-surface);
5438
+ border: 1px solid var(--ds-color-border);
5439
+ border-radius: var(--ds-radius-xl);
5440
+ box-shadow: var(--ds-shadow-lg);
5441
+ padding: var(--ds-space-4);
5442
+ min-width: 16rem;
5443
+ opacity: 0;
5444
+ visibility: hidden;
5445
+ transform: scale(0.96);
5446
+ transition:
5447
+ opacity var(--ds-duration-fast) var(--ds-ease),
5448
+ visibility var(--ds-duration-fast) var(--ds-ease),
5449
+ transform var(--ds-duration-fast) var(--ds-ease-out-expo);
5450
+ }
5451
+
5452
+ /* Open state */
5453
+ .ds-popover--open .ds-popover__content {
5454
+ opacity: 1;
5455
+ visibility: visible;
5456
+ transform: scale(1);
5457
+ }
5458
+
5459
+ /* Placement: bottom (default) */
5460
+ .ds-popover__content,
5461
+ .ds-popover--bottom .ds-popover__content {
5462
+ inset-block-start: calc(100% + var(--ds-offset-md));
5463
+ inset-inline-start: 50%;
5464
+ transform: translateX(-50%) scale(0.96);
5465
+ }
5466
+
5467
+ .ds-popover--open .ds-popover__content,
5468
+ .ds-popover--bottom.ds-popover--open .ds-popover__content {
5469
+ transform: translateX(-50%) scale(1);
5470
+ }
5471
+
5472
+ /* Placement: top */
5473
+ .ds-popover--top .ds-popover__content {
5474
+ inset-block-end: calc(100% + var(--ds-offset-md));
5475
+ inset-block-start: auto;
5476
+ inset-inline-start: 50%;
5477
+ transform: translateX(-50%) scale(0.96);
5478
+ }
5479
+
5480
+ .ds-popover--top.ds-popover--open .ds-popover__content {
5481
+ transform: translateX(-50%) scale(1);
5482
+ }
5483
+
5484
+ /* Placement: left */
5485
+ .ds-popover--left .ds-popover__content {
5486
+ inset-inline-end: calc(100% + var(--ds-offset-md));
5487
+ inset-block-start: 50%;
5488
+ inset-inline-start: auto;
5489
+ transform: translateY(-50%) scale(0.96);
5490
+ }
5491
+
5492
+ .ds-popover--left.ds-popover--open .ds-popover__content {
5493
+ transform: translateY(-50%) scale(1);
5494
+ }
5495
+
5496
+ /* Placement: right */
5497
+ .ds-popover--right .ds-popover__content {
5498
+ inset-inline-start: calc(100% + var(--ds-offset-md));
5499
+ inset-block-start: 50%;
5500
+ transform: translateY(-50%) scale(0.96);
5501
+ }
5502
+
5503
+ .ds-popover--right.ds-popover--open .ds-popover__content {
5504
+ transform: translateY(-50%) scale(1);
5505
+ }
5506
+
5507
+ /* Size: small */
5508
+ .ds-popover__content--sm {
5509
+ min-width: 12rem;
5510
+ padding: var(--ds-space-3);
5511
+ }
5512
+
5513
+ /* Size: large */
5514
+ .ds-popover__content--lg {
5515
+ min-width: 24rem;
5516
+ }
5517
+
5518
+ /* ==========================================================================
5519
+ Slider
5520
+ ==========================================================================
5521
+ Range slider input component with customizable thumb and track styling.
5522
+ Supports labels, value display, size variants, and disabled state.
5523
+
5524
+ ARIA requirements (consumer responsibility):
5525
+ - Use native <input type="range"> (inherits slider role)
5526
+ - Add aria-label or aria-labelledby for context
5527
+ - Add aria-valuemin, aria-valuemax, aria-valuenow if using custom slider
5528
+ - Add aria-valuetext for human-readable value (e.g., "50%")
5529
+
5530
+ Usage:
5531
+ <div class="ds-slider">
5532
+ <input type="range" min="0" max="100" value="50" aria-label="Volume" />
5533
+ <div class="ds-slider__labels">
5534
+ <span>0</span>
5535
+ <span>100</span>
5536
+ </div>
5537
+ </div>
5538
+
5539
+ Modifiers:
5540
+ .ds-slider--sm — Smaller track and thumb
5541
+ .ds-slider--disabled — Reduced opacity, no interaction
5542
+ ========================================================================== */
5543
+
5544
+ .ds-slider {
5545
+ width: 100%;
5546
+ position: relative;
5547
+ }
5548
+
5549
+ .ds-slider input[type="range"] {
5550
+ -webkit-appearance: none;
5551
+ -moz-appearance: none;
5552
+ appearance: none;
5553
+ width: 100%;
5554
+ height: 0.375rem;
5555
+ background-color: var(--ds-color-bg-muted);
5556
+ border-radius: var(--ds-radius-full);
5557
+ outline: none;
5558
+ cursor: pointer;
5559
+ }
5560
+
5561
+ /* Webkit thumb */
5562
+ .ds-slider input[type="range"]::-webkit-slider-thumb {
5563
+ -webkit-appearance: none;
5564
+ appearance: none;
5565
+ width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
5566
+ height: 1.5rem;
5567
+ border-radius: var(--ds-radius-full);
5568
+ background-color: var(--ds-color-inverted);
5569
+ border: 2px solid var(--ds-color-surface);
5570
+ box-shadow: var(--ds-shadow-sm);
5571
+ cursor: pointer;
5572
+ transition: transform var(--ds-duration-fast) var(--ds-ease);
5573
+ }
5574
+
5575
+ /* Firefox thumb */
5576
+ .ds-slider input[type="range"]::-moz-range-thumb {
5577
+ width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
5578
+ height: 1.5rem;
5579
+ border-radius: var(--ds-radius-full);
5580
+ background-color: var(--ds-color-inverted);
5581
+ border: 2px solid var(--ds-color-surface);
5582
+ box-shadow: var(--ds-shadow-sm);
5583
+ cursor: pointer;
5584
+ transition: transform var(--ds-duration-fast) var(--ds-ease);
5585
+ }
5586
+
5587
+ /* Hover: scale up thumb */
5588
+ .ds-slider input[type="range"]:hover::-webkit-slider-thumb {
5589
+ transform: scale(1.1);
5590
+ }
5591
+
5592
+ .ds-slider input[type="range"]:hover::-moz-range-thumb {
5593
+ transform: scale(1.1);
5594
+ }
5595
+
5596
+ /* Focus ring */
5597
+ .ds-slider input[type="range"]:focus-visible::-webkit-slider-thumb {
5598
+ outline: var(--ds-ring-width) solid var(--ds-ring-color);
5599
+ outline-offset: 0;
5600
+ scroll-margin-block: var(--ds-space-16, 4rem);
5601
+ }
5602
+
5603
+ .ds-slider input[type="range"]:focus-visible::-moz-range-thumb {
5604
+ outline: var(--ds-ring-width) solid var(--ds-ring-color);
5605
+ outline-offset: 0;
5606
+ }
5607
+
5608
+ /* Labels row */
5609
+ .ds-slider__labels {
5610
+ display: flex;
5611
+ justify-content: space-between;
5612
+ font-size: var(--ds-text-xs);
5613
+ color: var(--ds-color-text-tertiary);
5614
+ margin-block-start: var(--ds-space-1);
5615
+ }
5616
+
5617
+ /* Current value display */
5618
+ .ds-slider__value {
5619
+ font-size: var(--ds-text-sm);
5620
+ font-weight: var(--ds-weight-medium);
5621
+ color: var(--ds-color-text);
5622
+ }
5623
+
5624
+ /* Size: small */
5625
+ .ds-slider--sm input[type="range"] {
5626
+ height: 0.25rem;
5627
+ }
5628
+
5629
+ .ds-slider--sm input[type="range"]::-webkit-slider-thumb {
5630
+ width: 1rem;
5631
+ height: 1rem;
5632
+ }
5633
+
5634
+ .ds-slider--sm input[type="range"]::-moz-range-thumb {
5635
+ width: 1rem;
5636
+ height: 1rem;
5637
+ }
5638
+
5639
+ /* Disabled state */
5640
+ .ds-slider--disabled {
5641
+ opacity: var(--ds-opacity-disabled);
5642
+ cursor: not-allowed;
5643
+ }
5644
+
5645
+ .ds-slider--disabled input[type="range"] {
5646
+ cursor: not-allowed;
5647
+ pointer-events: none;
5648
+ }
5649
+
5650
+ /* ==========================================================================
5651
+ Timeline
5652
+ ==========================================================================
5653
+ Vertical timeline component for roadmaps, activity feeds, and step-based
5654
+ flows. Each item has a dot indicator on a vertical line with support for
5655
+ completed, current, and error states.
5656
+
5657
+ Usage:
5658
+ <div class="ds-timeline">
5659
+ <div class="ds-timeline__item ds-timeline__item--completed">
5660
+ <div class="ds-timeline__dot"></div>
5661
+ <div class="ds-timeline__content">
5662
+ <div class="ds-timeline__title">Step one</div>
5663
+ <div class="ds-timeline__description">Details here.</div>
5664
+ <div class="ds-timeline__time">2 hours ago</div>
5665
+ </div>
5666
+ </div>
5667
+ </div>
5668
+
5669
+ Modifiers:
5670
+ .ds-timeline__item--completed — Green dot (success)
5671
+ .ds-timeline__item--current — Inverted dot (active)
5672
+ .ds-timeline__item--error — Red dot (error)
5673
+ .ds-timeline--compact — Tighter spacing, smaller dots
5674
+ ========================================================================== */
5675
+
5676
+ .ds-timeline {
5677
+ position: relative;
5678
+ padding-inline-start: var(--ds-space-8);
5679
+ }
5680
+
5681
+ /* Vertical line */
5682
+ .ds-timeline::before {
5683
+ content: "";
5684
+ position: absolute;
5685
+ inset-inline-start: 0.9rem;
5686
+ inset-block-start: 0;
5687
+ inset-block-end: 0;
5688
+ width: 2px;
5689
+ background-color: var(--ds-color-border);
5690
+ }
5691
+
5692
+ /* Item */
5693
+ .ds-timeline__item {
5694
+ position: relative;
5695
+ padding-block-end: var(--ds-space-6);
5696
+ }
5697
+
5698
+ .ds-timeline__item:last-child {
5699
+ padding-block-end: 0;
5700
+ }
5701
+
5702
+ /* Dot */
5703
+ .ds-timeline__dot {
5704
+ position: absolute;
5705
+ inset-inline-start: calc(-1 * var(--ds-space-8) + 0.5rem);
5706
+ width: 1rem;
5707
+ height: 1rem;
5708
+ border-radius: var(--ds-radius-full);
5709
+ background-color: var(--ds-color-bg-elevated);
5710
+ border: 2px solid var(--ds-color-border);
5711
+ z-index: 1;
5712
+ }
5713
+
5714
+ /* Dot state: completed */
5715
+ .ds-timeline__item--completed .ds-timeline__dot {
5716
+ background-color: var(--ds-color-success);
5717
+ border-color: var(--ds-color-success);
5718
+ }
5719
+
5720
+ /* Dot state: current */
5721
+ .ds-timeline__item--current .ds-timeline__dot {
5722
+ background-color: var(--ds-color-inverted);
5723
+ border-color: var(--ds-color-inverted);
5724
+ }
5725
+
5726
+ /* Dot state: error */
5727
+ .ds-timeline__item--error .ds-timeline__dot {
5728
+ background-color: var(--ds-color-error);
5729
+ border-color: var(--ds-color-error);
5730
+ }
5731
+
5732
+ /* Content area */
5733
+ .ds-timeline__content {
5734
+ /* no additional layout needed; flows naturally after the dot */
5735
+ }
5736
+
5737
+ /* Title */
5738
+ .ds-timeline__title {
5739
+ font-size: var(--ds-text-sm);
5740
+ font-weight: var(--ds-weight-medium);
5741
+ color: var(--ds-color-text);
5742
+ }
5743
+
5744
+ /* Description */
5745
+ .ds-timeline__description {
5746
+ font-size: var(--ds-text-sm);
5747
+ color: var(--ds-color-text-secondary);
5748
+ margin-block-start: var(--ds-space-1);
5749
+ }
5750
+
5751
+ /* Timestamp */
5752
+ .ds-timeline__time {
5753
+ font-size: var(--ds-text-xs);
5754
+ color: var(--ds-color-text-tertiary);
5755
+ margin-block-start: var(--ds-space-0-5);
5756
+ }
5757
+
5758
+ /* Compact variant */
5759
+ .ds-timeline--compact {
5760
+ padding-inline-start: var(--ds-space-6);
5761
+ }
5762
+
5763
+ .ds-timeline--compact::before {
5764
+ inset-inline-start: 0.6rem;
5765
+ }
5766
+
5767
+ .ds-timeline--compact .ds-timeline__item {
5768
+ padding-block-end: var(--ds-space-4);
5769
+ }
5770
+
5771
+ .ds-timeline--compact .ds-timeline__item:last-child {
5772
+ padding-block-end: 0;
5773
+ }
5774
+
5775
+ .ds-timeline--compact .ds-timeline__dot {
5776
+ inset-inline-start: calc(-1 * var(--ds-space-6) + 0.25rem);
5777
+ width: 0.75rem;
5778
+ height: 0.75rem;
5779
+ }
5780
+
5781
+ /* ==========================================================================
5782
+ Kbd
5783
+ ==========================================================================
5784
+ Keyboard shortcut display component with a raised 3D key appearance.
5785
+ Use individually or group multiple keys with ds-kbd-group for combos
5786
+ like "Cmd + K".
5787
+
5788
+ Usage:
5789
+ <kbd class="ds-kbd">Esc</kbd>
5790
+
5791
+ <span class="ds-kbd-group">
5792
+ <kbd class="ds-kbd">Cmd</kbd>
5793
+ <span class="ds-kbd-group__separator">+</span>
5794
+ <kbd class="ds-kbd">K</kbd>
5795
+ </span>
5796
+
5797
+ Modifiers:
5798
+ .ds-kbd--lg — Larger key display
5799
+ ========================================================================== */
5800
+
5801
+ .ds-kbd {
5802
+ display: inline-flex;
5803
+ align-items: center;
5804
+ gap: var(--ds-space-1);
5805
+ padding: var(--ds-space-0-5) var(--ds-space-1-5);
5806
+ font-family: var(--ds-font-mono);
5807
+ font-size: var(--ds-text-xs);
5808
+ background-color: var(--ds-color-bg-elevated);
5809
+ border: 1px solid var(--ds-color-border);
5810
+ border-block-end-width: 2px;
5811
+ border-radius: var(--ds-radius-sm);
5812
+ color: var(--ds-color-text-secondary);
5813
+ line-height: 1;
5814
+ vertical-align: baseline;
5815
+ }
5816
+
5817
+ /* Large variant */
5818
+ .ds-kbd--lg {
5819
+ font-size: var(--ds-text-sm);
5820
+ padding: var(--ds-space-1) var(--ds-space-2);
5821
+ }
5822
+
5823
+ /* Key group (for combos like Cmd + K) */
5824
+ .ds-kbd-group {
5825
+ display: inline-flex;
5826
+ align-items: center;
5827
+ gap: var(--ds-space-1);
5828
+ }
5829
+
5830
+ /* Separator between keys */
5831
+ .ds-kbd-group__separator {
5832
+ font-size: var(--ds-text-xs);
5833
+ color: var(--ds-color-text-tertiary);
5834
+ }
5835
+
5836
+ /* ==========================================================================
5837
+ Command Palette
5838
+ ==========================================================================
5839
+ Command palette / search overlay following the Cmd+K pattern. Provides a
5840
+ full-screen overlay with a centered search dialog, grouped results,
5841
+ keyboard-navigable items, and a footer with shortcut hints.
5842
+
5843
+ ARIA requirements (consumer responsibility):
5844
+ - Input: role="combobox", aria-expanded="true", aria-controls="[list-id]"
5845
+ - Input: aria-activedescendant="[active-item-id]" for keyboard nav
5846
+ - List: role="listbox", id matching aria-controls
5847
+ - Items: role="option", id for aria-activedescendant
5848
+ - Groups: role="group", aria-labelledby="[heading-id]"
5849
+ - Overlay: role="dialog", aria-modal="true", aria-label="Command palette"
5850
+ - Keyboard: ArrowUp/Down to navigate, Enter to select, Escape to close
5851
+
5852
+ Usage:
5853
+ <div class="ds-command ds-command--open" role="dialog" aria-modal="true" aria-label="Command palette">
5854
+ <div class="ds-command__content">
5855
+ <div class="ds-command__input-wrapper">
5856
+ <span class="ds-command__input-icon">...</span>
5857
+ <input class="ds-command__input" role="combobox" placeholder="Search..." />
5858
+ </div>
5859
+ <div class="ds-command__list" role="listbox">
5860
+ <div class="ds-command__group">
5861
+ <div class="ds-command__group-heading">Results</div>
5862
+ <div class="ds-command__item ds-command__item--active" role="option">
5863
+ <span class="ds-command__item-icon">...</span>
5864
+ <span class="ds-command__item-label">Item</span>
5865
+ <span class="ds-command__item-shortcut">Ctrl+N</span>
5866
+ </div>
5867
+ </div>
5868
+ <div class="ds-command__empty">No results found.</div>
5869
+ </div>
5870
+ <div class="ds-command__footer">...</div>
5871
+ </div>
5872
+ </div>
5873
+
5874
+ Modifiers:
5875
+ .ds-command--open — Shows the command palette
5876
+ ========================================================================== */
5877
+
5878
+ /* Overlay */
5879
+ .ds-command {
5880
+ position: fixed;
5881
+ inset: 0;
5882
+ z-index: var(--ds-z-modal);
5883
+ display: flex;
5884
+ align-items: flex-start;
5885
+ justify-content: center;
5886
+ padding-block-start: 20vh;
5887
+ background-color: var(--ds-color-overlay);
5888
+ opacity: 0;
5889
+ visibility: hidden;
5890
+ transition:
5891
+ opacity var(--ds-duration-fast) var(--ds-ease),
5892
+ visibility var(--ds-duration-fast) var(--ds-ease);
4745
5893
  }
4746
5894
 
4747
- .ds-popover__content {
4748
- position: absolute;
4749
- z-index: var(--ds-z-dropdown);
5895
+ /* Open state */
5896
+ .ds-command--open {
5897
+ opacity: 1;
5898
+ visibility: visible;
5899
+ }
5900
+
5901
+ /* Content panel */
5902
+ .ds-command__content {
4750
5903
  background-color: var(--ds-color-surface);
4751
5904
  border: 1px solid var(--ds-color-border);
4752
5905
  border-radius: var(--ds-radius-xl);
4753
5906
  box-shadow: var(--ds-shadow-lg);
4754
- padding: var(--ds-space-4);
4755
- min-width: 16rem;
4756
- opacity: 0;
4757
- visibility: hidden;
4758
- transform: scale(0.96);
5907
+ width: 100%;
5908
+ max-width: 32rem;
5909
+ overflow: hidden;
5910
+ transform: scale(0.96) translateY(-8px);
4759
5911
  transition:
4760
- opacity var(--ds-duration-fast) var(--ds-ease),
4761
- visibility var(--ds-duration-fast) var(--ds-ease),
4762
5912
  transform var(--ds-duration-fast) var(--ds-ease-out-expo);
4763
5913
  }
4764
5914
 
4765
- /* Open state */
4766
- .ds-popover--open .ds-popover__content {
4767
- opacity: 1;
4768
- visibility: visible;
4769
- transform: scale(1);
5915
+ .ds-command--open .ds-command__content {
5916
+ transform: scale(1) translateY(0);
4770
5917
  }
4771
5918
 
4772
- /* Placement: bottom (default) */
4773
- .ds-popover__content,
4774
- .ds-popover--bottom .ds-popover__content {
4775
- top: calc(100% + var(--ds-offset-md));
4776
- left: 50%;
4777
- transform: translateX(-50%) scale(0.96);
5919
+ /* Input wrapper */
5920
+ .ds-command__input-wrapper {
5921
+ display: flex;
5922
+ align-items: center;
5923
+ gap: var(--ds-space-3);
5924
+ padding: var(--ds-space-3) var(--ds-space-4);
5925
+ border-block-end: 1px solid var(--ds-color-border);
4778
5926
  }
4779
5927
 
4780
- .ds-popover--open .ds-popover__content,
4781
- .ds-popover--bottom.ds-popover--open .ds-popover__content {
4782
- transform: translateX(-50%) scale(1);
5928
+ /* Search icon */
5929
+ .ds-command__input-icon {
5930
+ color: var(--ds-color-text-tertiary);
5931
+ flex-shrink: 0;
4783
5932
  }
4784
5933
 
4785
- /* Placement: top */
4786
- .ds-popover--top .ds-popover__content {
4787
- bottom: calc(100% + var(--ds-offset-md));
4788
- top: auto;
4789
- left: 50%;
4790
- transform: translateX(-50%) scale(0.96);
5934
+ /* Search input */
5935
+ .ds-command__input {
5936
+ flex: 1;
5937
+ background-color: transparent;
5938
+ border: none;
5939
+ outline: none;
5940
+ font-size: var(--ds-text-base);
5941
+ color: var(--ds-color-text);
5942
+ font-family: var(--ds-font-sans);
4791
5943
  }
4792
5944
 
4793
- .ds-popover--top.ds-popover--open .ds-popover__content {
4794
- transform: translateX(-50%) scale(1);
5945
+ .ds-command__input::placeholder {
5946
+ color: var(--ds-color-text-tertiary);
4795
5947
  }
4796
5948
 
4797
- /* Placement: left */
4798
- .ds-popover--left .ds-popover__content {
4799
- right: calc(100% + var(--ds-offset-md));
4800
- top: 50%;
4801
- left: auto;
4802
- transform: translateY(-50%) scale(0.96);
5949
+ /* Results list */
5950
+ .ds-command__list {
5951
+ max-height: 20rem;
5952
+ overflow-y: auto;
5953
+ padding: var(--ds-space-2);
4803
5954
  }
4804
5955
 
4805
- .ds-popover--left.ds-popover--open .ds-popover__content {
4806
- transform: translateY(-50%) scale(1);
5956
+ /* Group of results */
5957
+ .ds-command__group {
5958
+ /* structural grouping — no additional visual styles needed */
4807
5959
  }
4808
5960
 
4809
- /* Placement: right */
4810
- .ds-popover--right .ds-popover__content {
4811
- left: calc(100% + var(--ds-offset-md));
4812
- top: 50%;
4813
- transform: translateY(-50%) scale(0.96);
5961
+ /* Group heading */
5962
+ .ds-command__group-heading {
5963
+ font-size: var(--ds-text-xs);
5964
+ text-transform: uppercase;
5965
+ letter-spacing: var(--ds-tracking-wide);
5966
+ color: var(--ds-color-text-tertiary);
5967
+ padding: var(--ds-space-2) var(--ds-space-3);
5968
+ font-weight: var(--ds-weight-medium);
5969
+ }
5970
+
5971
+ /* Individual item */
5972
+ .ds-command__item {
5973
+ display: flex;
5974
+ align-items: center;
5975
+ gap: var(--ds-space-3);
5976
+ padding: var(--ds-space-2-5) var(--ds-space-3);
5977
+ border-radius: var(--ds-radius-lg);
5978
+ cursor: pointer;
5979
+ font-size: var(--ds-text-sm);
5980
+ color: var(--ds-color-text-secondary);
5981
+ transition:
5982
+ background-color var(--ds-duration-fast) var(--ds-ease),
5983
+ color var(--ds-duration-fast) var(--ds-ease);
5984
+ }
5985
+
5986
+ /* Item hover & active states */
5987
+ .ds-command__item:hover,
5988
+ .ds-command__item--active {
5989
+ background-color: var(--ds-color-bg-elevated);
5990
+ color: var(--ds-color-text);
5991
+ }
5992
+ .ds-command__item:focus-visible {
5993
+ outline: none;
5994
+ box-shadow: inset 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
5995
+ scroll-margin-block: var(--ds-space-16, 4rem);
5996
+ }
5997
+
5998
+ /* Item icon */
5999
+ .ds-command__item-icon {
6000
+ flex-shrink: 0;
6001
+ color: var(--ds-color-text-tertiary);
6002
+ }
6003
+
6004
+ /* Item label */
6005
+ .ds-command__item-label {
6006
+ flex: 1;
6007
+ }
6008
+
6009
+ /* Item shortcut hint */
6010
+ .ds-command__item-shortcut {
6011
+ font-size: var(--ds-text-xs);
6012
+ color: var(--ds-color-text-tertiary);
6013
+ flex-shrink: 0;
6014
+ }
6015
+
6016
+ /* Empty state */
6017
+ .ds-command__empty {
6018
+ padding: var(--ds-space-8);
6019
+ text-align: center;
6020
+ font-size: var(--ds-text-sm);
6021
+ color: var(--ds-color-text-tertiary);
6022
+ }
6023
+
6024
+ /* Footer */
6025
+ .ds-command__footer {
6026
+ display: flex;
6027
+ align-items: center;
6028
+ gap: var(--ds-space-4);
6029
+ padding: var(--ds-space-2) var(--ds-space-3);
6030
+ border-block-start: 1px solid var(--ds-color-border);
6031
+ font-size: var(--ds-text-xs);
6032
+ color: var(--ds-color-text-tertiary);
6033
+ }
6034
+
6035
+ /* ==========================================================================
6036
+ Component: Search
6037
+ Inline search bar with dropdown results, keyboard navigation, and
6038
+ responsive mobile expansion. Distinct from ds-command (modal overlay).
6039
+
6040
+ ARIA requirements (consumer responsibility):
6041
+ - Container: role="search" on wrapper or parent <form>
6042
+ - Input: role="combobox", aria-expanded="true|false", aria-controls="[list-id]"
6043
+ - Dropdown: role="listbox" with id matching aria-controls
6044
+ - Results: role="option", aria-selected="true" on active result
6045
+ - Clear button: aria-label="Clear search"
6046
+ - Keyboard: ArrowUp/Down to navigate, Enter to select, Escape to close
6047
+
6048
+ Usage:
6049
+ <div class="ds-search" role="search">
6050
+ <span class="ds-search__icon">...</span>
6051
+ <input class="ds-search__input" placeholder="Search..." />
6052
+ <kbd class="ds-search__shortcut">Ctrl+K</kbd>
6053
+ <button class="ds-search__clear" aria-label="Clear search">...</button>
6054
+ </div>
6055
+
6056
+ Modifiers:
6057
+ .ds-search--mobile-expanded — Fullscreen bar on mobile
6058
+ .ds-search__dropdown--mobile — Fullscreen dropdown on mobile
6059
+
6060
+ Mobile:
6061
+ .ds-search-mobile-trigger — Icon button shown on mobile (hidden desktop)
6062
+ .ds-search__close — Close button inside expanded mobile bar
6063
+ ========================================================================== */
6064
+
6065
+ .ds-search {
6066
+ position: relative;
6067
+ display: flex;
6068
+ align-items: center;
6069
+ gap: var(--ds-space-1);
6070
+ width: 100%;
6071
+ padding: var(--ds-space-2) var(--ds-space-2-5);
6072
+ background: var(--ds-color-surface);
6073
+ border: 1px solid var(--ds-color-border);
6074
+ border-radius: var(--ds-radius-lg);
6075
+ transition:
6076
+ border-color var(--ds-duration-fast) var(--ds-ease-default),
6077
+ box-shadow var(--ds-duration-fast) var(--ds-ease-default);
6078
+ }
6079
+
6080
+ .ds-search:focus-within {
6081
+ border-color: var(--ds-color-border-active);
6082
+ box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
6083
+ }
6084
+
6085
+ /* Icon */
6086
+ .ds-search__icon {
6087
+ color: var(--ds-color-text-tertiary);
6088
+ flex-shrink: 0;
6089
+ }
6090
+
6091
+ /* Input */
6092
+ .ds-search__input {
6093
+ flex: 1;
6094
+ min-width: 0;
6095
+ border: none;
6096
+ outline: none;
6097
+ background: transparent;
6098
+ color: var(--ds-color-text);
6099
+ font-size: var(--ds-text-base);
6100
+ font-family: inherit;
6101
+ line-height: 1.25;
6102
+ }
6103
+
6104
+ .ds-search__input::placeholder {
6105
+ color: var(--ds-color-text-tertiary);
6106
+ }
6107
+
6108
+ /* Keyboard shortcut hint */
6109
+ .ds-search__shortcut {
6110
+ flex-shrink: 0;
6111
+ padding: 0.125rem 0.375rem;
6112
+ font-size: var(--ds-text-2xs);
6113
+ font-family: inherit;
6114
+ line-height: 1;
6115
+ color: var(--ds-color-text-tertiary);
6116
+ background: var(--ds-color-bg-elevated);
6117
+ border: 1px solid var(--ds-color-border);
6118
+ border-radius: var(--ds-radius-sm);
6119
+ pointer-events: none;
6120
+ user-select: none;
6121
+ }
6122
+
6123
+ /* Clear button */
6124
+ .ds-search__clear {
6125
+ display: flex;
6126
+ align-items: center;
6127
+ justify-content: center;
6128
+ flex-shrink: 0;
6129
+ width: 1.25rem;
6130
+ height: 1.25rem;
6131
+ min-width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
6132
+ min-height: 1.5rem;
6133
+ border: none;
6134
+ border-radius: var(--ds-radius-sm);
6135
+ background: transparent;
6136
+ color: var(--ds-color-text-tertiary);
6137
+ cursor: pointer;
6138
+ transition:
6139
+ color var(--ds-duration-fast) var(--ds-ease-default),
6140
+ background-color var(--ds-duration-fast) var(--ds-ease-default);
6141
+ }
6142
+
6143
+ .ds-search__clear:hover {
6144
+ background: var(--ds-color-surface-hover);
6145
+ color: var(--ds-color-text-secondary);
6146
+ }
6147
+
6148
+ /* Dropdown results panel */
6149
+ .ds-search__dropdown {
6150
+ position: fixed;
6151
+ max-height: min(22rem, calc(100dvh - 5rem));
6152
+ overflow-y: auto;
6153
+ background: var(--ds-color-bg-elevated);
6154
+ border: 1px solid var(--ds-color-border);
6155
+ border-radius: var(--ds-radius-lg);
6156
+ box-shadow: var(--ds-shadow-lg);
6157
+ z-index: var(--ds-z-tooltip);
6158
+ padding: var(--ds-space-1) 0;
4814
6159
  }
4815
6160
 
4816
- .ds-popover--right.ds-popover--open .ds-popover__content {
4817
- transform: translateY(-50%) scale(1);
6161
+ /* Result groups */
6162
+ .ds-search__group {
6163
+ padding: var(--ds-space-1) 0;
4818
6164
  }
4819
6165
 
4820
- /* Size: small */
4821
- .ds-popover__content--sm {
4822
- min-width: 12rem;
4823
- padding: var(--ds-space-3);
6166
+ .ds-search__group + .ds-search__group {
6167
+ border-block-start: 1px solid var(--ds-color-border);
4824
6168
  }
4825
6169
 
4826
- /* Size: large */
4827
- .ds-popover__content--lg {
4828
- min-width: 24rem;
6170
+ .ds-search__group-label {
6171
+ display: block;
6172
+ padding: var(--ds-space-1) var(--ds-space-3);
6173
+ font-size: var(--ds-text-2xs);
6174
+ font-weight: var(--ds-weight-medium);
6175
+ color: var(--ds-color-text-tertiary);
6176
+ text-transform: uppercase;
6177
+ letter-spacing: 0.04em;
4829
6178
  }
4830
6179
 
4831
- /* ==========================================================================
4832
- Slider
4833
- ==========================================================================
4834
- Range slider input component with customizable thumb and track styling.
4835
- Supports labels, value display, size variants, and disabled state.
4836
-
4837
- Usage:
4838
- <div class="ds-slider">
4839
- <input type="range" min="0" max="100" value="50" />
4840
- <div class="ds-slider__labels">
4841
- <span>0</span>
4842
- <span>100</span>
4843
- </div>
4844
- </div>
4845
-
4846
- Modifiers:
4847
- .ds-slider--sm — Smaller track and thumb
4848
- .ds-slider--disabled — Reduced opacity, no interaction
4849
- ========================================================================== */
4850
-
4851
- .ds-slider {
6180
+ /* Individual result */
6181
+ .ds-search__result {
6182
+ display: flex;
6183
+ align-items: center;
6184
+ gap: var(--ds-space-2);
4852
6185
  width: 100%;
4853
- position: relative;
6186
+ padding: var(--ds-space-2-5) var(--ds-space-3);
6187
+ border: none;
6188
+ background: transparent;
6189
+ color: var(--ds-color-text);
6190
+ font-size: var(--ds-text-sm);
6191
+ font-family: inherit;
6192
+ text-align: start;
6193
+ cursor: pointer;
6194
+ transition: background-color var(--ds-duration-fast) var(--ds-ease-default);
4854
6195
  }
4855
6196
 
4856
- .ds-slider input[type="range"] {
4857
- -webkit-appearance: none;
4858
- -moz-appearance: none;
4859
- appearance: none;
4860
- width: 100%;
4861
- height: 0.375rem;
4862
- background-color: var(--ds-color-bg-muted);
4863
- border-radius: var(--ds-radius-full);
4864
- outline: none;
4865
- cursor: pointer;
6197
+ .ds-search__result:hover,
6198
+ .ds-search__result--active {
6199
+ background: var(--ds-color-surface-hover);
4866
6200
  }
4867
6201
 
4868
- /* Webkit thumb */
4869
- .ds-slider input[type="range"]::-webkit-slider-thumb {
4870
- -webkit-appearance: none;
4871
- appearance: none;
4872
- width: 1.25rem;
4873
- height: 1.25rem;
4874
- border-radius: var(--ds-radius-full);
4875
- background-color: var(--ds-color-inverted);
4876
- border: 2px solid var(--ds-color-surface);
4877
- box-shadow: var(--ds-shadow-sm);
4878
- cursor: pointer;
4879
- transition: transform var(--ds-duration-fast) var(--ds-ease);
6202
+ .ds-search__result:focus-visible {
6203
+ outline: none;
6204
+ box-shadow: inset 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
6205
+ scroll-margin-block: var(--ds-space-16, 4rem);
4880
6206
  }
4881
6207
 
4882
- /* Firefox thumb */
4883
- .ds-slider input[type="range"]::-moz-range-thumb {
4884
- width: 1.25rem;
4885
- height: 1.25rem;
4886
- border-radius: var(--ds-radius-full);
4887
- background-color: var(--ds-color-inverted);
4888
- border: 2px solid var(--ds-color-surface);
4889
- box-shadow: var(--ds-shadow-sm);
4890
- cursor: pointer;
4891
- transition: transform var(--ds-duration-fast) var(--ds-ease);
6208
+ .ds-search__result-icon {
6209
+ color: var(--ds-color-text-tertiary);
6210
+ flex-shrink: 0;
4892
6211
  }
4893
6212
 
4894
- /* Hover: scale up thumb */
4895
- .ds-slider input[type="range"]:hover::-webkit-slider-thumb {
4896
- transform: scale(1.1);
6213
+ .ds-search__result-db-icon {
6214
+ display: flex;
6215
+ align-items: center;
6216
+ justify-content: center;
6217
+ width: 1.5rem;
6218
+ height: 1.5rem;
6219
+ border-radius: var(--ds-radius-md);
6220
+ flex-shrink: 0;
4897
6221
  }
4898
6222
 
4899
- .ds-slider input[type="range"]:hover::-moz-range-thumb {
4900
- transform: scale(1.1);
6223
+ .ds-search__result-content {
6224
+ display: flex;
6225
+ flex-direction: column;
6226
+ min-width: 0;
4901
6227
  }
4902
6228
 
4903
- /* Focus ring */
4904
- .ds-slider input[type="range"]:focus-visible::-webkit-slider-thumb {
4905
- outline: var(--ds-ring-width) solid var(--ds-ring-color);
4906
- outline-offset: 0;
6229
+ .ds-search__result-title {
6230
+ white-space: nowrap;
6231
+ overflow: hidden;
6232
+ text-overflow: ellipsis;
6233
+ min-width: 0;
6234
+ line-height: var(--ds-leading-snug);
4907
6235
  }
4908
6236
 
4909
- .ds-slider input[type="range"]:focus-visible::-moz-range-thumb {
4910
- outline: var(--ds-ring-width) solid var(--ds-ring-color);
4911
- outline-offset: 0;
6237
+ .ds-search__result-meta {
6238
+ font-size: var(--ds-text-2xs);
6239
+ line-height: var(--ds-leading-snug);
6240
+ color: var(--ds-color-text-tertiary);
6241
+ white-space: nowrap;
6242
+ overflow: hidden;
6243
+ text-overflow: ellipsis;
4912
6244
  }
4913
6245
 
4914
- /* Labels row */
4915
- .ds-slider__labels {
6246
+ /* Empty state */
6247
+ .ds-search__empty {
4916
6248
  display: flex;
4917
- justify-content: space-between;
4918
- font-size: var(--ds-text-xs);
6249
+ align-items: center;
6250
+ justify-content: center;
6251
+ padding: var(--ds-space-4) var(--ds-space-2);
4919
6252
  color: var(--ds-color-text-tertiary);
4920
- margin-top: var(--ds-space-1);
6253
+ font-size: var(--ds-text-sm);
4921
6254
  }
4922
6255
 
4923
- /* Current value display */
4924
- .ds-slider__value {
4925
- font-size: var(--ds-text-sm);
4926
- font-weight: var(--ds-weight-medium);
6256
+ /* Mobile trigger (hidden on desktop) */
6257
+ .ds-search-mobile-trigger {
6258
+ display: none;
6259
+ align-items: center;
6260
+ justify-content: center;
6261
+ width: 2.25rem;
6262
+ height: 2.25rem;
6263
+ border: none;
6264
+ border-radius: var(--ds-radius-md);
6265
+ background: transparent;
4927
6266
  color: var(--ds-color-text);
6267
+ cursor: pointer;
6268
+ flex-shrink: 0;
6269
+ transition: background-color var(--ds-duration-fast) var(--ds-ease-default);
4928
6270
  }
4929
6271
 
4930
- /* Size: small */
4931
- .ds-slider--sm input[type="range"] {
4932
- height: 0.25rem;
6272
+ @media (hover: hover) {
6273
+ .ds-search-mobile-trigger:hover {
6274
+ background-color: var(--ds-color-surface-hover);
6275
+ }
4933
6276
  }
4934
6277
 
4935
- .ds-slider--sm input[type="range"]::-webkit-slider-thumb {
4936
- width: 1rem;
4937
- height: 1rem;
6278
+ /* Close button (mobile expanded) */
6279
+ .ds-search__close {
6280
+ display: flex;
6281
+ align-items: center;
6282
+ justify-content: center;
6283
+ width: 2.25rem;
6284
+ height: 2.25rem;
6285
+ border: none;
6286
+ border-radius: var(--ds-radius-md);
6287
+ background: transparent;
6288
+ color: var(--ds-color-text);
6289
+ cursor: pointer;
6290
+ flex-shrink: 0;
4938
6291
  }
4939
6292
 
4940
- .ds-slider--sm input[type="range"]::-moz-range-thumb {
4941
- width: 1rem;
4942
- height: 1rem;
4943
- }
6293
+ /* Responsive: mobile */
6294
+ @media (max-width: 1023px) /* below --ds-breakpoint-lg */ {
6295
+ .ds-search__shortcut { display: none; }
6296
+ .ds-search-mobile-trigger { display: flex; }
6297
+ .ds-search { display: none; }
4944
6298
 
4945
- /* Disabled state */
4946
- .ds-slider--disabled {
4947
- opacity: var(--ds-opacity-disabled);
4948
- cursor: not-allowed;
4949
- }
6299
+ .ds-search--mobile-expanded {
6300
+ display: flex;
6301
+ position: fixed;
6302
+ inset-block-start: 0;
6303
+ inset-inline-start: 0;
6304
+ inset-inline-end: 0;
6305
+ height: var(--ds-search-bar-height, 3.5rem);
6306
+ z-index: var(--ds-z-dropdown);
6307
+ border: none;
6308
+ border-block-end: 1px solid var(--ds-color-border);
6309
+ border-radius: 0;
6310
+ padding: 0 var(--ds-space-3);
6311
+ background: var(--ds-color-nav-bg);
6312
+ backdrop-filter: blur(20px) saturate(1.5);
6313
+ -webkit-backdrop-filter: blur(20px) saturate(1.5);
6314
+ }
4950
6315
 
4951
- .ds-slider--disabled input[type="range"] {
4952
- cursor: not-allowed;
4953
- pointer-events: none;
6316
+ .ds-search__dropdown--mobile {
6317
+ inset-inline-start: 0 !important;
6318
+ inset-inline-end: 0;
6319
+ width: auto !important;
6320
+ border-radius: 0;
6321
+ border-inline-start: none;
6322
+ border-inline-end: none;
6323
+ max-height: calc(100dvh - var(--ds-search-bar-height, 3.5rem));
6324
+ background: var(--ds-color-surface);
6325
+ backdrop-filter: blur(20px) saturate(1.5);
6326
+ -webkit-backdrop-filter: blur(20px) saturate(1.5);
6327
+ }
4954
6328
  }
4955
6329
 
4956
6330
  /* ==========================================================================
4957
- Timeline
4958
- ==========================================================================
4959
- Vertical timeline component for roadmaps, activity feeds, and step-based
4960
- flows. Each item has a dot indicator on a vertical line with support for
4961
- completed, current, and error states.
6331
+ Component: Toolbar
6332
+ Fixed bar with rows of actions: toolbar buttons, filter chips, segmented
6333
+ controls, and scrollable groups. Common in data-heavy UIs below a header.
4962
6334
 
4963
6335
  Usage:
4964
- <div class="ds-timeline">
4965
- <div class="ds-timeline__item ds-timeline__item--completed">
4966
- <div class="ds-timeline__dot"></div>
4967
- <div class="ds-timeline__content">
4968
- <div class="ds-timeline__title">Step one</div>
4969
- <div class="ds-timeline__description">Details here.</div>
4970
- <div class="ds-timeline__time">2 hours ago</div>
6336
+ <div class="ds-toolbar">
6337
+ <div class="ds-toolbar__row">
6338
+ <div class="ds-toolbar__group">...</div>
6339
+ <div class="ds-toolbar__spacer"></div>
6340
+ <div class="ds-toolbar__group">
6341
+ <button class="ds-toolbar__btn ds-toolbar__btn--active">
6342
+ <span>Filter</span>
6343
+ <span class="ds-toolbar__badge">3</span>
6344
+ </button>
6345
+ <button class="ds-toolbar__btn">Sort</button>
4971
6346
  </div>
4972
6347
  </div>
4973
6348
  </div>
4974
6349
 
4975
6350
  Modifiers:
4976
- .ds-timeline__item--completed Green dot (success)
4977
- .ds-timeline__item--current Inverted dot (active)
4978
- .ds-timeline__item--error Red dot (error)
4979
- .ds-timeline--compact — Tighter spacing, smaller dots
6351
+ .ds-toolbar__row--scroll Horizontally scrollable row
6352
+ .ds-toolbar__btn--active Active/toggled button
6353
+ .ds-toolbar__group--scroll Scrollable group within a row
6354
+
6355
+ Segmented control:
6356
+ <div class="ds-toolbar__segmented">
6357
+ <button class="ds-toolbar__segmented-btn ds-toolbar__segmented-btn--active">Past</button>
6358
+ <button class="ds-toolbar__segmented-btn">Future</button>
6359
+ </div>
4980
6360
  ========================================================================== */
4981
6361
 
4982
- .ds-timeline {
4983
- position: relative;
4984
- padding-left: var(--ds-space-8);
6362
+ /* Row container */
6363
+ .ds-toolbar__row {
6364
+ display: flex;
6365
+ align-items: center;
6366
+ height: var(--ds-toolbar-row-height, 2.5rem);
6367
+ gap: var(--ds-space-3);
6368
+ padding-inline: var(--ds-space-4);
4985
6369
  }
4986
6370
 
4987
- /* Vertical line */
4988
- .ds-timeline::before {
4989
- content: "";
4990
- position: absolute;
4991
- left: 0.9rem;
4992
- top: 0;
4993
- bottom: 0;
4994
- width: 2px;
4995
- background-color: var(--ds-color-border);
6371
+ .ds-toolbar__row--scroll {
6372
+ overflow-x: auto;
6373
+ scrollbar-width: none;
4996
6374
  }
4997
6375
 
4998
- /* Item */
4999
- .ds-timeline__item {
5000
- position: relative;
5001
- padding-bottom: var(--ds-space-6);
6376
+ .ds-toolbar__row--scroll::-webkit-scrollbar { display: none; }
6377
+
6378
+ /* Group (flex row of items) */
6379
+ .ds-toolbar__group {
6380
+ display: flex;
6381
+ align-items: center;
6382
+ flex-shrink: 0;
5002
6383
  }
5003
6384
 
5004
- .ds-timeline__item:last-child {
5005
- padding-bottom: 0;
6385
+ .ds-toolbar__group--scroll {
6386
+ flex-shrink: 1;
6387
+ min-width: 0;
6388
+ overflow-x: auto;
6389
+ scrollbar-width: none;
5006
6390
  }
5007
6391
 
5008
- /* Dot */
5009
- .ds-timeline__dot {
5010
- position: absolute;
5011
- left: calc(-1 * var(--ds-space-8) + 0.5rem);
5012
- width: 1rem;
5013
- height: 1rem;
5014
- border-radius: var(--ds-radius-full);
5015
- background-color: var(--ds-color-bg-elevated);
5016
- border: 2px solid var(--ds-color-border);
5017
- z-index: 1;
6392
+ .ds-toolbar__group--scroll::-webkit-scrollbar { display: none; }
6393
+
6394
+ /* Spacer */
6395
+ .ds-toolbar__spacer {
6396
+ flex: 1 1 0;
5018
6397
  }
5019
6398
 
5020
- /* Dot state: completed */
5021
- .ds-timeline__item--completed .ds-timeline__dot {
5022
- background-color: var(--ds-color-success);
5023
- border-color: var(--ds-color-success);
6399
+ /* Toolbar button */
6400
+ .ds-toolbar__btn {
6401
+ display: inline-flex;
6402
+ align-items: center;
6403
+ gap: var(--ds-space-1-5);
6404
+ padding: var(--ds-space-1) var(--ds-space-2-5);
6405
+ font-size: var(--ds-text-xs);
6406
+ font-weight: var(--ds-weight-medium);
6407
+ font-family: inherit;
6408
+ color: var(--ds-color-text-tertiary);
6409
+ background: transparent;
6410
+ border: 1px solid transparent;
6411
+ border-radius: var(--ds-radius-md);
6412
+ cursor: pointer;
6413
+ white-space: nowrap;
6414
+ transition:
6415
+ color var(--ds-duration-fast) var(--ds-ease-default),
6416
+ background-color var(--ds-duration-fast) var(--ds-ease-default),
6417
+ border-color var(--ds-duration-fast) var(--ds-ease-default);
5024
6418
  }
5025
6419
 
5026
- /* Dot state: current */
5027
- .ds-timeline__item--current .ds-timeline__dot {
5028
- background-color: var(--ds-color-inverted);
5029
- border-color: var(--ds-color-inverted);
6420
+ .ds-toolbar__btn:hover {
6421
+ color: var(--ds-color-text-secondary);
6422
+ background: var(--ds-color-surface-hover);
6423
+ }
6424
+
6425
+ .ds-toolbar__btn:focus-visible {
6426
+ outline: none;
6427
+ box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
6428
+ }
6429
+
6430
+ .ds-toolbar__btn--active {
6431
+ color: var(--ds-color-text);
6432
+ background: var(--ds-color-surface);
6433
+ border-color: var(--ds-color-border);
5030
6434
  }
5031
6435
 
5032
- /* Dot state: error */
5033
- .ds-timeline__item--error .ds-timeline__dot {
5034
- background-color: var(--ds-color-error);
5035
- border-color: var(--ds-color-error);
6436
+ /* Badge (count indicator inside button) */
6437
+ .ds-toolbar__badge {
6438
+ display: inline-flex;
6439
+ align-items: center;
6440
+ justify-content: center;
6441
+ min-width: 1rem;
6442
+ height: 1rem;
6443
+ padding: 0 0.25rem;
6444
+ font-size: 0.625rem;
6445
+ font-weight: 600;
6446
+ line-height: 1;
6447
+ color: var(--ds-color-on-inverted);
6448
+ background: var(--ds-color-interactive);
6449
+ border-radius: var(--ds-radius-full);
5036
6450
  }
5037
6451
 
5038
- /* Content area */
5039
- .ds-timeline__content {
5040
- /* no additional layout needed; flows naturally after the dot */
6452
+ /* Eye toggle (visibility) */
6453
+ .ds-toolbar__eye {
6454
+ display: inline-flex;
6455
+ align-items: center;
6456
+ cursor: pointer;
6457
+ opacity: 0.7;
6458
+ transition: opacity var(--ds-duration-fast) var(--ds-ease-default);
5041
6459
  }
5042
6460
 
5043
- /* Title */
5044
- .ds-timeline__title {
5045
- font-size: var(--ds-text-sm);
5046
- font-weight: var(--ds-weight-medium);
5047
- color: var(--ds-color-text);
6461
+ .ds-toolbar__eye:hover {
6462
+ opacity: 1;
5048
6463
  }
5049
6464
 
5050
- /* Description */
5051
- .ds-timeline__description {
5052
- font-size: var(--ds-text-sm);
5053
- color: var(--ds-color-text-secondary);
5054
- margin-top: var(--ds-space-1);
6465
+ /* Segmented control */
6466
+ .ds-toolbar__segmented {
6467
+ flex-shrink: 0;
6468
+ display: flex;
6469
+ background: var(--ds-color-surface);
6470
+ border-radius: var(--ds-radius-md);
6471
+ padding: 2px;
5055
6472
  }
5056
6473
 
5057
- /* Timestamp */
5058
- .ds-timeline__time {
6474
+ .ds-toolbar__segmented-btn {
6475
+ padding: var(--ds-space-1) var(--ds-space-3);
5059
6476
  font-size: var(--ds-text-xs);
6477
+ font-weight: var(--ds-weight-medium);
5060
6478
  color: var(--ds-color-text-tertiary);
5061
- margin-top: var(--ds-space-0-5);
6479
+ border: none;
6480
+ border-radius: var(--ds-radius-sm);
6481
+ background: transparent;
6482
+ cursor: pointer;
6483
+ transition:
6484
+ color var(--ds-duration-fast) var(--ds-ease-default),
6485
+ background-color var(--ds-duration-fast) var(--ds-ease-default);
5062
6486
  }
5063
6487
 
5064
- /* Compact variant */
5065
- .ds-timeline--compact {
5066
- padding-left: var(--ds-space-6);
6488
+ .ds-toolbar__segmented-btn:hover {
6489
+ color: var(--ds-color-text-secondary);
5067
6490
  }
5068
6491
 
5069
- .ds-timeline--compact::before {
5070
- left: 0.6rem;
6492
+ .ds-toolbar__segmented-btn--active {
6493
+ background: var(--ds-color-bg-elevated);
6494
+ color: var(--ds-color-text);
5071
6495
  }
5072
6496
 
5073
- .ds-timeline--compact .ds-timeline__item {
5074
- padding-bottom: var(--ds-space-4);
5075
- }
6497
+ /* Responsive: mobile */
6498
+ @media (max-width: 1023px) /* below --ds-breakpoint-lg */ {
6499
+ .ds-toolbar__row {
6500
+ padding-inline: var(--ds-space-3);
6501
+ gap: var(--ds-space-2);
6502
+ }
5076
6503
 
5077
- .ds-timeline--compact .ds-timeline__item:last-child {
5078
- padding-bottom: 0;
5079
- }
6504
+ .ds-toolbar__btn-label { display: none; }
5080
6505
 
5081
- .ds-timeline--compact .ds-timeline__dot {
5082
- left: calc(-1 * var(--ds-space-6) + 0.25rem);
5083
- width: 0.75rem;
5084
- height: 0.75rem;
6506
+ .ds-toolbar__btn {
6507
+ padding: var(--ds-space-2);
6508
+ min-width: var(--ds-size-2);
6509
+ min-height: var(--ds-size-2);
6510
+ justify-content: center;
6511
+ }
6512
+
6513
+ .ds-toolbar__badge {
6514
+ font-size: 0.5rem;
6515
+ min-width: 0.875rem;
6516
+ height: 0.875rem;
6517
+ }
5085
6518
  }
5086
6519
 
5087
6520
  /* ==========================================================================
5088
- Kbd
5089
- ==========================================================================
5090
- Keyboard shortcut display component with a raised 3D key appearance.
5091
- Use individually or group multiple keys with ds-kbd-group for combos
5092
- like "Cmd + K".
6521
+ Component: Chip
6522
+ Interactive filter/sort chips with optional remove button. Distinct from
6523
+ ds-tag (which is a static label). Chips represent active filter state.
5093
6524
 
5094
- Usage:
5095
- <kbd class="ds-kbd">Esc</kbd>
6525
+ ARIA requirements (consumer responsibility):
6526
+ - Remove button: add aria-label="Remove [filter name]"
6527
+ - Logic chip (AND/OR): use aria-pressed="true|false" on <button>
6528
+ - Chip group: wrap in role="group" with aria-label="Active filters"
5096
6529
 
5097
- <span class="ds-kbd-group">
5098
- <kbd class="ds-kbd">Cmd</kbd>
5099
- <span class="ds-kbd-group__separator">+</span>
5100
- <kbd class="ds-kbd">K</kbd>
6530
+ Usage:
6531
+ <span class="ds-chip">
6532
+ Status: Active
6533
+ <button class="ds-chip__remove" aria-label="Remove Status filter">×</button>
5101
6534
  </span>
6535
+ <button class="ds-chip ds-chip--logic">AND</button>
6536
+ <span class="ds-chip ds-chip--sort">Date ↑</span>
5102
6537
 
5103
6538
  Modifiers:
5104
- .ds-kbd--lgLarger key display
6539
+ .ds-chip--logicAND/OR toggle chip (clickable, no bg)
6540
+ .ds-chip--sort — Sort indicator (dashed border)
5105
6541
  ========================================================================== */
5106
6542
 
5107
- .ds-kbd {
6543
+ .ds-chip {
5108
6544
  display: inline-flex;
5109
6545
  align-items: center;
5110
6546
  gap: var(--ds-space-1);
5111
- padding: var(--ds-space-0-5) var(--ds-space-1-5);
5112
- font-family: var(--ds-font-mono);
6547
+ padding: var(--ds-space-0-5) var(--ds-space-2);
5113
6548
  font-size: var(--ds-text-xs);
5114
- background-color: var(--ds-color-bg-elevated);
5115
- border: 1px solid var(--ds-color-border);
5116
- border-bottom-width: 2px;
5117
- border-radius: var(--ds-radius-sm);
6549
+ font-family: inherit;
5118
6550
  color: var(--ds-color-text-secondary);
5119
- line-height: 1;
5120
- vertical-align: baseline;
6551
+ background: var(--ds-color-bg-elevated);
6552
+ border: 1px solid var(--ds-color-border);
6553
+ border-radius: var(--ds-radius-full);
6554
+ white-space: nowrap;
5121
6555
  }
5122
6556
 
5123
- /* Large variant */
5124
- .ds-kbd--lg {
5125
- font-size: var(--ds-text-sm);
5126
- padding: var(--ds-space-1) var(--ds-space-2);
6557
+ /* Logic chip (AND/OR toggle) */
6558
+ .ds-chip--logic {
6559
+ cursor: pointer;
6560
+ font-weight: 600;
6561
+ color: var(--ds-color-text-tertiary);
6562
+ background: transparent;
6563
+ border-color: var(--ds-color-border);
6564
+ transition: background-color var(--ds-duration-fast) var(--ds-ease-default);
5127
6565
  }
5128
6566
 
5129
- /* Key group (for combos like Cmd + K) */
5130
- .ds-kbd-group {
6567
+ .ds-chip--logic:hover {
6568
+ background: var(--ds-color-overlay-hover);
6569
+ }
6570
+
6571
+ /* Sort chip (dashed border) */
6572
+ .ds-chip--sort {
6573
+ border-style: dashed;
6574
+ }
6575
+
6576
+ /* Remove button */
6577
+ .ds-chip__remove {
5131
6578
  display: inline-flex;
5132
6579
  align-items: center;
5133
- gap: var(--ds-space-1);
6580
+ justify-content: center;
6581
+ width: 14px;
6582
+ height: 14px;
6583
+ min-width: 1.5rem; /* WCAG 2.5.8: minimum 24px target */
6584
+ min-height: 1.5rem;
6585
+ border: none;
6586
+ border-radius: var(--ds-radius-full);
6587
+ background: transparent;
6588
+ color: var(--ds-color-text-tertiary);
6589
+ cursor: pointer;
6590
+ transition:
6591
+ color var(--ds-duration-fast) var(--ds-ease-default),
6592
+ background-color var(--ds-duration-fast) var(--ds-ease-default);
5134
6593
  }
5135
6594
 
5136
- /* Separator between keys */
5137
- .ds-kbd-group__separator {
5138
- font-size: var(--ds-text-xs);
5139
- color: var(--ds-color-text-tertiary);
6595
+ .ds-chip__remove:hover {
6596
+ color: var(--ds-color-text);
6597
+ background: var(--ds-color-overlay-hover);
5140
6598
  }
5141
6599
 
5142
6600
  /* ==========================================================================
5143
- Command Palette
5144
- ==========================================================================
5145
- Command palette / search overlay following the Cmd+K pattern. Provides a
5146
- full-screen overlay with a centered search dialog, grouped results,
5147
- keyboard-navigable items, and a footer with shortcut hints.
6601
+ Component: Icon Button
6602
+ Standalone icon button for actions, toolbar items, and table rows.
6603
+ A minimal interactive element that wraps a single icon.
6604
+
6605
+ ARIA requirements (consumer responsibility):
6606
+ - Always add aria-label="[action]" (e.g., aria-label="Delete")
6607
+ - Disabled: add aria-disabled="true"
6608
+ - Toggle: use aria-pressed="true|false" for toggle icon buttons
5148
6609
 
5149
6610
  Usage:
5150
- <div class="ds-command ds-command--open">
5151
- <div class="ds-command__content">
5152
- <div class="ds-command__input-wrapper">
5153
- <span class="ds-command__input-icon">...</span>
5154
- <input class="ds-command__input" placeholder="Search..." />
5155
- </div>
5156
- <div class="ds-command__list">
5157
- <div class="ds-command__group">
5158
- <div class="ds-command__group-heading">Results</div>
5159
- <div class="ds-command__item ds-command__item--active">
5160
- <span class="ds-command__item-icon">...</span>
5161
- <span class="ds-command__item-label">Item</span>
5162
- <span class="ds-command__item-shortcut">Ctrl+N</span>
5163
- </div>
5164
- </div>
5165
- <div class="ds-command__empty">No results found.</div>
5166
- </div>
5167
- <div class="ds-command__footer">...</div>
5168
- </div>
5169
- </div>
6611
+ <button class="ds-icon-btn" aria-label="Delete">
6612
+ <svg>...</svg>
6613
+ </button>
6614
+ <button class="ds-icon-btn ds-icon-btn--sm" aria-label="Edit">...</button>
6615
+ <button class="ds-icon-btn ds-icon-btn--danger" aria-label="Remove">...</button>
6616
+
6617
+ Sizes:
6618
+ .ds-icon-btn--xs — 24px (var(--ds-size-1))
6619
+ .ds-icon-btn--sm — 28px
6620
+ (default) — 32px (var(--ds-size-2))
6621
+ .ds-icon-btn--lg — 40px (var(--ds-size-3))
5170
6622
 
5171
6623
  Modifiers:
5172
- .ds-command--openShows the command palette
6624
+ .ds-icon-btn--dangerError color on hover
6625
+ .ds-icon-btn--ghost — No background change, color-only transition
5173
6626
  ========================================================================== */
5174
6627
 
5175
- /* Overlay */
5176
- .ds-command {
5177
- position: fixed;
5178
- inset: 0;
5179
- z-index: var(--ds-z-modal);
5180
- display: flex;
5181
- align-items: flex-start;
6628
+ .ds-icon-btn {
6629
+ display: inline-flex;
6630
+ align-items: center;
5182
6631
  justify-content: center;
5183
- padding-top: 20vh;
5184
- background-color: var(--ds-color-overlay);
5185
- opacity: 0;
5186
- visibility: hidden;
6632
+ width: var(--ds-size-2);
6633
+ height: var(--ds-size-2);
6634
+ padding: var(--ds-space-1-5);
6635
+ border: none;
6636
+ border-radius: var(--ds-radius-lg);
6637
+ background: transparent;
6638
+ color: var(--ds-color-text-tertiary);
6639
+ cursor: pointer;
6640
+ flex-shrink: 0;
5187
6641
  transition:
5188
- opacity var(--ds-duration-fast) var(--ds-ease),
5189
- visibility var(--ds-duration-fast) var(--ds-ease);
6642
+ color var(--ds-duration-fast) var(--ds-ease-default),
6643
+ background-color var(--ds-duration-fast) var(--ds-ease-default);
5190
6644
  }
5191
6645
 
5192
- /* Open state */
5193
- .ds-command--open {
5194
- opacity: 1;
5195
- visibility: visible;
6646
+ .ds-icon-btn:hover {
6647
+ background-color: var(--ds-color-surface-hover);
6648
+ color: var(--ds-color-text-secondary);
5196
6649
  }
5197
6650
 
5198
- /* Content panel */
5199
- .ds-command__content {
5200
- background-color: var(--ds-color-surface);
5201
- border: 1px solid var(--ds-color-border);
5202
- border-radius: var(--ds-radius-xl);
5203
- box-shadow: var(--ds-shadow-lg);
5204
- width: 100%;
5205
- max-width: 32rem;
5206
- overflow: hidden;
5207
- transform: scale(0.96) translateY(-8px);
5208
- transition:
5209
- transform var(--ds-duration-fast) var(--ds-ease-out-expo);
6651
+ .ds-icon-btn:focus-visible {
6652
+ outline: none;
6653
+ box-shadow: 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
6654
+ scroll-margin-block: var(--ds-space-16, 4rem);
5210
6655
  }
5211
6656
 
5212
- .ds-command--open .ds-command__content {
5213
- transform: scale(1) translateY(0);
6657
+ .ds-icon-btn:disabled,
6658
+ .ds-icon-btn[aria-disabled="true"] {
6659
+ opacity: var(--ds-opacity-disabled);
6660
+ cursor: not-allowed;
6661
+ pointer-events: none;
5214
6662
  }
5215
6663
 
5216
- /* Input wrapper */
5217
- .ds-command__input-wrapper {
5218
- display: flex;
5219
- align-items: center;
5220
- gap: var(--ds-space-3);
5221
- padding: var(--ds-space-3) var(--ds-space-4);
5222
- border-bottom: 1px solid var(--ds-color-border);
6664
+ /* Sizes */
6665
+ .ds-icon-btn--xs {
6666
+ width: var(--ds-size-1);
6667
+ height: var(--ds-size-1);
6668
+ padding: var(--ds-space-0-5);
6669
+ border-radius: var(--ds-radius-md);
5223
6670
  }
5224
6671
 
5225
- /* Search icon */
5226
- .ds-command__input-icon {
5227
- color: var(--ds-color-text-tertiary);
5228
- flex-shrink: 0;
6672
+ .ds-icon-btn--sm {
6673
+ width: var(--ds-size-2);
6674
+ height: var(--ds-size-2);
6675
+ padding: var(--ds-space-1-5);
6676
+ border-radius: var(--ds-radius-md);
5229
6677
  }
5230
6678
 
5231
- /* Search input */
5232
- .ds-command__input {
6679
+ .ds-icon-btn--lg {
6680
+ width: var(--ds-size-3);
6681
+ height: var(--ds-size-3);
6682
+ padding: var(--ds-space-2);
6683
+ }
6684
+
6685
+ /* Danger variant */
6686
+ .ds-icon-btn--danger:hover {
6687
+ color: var(--ds-color-error);
6688
+ background-color: color-mix(in srgb, var(--ds-color-error) 10%, transparent);
6689
+ }
6690
+
6691
+ /* Ghost variant (color only, no bg) */
6692
+ .ds-icon-btn--ghost {
6693
+ padding: 0;
6694
+ width: auto;
6695
+ height: auto;
6696
+ }
6697
+
6698
+ .ds-icon-btn--ghost:hover {
6699
+ background: transparent;
6700
+ color: var(--ds-color-text);
6701
+ }
6702
+
6703
+ /* ==========================================================================
6704
+ Component: Bottom Nav
6705
+ Mobile bottom navigation bar. Hidden on desktop, shown on mobile.
6706
+ Fixed to bottom with safe-area insets for notched devices.
6707
+
6708
+ ARIA requirements (consumer responsibility):
6709
+ - Container: <nav aria-label="Bottom navigation">
6710
+ - Active item: aria-current="page" on the current tab
6711
+ - Badge: aria-label on badge parent (e.g., "Home, 3 notifications")
6712
+ - Create button: aria-label="Create new"
6713
+
6714
+ Usage:
6715
+ <nav class="ds-bottom-nav" aria-label="Bottom navigation">
6716
+ <a href="/" class="ds-bottom-nav__item ds-bottom-nav__item--active" aria-current="page">
6717
+ <span class="ds-bottom-nav__icon">
6718
+ <svg>...</svg>
6719
+ <span class="ds-bottom-nav__badge">3</span>
6720
+ </span>
6721
+ <span class="ds-bottom-nav__label">Home</span>
6722
+ </a>
6723
+ <button class="ds-bottom-nav__item ds-bottom-nav__item--create" aria-label="Create new">
6724
+ <span class="ds-bottom-nav__create-icon">+</span>
6725
+ <span class="ds-bottom-nav__label">New</span>
6726
+ </button>
6727
+ </nav>
6728
+
6729
+ Modifiers:
6730
+ .ds-bottom-nav__item--active — Active/current tab
6731
+ .ds-bottom-nav__item--create — Elevated center action button
6732
+ ========================================================================== */
6733
+
6734
+ .ds-bottom-nav {
6735
+ display: none;
6736
+ position: fixed;
6737
+ inset-block-end: 0;
6738
+ inset-inline-start: 0;
6739
+ inset-inline-end: 0;
6740
+ z-index: var(--ds-z-dropdown);
6741
+ background: var(--ds-color-surface);
6742
+ border-block-start: 1px solid var(--ds-color-border);
6743
+ padding-block-end: env(safe-area-inset-bottom, 0px);
6744
+ }
6745
+
6746
+ @media (max-width: 1023px) /* below --ds-breakpoint-lg */ {
6747
+ .ds-bottom-nav {
6748
+ display: flex;
6749
+ justify-content: space-around;
6750
+ align-items: stretch;
6751
+ }
6752
+ }
6753
+
6754
+ @media (min-width: 1024px) /* --ds-breakpoint-lg */ {
6755
+ .ds-bottom-nav { display: none !important; }
6756
+ }
6757
+
6758
+ /* Nav item */
6759
+ .ds-bottom-nav__item {
6760
+ display: flex;
6761
+ flex-direction: column;
6762
+ align-items: center;
6763
+ justify-content: center;
6764
+ gap: var(--ds-space-1);
5233
6765
  flex: 1;
5234
- background-color: transparent;
6766
+ padding: var(--ds-space-2) 0;
6767
+ color: var(--ds-color-text-tertiary);
6768
+ text-decoration: none;
5235
6769
  border: none;
5236
- outline: none;
5237
- font-size: var(--ds-text-base);
5238
- color: var(--ds-color-text);
5239
- font-family: var(--ds-font-sans);
6770
+ background: none;
6771
+ cursor: pointer;
6772
+ font: inherit;
6773
+ transition: color var(--ds-duration-fast) var(--ds-ease-default);
6774
+ -webkit-tap-highlight-color: transparent;
5240
6775
  }
5241
6776
 
5242
- .ds-command__input::placeholder {
5243
- color: var(--ds-color-text-tertiary);
6777
+ .ds-bottom-nav__item--active {
6778
+ color: var(--ds-color-text);
5244
6779
  }
5245
6780
 
5246
- /* Results list */
5247
- .ds-command__list {
5248
- max-height: 20rem;
5249
- overflow-y: auto;
5250
- padding: var(--ds-space-2);
6781
+ /* Icon wrapper */
6782
+ .ds-bottom-nav__icon {
6783
+ position: relative;
6784
+ display: flex;
6785
+ align-items: center;
6786
+ justify-content: center;
5251
6787
  }
5252
6788
 
5253
- /* Group of results */
5254
- .ds-command__group {
5255
- /* structural grouping — no additional visual styles needed */
6789
+ /* Notification badge */
6790
+ .ds-bottom-nav__badge {
6791
+ position: absolute;
6792
+ inset-block-start: calc(-1 * var(--ds-space-1));
6793
+ inset-inline-end: calc(-1 * var(--ds-space-2));
6794
+ min-width: var(--ds-space-4);
6795
+ height: var(--ds-space-4);
6796
+ padding: 0 var(--ds-space-1);
6797
+ border-radius: var(--ds-radius-full);
6798
+ background: var(--ds-color-error);
6799
+ color: var(--ds-color-on-inverted);
6800
+ font-size: var(--ds-text-2xs);
6801
+ font-weight: var(--ds-weight-semibold);
6802
+ line-height: var(--ds-space-4);
6803
+ text-align: center;
5256
6804
  }
5257
6805
 
5258
- /* Group heading */
5259
- .ds-command__group-heading {
5260
- font-size: var(--ds-text-xs);
5261
- text-transform: uppercase;
5262
- letter-spacing: var(--ds-tracking-wide);
5263
- color: var(--ds-color-text-tertiary);
5264
- padding: var(--ds-space-2) var(--ds-space-3);
6806
+ /* Label */
6807
+ .ds-bottom-nav__label {
6808
+ font-size: var(--ds-text-2xs);
5265
6809
  font-weight: var(--ds-weight-medium);
6810
+ letter-spacing: var(--ds-tracking-wide);
5266
6811
  }
5267
6812
 
5268
- /* Individual item */
5269
- .ds-command__item {
6813
+ /* Create button — elevated center icon */
6814
+ .ds-bottom-nav__create-icon {
5270
6815
  display: flex;
5271
6816
  align-items: center;
5272
- gap: var(--ds-space-3);
5273
- padding: var(--ds-space-2-5) var(--ds-space-3);
5274
- border-radius: var(--ds-radius-lg);
5275
- cursor: pointer;
5276
- font-size: var(--ds-text-sm);
5277
- color: var(--ds-color-text-secondary);
5278
- transition:
5279
- background-color var(--ds-duration-fast) var(--ds-ease),
5280
- color var(--ds-duration-fast) var(--ds-ease);
6817
+ justify-content: center;
6818
+ width: 2.25rem;
6819
+ height: 2.25rem;
6820
+ border-radius: var(--ds-radius-full);
6821
+ background: var(--ds-color-text);
6822
+ color: var(--ds-color-bg-base);
5281
6823
  }
5282
6824
 
5283
- /* Item hover & active states */
5284
- .ds-command__item:hover,
5285
- .ds-command__item--active {
5286
- background-color: var(--ds-color-bg-elevated);
5287
- color: var(--ds-color-text);
6825
+ .ds-bottom-nav__item--create {
6826
+ color: var(--ds-color-text-tertiary);
5288
6827
  }
5289
- .ds-command__item:focus-visible {
5290
- outline: none;
5291
- box-shadow: inset 0 0 0 var(--ds-ring-width) var(--ds-ring-color);
6828
+
6829
+ /* ==========================================================================
6830
+ Component: Spinner
6831
+ CSS-only loading spinner using border animation.
6832
+
6833
+ Usage:
6834
+ <span class="ds-spinner"></span>
6835
+ <span class="ds-spinner ds-spinner--sm"></span>
6836
+ <span class="ds-spinner ds-spinner--muted"></span>
6837
+
6838
+ Sizes:
6839
+ .ds-spinner--sm — 1.25rem (20px)
6840
+ (default) — 1.5rem (24px)
6841
+ .ds-spinner--lg — 2rem (32px)
6842
+
6843
+ Variants:
6844
+ (default) — Border + interactive top
6845
+ .ds-spinner--muted — Inverted colors (for dark backgrounds)
6846
+ .ds-spinner--light — Light colors (for inverted/colored backgrounds)
6847
+ ========================================================================== */
6848
+
6849
+ .ds-spinner {
6850
+ display: inline-block;
6851
+ width: 1.5rem;
6852
+ height: 1.5rem;
6853
+ border: 2px solid var(--ds-color-border);
6854
+ border-top-color: var(--ds-color-interactive);
6855
+ border-radius: var(--ds-radius-full);
6856
+ animation: ds-spin 0.8s linear infinite;
5292
6857
  }
5293
6858
 
5294
- /* Item icon */
5295
- .ds-command__item-icon {
5296
- flex-shrink: 0;
5297
- color: var(--ds-color-text-tertiary);
6859
+ @keyframes ds-spin {
6860
+ to { transform: rotate(360deg); }
5298
6861
  }
5299
6862
 
5300
- /* Item label */
5301
- .ds-command__item-label {
5302
- flex: 1;
6863
+ /* Sizes */
6864
+ .ds-spinner--sm {
6865
+ width: 1rem;
6866
+ height: 1rem;
5303
6867
  }
5304
6868
 
5305
- /* Item shortcut hint */
5306
- .ds-command__item-shortcut {
5307
- font-size: var(--ds-text-xs);
5308
- color: var(--ds-color-text-tertiary);
5309
- flex-shrink: 0;
6869
+ .ds-spinner--md {
6870
+ width: 1.25rem;
6871
+ height: 1.25rem;
5310
6872
  }
5311
6873
 
5312
- /* Empty state */
5313
- .ds-command__empty {
5314
- padding: var(--ds-space-8);
5315
- text-align: center;
5316
- font-size: var(--ds-text-sm);
5317
- color: var(--ds-color-text-tertiary);
6874
+ .ds-spinner--lg {
6875
+ width: 2rem;
6876
+ height: 2rem;
5318
6877
  }
5319
6878
 
5320
- /* Footer */
5321
- .ds-command__footer {
5322
- display: flex;
5323
- align-items: center;
5324
- gap: var(--ds-space-4);
5325
- padding: var(--ds-space-2) var(--ds-space-3);
5326
- border-top: 1px solid var(--ds-color-border);
5327
- font-size: var(--ds-text-xs);
5328
- color: var(--ds-color-text-tertiary);
6879
+ /* Color variants */
6880
+ .ds-spinner--muted {
6881
+ border-color: var(--ds-color-inverted);
6882
+ border-top-color: transparent;
5329
6883
  }
5330
6884
 
6885
+ .ds-spinner--light {
6886
+ border-color: var(--ds-color-on-inverted);
6887
+ border-top-color: transparent;
6888
+ }
5331
6889
 
6890
+
6891
+ }
6892
+ @layer utilities {
5332
6893
  /* ==========================================================================
5333
6894
  Utilities: Layout
5334
6895
  Container with clamp padding, generous responsive grid.
@@ -5409,7 +6970,7 @@ tr:hover .ds-sortable__handle,
5409
6970
  .ds-md\:flex-row { flex-direction: row; }
5410
6971
  }
5411
6972
 
5412
- @media (min-width: 1024px) {
6973
+ @media (min-width: 1024px) /* --ds-breakpoint-lg */ {
5413
6974
  .ds-lg\:grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
5414
6975
  .ds-lg\:grid-cols-3 { grid-template-columns: repeat(3, 1fr); }
5415
6976
  .ds-lg\:grid-cols-4 { grid-template-columns: repeat(4, 1fr); }
@@ -5448,7 +7009,7 @@ tr:hover .ds-sortable__handle,
5448
7009
  .ds-relative { position: relative; }
5449
7010
  .ds-absolute { position: absolute; }
5450
7011
  .ds-fixed { position: fixed; }
5451
- .ds-sticky { position: sticky; top: 0; }
7012
+ .ds-sticky { position: sticky; inset-block-start: 0; }
5452
7013
 
5453
7014
  /* --- Overflow --- */
5454
7015
  .ds-overflow-hidden { overflow: hidden; }
@@ -5464,14 +7025,14 @@ tr:hover .ds-sortable__handle,
5464
7025
  /* --- Stack (vertical spacing) ---
5465
7026
  :where() lowers specificity so ds-mt-* can override individual children. */
5466
7027
  :where(.ds-stack) > * + * {
5467
- margin-top: var(--ds-space-4);
7028
+ margin-block-start: var(--ds-space-4);
5468
7029
  }
5469
7030
 
5470
- :where(.ds-stack--sm) > * + * { margin-top: var(--ds-space-2); }
5471
- :where(.ds-stack--lg) > * + * { margin-top: var(--ds-space-8); }
5472
- :where(.ds-stack--xl) > * + * { margin-top: var(--ds-space-12); }
5473
- :where(.ds-stack--2xl) > * + * { margin-top: var(--ds-space-16); }
5474
- :where(.ds-stack--3xl) > * + * { margin-top: var(--ds-space-24); }
7031
+ :where(.ds-stack--sm) > * + * { margin-block-start: var(--ds-space-2); }
7032
+ :where(.ds-stack--lg) > * + * { margin-block-start: var(--ds-space-8); }
7033
+ :where(.ds-stack--xl) > * + * { margin-block-start: var(--ds-space-12); }
7034
+ :where(.ds-stack--2xl) > * + * { margin-block-start: var(--ds-space-16); }
7035
+ :where(.ds-stack--3xl) > * + * { margin-block-start: var(--ds-space-24); }
5475
7036
 
5476
7037
  /* --- Center content --- */
5477
7038
  .ds-center {
@@ -5524,22 +7085,22 @@ tr:hover .ds-sortable__handle,
5524
7085
 
5525
7086
  /* --- Inset / Position Values --- */
5526
7087
  .ds-inset-0 { inset: 0; }
5527
- .ds-inset-x-0 { left: 0; right: 0; }
5528
- .ds-inset-y-0 { top: 0; bottom: 0; }
5529
- .ds-top-0 { top: 0; }
5530
- .ds-top-full { top: 100%; }
5531
- .ds-right-0 { right: 0; }
5532
- .ds-bottom-0 { bottom: 0; }
5533
- .ds-bottom-full { bottom: 100%; }
5534
- .ds-left-0 { left: 0; }
5535
- .ds-top-2 { top: var(--ds-space-2); }
5536
- .ds-right-2 { right: var(--ds-space-2); }
5537
- .ds-left-2 { left: var(--ds-space-2); }
5538
- .ds-top-3 { top: var(--ds-space-3); }
5539
- .ds-right-3 { right: var(--ds-space-3); }
5540
- .ds-left-3 { left: var(--ds-space-3); }
5541
- .ds-top-1\/2 { top: 50%; }
5542
- .ds-left-1\/2 { left: 50%; }
7088
+ .ds-inset-x-0 { inset-inline-start: 0; inset-inline-end: 0; }
7089
+ .ds-inset-y-0 { inset-block-start: 0; inset-block-end: 0; }
7090
+ .ds-top-0 { inset-block-start: 0; }
7091
+ .ds-top-full { inset-block-start: 100%; }
7092
+ .ds-right-0 { inset-inline-end: 0; }
7093
+ .ds-bottom-0 { inset-block-end: 0; }
7094
+ .ds-bottom-full { inset-block-end: 100%; }
7095
+ .ds-left-0 { inset-inline-start: 0; }
7096
+ .ds-top-2 { inset-block-start: var(--ds-space-2); }
7097
+ .ds-right-2 { inset-inline-end: var(--ds-space-2); }
7098
+ .ds-left-2 { inset-inline-start: var(--ds-space-2); }
7099
+ .ds-top-3 { inset-block-start: var(--ds-space-3); }
7100
+ .ds-right-3 { inset-inline-end: var(--ds-space-3); }
7101
+ .ds-left-3 { inset-inline-start: var(--ds-space-3); }
7102
+ .ds-top-1\/2 { inset-block-start: 50%; }
7103
+ .ds-left-1\/2 { inset-inline-start: 50%; }
5543
7104
 
5544
7105
  /* --- Object Fit --- */
5545
7106
  .ds-object-cover { object-fit: cover; }
@@ -5557,7 +7118,7 @@ tr:hover .ds-sortable__handle,
5557
7118
  .ds-sm\:table-cell { display: table-cell; }
5558
7119
  }
5559
7120
 
5560
- @media (min-width: 1024px) {
7121
+ @media (min-width: 1024px) /* --ds-breakpoint-lg */ {
5561
7122
  .ds-lg\:hidden { display: none; }
5562
7123
  .ds-lg\:block { display: block; }
5563
7124
  .ds-lg\:flex { display: flex; }
@@ -5568,7 +7129,7 @@ tr:hover .ds-sortable__handle,
5568
7129
  .ds-sm\:grid-cols-4 { grid-template-columns: repeat(4, 1fr); }
5569
7130
  }
5570
7131
 
5571
- @media (min-width: 1024px) {
7132
+ @media (min-width: 1024px) /* --ds-breakpoint-lg */ {
5572
7133
  .ds-lg\:grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
5573
7134
  .ds-lg\:grid-cols-5 { grid-template-columns: repeat(5, 1fr); }
5574
7135
  }
@@ -5620,15 +7181,15 @@ tr:hover .ds-sortable__handle,
5620
7181
  .ds-py-20 { padding-block: var(--ds-space-20); }
5621
7182
  .ds-py-24 { padding-block: var(--ds-space-24); }
5622
7183
 
5623
- .ds-pt-2 { padding-top: var(--ds-space-2); }
5624
- .ds-pt-4 { padding-top: var(--ds-space-4); }
5625
- .ds-pt-6 { padding-top: var(--ds-space-6); }
5626
- .ds-pt-8 { padding-top: var(--ds-space-8); }
5627
- .ds-pt-12 { padding-top: var(--ds-space-12); }
5628
- .ds-pt-16 { padding-top: var(--ds-space-16); }
7184
+ .ds-pt-2 { padding-block-start: var(--ds-space-2); }
7185
+ .ds-pt-4 { padding-block-start: var(--ds-space-4); }
7186
+ .ds-pt-6 { padding-block-start: var(--ds-space-6); }
7187
+ .ds-pt-8 { padding-block-start: var(--ds-space-8); }
7188
+ .ds-pt-12 { padding-block-start: var(--ds-space-12); }
7189
+ .ds-pt-16 { padding-block-start: var(--ds-space-16); }
5629
7190
 
5630
- .ds-pb-4 { padding-bottom: var(--ds-space-4); }
5631
- .ds-pb-8 { padding-bottom: var(--ds-space-8); }
7191
+ .ds-pb-4 { padding-block-end: var(--ds-space-4); }
7192
+ .ds-pb-8 { padding-block-end: var(--ds-space-8); }
5632
7193
 
5633
7194
  /* --- Margin --- */
5634
7195
  .ds-m-0 { margin: var(--ds-space-0); }
@@ -5644,33 +7205,33 @@ tr:hover .ds-sortable__handle,
5644
7205
  .ds-my-6 { margin-block: var(--ds-space-6); }
5645
7206
  .ds-my-8 { margin-block: var(--ds-space-8); }
5646
7207
 
5647
- .ds-mt-0 { margin-top: var(--ds-space-0); }
5648
- .ds-mt-1 { margin-top: var(--ds-space-1); }
5649
- .ds-mt-2 { margin-top: var(--ds-space-2); }
5650
- .ds-mt-4 { margin-top: var(--ds-space-4); }
5651
- .ds-mt-6 { margin-top: var(--ds-space-6); }
5652
- .ds-mt-8 { margin-top: var(--ds-space-8); }
5653
-
5654
- .ds-mb-0 { margin-bottom: var(--ds-space-0); }
5655
- .ds-mb-1 { margin-bottom: var(--ds-space-1); }
5656
- .ds-mb-2 { margin-bottom: var(--ds-space-2); }
5657
- .ds-mb-4 { margin-bottom: var(--ds-space-4); }
5658
- .ds-mb-6 { margin-bottom: var(--ds-space-6); }
5659
- .ds-mb-8 { margin-bottom: var(--ds-space-8); }
5660
-
5661
- .ds-mt-auto { margin-top: auto; }
5662
- .ds-mb-auto { margin-bottom: auto; }
5663
- .ds-ml-auto { margin-left: auto; }
5664
- .ds-mr-auto { margin-right: auto; }
5665
- .ds-ml-2 { margin-left: var(--ds-space-2); }
5666
- .ds-ml-4 { margin-left: var(--ds-space-4); }
5667
- .ds-mr-2 { margin-right: var(--ds-space-2); }
5668
- .ds-mr-4 { margin-right: var(--ds-space-4); }
5669
-
5670
- .ds-mt-0\.5 { margin-top: var(--ds-space-0-5); }
5671
- .ds-mt-3 { margin-top: var(--ds-space-3); }
5672
- .ds-mb-0\.5 { margin-bottom: var(--ds-space-0-5); }
5673
- .ds-mb-3 { margin-bottom: var(--ds-space-3); }
7208
+ .ds-mt-0 { margin-block-start: var(--ds-space-0); }
7209
+ .ds-mt-1 { margin-block-start: var(--ds-space-1); }
7210
+ .ds-mt-2 { margin-block-start: var(--ds-space-2); }
7211
+ .ds-mt-4 { margin-block-start: var(--ds-space-4); }
7212
+ .ds-mt-6 { margin-block-start: var(--ds-space-6); }
7213
+ .ds-mt-8 { margin-block-start: var(--ds-space-8); }
7214
+
7215
+ .ds-mb-0 { margin-block-end: var(--ds-space-0); }
7216
+ .ds-mb-1 { margin-block-end: var(--ds-space-1); }
7217
+ .ds-mb-2 { margin-block-end: var(--ds-space-2); }
7218
+ .ds-mb-4 { margin-block-end: var(--ds-space-4); }
7219
+ .ds-mb-6 { margin-block-end: var(--ds-space-6); }
7220
+ .ds-mb-8 { margin-block-end: var(--ds-space-8); }
7221
+
7222
+ .ds-mt-auto { margin-block-start: auto; }
7223
+ .ds-mb-auto { margin-block-end: auto; }
7224
+ .ds-ml-auto { margin-inline-start: auto; }
7225
+ .ds-mr-auto { margin-inline-end: auto; }
7226
+ .ds-ml-2 { margin-inline-start: var(--ds-space-2); }
7227
+ .ds-ml-4 { margin-inline-start: var(--ds-space-4); }
7228
+ .ds-mr-2 { margin-inline-end: var(--ds-space-2); }
7229
+ .ds-mr-4 { margin-inline-end: var(--ds-space-4); }
7230
+
7231
+ .ds-mt-0\.5 { margin-block-start: var(--ds-space-0-5); }
7232
+ .ds-mt-3 { margin-block-start: var(--ds-space-3); }
7233
+ .ds-mb-0\.5 { margin-block-end: var(--ds-space-0-5); }
7234
+ .ds-mb-3 { margin-block-end: var(--ds-space-3); }
5674
7235
 
5675
7236
  /* --- Padding (granular) --- */
5676
7237
  .ds-p-0\.5 { padding: var(--ds-space-0-5); }
@@ -5683,29 +7244,29 @@ tr:hover .ds-sortable__handle,
5683
7244
  .ds-py-1 { padding-block: var(--ds-space-1); }
5684
7245
  .ds-py-1\.5 { padding-block: var(--ds-space-1-5); }
5685
7246
 
5686
- .ds-pb-2 { padding-bottom: var(--ds-space-2); }
5687
- .ds-pb-6 { padding-bottom: var(--ds-space-6); }
7247
+ .ds-pb-2 { padding-block-end: var(--ds-space-2); }
7248
+ .ds-pb-6 { padding-block-end: var(--ds-space-6); }
5688
7249
 
5689
- .ds-pl-2 { padding-left: var(--ds-space-2); }
5690
- .ds-pl-3 { padding-left: var(--ds-space-3); }
5691
- .ds-pl-4 { padding-left: var(--ds-space-4); }
5692
- .ds-pr-2 { padding-right: var(--ds-space-2); }
5693
- .ds-pr-3 { padding-right: var(--ds-space-3); }
5694
- .ds-pr-4 { padding-right: var(--ds-space-4); }
5695
- .ds-pr-7 { padding-right: 1.75rem; }
5696
- .ds-pr-10 { padding-right: var(--ds-space-10); }
7250
+ .ds-pl-2 { padding-inline-start: var(--ds-space-2); }
7251
+ .ds-pl-3 { padding-inline-start: var(--ds-space-3); }
7252
+ .ds-pl-4 { padding-inline-start: var(--ds-space-4); }
7253
+ .ds-pr-2 { padding-inline-end: var(--ds-space-2); }
7254
+ .ds-pr-3 { padding-inline-end: var(--ds-space-3); }
7255
+ .ds-pr-4 { padding-inline-end: var(--ds-space-4); }
7256
+ .ds-pr-7 { padding-inline-end: 1.75rem; }
7257
+ .ds-pr-10 { padding-inline-end: var(--ds-space-10); }
5697
7258
 
5698
7259
  /* --- Gap (fractional) --- */
5699
7260
  .ds-gap-0\.5 { gap: var(--ds-space-0-5); }
5700
7261
  .ds-gap-1\.5 { gap: var(--ds-space-1-5); }
5701
7262
 
5702
7263
  /* --- Space-Y (owl selector) --- */
5703
- .ds-space-y-1 > * + * { margin-top: var(--ds-space-1); }
5704
- .ds-space-y-2 > * + * { margin-top: var(--ds-space-2); }
5705
- .ds-space-y-3 > * + * { margin-top: var(--ds-space-3); }
5706
- .ds-space-y-4 > * + * { margin-top: var(--ds-space-4); }
5707
- .ds-space-y-6 > * + * { margin-top: var(--ds-space-6); }
5708
- .ds-space-y-8 > * + * { margin-top: var(--ds-space-8); }
7264
+ .ds-space-y-1 > * + * { margin-block-start: var(--ds-space-1); }
7265
+ .ds-space-y-2 > * + * { margin-block-start: var(--ds-space-2); }
7266
+ .ds-space-y-3 > * + * { margin-block-start: var(--ds-space-3); }
7267
+ .ds-space-y-4 > * + * { margin-block-start: var(--ds-space-4); }
7268
+ .ds-space-y-6 > * + * { margin-block-start: var(--ds-space-6); }
7269
+ .ds-space-y-8 > * + * { margin-block-start: var(--ds-space-8); }
5709
7270
 
5710
7271
  /* --- Responsive: Padding --- */
5711
7272
  @media (min-width: 768px) {
@@ -5717,7 +7278,7 @@ tr:hover .ds-sortable__handle,
5717
7278
  .ds-md\:py-16 { padding-block: var(--ds-space-16); }
5718
7279
  }
5719
7280
 
5720
- @media (min-width: 1024px) {
7281
+ @media (min-width: 1024px) /* --ds-breakpoint-lg */ {
5721
7282
  .ds-lg\:px-6 { padding-inline: var(--ds-space-6); }
5722
7283
  .ds-lg\:px-8 { padding-inline: var(--ds-space-8); }
5723
7284
  .ds-lg\:py-16 { padding-block: var(--ds-space-16); }
@@ -5755,7 +7316,7 @@ tr:hover .ds-sortable__handle,
5755
7316
  .ds-md\:text-4xl { font-size: var(--ds-text-4xl); }
5756
7317
  .ds-md\:text-6xl { font-size: var(--ds-text-6xl); }
5757
7318
  }
5758
- @media (min-width: 1024px) {
7319
+ @media (min-width: 1024px) /* --ds-breakpoint-lg */ {
5759
7320
  .ds-lg\:text-xl { font-size: var(--ds-text-xl); }
5760
7321
  .ds-lg\:text-2xl { font-size: var(--ds-text-2xl); }
5761
7322
  .ds-lg\:text-3xl { font-size: var(--ds-text-3xl); }
@@ -5772,9 +7333,9 @@ tr:hover .ds-sortable__handle,
5772
7333
  .ds-font-bold { font-weight: var(--ds-weight-bold); }
5773
7334
 
5774
7335
  /* --- Text Alignment --- */
5775
- .ds-text-left { text-align: left; }
7336
+ .ds-text-left { text-align: start; }
5776
7337
  .ds-text-center { text-align: center; }
5777
- .ds-text-right { text-align: right; }
7338
+ .ds-text-right { text-align: end; }
5778
7339
  .ds-text-balance { text-wrap: balance; }
5779
7340
 
5780
7341
  /* --- Text Color --- */
@@ -5891,10 +7452,10 @@ tr:hover .ds-sortable__handle,
5891
7452
 
5892
7453
  /* --- Border --- */
5893
7454
  .ds-border { border: 1px solid var(--ds-color-border); }
5894
- .ds-border-t { border-top: 1px solid var(--ds-color-border); }
5895
- .ds-border-b { border-bottom: 1px solid var(--ds-color-border); }
5896
- .ds-border-l { border-left: 1px solid var(--ds-color-border); }
5897
- .ds-border-r { border-right: 1px solid var(--ds-color-border); }
7455
+ .ds-border-t { border-block-start: 1px solid var(--ds-color-border); }
7456
+ .ds-border-b { border-block-end: 1px solid var(--ds-color-border); }
7457
+ .ds-border-l { border-inline-start: 1px solid var(--ds-color-border); }
7458
+ .ds-border-r { border-inline-end: 1px solid var(--ds-color-border); }
5898
7459
  .ds-border-none { border: none; }
5899
7460
 
5900
7461
  /* --- Border Color --- */
@@ -5906,7 +7467,7 @@ tr:hover .ds-sortable__handle,
5906
7467
  .ds-border-nav { border-color: var(--ds-color-nav-border); }
5907
7468
 
5908
7469
  /* --- Divide --- */
5909
- .ds-divide-y > * + * { border-top: 1px solid var(--ds-color-border); }
7470
+ .ds-divide-y > * + * { border-block-start: 1px solid var(--ds-color-border); }
5910
7471
 
5911
7472
  /* --- Border Radius --- */
5912
7473
  .ds-rounded-none { border-radius: var(--ds-radius-none); }
@@ -6050,4 +7611,225 @@ tr:hover .ds-sortable__handle,
6050
7611
  border-width: 0;
6051
7612
  }
6052
7613
 
7614
+ /* ==========================================================================
7615
+ Utilities: Interactive
7616
+ Hover states, reveal patterns, focus rings, and cursor utilities.
7617
+ ========================================================================== */
7618
+
7619
+ /* --- Hover Row (background elevation on hover) --- */
7620
+ .ds-hover-row {
7621
+ transition: background-color var(--ds-duration-fast) var(--ds-ease-default);
7622
+ }
7623
+ .ds-hover-row:hover {
7624
+ background-color: var(--ds-color-bg-elevated);
7625
+ }
7626
+
7627
+ /* Subtle variant (semi-transparent) */
7628
+ .ds-hover-row--subtle {
7629
+ transition: background-color var(--ds-duration-fast) var(--ds-ease-default);
7630
+ }
7631
+ .ds-hover-row--subtle:hover {
7632
+ background-color: color-mix(in srgb, var(--ds-color-bg-elevated) 50%, transparent);
7633
+ }
7634
+
7635
+ /* --- Group Reveal (children appear on parent hover) --- */
7636
+ .ds-reveal {
7637
+ opacity: 0;
7638
+ transition: opacity var(--ds-duration-fast) var(--ds-ease-default);
7639
+ }
7640
+ .group:hover > .ds-reveal,
7641
+ .group:hover .ds-reveal {
7642
+ opacity: 1;
7643
+ }
7644
+
7645
+ /* --- Editable Cell (pointer + bg on hover) --- */
7646
+ .ds-editable {
7647
+ cursor: pointer;
7648
+ border-radius: var(--ds-radius-default);
7649
+ transition: background-color var(--ds-duration-fast) var(--ds-ease-default);
7650
+ }
7651
+ .ds-editable:hover {
7652
+ background-color: var(--ds-color-surface-hover);
7653
+ }
7654
+
7655
+ /* --- Hover Border --- */
7656
+ .ds-hover-border {
7657
+ transition: border-color var(--ds-duration-fast) var(--ds-ease-default);
7658
+ }
7659
+ .ds-hover-border:hover {
7660
+ border-color: var(--ds-color-border-hover);
7661
+ }
7662
+
7663
+ /* --- Hover Zoom (scale on hover) --- */
7664
+ .ds-hover-zoom {
7665
+ transition: transform var(--ds-duration-fast) var(--ds-ease-default);
7666
+ }
7667
+ .ds-hover-zoom:hover {
7668
+ transform: scale(1.05);
7669
+ }
7670
+
7671
+ /* --- Opacity Hover (dim → full on hover) --- */
7672
+ .ds-opacity-hover {
7673
+ opacity: 0.5;
7674
+ transition: opacity var(--ds-duration-fast) var(--ds-ease-default);
7675
+ }
7676
+ .ds-opacity-hover:hover {
7677
+ opacity: 1;
7678
+ }
7679
+
7680
+ /* --- Focus Ring (inset ring for custom inputs) --- */
7681
+ .ds-focus-ring:focus {
7682
+ border-color: var(--ds-color-border-active);
7683
+ box-shadow: inset 0 0 0 1px var(--ds-color-border-active);
7684
+ outline: none;
7685
+ }
7686
+
7687
+ /* --- Ring States --- */
7688
+ .ds-ring-active {
7689
+ box-shadow: 0 0 0 2px var(--ds-color-border-active);
7690
+ }
7691
+
7692
+ .ds-ring-selected {
7693
+ box-shadow: 0 0 0 2px var(--ds-color-interactive), 0 0 0 3px var(--ds-color-surface);
7694
+ }
7695
+
7696
+ /* --- Semi-transparent Backgrounds --- */
7697
+ .ds-bg-elevated-50 {
7698
+ background-color: color-mix(in srgb, var(--ds-color-bg-elevated) 50%, transparent);
7699
+ }
7700
+
7701
+ .ds-bg-base-50 {
7702
+ background-color: color-mix(in srgb, var(--ds-color-bg-base) 50%, transparent);
7703
+ }
7704
+
7705
+ /* --- Hover Color Variants --- */
7706
+ .ds-hover-text-error:hover {
7707
+ color: var(--ds-color-error);
7708
+ }
7709
+
7710
+ .ds-hover-bg-error:hover {
7711
+ background-color: color-mix(in srgb, var(--ds-color-error) 10%, transparent);
7712
+ }
7713
+
7714
+ /* --- Cursor Utilities --- */
7715
+ .ds-cursor-grab {
7716
+ cursor: grab;
7717
+ }
7718
+ .ds-cursor-grab:active {
7719
+ cursor: grabbing;
7720
+ }
7721
+
7722
+ /* --- Grip Icon (secondary → primary on group hover) --- */
7723
+ .ds-grip {
7724
+ color: var(--ds-color-text-secondary);
7725
+ transition: color var(--ds-duration-fast) var(--ds-ease-default);
7726
+ }
7727
+ .group:hover .ds-grip {
7728
+ color: var(--ds-color-text);
7729
+ }
7730
+
7731
+ /* ==========================================================================
7732
+ Utilities: Accessibility
7733
+ Screen-reader helpers, skip links, focus utilities, and motion control.
7734
+ ========================================================================== */
7735
+
7736
+ /* ---------------------------------------------------------------------------
7737
+ Screen Reader Only — visually hidden, available to assistive technology
7738
+ --------------------------------------------------------------------------- */
7739
+
7740
+ .ds-sr-only {
7741
+ position: absolute;
7742
+ width: 1px;
7743
+ height: 1px;
7744
+ padding: 0;
7745
+ margin: -1px;
7746
+ overflow: hidden;
7747
+ clip: rect(0, 0, 0, 0);
7748
+ white-space: nowrap;
7749
+ border-width: 0;
7750
+ }
7751
+
7752
+ /* ---------------------------------------------------------------------------
7753
+ Not Screen Reader Only — reset ds-sr-only when needed (e.g., responsive)
7754
+ --------------------------------------------------------------------------- */
7755
+
7756
+ .ds-not-sr-only {
7757
+ position: static;
7758
+ width: auto;
7759
+ height: auto;
7760
+ padding: 0;
7761
+ margin: 0;
7762
+ overflow: visible;
7763
+ clip: auto;
7764
+ white-space: normal;
7765
+ }
7766
+
7767
+ /* ---------------------------------------------------------------------------
7768
+ Focus Visible Only — invisible by default, shown on keyboard focus
7769
+ --------------------------------------------------------------------------- */
7770
+
7771
+ .ds-focus-visible-only {
7772
+ opacity: 0;
7773
+ pointer-events: none;
7774
+ }
7775
+
7776
+ .ds-focus-visible-only:focus-visible {
7777
+ opacity: 1;
7778
+ pointer-events: auto;
7779
+ }
7780
+
7781
+ /* ---------------------------------------------------------------------------
7782
+ Skip Link — skip to main content, visible only on focus
7783
+ --------------------------------------------------------------------------- */
7784
+
7785
+ .ds-skip-link {
7786
+ position: absolute;
7787
+ width: 1px;
7788
+ height: 1px;
7789
+ padding: 0;
7790
+ margin: -1px;
7791
+ overflow: hidden;
7792
+ clip: rect(0, 0, 0, 0);
7793
+ white-space: nowrap;
7794
+ border-width: 0;
7795
+ }
7796
+
7797
+ .ds-skip-link:focus-visible {
7798
+ position: fixed;
7799
+ inset-block-start: 0;
7800
+ inset-inline-start: 0;
7801
+ z-index: var(--ds-z-tooltip, 200);
7802
+ width: auto;
7803
+ height: auto;
7804
+ padding: var(--ds-space-3) var(--ds-space-5);
7805
+ margin: 0;
7806
+ overflow: visible;
7807
+ clip: auto;
7808
+ white-space: normal;
7809
+ background-color: var(--ds-color-inverted);
7810
+ color: var(--ds-color-on-inverted);
7811
+ font-family: var(--ds-font-sans);
7812
+ font-size: var(--ds-text-sm);
7813
+ font-weight: var(--ds-weight-medium);
7814
+ border-radius: 0 0 var(--ds-radius-lg) 0;
7815
+ box-shadow: var(--ds-shadow-lg);
7816
+ text-decoration: none;
7817
+ outline: none;
7818
+ }
7819
+
7820
+ /* ---------------------------------------------------------------------------
7821
+ Reduce Motion — force reduced motion on a specific element
7822
+ --------------------------------------------------------------------------- */
7823
+
7824
+ .ds-reduce-motion,
7825
+ .ds-reduce-motion *,
7826
+ .ds-reduce-motion *::before,
7827
+ .ds-reduce-motion *::after {
7828
+ animation-duration: 0.01ms !important;
7829
+ animation-iteration-count: 1 !important;
7830
+ transition-duration: 0.01ms !important;
7831
+ scroll-behavior: auto !important;
7832
+ }
7833
+
6053
7834
 
7835
+ }