@duskmoon-dev/core 1.19.3 → 1.19.4

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