ds-tis 1.0.0-beta.10

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 (79) hide show
  1. package/LICENSE +49 -0
  2. package/README.md +125 -0
  3. package/css/base/forced-colors.css +62 -0
  4. package/css/base/icons.css +73 -0
  5. package/css/base/index.css +4 -0
  6. package/css/base/reset.css +80 -0
  7. package/css/base/typography.css +211 -0
  8. package/css/components/accordion.css +153 -0
  9. package/css/components/alert.css +161 -0
  10. package/css/components/avatar.css +76 -0
  11. package/css/components/badge.css +83 -0
  12. package/css/components/breadcrumb.css +58 -0
  13. package/css/components/button.css +364 -0
  14. package/css/components/card.css +159 -0
  15. package/css/components/checkbox.css +296 -0
  16. package/css/components/combobox.css +330 -0
  17. package/css/components/divider.css +20 -0
  18. package/css/components/form-field.css +137 -0
  19. package/css/components/index.css +28 -0
  20. package/css/components/input.css +356 -0
  21. package/css/components/link.css +67 -0
  22. package/css/components/menu.css +246 -0
  23. package/css/components/modal.css +236 -0
  24. package/css/components/pagination.css +132 -0
  25. package/css/components/radio.css +280 -0
  26. package/css/components/select.css +399 -0
  27. package/css/components/skeleton.css +52 -0
  28. package/css/components/spinner.css +59 -0
  29. package/css/components/tabs.css +79 -0
  30. package/css/components/textarea.css +218 -0
  31. package/css/components/toggle.css +202 -0
  32. package/css/components/tooltip.css +116 -0
  33. package/css/design-system.css +13 -0
  34. package/css/tokens/generated/component.css +720 -0
  35. package/css/tokens/generated/foundation.css +282 -0
  36. package/css/tokens/generated/index.css +5 -0
  37. package/css/tokens/generated/theme-dark.css +243 -0
  38. package/css/tokens/generated/theme-light.css +244 -0
  39. package/css/tokens/index.css +6 -0
  40. package/css/utilities/elevation.css +24 -0
  41. package/css/utilities/index.css +2 -0
  42. package/css/utilities/layout.css +132 -0
  43. package/docs/agent-consumer-usage.en.md +322 -0
  44. package/docs/agent-consumer-usage.md +294 -0
  45. package/docs/api/adrs.json +186 -0
  46. package/docs/api/components.json +2071 -0
  47. package/docs/api/consumer-context.json +66 -0
  48. package/docs/api/foundations.json +94 -0
  49. package/docs/api/tokens.json +6839 -0
  50. package/docs/llms-full.txt +23699 -0
  51. package/docs/llms.txt +109 -0
  52. package/docs/templates/contact.html +364 -0
  53. package/docs/templates/dashboard.html +318 -0
  54. package/docs/templates/index.html +232 -0
  55. package/docs/templates/login.html +286 -0
  56. package/docs/templates/settings.html +365 -0
  57. package/docs/templates/signup.html +350 -0
  58. package/js/accordion.js +192 -0
  59. package/js/combobox.js +263 -0
  60. package/js/menu.js +301 -0
  61. package/js/modal.js +256 -0
  62. package/js/package.json +3 -0
  63. package/js/tabs.js +200 -0
  64. package/js/theme/apply.js +75 -0
  65. package/js/theme/brand-contrast-audit.js +133 -0
  66. package/js/theme/color.js +149 -0
  67. package/js/theme/config-schema.js +40 -0
  68. package/js/theme/contrast.js +76 -0
  69. package/js/theme/export.js +118 -0
  70. package/js/theme/index.js +21 -0
  71. package/js/theme/overlay.js +29 -0
  72. package/js/theme/package.json +3 -0
  73. package/js/theme/palette.js +103 -0
  74. package/js/theme/radius.js +76 -0
  75. package/js/theme/semantic-mapper.js +138 -0
  76. package/js/theme/typography.js +33 -0
  77. package/js/theme/url-state.js +52 -0
  78. package/js/tooltip.js +227 -0
  79. package/package.json +139 -0
