@duskmoon-dev/core 1.19.3 → 1.19.5

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 (121) hide show
  1. package/README.md +1 -1
  2. package/dist/base.css +484 -0
  3. package/dist/components/accordion.css +33 -9
  4. package/dist/components/alert.css +81 -3
  5. package/dist/components/autocomplete.css +67 -13
  6. package/dist/components/avatar.css +39 -15
  7. package/dist/components/badge.css +15 -3
  8. package/dist/components/card.css +12 -0
  9. package/dist/components/carousel.css +28 -0
  10. package/dist/components/chat.css +15 -11
  11. package/dist/components/checkbox.css +83 -13
  12. package/dist/components/collapse.css +67 -16
  13. package/dist/components/countdown.css +22 -0
  14. package/dist/components/datepicker.css +76 -0
  15. package/dist/components/diff.css +33 -0
  16. package/dist/components/file-input.css +107 -0
  17. package/dist/components/filter-group.css +452 -0
  18. package/dist/components/form-group.css +95 -204
  19. package/dist/components/form.css +1551 -421
  20. package/dist/components/index.css +6248 -5799
  21. package/dist/components/input.css +114 -31
  22. package/dist/components/kbd.css +28 -0
  23. package/dist/components/list.css +22 -3
  24. package/dist/components/loading.css +105 -0
  25. package/dist/components/modal.css +62 -1
  26. package/dist/components/otp-input.css +124 -6
  27. package/dist/components/progress.css +74 -0
  28. package/dist/components/radial-progress.css +47 -0
  29. package/dist/components/radio.css +70 -0
  30. package/dist/components/range.css +41 -0
  31. package/dist/components/rating.css +39 -12
  32. package/dist/components/select.css +74 -0
  33. package/dist/components/skeleton.css +13 -1
  34. package/dist/components/snackbar.css +48 -18
  35. package/dist/components/stat.css +40 -0
  36. package/dist/components/switch.css +70 -0
  37. package/dist/components/table.css +8 -0
  38. package/dist/components/textarea.css +74 -0
  39. package/dist/components/timeline.css +40 -25
  40. package/dist/components/toast.css +188 -34
  41. package/dist/components/toggle-switch.css +165 -0
  42. package/dist/components/toggle.css +198 -32
  43. package/dist/components/tooltip.css +2 -47
  44. package/dist/components/validator.css +66 -0
  45. package/dist/effects/aura.css +22 -0
  46. package/dist/effects/hover-3d.css +8 -0
  47. package/dist/effects/hover-gallery.css +14 -0
  48. package/dist/effects/index.css +65 -0
  49. package/dist/effects/text-rotate.css +16 -0
  50. package/dist/esm/components/accordion.js +33 -9
  51. package/dist/esm/components/alert.js +81 -3
  52. package/dist/esm/components/autocomplete.js +67 -13
  53. package/dist/esm/components/avatar.js +39 -15
  54. package/dist/esm/components/badge.js +15 -3
  55. package/dist/esm/components/card.js +12 -0
  56. package/dist/esm/components/carousel.d.ts +5 -0
  57. package/dist/esm/components/carousel.js +37 -0
  58. package/dist/esm/components/chat.js +15 -11
  59. package/dist/esm/components/checkbox.js +84 -14
  60. package/dist/esm/components/collapse.js +67 -16
  61. package/dist/esm/components/countdown.d.ts +5 -0
  62. package/dist/esm/components/countdown.js +31 -0
  63. package/dist/esm/components/datepicker.js +77 -1
  64. package/dist/esm/components/diff.d.ts +5 -0
  65. package/dist/esm/components/diff.js +42 -0
  66. package/dist/esm/components/file-input.d.ts +5 -0
  67. package/dist/esm/components/file-input.js +116 -0
  68. package/dist/esm/components/filter-group.d.ts +5 -0
  69. package/dist/esm/components/filter-group.js +461 -0
  70. package/dist/esm/components/form-group.js +96 -205
  71. package/dist/esm/components/form.js +1539 -409
  72. package/dist/esm/components/input.js +115 -32
  73. package/dist/esm/components/kbd.d.ts +5 -0
  74. package/dist/esm/components/kbd.js +37 -0
  75. package/dist/esm/components/list.js +22 -3
  76. package/dist/esm/components/loading.d.ts +5 -0
  77. package/dist/esm/components/loading.js +114 -0
  78. package/dist/esm/components/modal.js +62 -1
  79. package/dist/esm/components/otp-input.js +125 -7
  80. package/dist/esm/components/progress.js +74 -0
  81. package/dist/esm/components/radial-progress.d.ts +5 -0
  82. package/dist/esm/components/radial-progress.js +56 -0
  83. package/dist/esm/components/radio.js +71 -1
  84. package/dist/esm/components/range.d.ts +5 -0
  85. package/dist/esm/components/range.js +50 -0
  86. package/dist/esm/components/rating.js +39 -12
  87. package/dist/esm/components/select.js +75 -1
  88. package/dist/esm/components/skeleton.js +13 -1
  89. package/dist/esm/components/snackbar.js +48 -18
  90. package/dist/esm/components/stat.d.ts +5 -0
  91. package/dist/esm/components/stat.js +49 -0
  92. package/dist/esm/components/switch.js +71 -1
  93. package/dist/esm/components/table.js +8 -0
  94. package/dist/esm/components/textarea.js +75 -1
  95. package/dist/esm/components/timeline.js +40 -25
  96. package/dist/esm/components/toast.js +188 -34
  97. package/dist/esm/components/toggle-switch.d.ts +5 -0
  98. package/dist/esm/components/toggle-switch.js +174 -0
  99. package/dist/esm/components/toggle.js +198 -32
  100. package/dist/esm/components/tooltip.js +2 -47
  101. package/dist/esm/components/validator.d.ts +5 -0
  102. package/dist/esm/components/validator.js +75 -0
  103. package/dist/esm/effects/aura.d.ts +5 -0
  104. package/dist/esm/effects/aura.js +31 -0
  105. package/dist/esm/effects/hover-3d.d.ts +5 -0
  106. package/dist/esm/effects/hover-3d.js +17 -0
  107. package/dist/esm/effects/hover-gallery.d.ts +5 -0
  108. package/dist/esm/effects/hover-gallery.js +23 -0
  109. package/dist/esm/effects/index.d.ts +5 -0
  110. package/dist/esm/effects/index.js +74 -0
  111. package/dist/esm/effects/text-rotate.d.ts +5 -0
  112. package/dist/esm/effects/text-rotate.js +25 -0
  113. package/dist/index.css +7652 -7204
  114. package/dist/index.min.css +1 -1
  115. package/dist/standalone/duskmoonui-themes.css +1 -1
  116. package/dist/standalone/duskmoonui.css +7598 -7141
  117. package/dist/standalone/duskmoonui.js +18 -16
  118. package/dist/standalone/duskmoonui.mjs +18 -16
  119. package/dist/types/types/plugin.d.ts +1 -1
  120. package/dist/types/types/plugin.d.ts.map +1 -1
  121. package/package.json +110 -1
@@ -1,667 +1,1797 @@
1
1
  // Auto-generated from form.css