@@ -0,0 +1,52 @@
1
+ /* ============================================================
2
+ COMPONENT: Skeleton
3
+ ============================================================ */
4
+
5
+ @keyframes ds-pulse {
6
+ 0%, 100% { opacity: 1; }
7
+ 50% { opacity: 0.4; }
8
+ }
9
+
10
+ /* Base skeleton — Figma: fill via component tokens */
11
+ .ds-skeleton {
12
+ display: block;
13
+ animation: ds-pulse 1.5s ease-in-out infinite;
14
+ }
15
+
16
+ /* ---------------------------------------------------------
17
+ Variants
18
+ --------------------------------------------------------- */
19
+
20
+ /* Text skeleton — Figma: 12px height, radius/md */
21
+ .ds-skeleton--text {
22
+ background-color: var(--ds-skeleton-text-fill-default);
23
+ height: var(--ds-skeleton-text-height-default);
24
+ width: 100%;
25
+ border-radius: var(--ds-skeleton-text-radius-default);
26
+ }
27
+
28
+ /* Circle skeleton */
29
+ .ds-skeleton--circle {
30
+ background-color: var(--ds-skeleton-circle-fill-default);
31
+ width: var(--ds-skeleton-circle-size-default);
32
+ height: var(--ds-skeleton-circle-size-default);
33
+ border-radius: var(--ds-skeleton-circle-radius-default);
34
+ flex-shrink: 0;
35
+ }
36
+
37
+ /* Rectangle skeleton */
38
+ .ds-skeleton--rectangle {
39
+ background-color: var(--ds-skeleton-rectangle-fill-default);
40
+ width: 100%;
41
+ height: var(--ds-skeleton-rectangle-height-default);
42
+ border-radius: var(--ds-skeleton-rectangle-radius-default);
43
+ }
44
+
45
+ /* ---------------------------------------------------------
46
+ Reduced motion
47
+ --------------------------------------------------------- */
48
+ @media (prefers-reduced-motion: reduce) {
49
+ .ds-skeleton {
50
+ animation: none;
51
+ }
52
+ }
@@ -0,0 +1,59 @@
1
+ /* ============================================================
2
+ COMPONENT: Spinner
3
+ ============================================================ */
4
+
5
+ @keyframes ds-spin {
6
+ to { transform: rotate(360deg); }
7
+ }
8
+
9
+ /* Base spinner — Figma: track + indicator via component tokens */
10
+ .ds-spinner {
11
+ display: inline-block;
12
+ border-radius: var(--ds-spinner-radius-default);
13
+ border: var(--ds-spinner-ring-stroke-width-default) solid var(--ds-spinner-track-border-color-default);
14
+ border-top-color: var(--ds-spinner-indicator-border-color-default);
15
+ /* motion.duration.slower (600ms) e motion.ease.linear são tokens
16
+ dedicados a loops contínuos. NÃO usar duration.fast/normal/slow
17
+ (são pra transições de estado, 150-300ms) nem ease.default (curva
18
+ quebra rotação constante). */
19
+ animation: ds-spin var(--ds-motion-duration-slower) var(--ds-motion-ease-linear) infinite;
20
+ flex-shrink: 0;
21
+ }
22
+
23
+ /* Default size: md */
24
+ .ds-spinner,
25
+ .ds-spinner--md {
26
+ width: var(--ds-spinner-size-md);
27
+ height: var(--ds-spinner-size-md);
28
+ }
29
+
30
+ /* ---------------------------------------------------------
31
+ Sizes
32
+ --------------------------------------------------------- */
33
+ .ds-spinner--sm {
34
+ width: var(--ds-spinner-size-sm);
35
+ height: var(--ds-spinner-size-sm);
36
+ }
37
+
38
+ .ds-spinner--lg {
39
+ width: var(--ds-spinner-size-lg);
40
+ height: var(--ds-spinner-size-lg);
41
+ }
42
+
43
+ /* ---------------------------------------------------------
44
+ Styles
45
+ --------------------------------------------------------- */
46
+ .ds-spinner--on-color {
47
+ border-color: var(--ds-spinner-track-border-color-on-color);
48
+ border-top-color: var(--ds-spinner-indicator-border-color-on-color);
49
+ }
50
+
51
+ /* ---------------------------------------------------------
52
+ Reduced motion
53
+ --------------------------------------------------------- */
54
+ @media (prefers-reduced-motion: reduce) {
55
+ .ds-spinner {
56
+ animation: none;
57
+ opacity: var(--ds-opacity-disabled);
58
+ }
59
+ }
@@ -0,0 +1,79 @@
1
+ /* ============================================================
2
+ COMPONENT: Tabs
3
+ ============================================================ */
4
+
5
+ .ds-tabs {
6
+ display: flex;
7
+ border-bottom: var(--ds-tabs-list-border-width-default) solid var(--ds-overlay-default);
8
+ gap: 0;
9
+ }
10
+
11
+ /* Tab — Figma: padding pl/pr space/lg, pt space/md (12), pb 0
12
+ Gap: 10px before the 2px indicator; Label: label/md (14px Bold / 20px) */
13
+ .ds-tab {
14
+ display: inline-flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ position: relative;
18
+ padding: var(--ds-tabs-item-padding-top-default) var(--ds-tabs-item-padding-x-default) var(--ds-tabs-item-indicator-gap-default);
19
+ font-size: var(--ds-tabs-label-font-size-default);
20
+ font-weight: var(--ds-tabs-label-font-weight-default);
21
+ line-height: var(--ds-tabs-label-line-height-default);
22
+ letter-spacing: var(--ds-tabs-label-letter-spacing-default);
23
+ color: var(--ds-tabs-label-color-default);
24
+ background: none;
25
+ border: none;
26
+ border-bottom: var(--ds-tabs-indicator-height-default) solid transparent;
27
+ border-radius: 0;
28
+ cursor: pointer;
29
+ transition: color var(--ds-motion-duration-fast) var(--ds-motion-ease-default),
30
+ border-color var(--ds-motion-duration-fast) var(--ds-motion-ease-default);
31
+ margin-bottom: calc(var(--ds-tabs-list-border-width-default) * -1);
32
+ }
33
+
34
+ .ds-tab:hover:not(:disabled):not(.ds-tab--active) {
35
+ color: var(--ds-tabs-label-color-hover);
36
+ }
37
+
38
+ .ds-tab--active {
39
+ color: var(--ds-tabs-label-color-active);
40
+ border-bottom-color: var(--ds-tabs-indicator-fill-active);
41
+ }
42
+
43
+ .ds-tab:focus-visible {
44
+ outline: none;
45
+ }
46
+
47
+ .ds-tab:focus-visible::after {
48
+ content: "";
49
+ position: absolute;
50
+ top: calc(var(--ds-focus-ring-width) * -2);
51
+ right: calc(var(--ds-focus-ring-width) * -2);
52
+ bottom: calc((var(--ds-focus-ring-width) * -2) - var(--ds-tabs-indicator-height-default));
53
+ left: calc(var(--ds-focus-ring-width) * -2);
54
+ box-sizing: border-box;
55
+ border: var(--ds-focus-ring-width) solid var(--ds-focus-ring-color-default);
56
+ border-radius: var(--ds-tabs-focus-ring-radius-default);
57
+ pointer-events: none;
58
+ }
59
+
60
+ .ds-tab:disabled {
61
+ color: var(--ds-tabs-label-color-disabled);
62
+ cursor: not-allowed;
63
+ }
64
+
65
+ /* Tab panel */
66
+ .ds-tab-panel {
67
+ padding: var(--ds-tabs-panel-padding-y-default) 0;
68
+ }
69
+
70
+ .ds-tab-panel[hidden] {
71
+ display: none;
72
+ }
73
+
74
+ /* ---------------------------------------------------------
75
+ Reduced motion
76
+ --------------------------------------------------------- */
77
+ @media (prefers-reduced-motion: reduce) {
78
+ .ds-tab { transition: none; }
79
+ }
@@ -0,0 +1,218 @@
1
+ /* ==========================================================================
2
+ Textarea – .ds-textarea
3
+ ========================================================================== */
4
+
5
+ /* Wrapper
6
+ ========================================================================== */
7
+
8
+ .ds-textarea {
9
+ display: flex;
10
+ width: 100%;
11
+ border: var(--ds-field-border-width) solid var(--ds-field-border-color-default);
12
+ border-radius: var(--ds-field-radius);
13
+ background: var(--ds-field-bg-default);
14
+ transition: border-color var(--ds-motion-duration-fast) var(--ds-motion-ease-default),
15
+ box-shadow var(--ds-motion-duration-fast) var(--ds-motion-ease-default);
16
+ }
17
+
18
+ /* Sizes
19
+ ========================================================================== */
20
+
21
+ .ds-textarea,
22
+ .ds-textarea--md {
23
+ font-size: var(--ds-textarea-text-font-size-md);
24
+ line-height: var(--ds-textarea-text-line-height-md);
25
+ }
26
+
27
+ .ds-textarea--sm {
28
+ font-size: var(--ds-textarea-text-font-size-sm);
29
+ line-height: var(--ds-textarea-text-line-height-sm);
30
+ }
31
+
32
+ .ds-textarea--lg {
33
+ font-size: var(--ds-textarea-text-font-size-lg);
34
+ line-height: var(--ds-textarea-text-line-height-lg);
35
+ }
36
+
37
+ /* Field
38
+ ========================================================================== */
39
+
40
+ .ds-textarea__field {
41
+ flex: 1;
42
+ width: 100%;
43
+ box-sizing: border-box;
44
+ border: none;
45
+ background: transparent;
46
+ outline: none;
47
+ color: var(--ds-field-value-color-default);
48
+ font-size: inherit;
49
+ font-family: var(--ds-textarea-text-font-family-default);
50
+ font-weight: var(--ds-textarea-text-font-weight-default);
51
+ letter-spacing: var(--ds-textarea-text-letter-spacing-default);
52
+ resize: vertical;
53
+ min-height: var(--ds-textarea-field-min-height-md);
54
+ min-width: 0;
55
+ padding: var(--ds-field-padding-y-md) var(--ds-textarea-padding-x-md);
56
+ line-height: inherit;
57
+ }
58
+
59
+ .ds-textarea--sm .ds-textarea__field {
60
+ min-height: var(--ds-textarea-field-min-height-sm);
61
+ padding: var(--ds-field-padding-y-sm) var(--ds-textarea-padding-x-sm);
62
+ }
63
+
64
+ .ds-textarea--lg .ds-textarea__field {
65
+ min-height: var(--ds-textarea-field-min-height-lg);
66
+ padding: var(--ds-field-padding-y-lg) var(--ds-textarea-padding-x-lg);
67
+ }
68
+
69
+ .ds-textarea__field::placeholder {
70
+ color: var(--ds-field-placeholder-color-default);
71
+ }
72
+
73
+ /* States
74
+ ========================================================================== */
75
+
76
+ /* Hover */
77
+ .ds-textarea:hover:not(.ds-textarea--disabled):not(:focus-within) {
78
+ border-color: var(--ds-field-border-color-hover);
79
+ }
80
+
81
+ /* Focused */
82
+ .ds-textarea:focus-within {
83
+ background: var(--ds-field-bg-default);
84
+ border-color: var(--ds-field-border-color-focus);
85
+ border-radius: var(--ds-field-radius);
86
+ outline: var(--ds-focus-ring-width) solid var(--ds-focus-ring-color-default);
87
+ outline-offset: var(--ds-focus-ring-width);
88
+ }
89
+
90
+ /* Error */
91
+ .ds-textarea--error,
92
+ .ds-field--error .ds-textarea {
93
+ background: var(--ds-field-bg-default);
94
+ border-color: var(--ds-field-border-color-error);
95
+ }
96
+
97
+ .ds-textarea--error:hover:not(.ds-textarea--disabled):not(:focus-within),
98
+ .ds-field--error .ds-textarea:hover:not(.ds-textarea--disabled):not(:focus-within) {
99
+ background: var(--ds-field-bg-default);
100
+ border-color: var(--ds-field-border-color-error-hover);
101
+ }
102
+
103
+ .ds-textarea--error:focus-within,
104
+ .ds-field--error .ds-textarea:focus-within {
105
+ border-color: var(--ds-field-border-color-error);
106
+ outline-color: var(--ds-focus-ring-color-error);
107
+ }
108
+
109
+ /* Filled */
110
+ .ds-textarea--filled {
111
+ background: var(--ds-field-bg-default);
112
+ border-color: var(--ds-field-border-color-filled);
113
+ }
114
+
115
+ .ds-textarea--filled.ds-textarea--error,
116
+ .ds-field--error .ds-textarea--filled {
117
+ background: var(--ds-field-bg-default);
118
+ border-color: var(--ds-field-border-color-error);
119
+ }
120
+
121
+ .ds-textarea--filled.ds-textarea--error:hover:not(.ds-textarea--disabled):not(:focus-within),
122
+ .ds-field--error .ds-textarea--filled:hover:not(.ds-textarea--disabled):not(:focus-within) {
123
+ background: var(--ds-field-bg-default);
124
+ border-color: var(--ds-field-border-color-error-hover);
125
+ }
126
+
127
+ /* Disabled */
128
+ .ds-textarea--disabled,
129
+ .ds-textarea:has(.ds-textarea__field:disabled) {
130
+ background: var(--ds-field-bg-disabled);
131
+ border-color: var(--ds-field-border-color-disabled);
132
+ pointer-events: none;
133
+ }
134
+
135
+ .ds-textarea--disabled .ds-textarea__field,
136
+ .ds-textarea__field:disabled {
137
+ color: var(--ds-field-value-color-disabled);
138
+ resize: none;
139
+ }
140
+
141
+ .ds-textarea--disabled .ds-textarea__field::placeholder,
142
+ .ds-textarea__field:disabled::placeholder {
143
+ color: var(--ds-field-placeholder-color-disabled);
144
+ }
145
+
146
+ /* Readonly */
147
+ .ds-textarea--readonly,
148
+ .ds-textarea:has(.ds-textarea__field:read-only) {
149
+ background: var(--ds-field-bg-readonly);
150
+ border-color: var(--ds-field-border-color-readonly);
151
+ }
152
+
153
+ .ds-textarea--readonly .ds-textarea__field,
154
+ .ds-textarea__field:read-only {
155
+ color: var(--ds-field-value-color-readonly);
156
+ }
157
+
158
+ .ds-textarea--readonly:focus-within,
159
+ .ds-textarea:has(.ds-textarea__field:read-only):focus-within {
160
+ background: var(--ds-field-bg-readonly);
161
+ border-color: var(--ds-field-border-color-readonly);
162
+ outline-color: var(--ds-focus-ring-color-readonly);
163
+ }
164
+
165
+ /* Character Counter
166
+ ========================================================================== */
167
+
168
+ .ds-field__counter {
169
+ font-size: var(--ds-body-font-size-xs);
170
+ color: var(--ds-content-default);
171
+ text-align: right;
172
+ font-variant-numeric: tabular-nums;
173
+ }
174
+
175
+ .ds-field__counter--over {
176
+ color: var(--ds-feedback-error-content-default);
177
+ font-weight: var(--ds-body-font-weight-medium);
178
+ }
179
+
180
+ /* ---------------------------------------------------------
181
+ Field anatomy — Label row, Required indicator
182
+ --------------------------------------------------------- */
183
+
184
+ /*
185
+ Uses shared .ds-field wrapper and .ds-field__helper from form-field.css.
186
+ .ds-field__counter (above) is the character counter — separate from .ds-field__helper.
187
+
188
+ HTML structure:
189
+ <div class="ds-field">
190
+ <div class="ds-textarea__label-row">
191
+ <label class="ds-textarea__label" for="field-id">Rótulo</label>
192
+ <span class="ds-textarea__required" aria-hidden="true">*</span>
193
+ </div>
194
+ <div class="ds-textarea">
195
+ <textarea class="ds-textarea__field" id="field-id"
196
+ aria-required="true"
197
+ aria-describedby="field-id-helper"></textarea>
198
+ </div>
199
+ <div style="display:flex; justify-content:space-between">
200
+ <span class="ds-field__helper" id="field-id-helper">Texto auxiliar</span>
201
+ <span class="ds-field__counter">0/200</span>
202
+ </div>
203
+ </div>
204
+ */
205
+
206
+ /*
207
+ Field anatomy (Label row, Required, Helper) uses shared .ds-field__* classes
208
+ defined in form-field.css. See form-field.css for .ds-field__label-row, .ds-field__label,
209
+ .ds-field__required, .ds-field__helper, .ds-field--no-label, .ds-field--no-helper.
210
+ .ds-field__counter (above) is the character counter — distinct from .ds-field__helper.
211
+ */
212
+
213
+ /* ---------------------------------------------------------
214
+ Reduced motion
215
+ --------------------------------------------------------- */
216
+ @media (prefers-reduced-motion: reduce) {
217
+ .ds-textarea { transition: none; }
218
+ }
@@ -0,0 +1,202 @@
1
+ /* ============================================================
2
+ COMPONENT: Toggle / Switch
3
+ ============================================================ */
4
+
5
+ /**
6
+ * LÓGICA DE ALINHAMENTO (Figma spec):
7
+ * O Toggle segue o padrão "Content Frame". O interruptor (track) fica à esquerda
8
+ * e o conteúdo (Label + Desc + Helper) fica à direita em uma coluna flex.
9
+ * Alinhamento é sempre TOP (flex-start) para suportar textos multilinha.
10
+ *
11
+ * ESCALA DIMENSIONAL (ADR-013/015/019):
12
+ * Proporção corrigida (aprox 1.8x - 2x):
13
+ * Sm: 28x16px | Md: 44x24px | Lg: 56x32px (Grade 4px).
14
+ */
15
+
16
+ .ds-toggle-label {
17
+ --toggle-target-height: var(--ds-toggle-target-height-md);
18
+ --toggle-thumb-size: var(--ds-toggle-thumb-size-md);
19
+
20
+ display: inline-flex;
21
+ align-items: flex-start;
22
+ gap: var(--ds-toggle-target-gap-default);
23
+ cursor: pointer;
24
+ padding: var(--ds-space-xs);
25
+ min-height: var(--toggle-target-height);
26
+ color: var(--ds-form-field-label-color-default);
27
+ user-select: none;
28
+ }
29
+
30
+ /* Track — Hidden native checkbox styled */
31
+ .ds-toggle {
32
+ appearance: none;
33
+ -webkit-appearance: none;
34
+ position: relative;
35
+ background-color: var(--ds-toggle-track-fill-off-default);
36
+ border: var(--ds-toggle-track-border-width-default) solid var(--ds-toggle-track-border-color-off-default);
37
+ border-radius: var(--ds-toggle-track-radius-default);
38
+ cursor: pointer;
39
+ flex-shrink: 0;
40
+ transition: background-color var(--ds-motion-duration-fast) var(--ds-motion-ease-default),
41
+ border-color var(--ds-motion-duration-fast) var(--ds-motion-ease-default);
42
+
43
+ --toggle-height: var(--ds-toggle-track-height-md);
44
+ --toggle-width: var(--ds-toggle-track-width-md);
45
+ --thumb-gap: var(--ds-toggle-thumb-inset-default);
46
+ --track-border-width: var(--ds-toggle-track-border-width-default);
47
+
48
+ width: var(--toggle-width);
49
+ height: var(--toggle-height);
50
+ }
51
+
52
+ /* Thumb */
53
+ .ds-toggle::after {
54
+ content: '';
55
+ position: absolute;
56
+ top: calc(var(--thumb-gap) - var(--track-border-width));
57
+ left: calc(var(--thumb-gap) - var(--track-border-width));
58
+ width: var(--toggle-thumb-size);
59
+ height: var(--toggle-thumb-size);
60
+ border-radius: var(--ds-toggle-thumb-radius-default);
61
+ background-color: var(--ds-toggle-thumb-fill-off-default);
62
+ box-shadow: var(--ds-shadow-card);
63
+ transition: transform var(--ds-motion-duration-fast) var(--ds-motion-ease-default),
64
+ background-color var(--ds-motion-duration-fast) var(--ds-motion-ease-default);
65
+ }
66
+
67
+ /* Checked (On) State */
68
+ .ds-toggle:checked {
69
+ background-color: var(--ds-toggle-track-fill-on-default);
70
+ border-width: 0;
71
+ --track-border-width: 0px;
72
+ }
73
+
74
+ .ds-toggle:checked::after {
75
+ background-color: var(--ds-toggle-thumb-fill-on-default);
76
+ transform: translateX(calc(var(--toggle-width) - var(--toggle-thumb-size) - (var(--thumb-gap) * 2)));
77
+ }
78
+
79
+ /* Hover State */
80
+ .ds-toggle:hover:not(:disabled) {
81
+ background-color: var(--ds-toggle-track-fill-off-hover);
82
+ border-color: var(--ds-toggle-track-border-color-off-hover);
83
+ }
84
+
85
+ .ds-toggle:hover:not(:disabled)::after {
86
+ background-color: var(--ds-toggle-thumb-fill-off-hover);
87
+ }
88
+
89
+ .ds-toggle:checked:hover:not(:disabled) {
90
+ background-color: var(--ds-toggle-track-fill-on-hover);
91
+ }
92
+
93
+ .ds-toggle:checked:hover:not(:disabled)::after {
94
+ background-color: var(--ds-toggle-thumb-fill-on-hover);
95
+ }
96
+
97
+ /* Focus State */
98
+ .ds-toggle:focus-visible {
99
+ outline: var(--ds-focus-ring-width) solid var(--ds-focus-ring-color-default);
100
+ outline-offset: var(--ds-focus-ring-width);
101
+ }
102
+
103
+ /* Disabled State */
104
+ .ds-toggle:disabled {
105
+ background-color: var(--ds-toggle-track-fill-off-disabled);
106
+ border-color: var(--ds-toggle-track-border-color-off-disabled);
107
+ cursor: not-allowed;
108
+ }
109
+
110
+ .ds-toggle:disabled::after {
111
+ background-color: var(--ds-toggle-thumb-fill-off-disabled);
112
+ }
113
+
114
+ .ds-toggle:checked:disabled {
115
+ background-color: var(--ds-toggle-track-fill-on-disabled);
116
+ border-width: 0;
117
+ }
118
+
119
+ .ds-toggle:checked:disabled::after {
120
+ background-color: var(--ds-toggle-thumb-fill-on-disabled);
121
+ }
122
+
123
+ .ds-toggle-label:has(.ds-toggle:disabled) {
124
+ cursor: not-allowed;
125
+ color: var(--ds-form-field-label-color-disabled);
126
+ }
127
+
128
+ /* ---------------------------------------------------------
129
+ Sizes (Figma Specs - Grid 4px)
130
+ --------------------------------------------------------- */
131
+
132
+ /* Small (28x16px) */
133
+ .ds-toggle-label:has(.ds-toggle--sm) {
134
+ --toggle-target-height: var(--ds-toggle-target-height-sm);
135
+ --toggle-thumb-size: var(--ds-toggle-thumb-size-sm);
136
+ }
137
+
138
+ .ds-toggle--sm {
139
+ --toggle-height: var(--ds-toggle-track-height-sm);
140
+ --toggle-width: var(--ds-toggle-track-width-sm);
141
+ }
142
+
143
+ /* Large (56x32px) */
144
+ .ds-toggle-label:has(.ds-toggle--lg) {
145
+ --toggle-target-height: var(--ds-toggle-target-height-lg);
146
+ --toggle-thumb-size: var(--ds-toggle-thumb-size-lg);
147
+ }
148
+
149
+ .ds-toggle--lg {
150
+ --toggle-height: var(--ds-toggle-track-height-lg);
151
+ --toggle-width: var(--ds-toggle-track-width-lg);
152
+ }
153
+
154
+ /* ---------------------------------------------------------
155
+ Content Frame Anatomy
156
+ --------------------------------------------------------- */
157
+
158
+ .ds-toggle__content {
159
+ display: flex;
160
+ flex-direction: column;
161
+ gap: var(--ds-toggle-content-gap-default);
162
+ min-width: 0;
163
+ }
164
+
165
+ /* Label text (label/md: 14px bold) */
166
+ .ds-toggle__label {
167
+ font-family: var(--ds-form-field-label-font-family-default);
168
+ font-size: var(--ds-form-field-label-font-size-default);
169
+ font-weight: var(--ds-body-font-weight-bold);
170
+ line-height: var(--ds-form-field-label-line-height-default);
171
+ color: var(--ds-form-field-label-color-default);
172
+ }
173
+
174
+ /* Description (body/sm: 14px regular) */
175
+ .ds-toggle__description {
176
+ font-size: var(--ds-body-font-size-sm);
177
+ font-weight: var(--ds-body-font-weight-regular);
178
+ line-height: var(--ds-body-line-height-sm);
179
+ color: var(--ds-form-field-description-color-default);
180
+ white-space: pre-wrap;
181
+ }
182
+
183
+ /* Helper text (caption/sm: 11px regular) */
184
+ .ds-toggle__helper {
185
+ font-size: var(--ds-body-font-size-xs);
186
+ font-weight: var(--ds-body-font-weight-regular);
187
+ line-height: var(--ds-body-line-height-xs);
188
+ color: var(--ds-form-field-helper-color-default);
189
+ }
190
+
191
+ .ds-toggle-label:has(.ds-toggle:disabled) .ds-toggle__label {
192
+ color: var(--ds-form-field-label-color-disabled);
193
+ }
194
+
195
+ /* ---------------------------------------------------------
196
+ Reduced motion
197
+ --------------------------------------------------------- */
198
+ @media (prefers-reduced-motion: reduce) {
199
+ .ds-toggle-label,
200
+ .ds-toggle,
201
+ .ds-toggle::after { transition: none; }
202
+ }