2
2
  export const css = `/**
3
- * Form Component Styles
4
- * DuskMoonUI - Material Design 3 inspired form system
3
+ * Legacy form entry: native controls and composition reuse canonical CSS.
4
+ * No control state rules belong in this compatibility wrapper.
5
5
  */
6
-
6
+ /** Opt-in native presentation. Applications own errors, messages and announcements. */
7
7
  @layer components {
8
- /* Form Control */
9
- .form-control {
10
- display: flex;
11
- flex-direction: column;
12
- gap: 0.25rem;
13
- width: 100%;
14
- margin-bottom: 1rem;
15
- color: var(--color-on-surface);
8
+ /* Compatibility wrapper states are intentionally immediate. */
9
+ :is(.form-group-info, .form-control.info) { --dm-validation-color: var(--color-info); }
10
+ :is(.form-group-warning, .form-control.warning) { --dm-validation-color: var(--color-warning); }
11
+ :is(.form-group-success, .form-control.success) { --dm-validation-color: var(--color-success); }
12
+
13
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is(.validate :valid, .validate:valid, .validator.validator-success:user-valid):not([aria-invalid="true"]):not(:where(.form-group-error *, .form-control.error *)):not(:disabled) {
14
+ --dm-validation-color: var(--color-success);
15
+ --dm-validation-content: var(--color-success-content);
16
16
  }
17
17
 
18
- /* Label */
19
- .label {
20
- display: flex;
21
- justify-content: space-between;
22
- align-items: center;
23
- padding: 0.25rem 0;
18
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is(.validate :invalid, .validate:invalid, .validator:user-invalid):not(:disabled) {
19
+ --dm-validation-color: var(--color-error);
20
+ --dm-validation-content: var(--color-error-content);
24
21
  }
25
22
 
26
- .label-text {
27
- font-size: 0.875rem;
28
- font-weight: 500;
29
- color: var(--color-on-surface);
30
- padding-bottom: 0.25rem;
23
+ :is(.form-group-error, .form-control.error) {
24
+ --dm-validation-color: var(--color-error);
25
+ --dm-validation-content: var(--color-error-content);
31
26
  }
32
27
 
33
- .label-text-alt {
34
- font-size: 0.75rem;
35
- color: var(--color-on-surface-variant);
28
+ /* Higher specificity than native states, including server-invalid + user-valid. */
29
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is([aria-invalid="true"], .form-group-error *, .form-control.error *):not(:disabled):is(:focus, :not(:focus)) {
30
+ --dm-validation-color: var(--color-error);
31
+ --dm-validation-content: var(--color-error-content);
36
32
  }
37
33
 
38
- /* Checkbox */
39
- .checkbox {
40
- appearance: none;
41
- width: 1.25rem;
42
- height: 1.25rem;
43
- border: 2px solid var(--color-outline);
44
- border-radius: var(--radius-xs);
45
- background-color: transparent;
46
- cursor: pointer;
47
- transition: all 150ms ease-in-out;
48
- position: relative;
34
+ /* This shared selector wins over filled/ghost focus and semantic modifiers. */
35
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is([aria-invalid="true"], .validator:user-invalid, .validator.validator-success:user-valid, .validate :invalid, .validate :valid, .validate:invalid, .validate:valid, .form-group-error *, .form-group-success *, .form-group-warning *, .form-group-info *, .form-control.error *, .form-control.success *, .form-control.warning *, .form-control.info *):not(:disabled) {
36
+ color: var(--dm-validation-color);
37
+ border-color: var(--dm-validation-color);
38
+ --checkbox-color: var(--dm-validation-color);
39
+ --radio-color: var(--dm-validation-color);
40
+ --switch-color: var(--dm-validation-color);
41
+ --switch-content-color: var(--dm-validation-content, var(--color-primary-content));
42
+ --toggle-color: var(--dm-validation-color);
49
43
  }
50
44
 
51
- .checkbox:hover {
52
- border-color: var(--color-primary);
45
+ :is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):is([aria-invalid="true"], .validator:user-invalid, .validator.validator-success:user-valid, .validate :invalid, .validate :valid, .validate:invalid, .validate:valid, .form-group-error *, .form-group-success *, .form-group-warning *, .form-group-info *, .form-control.error *, .form-control.success *, .form-control.warning *, .form-control.info *):not(:disabled):focus {
46
+ border-color: var(--dm-validation-color);
47
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--dm-validation-color) 20%, transparent);
53
48
  }
54
49
 
55
- .checkbox:checked {
56
- background-color: var(--color-primary);
57
- border-color: var(--color-primary);
50
+ :is(.form-group-error, .form-control.error) :is(.form-label, .label-text) { color: var(--color-error); }
51
+ :is(.form-group-success, .form-control.success) :is(.form-label, .label-text) { color: var(--color-success); }
52
+ :is(.form-group-warning, .form-control.warning) :is(.form-label, .label-text) { color: var(--color-warning); }
53
+ :is(.form-group-info, .form-control.info) :is(.form-label, .label-text) { color: var(--color-info); }
54
+
55
+ .validator-hint { font-size: 0.75rem; color: var(--color-on-surface-variant); }
56
+ .validator-error { display: none; font-size: 0.75rem; color: var(--color-error); }
57
+ /* Keep OTP actions stationary when blur reveals an error during a click. */
58
+ .otp-code ~ .validator-error { display: block; visibility: hidden; }
59
+ :is(.form-group, .form-control):has(:is([aria-invalid="true"], .validator:user-invalid, .validate :invalid, .validate:invalid):not(:disabled)) > .validator-error,
60
+ :is(.form-group-error, .form-control.error) > .validator-error { display: block; visibility: visible; }
61
+
62
+ @media (forced-colors: active) {
63
+ :is(input, select, textarea):is(.input, .select, .textarea, .checkbox, .radio, .switch, .toggle, .file-input, .otp-input-field, .otp-code, .datepicker-input):focus-visible {
64
+ outline: 2px solid Highlight;
65
+ outline-offset: 2px;
66
+ }
67
+ input:is(.checkbox, .radio, .switch, .toggle) { appearance: auto; }
68
+ input:is(.checkbox, .radio, .switch, .toggle)::before,
69
+ input:is(.checkbox, .radio, .switch, .toggle)::after { content: none; }
58
70
  }
71
+ }
59
72
 
60
- .checkbox:checked::after {
61
- content: '';
62
- position: absolute;
63
- left: 0.25rem;
64
- top: 0.0625rem;
65
- width: 0.375rem;
66
- height: 0.625rem;
67
- border: solid var(--color-primary-content);
68
- border-width: 0 2px 2px 0;
69
- transform: rotate(45deg);
73
+
74
+
75
+ @layer components {
76
+ .file-input {
77
+ display: block;
78
+ box-sizing: border-box;
79
+ width: 100%;
80
+ min-width: 0;
81
+ padding: 0;
82
+ font: inherit;
83
+ color: var(--color-on-surface);
84
+ background-color: var(--color-surface);
85
+ border: 1px solid currentColor;
86
+ border-radius: var(--radius-sm);
87
+ cursor: pointer;
70
88
  }
89
+ .file-input::file-selector-button {
90
+ padding: 0.75rem 1rem;
91
+ margin-inline-end: 0.75rem;
92
+ font: inherit;
93
+ color: inherit;
94
+ background-color: var(--color-surface-container);
95
+ border: none;
96
+ border-inline-end: 1px solid currentColor;
97
+ cursor: inherit;
98
+ }
99
+ .file-input:hover:not(:disabled)::file-selector-button { background-color: var(--color-surface-container-high); }
100
+ .file-input:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent); }
101
+ .file-input:disabled { opacity: 0.5; cursor: not-allowed; }
102
+ .file-input-xs::file-selector-button { padding: 0.375rem 0.625rem; }
103
+ .file-input-sm::file-selector-button { padding: 0.5rem 0.75rem; }
104
+ .file-input-lg::file-selector-button { padding: 1rem 1.25rem; }
105
+ .file-input-primary { color: var(--color-primary); }
106
+ .file-input-secondary { color: var(--color-secondary); }
107
+ .file-input-tertiary { color: var(--color-tertiary); }
108
+ .file-input-info { color: var(--color-info); }
109
+ .file-input-success { color: var(--color-success); }
110
+ .file-input-warning { color: var(--color-warning); }
111
+ .file-input-error { color: var(--color-error); }
112
+ .file-input-ghost { border-color: transparent; }
113
+ }
71
114
 
72
- .checkbox:focus-visible {
115
+
116
+ /**
117
+ * Input Component Styles
118
+ * DuskMoonUI - Material Design 3 inspired input system
119
+ */
120
+
121
+ @layer components {
122
+ /* Base Input */
123
+ .input {
124
+ box-sizing: border-box;
125
+ min-width: 0;
126
+ display: flex;
127
+ width: 100%;
128
+ padding: 0.75rem 1rem;
129
+ font-size: 1rem;
130
+ line-height: 1.5rem;
131
+ background-color: var(--color-surface);
132
+ color: var(--color-on-surface);
133
+ border: 1px solid currentColor;
134
+ border-radius: var(--radius-sm);
135
+ transition: border-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
136
+ }
137
+
138
+ .input::placeholder {
139
+ color: var(--color-on-surface-variant);
140
+ opacity: 0.7;
141
+ }
142
+
143
+ .input:focus {
73
144
  outline: none;
145
+ }
146
+
147
+ .input:focus-visible {
74
148
  box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
149
+ outline: none;
75
150
  }
76
151
 
77
- .checkbox:disabled {
152
+ .input:disabled {
78
153
  cursor: not-allowed;
79
154
  opacity: 0.5;
155
+ background-color: var(--color-surface-container);
80
156
  }
81
157
 
82
- /* Checkbox Color Variants */
83
- .checkbox-primary:checked {
84
- background-color: var(--color-primary);
85
- border-color: var(--color-primary);
158
+ /* Bordered Input */
159
+ .input-outlined { background-color: transparent; border-color: var(--color-outline); }
160
+ .input-bordered {
161
+ border-color: var(--color-outline);
162
+ background-color: var(--color-surface);
86
163
  }
87
164
 
88
- .checkbox-secondary:checked {
89
- background-color: var(--color-secondary);
90
- border-color: var(--color-secondary);
165
+ .input-bordered:hover:not(:disabled):not(:focus) {
166
+ border-color: var(--color-outline-variant);
91
167
  }
92
168
 
93
- .checkbox-success:checked {
94
- background-color: var(--color-success);
95
- border-color: var(--color-success);
169
+ /* Color Variants — base .input:focus-visible uses currentColor 20%, so no per-variant
170
+ focus-visible overrides needed; setting color: here is sufficient. */
171
+ .input-primary { color: var(--color-primary); }
172
+ .input-secondary { color: var(--color-secondary); }
173
+ .input-tertiary { color: var(--color-tertiary); }
174
+
175
+ /* Semantic Colors */
176
+ .input-info { color: var(--color-info); }
177
+ .input-success { color: var(--color-success); }
178
+ .input-warning { color: var(--color-warning); }
179
+ .input-error { color: var(--color-error); }
180
+ .input-accent { color: var(--color-accent); }
181
+ .input-neutral { color: var(--color-neutral); }
182
+ .input-base { color: var(--color-base-content); }
183
+
184
+ /* Size Variants */
185
+ .input-xs {
186
+ padding: 0.375rem 0.625rem;
187
+ font-size: 0.75rem;
188
+ line-height: 1rem;
189
+ border-radius: var(--radius-xs);
190
+ height: 1.75rem;
96
191
  }
97
192
 
98
- .checkbox-tertiary:checked {
99
- background-color: var(--color-tertiary);
100
- border-color: var(--color-tertiary);
193
+ .input-sm {
194
+ padding: 0.5rem 0.75rem;
195
+ font-size: 0.875rem;
196
+ line-height: 1.25rem;
197
+ border-radius: var(--radius-xs);
198
+ height: 2.25rem;
101
199
  }
102
200
 
103
- .checkbox-info:checked {
104
- background-color: var(--color-info);
105
- border-color: var(--color-info);
201
+ .input-lg {
202
+ padding: 1rem 1.25rem;
203
+ font-size: 1.125rem;
204
+ line-height: 1.75rem;
205
+ border-radius: var(--radius-sm);
206
+ height: 3.5rem;
106
207
  }
107
208
 
108
- .checkbox-warning:checked {
109
- background-color: var(--color-warning);
110
- border-color: var(--color-warning);
209
+ /* Ghost Input */
210
+ .input-md { height: 3rem; }
211
+ .input-ghost {
212
+ background-color: transparent;
213
+ border-color: transparent;
111
214
  }
112
215
 
113
- .checkbox-error:checked {
114
- background-color: var(--color-error);
115
- border-color: var(--color-error);
216
+ .input-ghost:focus-visible {
217
+ background-color: var(--color-surface-container);
218
+ border-color: transparent;
219
+ box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
116
220
  }
117
221
 
118
- .checkbox-accent:checked {
119
- background-color: var(--color-accent);
120
- border-color: var(--color-accent);
222
+ /* Filled Input */
223
+ .input-filled {
224
+ background-color: var(--color-surface-container);
225
+ border-color: transparent;
226
+ border-bottom: 2px solid var(--color-outline);
227
+ border-radius: var(--radius-sm) var(--radius-sm) 0 0;
121
228
  }
122
229
 
123
- .checkbox-accent:checked::after {
124
- border-color: var(--color-accent-content);
230
+ .input-filled:focus-visible {
231
+ border-bottom-color: var(--color-primary);
232
+ box-shadow: none;
125
233
  }
126
234
 
127
- .checkbox-neutral:checked {
128
- background-color: var(--color-neutral);
129
- border-color: var(--color-neutral);
235
+ .input-filled.input-primary:focus-visible { border-bottom-color: var(--color-primary); }
236
+ .input-filled.input-secondary:focus-visible { border-bottom-color: var(--color-secondary); }
237
+ .input-filled.input-tertiary:focus-visible { border-bottom-color: var(--color-tertiary); }
238
+ .input-filled.input-info:focus-visible { border-bottom-color: var(--color-info); }
239
+ .input-filled.input-success:focus-visible { border-bottom-color: var(--color-success); }
240
+ .input-filled.input-warning:focus-visible { border-bottom-color: var(--color-warning); }
241
+ .input-filled.input-error:focus-visible { border-bottom-color: var(--color-error); }
242
+ .input-filled.input-accent:focus-visible { border-bottom-color: var(--color-accent); }
243
+ .input-filled.input-neutral:focus-visible { border-bottom-color: var(--color-neutral); }
244
+ .input-filled.input-base:focus-visible { border-bottom-color: var(--color-base-content); }
245
+
246
+ /* Input with Icon */
247
+ .input-group {
248
+ display: flex;
249
+ align-items: stretch;
130
250
  }
131
251
 
132
- .checkbox-neutral:checked::after {
133
- border-color: var(--color-neutral-content);
252
+ .input-group > .input {
253
+ flex: 1;
254
+ border-radius: 0;
134
255
  }
135
256
 
136
- .checkbox-base:checked {
137
- background-color: var(--color-base-100);
138
- border-color: var(--color-base-100);
257
+ .input-group > .input:first-child {
258
+ border-top-left-radius: var(--radius-sm);
259
+ border-bottom-left-radius: var(--radius-sm);
139
260
  }
140
261
 
141
- .checkbox-base:checked::after {
142
- border-color: var(--color-base-content);
262
+ .input-group > .input:last-child {
263
+ border-top-right-radius: var(--radius-sm);
264
+ border-bottom-right-radius: var(--radius-sm);
143
265
  }
144
266
 
145
- /* Checkbox Sizes */
146
- .checkbox-xs {
147
- width: 0.875rem;
148
- height: 0.875rem;
267
+ .input-group-text {
268
+ display: flex;
269
+ align-items: center;
270
+ justify-content: center;
271
+ padding: 0 1rem;
272
+ background-color: var(--color-surface-container);
273
+ color: var(--color-on-surface-variant);
274
+ border: 1px solid var(--color-outline);
149
275
  }
150
276
 
151
- .checkbox-sm {
152
- width: 1rem;
153
- height: 1rem;
277
+ .input-group-text:first-child {
278
+ border-top-left-radius: var(--radius-sm);
279
+ border-bottom-left-radius: var(--radius-sm);
280
+ border-right: none;
154
281
  }
155
282
 
156
- .checkbox-lg {
157
- width: 1.5rem;
158
- height: 1.5rem;
283
+ .input-group-text:last-child {
284
+ border-top-right-radius: var(--radius-sm);
285
+ border-bottom-right-radius: var(--radius-sm);
286
+ border-left: none;
159
287
  }
160
288
 
161
- /* Radio */
162
- .radio {
289
+ /* Reduce Motion */
290
+ @media (prefers-reduced-motion: reduce) {
291
+ .input,
292
+ .input-filled,
293
+ .file-input {
294
+ transition: none;
295
+ }
296
+ }
297
+ }
298
+
299
+
300
+
301
+ /**
302
+ * Checkbox Component Styles
303
+ * DuskMoonUI - Material Design 3 inspired checkbox
304
+ *
305
+ * Usage: <input type="checkbox" class="checkbox" />
306
+ * With label: <label class="label cursor-pointer gap-2"><input type="checkbox" class="checkbox" /><span>Label</span></label>
307
+ */
308
+
309
+ @layer components {
310
+ :where(.checkbox) { box-sizing: border-box; }
311
+
312
+ /* Base Checkbox - applied directly to input[type="checkbox"] */
313
+ .checkbox {
314
+ --checkbox-size: 1.25rem;
315
+ --checkbox-color: var(--color-primary);
316
+ --checkbox-border-color: var(--color-on-surface-variant);
317
+ color: var(--color-on-surface);
318
+
319
+ position: relative;
320
+ display: inline-grid;
321
+ place-content: center;
322
+ width: var(--checkbox-size);
323
+ height: var(--checkbox-size);
324
+ margin: 0;
325
+ cursor: pointer;
163
326
  appearance: none;
164
- width: 1.25rem;
165
- height: 1.25rem;
166
- border: 2px solid var(--color-outline);
167
- border-radius: var(--radius-full);
168
327
  background-color: transparent;
169
- cursor: pointer;
170
- transition: all 150ms ease-in-out;
171
- position: relative;
328
+ border: 2px solid var(--checkbox-border-color);
329
+ border-radius: var(--radius-xs);
330
+ transition: background-color 150ms ease-in-out, border-color 150ms ease-in-out;
331
+ }
332
+
333
+ /* Checkmark using ::before pseudo-element */
334
+ .checkbox::before {
335
+ content: "";
336
+ width: 0.65em;
337
+ height: 0.35em;
338
+ transform: scale(0) rotate(-45deg);
339
+ transform-origin: center;
340
+ border-bottom: 2px solid var(--dm-validation-content, var(--color-primary-content));
341
+ border-left: 2px solid var(--dm-validation-content, var(--color-primary-content));
342
+ transition: transform 150ms ease-in-out;
172
343
  }
173
344
 
174
- .radio:hover {
175
- border-color: var(--color-primary);
345
+ /* Checked State */
346
+ .checkbox:checked {
347
+ background-color: var(--checkbox-color);
348
+ border-color: var(--checkbox-color);
176
349
  }
177
350
 
178
- .radio:checked {
179
- border-color: var(--color-primary);
351
+ .checkbox:checked::before {
352
+ transform: scale(1) rotate(-45deg);
180
353
  }
181
354
 
182
- .radio:checked::after {
183
- content: '';
184
- position: absolute;
185
- left: 50%;
186
- top: 50%;
187
- width: 0.5rem;
188
- height: 0.5rem;
189
- background-color: var(--color-primary);
190
- border-radius: var(--radius-full);
191
- transform: translate(-50%, -50%);
355
+ /* Indeterminate State */
356
+ .checkbox:indeterminate {
357
+ background-color: var(--checkbox-color);
358
+ border-color: var(--checkbox-color);
192
359
  }
193
360
 
194
- .radio:focus-visible {
195
- outline: none;
196
- box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
361
+ .checkbox:indeterminate::before {
362
+ width: 0.5em;
363
+ height: 0;
364
+ border-bottom: 2px solid var(--dm-validation-content, var(--color-primary-content));
365
+ border-left: 0;
366
+ transform: scale(1) rotate(0deg);
197
367
  }
198
368
 
199
- .radio:disabled {
200
- cursor: not-allowed;
201
- opacity: 0.5;
369
+ /* Hover State */
370
+ .checkbox:hover:not(:disabled):not(:checked) {
371
+ border-color: var(--color-on-surface);
202
372
  }
203
373
 
204
- /* Radio Color Variants */
205
- .radio-primary:checked {
206
- border-color: var(--color-primary);
374
+ .checkbox:checked:hover:not(:disabled),
375
+ .checkbox:indeterminate:hover:not(:disabled) {
376
+ background-color: color-mix(in oklch, var(--checkbox-color), black 10%);
377
+ border-color: color-mix(in oklch, var(--checkbox-color), black 10%);
207
378
  }
208
379
 
209
- .radio-primary:checked::after {
210
- background-color: var(--color-primary);
380
+ /* Focus State */
381
+ .checkbox:focus-visible {
382
+ outline: none;
383
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--checkbox-color) 20%, transparent);
211
384
  }
212
385
 
213
- .radio-secondary:checked {
214
- border-color: var(--color-secondary);
386
+ /* Disabled State */
387
+ .checkbox:disabled {
388
+ cursor: not-allowed;
389
+ opacity: 0.38;
215
390
  }
216
391
 
217
- .radio-secondary:checked::after {
218
- background-color: var(--color-secondary);
219
- }
392
+ /* ========================================
393
+ * Size Variants
394
+ * ======================================== */
220
395
 
221
- .radio-tertiary:checked {
222
- border-color: var(--color-tertiary);
396
+ .checkbox-xs {
397
+ --checkbox-size: 0.875rem;
223
398
  }
224
399
 
225
- .radio-tertiary:checked::after {
226
- background-color: var(--color-tertiary);
400
+ .checkbox-sm {
401
+ --checkbox-size: 1rem;
227
402
  }
228
403
 
229
- .radio-info:checked {
230
- border-color: var(--color-info);
404
+ .checkbox-md {
405
+ --checkbox-size: 1.25rem;
231
406
  }
232
407
 
233
- .radio-info:checked::after {
234
- background-color: var(--color-info);
408
+ .checkbox-lg {
409
+ --checkbox-size: 1.5rem;
235
410
  }
236
411
 
237
- .radio-success:checked {
238
- border-color: var(--color-success);
412
+ .checkbox-xl {
413
+ --checkbox-size: 1.75rem;
239
414
  }
240
415
 
241
- .radio-success:checked::after {
242
- background-color: var(--color-success);
243
- }
416
+ /* ========================================
417
+ * Color Variants
418
+ * ======================================== */
244
419
 
245
- .radio-warning:checked {
246
- border-color: var(--color-warning);
420
+ .checkbox-primary {
421
+ --checkbox-color: var(--color-primary);
247
422
  }
248
423
 
249
- .radio-warning:checked::after {
250
- background-color: var(--color-warning);
424
+ .checkbox-primary:checked::before,
425
+ .checkbox-primary:indeterminate::before {
426
+ border-color: var(--dm-validation-content, var(--color-primary-content));
251
427
  }
252
428
 
253
- .radio-error:checked {
254
- border-color: var(--color-error);
429
+ .checkbox-secondary {
430
+ --checkbox-color: var(--color-secondary);
255
431
  }
256
432
 
257
- .radio-error:checked::after {
258
- background-color: var(--color-error);
433
+ .checkbox-secondary:checked::before,
434
+ .checkbox-secondary:indeterminate::before {
435
+ border-color: var(--dm-validation-content, var(--color-secondary-content));
259
436
  }
260
437
 
261
- .radio-accent:checked {
262
- border-color: var(--color-accent);
438
+ .checkbox-tertiary {
439
+ --checkbox-color: var(--color-tertiary);
263
440
  }
264
441
 
265
- .radio-accent:checked::after {
266
- background-color: var(--color-accent);
442
+ .checkbox-tertiary:checked::before,
443
+ .checkbox-tertiary:indeterminate::before {
444
+ border-color: var(--dm-validation-content, var(--color-tertiary-content));
267
445
  }
268
446
 
269
- .radio-neutral:checked {
270
- border-color: var(--color-neutral);
447
+ .checkbox-success {
448
+ --checkbox-color: var(--color-success);
271
449
  }
272
450
 
273
- .radio-neutral:checked::after {
274
- background-color: var(--color-neutral);
451
+ .checkbox-success:checked::before,
452
+ .checkbox-success:indeterminate::before {
453
+ border-color: var(--dm-validation-content, var(--color-success-content));
275
454
  }
276
455
 
277
- .radio-base:checked {
278
- border-color: var(--color-base-content);
456
+ .checkbox-warning {
457
+ --checkbox-color: var(--color-warning);
279
458
  }
280
459
 
281
- .radio-base:checked::after {
282
- background-color: var(--color-base-content);
460
+ .checkbox-warning:checked::before,
461
+ .checkbox-warning:indeterminate::before {
462
+ border-color: var(--dm-validation-content, var(--color-warning-content));
283
463
  }
284
464
 
285
- /* Toggle/Switch */
286
- .toggle {
287
- appearance: none;
288
- width: 3rem;
289
- height: 1.5rem;
290
- border-radius: var(--radius-lg);
291
- background-color: var(--color-surface-container-highest);
292
- cursor: pointer;
293
- transition: all 150ms ease-in-out;
294
- position: relative;
465
+ .checkbox-error {
466
+ --checkbox-color: var(--color-error);
295
467
  }
296
468
 
297
- .toggle::after {
298
- content: '';
299
- position: absolute;
300
- left: 0.25rem;
301
- top: 50%;
302
- transform: translateY(-50%);
303
- width: 1rem;
304
- height: 1rem;
305
- background-color: var(--color-on-surface-variant);
306
- border-radius: var(--radius-full);
307
- transition: all 150ms ease-in-out;
469
+ .checkbox-error:checked::before,
470
+ .checkbox-error:indeterminate::before {
471
+ border-color: var(--dm-validation-content, var(--color-error-content));
308
472
  }
309
473
 
310
- .toggle:checked {
311
- background-color: var(--color-primary);
474
+ .checkbox-info {
475
+ --checkbox-color: var(--color-info);
312
476
  }
313
477
 
314
- .toggle:checked::after {
315
- left: calc(100% - 1.25rem);
316
- background-color: var(--color-primary-content);
478
+ .checkbox-info:checked::before,
479
+ .checkbox-info:indeterminate::before {
480
+ border-color: var(--dm-validation-content, var(--color-info-content));
317
481
  }
318
482
 
319
- .toggle:focus-visible {
320
- outline: none;
321
- box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
483
+ .checkbox-accent {
484
+ --checkbox-color: var(--color-accent);
322
485
  }
323
486
 
324
- .toggle:disabled {
325
- cursor: not-allowed;
326
- opacity: 0.5;
487
+ .checkbox-accent:checked::before,
488
+ .checkbox-accent:indeterminate::before {
489
+ border-color: var(--dm-validation-content, var(--color-accent-content));
327
490
  }
328
491
 
329
- /* Toggle Sizes */
330
- .toggle-xs {
331
- width: 2rem;
332
- height: 1rem;
492
+ .checkbox-neutral {
493
+ --checkbox-color: var(--color-neutral);
333
494
  }
334
495
 
335
- .toggle-xs::after {
336
- width: 0.625rem;
337
- height: 0.625rem;
338
- left: 0.1875rem;
496
+ .checkbox-neutral:checked::before,
497
+ .checkbox-neutral:indeterminate::before {
498
+ border-color: var(--dm-validation-content, var(--color-neutral-content));
339
499
  }
340
500
 
341
- .toggle-xs:checked::after {
342
- left: calc(100% - 0.8125rem);
501
+ .checkbox-base {
502
+ --checkbox-color: var(--color-base-100);
343
503
  }
344
504
 
345
- .toggle-sm {
346
- width: 2.5rem;
347
- height: 1.25rem;
505
+ .checkbox-base:checked::before,
506
+ .checkbox-base:indeterminate::before {
507
+ border-color: var(--dm-validation-content, var(--color-base-content));
348
508
  }
349
509
 
350
- .toggle-sm::after {
351
- width: 0.75rem;
352
- height: 0.75rem;
510
+ /* Ghost Variant — borderless unchecked state */
511
+ .checkbox-ghost {
512
+ --checkbox-border-color: transparent;
353
513
  }
354
514
 
355
- .toggle-lg {
356
- width: 4rem;
357
- height: 2rem;
515
+ .checkbox-ghost:hover:not(:disabled):not(:checked) {
516
+ border-color: transparent;
358
517
  }
359
518
 
360
- .toggle-lg::after {
361
- width: 1.5rem;
362
- height: 1.5rem;
363
- }
519
+ /* ========================================
520
+ * Checkbox with Label (wrapper pattern)
521
+ * ======================================== */
364
522
 
365
- /* Select */
366
- .select {
367
- appearance: none;
368
- width: 100%;
369
- padding: 0.75rem 2.5rem 0.75rem 1rem;
370
- font-size: 1rem;
371
- line-height: 1.5rem;
372
- background-color: var(--color-surface);
373
- color: var(--color-on-surface);
374
- border: 1px solid currentColor;
375
- border-radius: var(--radius-sm);
523
+ .checkbox-label {
524
+ display: inline-flex;
525
+ align-items: center;
526
+ gap: 0.5rem;
376
527
  cursor: pointer;
377
- background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
378
- background-position: right 0.75rem center;
379
- background-repeat: no-repeat;
380
- background-size: 1.25rem;
381
- transition: border-color 150ms ease-in-out;
528
+ user-select: none;
529
+ font-size: 0.875rem;
530
+ color: var(--color-on-surface);
382
531
  }
383
532
 
384
- .select:focus {
385
- outline: none;
533
+ .checkbox-label:has(.checkbox:disabled) {
534
+ cursor: not-allowed;
535
+ opacity: 0.38;
386
536
  }
387
537
 
388
- .select:focus-visible {
389
- border-color: var(--color-primary);
390
- outline: none;
391
- }
538
+ /* ========================================
539
+ * Checkbox Group
540
+ * ======================================== */
392
541
 
393
- .select:disabled {
394
- cursor: not-allowed;
395
- opacity: 0.5;
542
+ .checkbox-group {
543
+ display: flex;
544
+ flex-direction: column;
545
+ gap: 0.75rem;
396
546
  }
397
547
 
398
- /* Select Bordered */
399
- .select-bordered {
400
- border-color: var(--color-outline);
548
+ .checkbox-group-horizontal {
549
+ flex-direction: row;
550
+ flex-wrap: wrap;
551
+ gap: 1.5rem;
401
552
  }
402
553
 
403
- /* Select Color Variants */
404
- .select-primary:focus-visible {
405
- border-color: var(--color-primary);
554
+ .checkbox-group-label {
555
+ font-size: 0.875rem;
556
+ font-weight: 500;
557
+ color: var(--color-on-surface);
558
+ margin-bottom: 0.5rem;
406
559
  }
407
560
 
408
- .select-secondary:focus-visible {
409
- border-color: var(--color-secondary);
410
- }
561
+ /* ========================================
562
+ * Reduce Motion
563
+ * ======================================== */
411
564
 
412
- .select-tertiary:focus-visible {
413
- border-color: var(--color-tertiary);
565
+ @media (prefers-reduced-motion: reduce) {
566
+ .checkbox,
567
+ .checkbox::before {
568
+ transition: none;
569
+ }
414
570
  }
571
+ }
572
+
573
+
415
574
 
416
- .select-info:focus-visible {
417
- border-color: var(--color-info);
575
+ /**
576
+ * Radio Component Styles
577
+ * DuskMoonUI - Material Design 3 inspired radio button
578
+ *
579
+ * Usage: <input type="radio" name="group" class="radio" />
580
+ * With label: <label class="radio-label"><input type="radio" name="group" class="radio" /><span>Option</span></label>
581
+ */
582
+
583
+ @layer components {
584
+ :where(.radio) { box-sizing: border-box; }
585
+
586
+ /* Base Radio - applied directly to input[type="radio"] */
587
+ .radio {
588
+ --radio-size: 1.25rem;
589
+ --radio-color: var(--color-primary);
590
+ --radio-border-color: var(--color-on-surface-variant);
591
+ color: var(--color-on-surface);
592
+
593
+ position: relative;
594
+ display: inline-grid;
595
+ place-content: center;
596
+ width: var(--radio-size);
597
+ height: var(--radio-size);
598
+ margin: 0;
599
+ cursor: pointer;
600
+ appearance: none;
601
+ background-color: transparent;
602
+ border: 2px solid var(--radio-border-color);
603
+ border-radius: var(--radius-full);
604
+ transition: border-color 150ms ease-in-out;
418
605
  }
419
606
 
420
- .select-success:focus-visible {
421
- border-color: var(--color-success);
607
+ /* Inner dot using ::before pseudo-element */
608
+ .radio::before {
609
+ content: "";
610
+ width: 0.5em;
611
+ height: 0.5em;
612
+ border-radius: var(--radius-full);
613
+ background-color: var(--radio-color);
614
+ transform: scale(0);
615
+ transition: transform 150ms ease-in-out;
422
616
  }
423
617
 
424
- .select-warning:focus-visible {
425
- border-color: var(--color-warning);
618
+ /* Checked State */
619
+ .radio:checked {
620
+ border-color: var(--radio-color);
426
621
  }
427
622
 
428
- .select-error:focus-visible {
429
- border-color: var(--color-error);
623
+ .radio:checked::before {
624
+ transform: scale(1);
430
625
  }
431
626
 
432
- .select-accent:focus-visible {
433
- border-color: var(--color-accent);
627
+ /* Hover State */
628
+ .radio:hover:not(:disabled):not(:checked) {
629
+ border-color: var(--color-on-surface);
434
630
  }
435
631
 
436
- .select-neutral:focus-visible {
437
- border-color: var(--color-neutral);
632
+ .radio:checked:hover:not(:disabled) {
633
+ border-color: color-mix(in oklch, var(--radio-color), black 10%);
438
634
  }
439
635
 
440
- .select-base:focus-visible {
441
- border-color: var(--color-base-content);
636
+ .radio:checked:hover:not(:disabled)::before {
637
+ background-color: color-mix(in oklch, var(--radio-color), black 10%);
442
638
  }
443
639
 
444
- /* Select Sizes */
640
+ /* Focus State */
641
+ .radio:focus-visible {
642
+ outline: none;
643
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--radio-color) 20%, transparent);
644
+ }
645
+
646
+ /* Disabled State */
647
+ .radio:disabled {
648
+ cursor: not-allowed;
649
+ opacity: 0.38;
650
+ }
651
+
652
+ /* ========================================
653
+ * Size Variants
654
+ * ======================================== */
655
+
656
+ .radio-xs {
657
+ --radio-size: 0.875rem;
658
+ }
659
+
660
+ .radio-sm {
661
+ --radio-size: 1rem;
662
+ }
663
+
664
+ .radio-md {
665
+ --radio-size: 1.25rem;
666
+ }
667
+
668
+ .radio-lg {
669
+ --radio-size: 1.5rem;
670
+ }
671
+
672
+ .radio-xl {
673
+ --radio-size: 1.75rem;
674
+ }
675
+
676
+ /* ========================================
677
+ * Color Variants
678
+ * ======================================== */
679
+
680
+ .radio-primary {
681
+ --radio-color: var(--color-primary);
682
+ }
683
+
684
+ .radio-secondary {
685
+ --radio-color: var(--color-secondary);
686
+ }
687
+
688
+ .radio-tertiary {
689
+ --radio-color: var(--color-tertiary);
690
+ }
691
+
692
+ .radio-success {
693
+ --radio-color: var(--color-success);
694
+ }
695
+
696
+ .radio-warning {
697
+ --radio-color: var(--color-warning);
698
+ }
699
+
700
+ .radio-error {
701
+ --radio-color: var(--color-error);
702
+ }
703
+
704
+ .radio-info {
705
+ --radio-color: var(--color-info);
706
+ }
707
+
708
+ .radio-accent {
709
+ --radio-color: var(--color-accent);
710
+ }
711
+
712
+ .radio-neutral {
713
+ --radio-color: var(--color-neutral);
714
+ }
715
+
716
+ .radio-base {
717
+ --radio-color: var(--color-base-content);
718
+ }
719
+
720
+ /* Ghost Variant — borderless unchecked state */
721
+ .radio-ghost {
722
+ --radio-border-color: transparent;
723
+ }
724
+
725
+ .radio-ghost:hover:not(:disabled):not(:checked) {
726
+ border-color: transparent;
727
+ }
728
+
729
+ /* ========================================
730
+ * Radio with Label (wrapper pattern)
731
+ * ======================================== */
732
+
733
+ .radio-label {
734
+ display: inline-flex;
735
+ align-items: center;
736
+ gap: 0.5rem;
737
+ cursor: pointer;
738
+ user-select: none;
739
+ font-size: 0.875rem;
740
+ color: var(--color-on-surface);
741
+ }
742
+
743
+ .radio-label:has(.radio:disabled) {
744
+ cursor: not-allowed;
745
+ opacity: 0.38;
746
+ }
747
+
748
+ /* ========================================
749
+ * Radio Group
750
+ * ======================================== */
751
+
752
+ .radio-group {
753
+ display: flex;
754
+ flex-direction: column;
755
+ gap: 0.75rem;
756
+ }
757
+
758
+ .radio-group-horizontal {
759
+ flex-direction: row;
760
+ flex-wrap: wrap;
761
+ gap: 1.5rem;
762
+ }
763
+
764
+ .radio-group-label {
765
+ font-size: 0.875rem;
766
+ font-weight: 500;
767
+ color: var(--color-on-surface);
768
+ margin-bottom: 0.5rem;
769
+ }
770
+
771
+ /* ========================================
772
+ * Reduce Motion
773
+ * ======================================== */
774
+
775
+ @media (prefers-reduced-motion: reduce) {
776
+ .radio,
777
+ .radio::before {
778
+ transition: none;
779
+ }
780
+ }
781
+ }
782
+
783
+
784
+
785
+ @layer components {
786
+ /* Toggle/Switch */
787
+ .toggle {
788
+ --toggle-color: var(--color-primary);
789
+ box-sizing: border-box;
790
+ color: var(--color-on-surface);
791
+ border: 1px solid currentColor;
792
+ appearance: none;
793
+ width: 3rem;
794
+ height: 1.5rem;
795
+ border-radius: var(--radius-lg);
796
+ background-color: var(--color-surface-container-highest);
797
+ cursor: pointer;
798
+ transition: all 150ms ease-in-out;
799
+ position: relative;
800
+ }
801
+
802
+ .toggle::after {
803
+ content: '';
804
+ position: absolute;
805
+ inset-inline-start: 0.25rem;
806
+ top: 50%;
807
+ transform: translateY(-50%);
808
+ width: 1rem;
809
+ height: 1rem;
810
+ background-color: var(--color-on-surface-variant);
811
+ border-radius: var(--radius-full);
812
+ transition: all 150ms ease-in-out;
813
+ }
814
+
815
+ .toggle:checked {
816
+ background-color: var(--toggle-color);
817
+ }
818
+
819
+ .toggle:checked::after {
820
+ inset-inline-start: calc(100% - 1.25rem);
821
+ background-color: var(--dm-validation-content, var(--color-primary-content));
822
+ }
823
+
824
+ .toggle:focus-visible {
825
+ outline: none;
826
+ box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
827
+ }
828
+
829
+ .toggle:disabled {
830
+ cursor: not-allowed;
831
+ opacity: 0.5;
832
+ }
833
+
834
+ /* Toggle Sizes */
835
+ .toggle-xs {
836
+ width: 2rem;
837
+ height: 1rem;
838
+ }
839
+
840
+ .toggle-xs::after {
841
+ width: 0.625rem;
842
+ height: 0.625rem;
843
+ inset-inline-start: 0.1875rem;
844
+ }
845
+
846
+ .toggle-xs:checked::after {
847
+ inset-inline-start: calc(100% - 0.8125rem);
848
+ }
849
+
850
+ .toggle-sm {
851
+ width: 2.5rem;
852
+ height: 1.25rem;
853
+ }
854
+
855
+ .toggle-sm::after {
856
+ width: 0.75rem;
857
+ height: 0.75rem;
858
+ }
859
+
860
+ .toggle-lg {
861
+ width: 4rem;
862
+ height: 2rem;
863
+ }
864
+
865
+ .toggle-lg::after {
866
+ width: 1.5rem;
867
+ height: 1.5rem;
868
+ }
869
+
870
+ .toggle-sm:checked::after { inset-inline-start: calc(100% - 1rem); }
871
+ .toggle-lg:checked::after { inset-inline-start: calc(100% - 1.75rem); }
872
+ .toggle-primary { --toggle-color: var(--color-primary); }
873
+ .toggle-secondary { --toggle-color: var(--color-secondary); }
874
+ .toggle-tertiary { --toggle-color: var(--color-tertiary); }
875
+ .toggle-info { --toggle-color: var(--color-info); }
876
+ .toggle-success { --toggle-color: var(--color-success); }
877
+ .toggle-warning { --toggle-color: var(--color-warning); }
878
+ .toggle-error { --toggle-color: var(--color-error); }
879
+ .toggle-ghost { border-color: transparent; }
880
+ @media (prefers-reduced-motion: reduce) { .toggle, .toggle::after { transition: none; } }
881
+ }
882
+
883
+
884
+
885
+ /**
886
+ * Select Component Styles
887
+ * DuskMoonUI - Material Design 3 inspired select/dropdown system
888
+ */
889
+
890
+ @layer components {
891
+ :where(.select) { box-sizing: border-box; }
892
+
893
+ /* Base Select */
894
+ .select {
895
+ display: block;
896
+ width: 100%;
897
+ padding: 0.75rem 2.5rem 0.75rem 1rem;
898
+ font-size: 1rem;
899
+ line-height: 1.5rem;
900
+ color: var(--color-on-surface);
901
+ background-color: var(--color-surface);
902
+ border: 1px solid currentColor;
903
+ border-radius: var(--radius-sm);
904
+ outline: none;
905
+ cursor: pointer;
906
+ appearance: none;
907
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
908
+ background-position: right 0.75rem center;
909
+ background-repeat: no-repeat;
910
+ background-size: 1.25rem 1.25rem;
911
+ transition: border-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
912
+ }
913
+
914
+ .select:hover:not(:disabled) {
915
+ background-color: var(--color-surface-container);
916
+ }
917
+
918
+ .select:focus {
919
+ outline: none;
920
+ }
921
+
922
+ .select:focus-visible {
923
+ box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
924
+ }
925
+
926
+ .select:disabled {
927
+ cursor: not-allowed;
928
+ opacity: 0.5;
929
+ background-color: var(--color-surface-container);
930
+ }
931
+
932
+ /* Filled Variant */
933
+ .select-filled {
934
+ background-color: var(--color-surface-container);
935
+ border: none;
936
+ border-bottom: 2px solid var(--color-outline);
937
+ border-radius: var(--radius-sm) var(--radius-sm) 0 0;
938
+ }
939
+
940
+ .select-filled:hover:not(:disabled) {
941
+ background-color: var(--color-surface-container-high);
942
+ border-bottom-color: var(--color-on-surface);
943
+ }
944
+
945
+ .select-filled:focus-visible {
946
+ border-bottom-color: var(--color-primary);
947
+ box-shadow: none;
948
+ }
949
+
950
+ .select-filled.select-primary:focus-visible { border-bottom-color: var(--color-primary); }
951
+ .select-filled.select-secondary:focus-visible { border-bottom-color: var(--color-secondary); }
952
+ .select-filled.select-tertiary:focus-visible { border-bottom-color: var(--color-tertiary); }
953
+ .select-filled.select-info:focus-visible { border-bottom-color: var(--color-info); }
954
+ .select-filled.select-success:focus-visible { border-bottom-color: var(--color-success); }
955
+ .select-filled.select-warning:focus-visible { border-bottom-color: var(--color-warning); }
956
+ .select-filled.select-error:focus-visible { border-bottom-color: var(--color-error); }
957
+ .select-filled.select-accent:focus-visible { border-bottom-color: var(--color-accent); }
958
+ .select-filled.select-neutral:focus-visible { border-bottom-color: var(--color-neutral); }
959
+ .select-filled.select-base:focus-visible { border-bottom-color: var(--color-base-content); }
960
+
961
+ /* Outlined Variant (default is outlined) */
962
+ .select-outlined {
963
+ background-color: transparent;
964
+ border: 1px solid var(--color-outline);
965
+ border-radius: var(--radius-sm);
966
+ }
967
+
968
+ /* Color Variants — base .select:focus-visible uses currentColor 20%, so no per-variant
969
+ focus-visible overrides needed; setting color: here is sufficient. */
970
+ .select-primary { color: var(--color-primary); }
971
+ .select-secondary { color: var(--color-secondary); }
972
+ .select-tertiary { color: var(--color-tertiary); }
973
+
974
+ /* Semantic Colors */
975
+ .select-info { color: var(--color-info); }
976
+ .select-success { color: var(--color-success); }
977
+ .select-warning { color: var(--color-warning); }
978
+ .select-error { color: var(--color-error); }
979
+ .select-accent { color: var(--color-accent); }
980
+ .select-neutral { color: var(--color-neutral); }
981
+ .select-base { color: var(--color-base-content); }
982
+
983
+ .select-bordered { border-color: var(--color-outline); }
984
+ .select-md { font-size: 1rem; }
985
+ .select:dir(rtl) { padding-inline: 1rem 2.5rem; background-position: left 0.75rem center; }
986
+
987
+ /* Size Variants */
445
988
  .select-xs {
446
989
  padding: 0.375rem 2rem 0.375rem 0.625rem;
447
990
  font-size: 0.75rem;
448
991
  line-height: 1rem;
992
+ border-radius: var(--radius-xs);
993
+ background-size: 1rem 1rem;
994
+ }
995
+
996
+ .select-sm {
997
+ padding: 0.5rem 2.25rem 0.5rem 0.75rem;
998
+ font-size: 0.875rem;
999
+ line-height: 1.25rem;
1000
+ border-radius: var(--radius-xs);
1001
+ background-size: 1.125rem 1.125rem;
1002
+ }
1003
+
1004
+ .select-lg {
1005
+ padding: 1rem 3rem 1rem 1.25rem;
1006
+ font-size: 1.125rem;
1007
+ line-height: 1.75rem;
1008
+ border-radius: var(--radius-sm);
1009
+ background-size: 1.5rem 1.5rem;
1010
+ background-position: right 1rem center;
1011
+ }
1012
+
1013
+ /* Select Container */
1014
+ .select-container {
1015
+ position: relative;
1016
+ display: flex;
1017
+ flex-direction: column;
1018
+ gap: 0.25rem;
1019
+ width: 100%;
1020
+ }
1021
+
1022
+ .select-label {
1023
+ font-size: 0.875rem;
1024
+ font-weight: 500;
1025
+ color: var(--color-on-surface);
1026
+ }
1027
+
1028
+ .select-helper {
1029
+ font-size: 0.75rem;
1030
+ color: var(--color-on-surface-variant);
1031
+ }
1032
+
1033
+ .select-container-error .select-helper {
1034
+ color: var(--color-error);
1035
+ }
1036
+
1037
+ /* Select Group */
1038
+ .select-group {
1039
+ display: flex;
1040
+ flex-direction: column;
1041
+ gap: 0.25rem;
1042
+ }
1043
+
1044
+ /* Multiple Select */
1045
+ .select-multiple {
1046
+ padding: 0.5rem;
1047
+ height: auto;
1048
+ min-height: 6rem;
1049
+ background-image: none;
1050
+ }
1051
+
1052
+ .select-multiple option {
1053
+ padding: 0.5rem 0.75rem;
1054
+ border-radius: var(--radius-xs);
1055
+ }
1056
+
1057
+ .select-multiple option:checked {
1058
+ background-color: var(--color-primary-container);
1059
+ color: var(--color-on-primary-container);
1060
+ }
1061
+
1062
+ /* Ghost Select */
1063
+ .select-ghost {
1064
+ background-color: transparent;
1065
+ border-color: transparent;
1066
+ }
1067
+
1068
+ .select-ghost:hover:not(:disabled) {
1069
+ background-color: var(--color-surface-container);
1070
+ border-color: transparent;
1071
+ }
1072
+
1073
+ .select-ghost:focus-visible {
1074
+ background-color: var(--color-surface-container);
1075
+ border-color: transparent;
1076
+ box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
1077
+ }
1078
+
1079
+ /* Reduce Motion */
1080
+ @media (prefers-reduced-motion: reduce) {
1081
+ .select {
1082
+ transition: none;
1083
+ }
1084
+ }
1085
+ }
1086
+
1087
+
1088
+
1089
+ /**
1090
+ * Textarea Component Styles
1091
+ * DuskMoonUI - Material Design 3 inspired multi-line text input
1092
+ */
1093
+
1094
+ @layer components {
1095
+ :where(.textarea) { box-sizing: border-box; }
1096
+
1097
+ /* ============================================
1098
+ * TEXTAREA CONTAINER
1099
+ * ============================================ */
1100
+
1101
+ .textarea-container {
1102
+ position: relative;
1103
+ display: flex;
1104
+ flex-direction: column;
1105
+ gap: 0.25rem;
1106
+ width: 100%;
1107
+ }
1108
+
1109
+ /* ============================================
1110
+ * TEXTAREA LABEL
1111
+ * ============================================ */
1112
+
1113
+ .textarea-label {
1114
+ display: block;
1115
+ font-size: 0.875rem;
1116
+ font-weight: 500;
1117
+ line-height: 1.25rem;
1118
+ color: var(--color-on-surface);
1119
+ margin-bottom: 0.25rem;
1120
+ }
1121
+
1122
+ /* Floating Label */
1123
+ .textarea-label-floating {
1124
+ position: absolute;
1125
+ top: 0.75rem;
1126
+ left: 1rem;
1127
+ font-size: 1rem;
1128
+ font-weight: 400;
1129
+ color: var(--color-on-surface-variant);
1130
+ pointer-events: none;
1131
+ transition: all 150ms ease-in-out;
1132
+ transform-origin: left top;
1133
+ z-index: 1;
1134
+ }
1135
+
1136
+ /* Floating label active state - when textarea has content or focus */
1137
+ .textarea:focus ~ .textarea-label-floating,
1138
+ .textarea:not(:placeholder-shown) ~ .textarea-label-floating {
1139
+ top: -0.5rem;
1140
+ left: 0.75rem;
1141
+ font-size: 0.75rem;
1142
+ font-weight: 500;
1143
+ color: var(--color-primary);
1144
+ background-color: var(--color-surface);
1145
+ padding: 0 0.25rem;
1146
+ }
1147
+
1148
+ /* Floating label for filled variant */
1149
+ .textarea-filled ~ .textarea-label-floating {
1150
+ background-color: transparent;
1151
+ }
1152
+
1153
+ .textarea-filled:focus ~ .textarea-label-floating,
1154
+ .textarea-filled:not(:placeholder-shown) ~ .textarea-label-floating {
1155
+ top: 0.25rem;
1156
+ left: 0.75rem;
1157
+ background-color: transparent;
1158
+ }
1159
+
1160
+ /* ============================================
1161
+ * HELPER TEXT
1162
+ * ============================================ */
1163
+
1164
+ .textarea-helper {
1165
+ font-size: 0.75rem;
1166
+ line-height: 1rem;
1167
+ color: var(--color-on-surface-variant);
1168
+ margin-top: 0.25rem;
1169
+ }
1170
+
1171
+ /* ============================================
1172
+ * BASE TEXTAREA
1173
+ * ============================================ */
1174
+
1175
+ .textarea {
1176
+ display: block;
1177
+ width: 100%;
1178
+ min-height: 6rem;
1179
+ padding: 0.75rem 1rem;
1180
+ font-size: 1rem;
1181
+ line-height: 1.5rem;
1182
+ font-family: inherit;
1183
+ color: var(--color-on-surface);
1184
+ background-color: var(--color-surface);
1185
+ border: 1px solid currentColor;
1186
+ border-radius: var(--radius-sm);
1187
+ outline: none;
1188
+ resize: vertical;
1189
+ field-sizing: content;
1190
+ transition: border-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
1191
+ }
1192
+
1193
+ .textarea::placeholder {
1194
+ color: var(--color-on-surface-variant);
1195
+ opacity: 0.7;
1196
+ }
1197
+
1198
+ .textarea:hover:not(:disabled) {
1199
+ background-color: var(--color-surface-container);
1200
+ }
1201
+
1202
+ .textarea:focus {
1203
+ outline: none;
1204
+ }
1205
+
1206
+ .textarea:focus-visible {
1207
+ box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
1208
+ }
1209
+
1210
+ .textarea:disabled {
1211
+ cursor: not-allowed;
1212
+ opacity: 0.5;
1213
+ background-color: var(--color-surface-container);
1214
+ resize: none;
1215
+ }
1216
+
1217
+ .textarea[readonly] {
1218
+ background-color: var(--color-surface-container);
1219
+ cursor: default;
1220
+ }
1221
+
1222
+ /* Full Width */
1223
+ .textarea-full {
1224
+ width: 100%;
1225
+ }
1226
+
1227
+ /* ============================================
1228
+ * VARIANTS
1229
+ * ============================================ */
1230
+
1231
+ /* Filled Variant */
1232
+ .textarea-filled {
1233
+ background-color: var(--color-surface-container);
1234
+ border: none;
1235
+ border-bottom: 2px solid var(--color-outline);
1236
+ border-radius: var(--radius-sm) var(--radius-sm) 0 0;
1237
+ }
1238
+
1239
+ .textarea-filled:hover:not(:disabled) {
1240
+ background-color: var(--color-surface-container-high);
1241
+ border-bottom-color: var(--color-on-surface);
1242
+ }
1243
+
1244
+ .textarea-filled:focus-visible {
1245
+ border-bottom-color: var(--color-primary);
1246
+ box-shadow: none;
1247
+ }
1248
+
1249
+ .textarea-filled.textarea-primary:focus-visible { border-bottom-color: var(--color-primary); }
1250
+ .textarea-filled.textarea-secondary:focus-visible { border-bottom-color: var(--color-secondary); }
1251
+ .textarea-filled.textarea-tertiary:focus-visible { border-bottom-color: var(--color-tertiary); }
1252
+ .textarea-filled.textarea-info:focus-visible { border-bottom-color: var(--color-info); }
1253
+ .textarea-filled.textarea-success:focus-visible { border-bottom-color: var(--color-success); }
1254
+ .textarea-filled.textarea-warning:focus-visible { border-bottom-color: var(--color-warning); }
1255
+ .textarea-filled.textarea-error:focus-visible { border-bottom-color: var(--color-error); }
1256
+ .textarea-filled.textarea-accent:focus-visible { border-bottom-color: var(--color-accent); }
1257
+ .textarea-filled.textarea-neutral:focus-visible { border-bottom-color: var(--color-neutral); }
1258
+ .textarea-filled.textarea-base:focus-visible { border-bottom-color: var(--color-base-content); }
1259
+
1260
+ /* Outlined Variant (default) */
1261
+ .textarea-outlined {
1262
+ background-color: transparent;
1263
+ border: 1px solid var(--color-outline);
1264
+ border-radius: var(--radius-sm);
449
1265
  }
450
1266
 
451
- .select-sm {
452
- padding: 0.5rem 2.25rem 0.5rem 0.75rem;
1267
+ /* Ghost Variant */
1268
+ .textarea-ghost {
1269
+ background-color: transparent;
1270
+ border-color: transparent;
1271
+ }
1272
+
1273
+ .textarea-ghost:hover:not(:disabled) {
1274
+ background-color: var(--color-surface-container);
1275
+ border-color: transparent;
1276
+ }
1277
+
1278
+ .textarea-ghost:focus-visible {
1279
+ background-color: var(--color-surface-container);
1280
+ border-color: transparent;
1281
+ box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
1282
+ }
1283
+
1284
+ /* ============================================
1285
+ * COLOR VARIANTS
1286
+ * ============================================ */
1287
+
1288
+ /* Color Variants — base .textarea:focus-visible uses currentColor 20%, so no per-variant
1289
+ focus-visible overrides needed; setting color: here is sufficient. */
1290
+ .textarea-primary { color: var(--color-primary); }
1291
+ .textarea-secondary { color: var(--color-secondary); }
1292
+ .textarea-tertiary { color: var(--color-tertiary); }
1293
+
1294
+ /* ============================================
1295
+ * SEMANTIC COLORS
1296
+ * ============================================ */
1297
+
1298
+ .textarea-info { color: var(--color-info); }
1299
+ .textarea-success { color: var(--color-success); }
1300
+ .textarea-warning { color: var(--color-warning); }
1301
+ .textarea-error { color: var(--color-error); }
1302
+ .textarea-accent { color: var(--color-accent); }
1303
+ .textarea-neutral { color: var(--color-neutral); }
1304
+ .textarea-base { color: var(--color-base-content); }
1305
+
1306
+ /* ============================================
1307
+ * SIZE VARIANTS
1308
+ * ============================================ */
1309
+
1310
+ .textarea-xs { min-height: 3rem; padding: 0.375rem 0.625rem; font-size: 0.75rem; line-height: 1rem; }
1311
+ .textarea-md { min-height: 6rem; }
1312
+ .textarea-bordered { border-color: var(--color-outline); }
1313
+
1314
+ .textarea-sm {
1315
+ min-height: 4rem;
1316
+ padding: 0.5rem 0.75rem;
453
1317
  font-size: 0.875rem;
454
1318
  line-height: 1.25rem;
1319
+ border-radius: var(--radius-xs);
455
1320
  }
456
1321
 
457
- .select-lg {
458
- padding: 1rem 3rem 1rem 1.25rem;
1322
+ .textarea-lg {
1323
+ min-height: 8rem;
1324
+ padding: 1rem 1.25rem;
459
1325
  font-size: 1.125rem;
460
1326
  line-height: 1.75rem;
1327
+ border-radius: var(--radius-sm);
461
1328
  }
462
1329
 
463
- /* Textarea */
464
- .textarea {
465
- width: 100%;
466
- min-height: 6rem;
467
- padding: 0.75rem 1rem;
468
- font-size: 1rem;
469
- line-height: 1.5rem;
470
- background-color: var(--color-surface);
471
- color: var(--color-on-surface);
472
- border: 1px solid currentColor;
473
- border-radius: var(--radius-sm);
474
- resize: vertical;
475
- field-sizing: content;
476
- transition: border-color 150ms ease-in-out;
1330
+ /* ============================================
1331
+ * RESIZE OPTIONS
1332
+ * ============================================ */
1333
+
1334
+ .textarea-resize-none {
1335
+ resize: none;
477
1336
  }
478
1337
 
479
- .textarea::placeholder {
480
- color: var(--color-on-surface-variant);
481
- opacity: 0.7;
1338
+ .textarea-resize-vertical {
1339
+ resize: vertical;
482
1340
  }
483
1341
 
484
- .textarea:focus {
485
- outline: none;
1342
+ .textarea-resize-horizontal {
1343
+ resize: horizontal;
486
1344
  }
487
1345
 
488
- .textarea:focus-visible {
489
- border-color: var(--color-primary);
1346
+ .textarea-resize-both {
1347
+ resize: both;
490
1348
  }
491
1349
 
492
- .textarea:disabled {
493
- cursor: not-allowed;
494
- opacity: 0.5;
1350
+ /* ============================================
1351
+ * COMPACT AUTO-RESIZE
1352
+ * ============================================ */
1353
+
1354
+ .textarea-auto-resize,
1355
+ .textarea-autosize {
495
1356
  resize: none;
1357
+ overflow: hidden;
1358
+ min-height: 3rem;
1359
+ field-sizing: content; /* Modern CSS - auto-grows without JS in supported browsers */
496
1360
  }
497
1361
 
498
- .textarea-bordered {
499
- border-color: var(--color-outline);
1362
+ /* ============================================
1363
+ * CHARACTER COUNTER
1364
+ * ============================================ */
1365
+
1366
+ .textarea-counter {
1367
+ font-size: 0.75rem;
1368
+ line-height: 1rem;
1369
+ color: var(--color-on-surface-variant);
1370
+ text-align: right;
1371
+ }
1372
+
1373
+ .textarea-counter-error,
1374
+ .textarea-counter-exceeded {
1375
+ color: var(--color-error);
500
1376
  }
501
1377
 
502
- /* Validation States */
503
- .form-control.error .input,
504
- .form-control.error .select,
505
- .form-control.error .textarea,
506
- .input-error,
507
- .select-error,
508
- .textarea-error {
509
- border-color: var(--color-error);
1378
+ /* ============================================
1379
+ * CONTAINER STATES
1380
+ * ============================================ */
1381
+
1382
+ .textarea-container-error .textarea-label {
1383
+ color: var(--color-error);
510
1384
  }
511
1385
 
512
- .form-control.error .input:focus-visible,
513
- .form-control.error .select:focus-visible,
514
- .form-control.error .textarea:focus-visible {
515
- border-color: var(--color-error);
516
- box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-error) 10%, transparent);
1386
+ .textarea-container-error .textarea-helper {
1387
+ color: var(--color-error);
517
1388
  }
518
1389
 
519
- .form-control.error .label-text {
1390
+ .textarea-container-error .textarea-label-floating {
520
1391
  color: var(--color-error);
521
1392
  }
522
1393
 
523
- .form-control.success .input,
524
- .form-control.success .select,
525
- .form-control.success .textarea,
526
- .input-success,
527
- .select-success,
528
- .textarea-success {
529
- border-color: var(--color-success);
1394
+ .textarea-container-error .textarea:focus ~ .textarea-label-floating {
1395
+ color: var(--color-error);
530
1396
  }
531
1397
 
532
- .form-control.success .input:focus-visible,
533
- .form-control.success .select:focus-visible,
534
- .form-control.success .textarea:focus-visible {
535
- border-color: var(--color-success);
536
- box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-success) 10%, transparent);
1398
+ .textarea-container-success .textarea-label {
1399
+ color: var(--color-success);
537
1400
  }
538
1401
 
539
- .form-control.success .label-text {
1402
+ .textarea-container-success .textarea-helper {
540
1403
  color: var(--color-success);
541
1404
  }
542
1405
 
543
- .form-control.warning .input,
544
- .form-control.warning .select,
545
- .form-control.warning .textarea,
546
- .input-warning,
547
- .select-warning,
548
- .textarea-warning {
549
- border-color: var(--color-warning);
1406
+ .textarea-container-success .textarea-label-floating {
1407
+ color: var(--color-success);
550
1408
  }
551
1409
 
552
- .form-control.warning .input:focus-visible,
553
- .form-control.warning .select:focus-visible,
554
- .form-control.warning .textarea:focus-visible {
555
- border-color: var(--color-warning);
556
- box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-warning) 10%, transparent);
1410
+ .textarea-container-success .textarea:focus ~ .textarea-label-floating {
1411
+ color: var(--color-success);
557
1412
  }
558
1413
 
559
- .form-control.warning .label-text {
560
- color: var(--color-warning);
1414
+ /* ============================================
1415
+ * REDUCE MOTION
1416
+ * ============================================ */
1417
+
1418
+ @media (prefers-reduced-motion: reduce) {
1419
+ .textarea,
1420
+ .textarea-label-floating {
1421
+ transition: none;
1422
+ }
1423
+ }
1424
+ }
1425
+
1426
+ @layer components {
1427
+ .range {
1428
+ --range-thumb-size: 1.25rem;
1429
+ appearance: none;
1430
+ display: block;
1431
+ box-sizing: border-box;
1432
+ width: 100%;
1433
+ min-width: 0;
1434
+ height: var(--range-thumb-size);
1435
+ margin: 0;
1436
+ color: var(--color-on-surface);
1437
+ accent-color: currentColor;
1438
+ background: transparent;
1439
+ cursor: pointer;
1440
+ }
1441
+ .range::-webkit-slider-runnable-track { height: 0.5rem; background: var(--color-surface-container-highest); border-radius: var(--radius-xs); }
1442
+ .range::-moz-range-track { height: 0.5rem; background: var(--color-surface-container-highest); border-radius: var(--radius-xs); }
1443
+ .range::-webkit-slider-thumb {
1444
+ appearance: none;
1445
+ width: var(--range-thumb-size);
1446
+ height: var(--range-thumb-size);
1447
+ margin-top: calc((0.5rem - var(--range-thumb-size)) / 2);
1448
+ background: currentColor;
1449
+ border: 1px solid currentColor;
1450
+ border-radius: var(--radius-full);
1451
+ }
1452
+ .range::-moz-range-thumb { width: var(--range-thumb-size); height: var(--range-thumb-size); background: currentColor; border: 1px solid currentColor; border-radius: var(--radius-full); }
1453
+ .range:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
1454
+ .range:disabled { opacity: 0.5; cursor: not-allowed; }
1455
+ .range-xs { --range-thumb-size: 0.875rem; }
1456
+ .range-sm { --range-thumb-size: 1rem; }
1457
+ .range-lg { --range-thumb-size: 1.5rem; }
1458
+ .range-primary { color: var(--color-primary); }
1459
+ .range-secondary { color: var(--color-secondary); }
1460
+ .range-tertiary { color: var(--color-tertiary); }
1461
+ .range-info { color: var(--color-info); }
1462
+ .range-success { color: var(--color-success); }
1463
+ .range-warning { color: var(--color-warning); }
1464
+ .range-error { color: var(--color-error); }
1465
+ @media (forced-colors: active) { .range { appearance: auto; } .range:focus-visible { outline-color: Highlight; } }
1466
+ }
1467
+
1468
+
1469
+
1470
+ /**
1471
+ * Form Group Component Styles
1472
+ * DuskMoonUI - Form layout utilities including label, helper text, fieldset
1473
+ */
1474
+
1475
+ @layer components {
1476
+ /* Form Group Container */
1477
+ .form-group {
1478
+ display: flex;
1479
+ flex-direction: column;
1480
+ gap: 0.25rem;
1481
+ width: 100%;
561
1482
  }
562
1483
 
563
- .form-control.info .input,
564
- .form-control.info .select,
565
- .form-control.info .textarea,
566
- .input-info,
567
- .select-info,
568
- .textarea-info {
569
- border-color: var(--color-info);
1484
+ .form-group-horizontal {
1485
+ flex-direction: row;
1486
+ align-items: flex-start;
1487
+ gap: 1rem;
570
1488
  }
571
1489
 
572
- .form-control.info .input:focus-visible,
573
- .form-control.info .select:focus-visible,
574
- .form-control.info .textarea:focus-visible {
575
- border-color: var(--color-info);
576
- box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-info) 10%, transparent);
1490
+ .form-group-horizontal .form-label {
1491
+ min-width: 8rem;
1492
+ padding-top: 0.75rem;
577
1493
  }
578
1494
 
579
- .form-control.info .label-text {
580
- color: var(--color-info);
1495
+ .form-group-horizontal .form-control {
1496
+ flex: 1;
1497
+ }
1498
+
1499
+ /* Form Label */
1500
+ .form-label {
1501
+ display: block;
1502
+ font-size: 0.875rem;
1503
+ font-weight: 500;
1504
+ line-height: 1.25rem;
1505
+ color: var(--color-on-surface);
1506
+ margin-bottom: 0.25rem;
1507
+ }
1508
+
1509
+ .form-label-required::after {
1510
+ content: ' *';
1511
+ color: var(--color-error);
1512
+ }
1513
+
1514
+ .form-label-optional::after {
1515
+ content: ' (optional)';
1516
+ font-size: 0.75rem;
1517
+ font-weight: 400;
1518
+ color: var(--color-on-surface-variant);
1519
+ }
1520
+
1521
+ /* Label Sizes */
1522
+ .form-label-sm {
1523
+ font-size: 0.75rem;
1524
+ line-height: 1rem;
1525
+ }
1526
+
1527
+ .form-label-lg {
1528
+ font-size: 1rem;
1529
+ line-height: 1.5rem;
1530
+ }
1531
+
1532
+ /* Form Control Wrapper */
1533
+ .form-control {
1534
+ display: flex;
1535
+ flex-direction: column;
1536
+ gap: 0.25rem;
581
1537
  }
582
1538
 
583
1539
  /* Helper Text */
584
1540
  .helper-text {
585
1541
  font-size: 0.75rem;
1542
+ line-height: 1rem;
586
1543
  color: var(--color-on-surface-variant);
587
1544
  margin-top: 0.25rem;
588
1545
  }
589
1546
 
590
- .helper-text.error {
1547
+ .helper-text-error {
591
1548
  color: var(--color-error);
592
1549
  }
593
1550
 
594
- .helper-text.success {
1551
+ .helper-text-success {
595
1552
  color: var(--color-success);
596
1553
  }
597
1554
 
598
- .helper-text.warning {
1555
+ .helper-text-warning {
599
1556
  color: var(--color-warning);
600
1557
  }
601
1558
 
602
- .helper-text.info {
1559
+ .helper-text-info {
603
1560
  color: var(--color-info);
604
1561
  }
605
1562
 
606
- /* Native HTML Validation (:user-invalid/:user-valid, :invalid/:valid, ARIA)
607
- * is handled in form-group.css to avoid duplication. */
1563
+ /* Helper Text with Icon */
1564
+ .helper-text-icon {
1565
+ display: inline-flex;
1566
+ align-items: center;
1567
+ gap: 0.25rem;
1568
+ }
608
1569
 
609
- /* Required Field Indicator */
610
- .label-text.required::after {
611
- content: ' *';
612
- color: var(--color-error);
1570
+ .helper-text-icon svg,
1571
+ .helper-text-icon .icon {
1572
+ width: 0.875rem;
1573
+ height: 0.875rem;
613
1574
  }
614
1575
 
615
- /* Range Input */
616
- .range {
617
- appearance: none;
618
- width: 100%;
619
- height: 0.5rem;
620
- background-color: var(--color-surface-container-highest);
621
- border-radius: var(--radius-xs);
622
- cursor: pointer;
1576
+ /* Form Hint (above input) */
1577
+ .form-hint {
1578
+ font-size: 0.75rem;
1579
+ line-height: 1rem;
1580
+ color: var(--color-on-surface-variant);
1581
+ margin-bottom: 0.25rem;
623
1582
  }
624
1583
 
625
- .range::-webkit-slider-thumb {
626
- appearance: none;
627
- width: 1.25rem;
628
- height: 1.25rem;
629
- background-color: var(--color-primary);
630
- border-radius: var(--radius-full);
631
- cursor: pointer;
632
- transition: transform 150ms ease-in-out;
1584
+ /* Fieldset */
1585
+ .fieldset {
1586
+ display: flex;
1587
+ flex-direction: column;
1588
+ gap: 1rem;
1589
+ padding: 1rem;
1590
+ margin: 0;
1591
+ border: 1px solid var(--color-outline-variant);
1592
+ border-radius: var(--radius-sm);
1593
+ background-color: transparent;
1594
+ }
1595
+
1596
+ .fieldset-legend {
1597
+ padding: 0 0.5rem;
1598
+ font-size: 0.875rem;
1599
+ font-weight: 600;
1600
+ line-height: 1.25rem;
1601
+ color: var(--color-on-surface);
633
1602
  }
634
1603
 
635
- .range::-webkit-slider-thumb:hover {
636
- transform: scale(1.1);
1604
+ /* Fieldset Variants */
1605
+ .fieldset-filled {
1606
+ background-color: var(--color-surface-container-lowest);
1607
+ border-color: transparent;
637
1608
  }
638
1609
 
639
- .range::-moz-range-thumb {
640
- width: 1.25rem;
641
- height: 1.25rem;
642
- background-color: var(--color-primary);
1610
+ .fieldset-borderless {
643
1611
  border: none;
644
- border-radius: var(--radius-full);
645
- cursor: pointer;
1612
+ padding: 0;
646
1613
  }
647
1614
 
648
- .range:focus-visible {
649
- outline: none;
650
- box-shadow: 0 0 0 3px color-mix(in oklch, currentColor 20%, transparent);
1615
+ /* Fieldset with Card Style */
1616
+ .fieldset-card {
1617
+ background-color: var(--color-surface);
1618
+ border-color: transparent;
1619
+ box-shadow: var(--shadow-xs);
1620
+ padding: 1.5rem;
651
1621
  }
652
1622
 
653
- /* Reduce Motion */
654
- @media (prefers-reduced-motion: reduce) {
655
- .form-control,
656
- .input,
657
- .select,
658
- .textarea,
659
- .range,
660
- .label-float .form-control ~ label {
661
- transition: none;
1623
+ /* Form Row */
1624
+ .form-row {
1625
+ display: flex;
1626
+ flex-wrap: wrap;
1627
+ gap: 1rem;
1628
+ }
1629
+
1630
+ .form-row > .form-group {
1631
+ flex: 1;
1632
+ min-width: 200px;
1633
+ }
1634
+
1635
+ /* Form Grid */
1636
+ .form-grid {
1637
+ display: grid;
1638
+ gap: 1rem;
1639
+ }
1640
+
1641
+ .form-grid-2 {
1642
+ grid-template-columns: repeat(2, 1fr);
1643
+ }
1644
+
1645
+ .form-grid-3 {
1646
+ grid-template-columns: repeat(3, 1fr);
1647
+ }
1648
+
1649
+ .form-grid-4 {
1650
+ grid-template-columns: repeat(4, 1fr);
1651
+ }
1652
+
1653
+ @media (max-width: 640px) {
1654
+ .form-grid-2,
1655
+ .form-grid-3,
1656
+ .form-grid-4 {
1657
+ grid-template-columns: 1fr;
662
1658
  }
663
1659
  }
1660
+
1661
+ /* Form Actions */
1662
+ .form-actions {
1663
+ display: flex;
1664
+ align-items: center;
1665
+ gap: 0.75rem;
1666
+ margin-top: 1rem;
1667
+ }
1668
+
1669
+ .form-actions-right {
1670
+ justify-content: flex-end;
1671
+ }
1672
+
1673
+ .form-actions-center {
1674
+ justify-content: center;
1675
+ }
1676
+
1677
+ .form-actions-between {
1678
+ justify-content: space-between;
1679
+ }
1680
+
1681
+ /* Form Divider */
1682
+ .form-divider {
1683
+ height: 1px;
1684
+ background-color: var(--color-outline-variant);
1685
+ margin: 1rem 0;
1686
+ }
1687
+
1688
+ .form-divider-text {
1689
+ display: flex;
1690
+ align-items: center;
1691
+ gap: 1rem;
1692
+ color: var(--color-on-surface-variant);
1693
+ font-size: 0.75rem;
1694
+ text-transform: uppercase;
1695
+ }
1696
+
1697
+ .form-divider-text::before,
1698
+ .form-divider-text::after {
1699
+ content: '';
1700
+ flex: 1;
1701
+ height: 1px;
1702
+ background-color: var(--color-outline-variant);
1703
+ }
1704
+
1705
+ /* Form Section */
1706
+ .form-section {
1707
+ display: flex;
1708
+ flex-direction: column;
1709
+ gap: 1rem;
1710
+ }
1711
+
1712
+ .form-section-title {
1713
+ font-size: 1rem;
1714
+ font-weight: 600;
1715
+ color: var(--color-on-surface);
1716
+ margin-bottom: 0.5rem;
1717
+ }
1718
+
1719
+ .form-section-description {
1720
+ font-size: 0.875rem;
1721
+ color: var(--color-on-surface-variant);
1722
+ margin-bottom: 1rem;
1723
+ }
1724
+
1725
+ /* Inline Form */
1726
+ .form-inline {
1727
+ display: flex;
1728
+ flex-wrap: wrap;
1729
+ align-items: flex-end;
1730
+ gap: 0.75rem;
1731
+ }
1732
+
1733
+ .form-inline .form-group {
1734
+ flex: none;
1735
+ width: auto;
1736
+ }
1737
+
1738
+ /* Character Counter */
1739
+ .form-counter {
1740
+ font-size: 0.75rem;
1741
+ color: var(--color-on-surface-variant);
1742
+ text-align: right;
1743
+ margin-top: 0.25rem;
1744
+ }
1745
+
1746
+ .form-counter-error {
1747
+ color: var(--color-error);
1748
+ }
1749
+
1750
+ /* Legacy label and helper APIs share the composition implementation. */
1751
+ /* Label */
1752
+ .label {
1753
+ display: flex;
1754
+ justify-content: space-between;
1755
+ align-items: center;
1756
+ padding: 0.25rem 0;
1757
+ }
1758
+
1759
+ .label-text {
1760
+ font-size: 0.875rem;
1761
+ font-weight: 500;
1762
+ color: var(--color-on-surface);
1763
+ padding-bottom: 0.25rem;
1764
+ }
1765
+
1766
+ .label-text-alt {
1767
+ font-size: 0.75rem;
1768
+ color: var(--color-on-surface-variant);
1769
+ }
1770
+
1771
+
1772
+ .label-text.required::after,
1773
+ .form-label[aria-required="true"]::after,
1774
+ label[aria-required="true"]::after {
1775
+ content: ' *';
1776
+ color: var(--color-error);
1777
+ }
1778
+
1779
+ .helper-text.error { color: var(--color-error); }
1780
+ .helper-text.success { color: var(--color-success); }
1781
+ .helper-text.warning { color: var(--color-warning); }
1782
+ .helper-text.info { color: var(--color-info); }
1783
+
1784
+ .form-group-disabled { opacity: 0.5; pointer-events: none; }
1785
+ .fieldset { min-inline-size: 0; }
1786
+ .form-group, .form-control { min-inline-size: 0; }
1787
+ .form-control { width: 100%; margin-bottom: 1rem; color: var(--color-on-surface); }
1788
+
1789
+ @media (max-width: 640px) {
1790
+ .form-group-horizontal { flex-direction: column; }
1791
+ .form-row > .form-group { min-width: 0; flex-basis: 100%; }
1792
+ }
664
1793
  }
1794
+
665
1795
  `;
666
1796
 
667
1797
  const sheet = typeof CSSStyleSheet !== 'undefined' ? new CSSStyleSheet() : null;