@duskmoon-dev/core 1.1.0 → 1.2.0

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 (33) hide show
  1. package/dist/components/button.css +47 -46
  2. package/dist/components/checkbox.css +231 -0
  3. package/dist/components/datepicker.css +404 -10
  4. package/dist/components/form-group.css +308 -0
  5. package/dist/components/index.css +4077 -687
  6. package/dist/components/multi-select.css +491 -0
  7. package/dist/components/navigation.css +153 -2
  8. package/dist/components/otp-input.css +195 -0
  9. package/dist/components/pin-input.css +184 -0
  10. package/dist/components/radio.css +183 -0
  11. package/dist/components/segment-control.css +186 -0
  12. package/dist/components/select.css +205 -0
  13. package/dist/components/switch.css +150 -193
  14. package/dist/components/textarea.css +202 -0
  15. package/dist/components/time-input.css +252 -0
  16. package/dist/components/tree-select.css +439 -0
  17. package/dist/esm/components/button.js +47 -46
  18. package/dist/esm/components/checkbox.js +238 -0
  19. package/dist/esm/components/datepicker.js +404 -10
  20. package/dist/esm/components/form-group.js +315 -0
  21. package/dist/esm/components/multi-select.js +498 -0
  22. package/dist/esm/components/navigation.js +153 -2
  23. package/dist/esm/components/otp-input.js +202 -0
  24. package/dist/esm/components/pin-input.js +191 -0
  25. package/dist/esm/components/radio.js +190 -0
  26. package/dist/esm/components/segment-control.js +193 -0
  27. package/dist/esm/components/select.js +212 -0
  28. package/dist/esm/components/switch.js +150 -193
  29. package/dist/esm/components/textarea.js +209 -0
  30. package/dist/esm/components/time-input.js +259 -0
  31. package/dist/esm/components/tree-select.js +446 -0
  32. package/dist/index.css +4073 -683
  33. package/package.json +56 -1
@@ -1,277 +1,234 @@
1
1
  /**
2
2
  * Switch Component Styles
3
- * DuskMoonUI - Material Design 3 inspired switch/toggle system
3
+ * DuskMoonUI - Material Design 3 inspired switch/toggle
4
+ *
5
+ * Usage: <input type="checkbox" class="switch" />
6
+ * With label: <label class="switch-label"><input type="checkbox" class="switch" /><span>Label</span></label>
4
7
  */
5
8
 
6
9
  @layer components {
7
- /* Base Switch */
10
+ /* Base Switch - applied directly to input[type="checkbox"] */
8
11
  .switch {
12
+ --switch-width: 3.25rem;
13
+ --switch-height: 2rem;
14
+ --switch-thumb-size: 1rem;
15
+ --switch-color: var(--color-primary);
16
+ --switch-track-color: var(--color-surface-container-highest);
17
+ --switch-border-color: var(--color-outline);
18
+ --switch-thumb-color: var(--color-outline);
19
+
9
20
  position: relative;
10
- display: inline-flex;
21
+ display: inline-grid;
22
+ grid-template-columns: 0fr 1fr 1fr;
11
23
  align-items: center;
12
- gap: 0.75rem;
24
+ width: var(--switch-width);
25
+ height: var(--switch-height);
26
+ margin: 0;
27
+ padding: 0.25rem;
13
28
  cursor: pointer;
14
- user-select: none;
15
- }
16
-
17
- /* Hidden Input */
18
- .switch-input {
19
- position: absolute;
20
- width: 0;
21
- height: 0;
22
- opacity: 0;
23
- }
24
-
25
- /* Switch Track */
26
- .switch-track {
27
- position: relative;
28
- width: 3.25rem;
29
- height: 2rem;
30
- background-color: var(--color-surface-container-highest);
31
- border: 2px solid var(--color-outline);
32
- border-radius: 9999px;
33
- transition: background-color 200ms ease-in-out, border-color 200ms ease-in-out;
34
- }
35
-
36
- /* Switch Thumb */
37
- .switch-thumb {
38
- position: absolute;
39
- top: 50%;
40
- left: 0.25rem;
41
- width: 1rem;
42
- height: 1rem;
43
- background-color: var(--color-outline);
29
+ appearance: none;
30
+ background-color: var(--switch-track-color);
31
+ border: 2px solid var(--switch-border-color);
32
+ border-radius: var(--switch-height);
33
+ /* Use !important to override global * { transition } rules from layers */
34
+ transition: background-color 200ms ease-in-out !important,
35
+ border-color 200ms ease-in-out !important,
36
+ grid-template-columns 200ms ease-in-out !important;
37
+ }
38
+
39
+ /* Thumb using ::before pseudo-element */
40
+ .switch::before {
41
+ content: "";
42
+ grid-column: 2;
43
+ width: var(--switch-thumb-size);
44
+ height: var(--switch-thumb-size);
45
+ background-color: var(--switch-thumb-color);
44
46
  border-radius: 50%;
45
- transform: translateY(-50%);
46
- transition: left 200ms ease-in-out, width 200ms ease-in-out, height 200ms ease-in-out, background-color 200ms ease-in-out;
47
+ /* Use !important to override global * { transition } rules */
48
+ transition: background-color 200ms ease-in-out !important,
49
+ width 200ms ease-in-out !important,
50
+ margin 200ms ease-in-out !important;
47
51
  }
48
52
 
49
53
  /* Checked State */
50
- .switch-input:checked + .switch-track {
51
- background-color: var(--color-primary);
52
- border-color: var(--color-primary);
54
+ .switch:checked {
55
+ grid-template-columns: 1fr 1fr 0fr;
56
+ background-color: var(--switch-color);
57
+ border-color: var(--switch-color);
53
58
  }
54
59
 
55
- .switch-input:checked + .switch-track .switch-thumb {
56
- left: calc(100% - 1.5rem);
57
- width: 1.5rem;
58
- height: 1.5rem;
60
+ .switch:checked::before {
59
61
  background-color: var(--color-primary-content);
60
62
  }
61
63
 
62
- /* Hover State */
63
- .switch:hover .switch-track::before {
64
- content: '';
65
- position: absolute;
66
- top: 50%;
67
- left: 0.125rem;
68
- width: 2.5rem;
69
- height: 2.5rem;
70
- background-color: var(--color-on-surface);
71
- border-radius: 50%;
72
- transform: translate(-0.75rem, -50%);
73
- opacity: 0.08;
74
- transition: opacity 150ms ease-in-out;
64
+ /* Hover State (unchecked) */
65
+ .switch:hover:not(:disabled):not(:checked) {
66
+ background-color: var(--color-surface-container-high);
75
67
  }
76
68
 
77
- .switch:hover .switch-input:checked + .switch-track::before {
78
- left: auto;
79
- right: 0.125rem;
80
- transform: translate(0.75rem, -50%);
81
- background-color: var(--color-primary);
69
+ .switch:hover:not(:disabled):not(:checked)::before {
70
+ background-color: var(--color-on-surface-variant);
82
71
  }
83
72
 
84
- /* Focus State */
85
- .switch-input:focus-visible + .switch-track {
86
- outline: 2px solid var(--color-primary);
87
- outline-offset: 2px;
73
+ /* Hover State (checked) */
74
+ .switch:checked:hover:not(:disabled) {
75
+ background-color: color-mix(in oklch, var(--switch-color), black 10%);
76
+ border-color: color-mix(in oklch, var(--switch-color), black 10%);
88
77
  }
89
78
 
90
- /* Disabled State */
91
- .switch-input:disabled + .switch-track {
92
- opacity: 0.38;
93
- cursor: not-allowed;
79
+ .switch:checked:hover:not(:disabled)::before {
80
+ background-color: var(--color-primary-container);
94
81
  }
95
82
 
96
- .switch:has(.switch-input:disabled) {
97
- cursor: not-allowed;
83
+ /* Focus State */
84
+ .switch:focus-visible {
85
+ outline: 2px solid var(--switch-color);
86
+ outline-offset: 2px;
98
87
  }
99
88
 
100
- /* Switch Label */
101
- .switch-label {
102
- font-size: 0.875rem;
103
- color: var(--color-on-surface);
89
+ /* Pressed/Active State - expand thumb slightly */
90
+ .switch:active:not(:disabled)::before {
91
+ width: calc(var(--switch-thumb-size) + 0.25rem);
104
92
  }
105
93
 
106
- .switch-label-left {
107
- order: -1;
94
+ .switch:checked:active:not(:disabled)::before {
95
+ margin-left: -0.25rem;
108
96
  }
109
97
 
110
- /* Size Variants */
111
- .switch-sm .switch-track {
112
- width: 2.5rem;
113
- height: 1.5rem;
98
+ /* Disabled State */
99
+ .switch:disabled {
100
+ cursor: not-allowed;
101
+ opacity: 0.38;
114
102
  }
115
103
 
116
- .switch-sm .switch-thumb {
117
- width: 0.75rem;
118
- height: 0.75rem;
104
+ .switch:disabled:not(:checked) {
105
+ background-color: color-mix(in oklch, var(--color-surface-container-highest) 12%, transparent);
106
+ border-color: color-mix(in oklch, var(--color-on-surface) 12%, transparent);
119
107
  }
120
108
 
121
- .switch-sm .switch-input:checked + .switch-track .switch-thumb {
122
- left: calc(100% - 1.125rem);
123
- width: 1.125rem;
124
- height: 1.125rem;
109
+ .switch:disabled:not(:checked)::before {
110
+ background-color: color-mix(in oklch, var(--color-on-surface) 38%, transparent);
125
111
  }
126
112
 
127
- .switch-lg .switch-track {
128
- width: 4rem;
129
- height: 2.5rem;
113
+ .switch:disabled:checked {
114
+ background-color: color-mix(in oklch, var(--color-on-surface) 12%, transparent);
115
+ border-color: transparent;
130
116
  }
131
117
 
132
- .switch-lg .switch-thumb {
133
- width: 1.25rem;
134
- height: 1.25rem;
135
- left: 0.375rem;
118
+ .switch:disabled:checked::before {
119
+ background-color: var(--color-surface);
136
120
  }
137
121
 
138
- .switch-lg .switch-input:checked + .switch-track .switch-thumb {
139
- left: calc(100% - 1.875rem);
140
- width: 1.875rem;
141
- height: 1.875rem;
142
- }
122
+ /* ========================================
123
+ * Size Variants
124
+ * Thumb size = height - 4px (border) - 8px (padding/gap)
125
+ * ======================================== */
143
126
 
144
- /* Color Variants - on parent .switch or on .switch-track */
145
- /* Primary is default, but explicit class for consistency */
146
- .switch-primary .switch-input:checked + .switch-track,
147
- .switch-input:checked + .switch-track.switch-primary {
148
- background-color: var(--color-primary);
149
- border-color: var(--color-primary);
127
+ .switch-xs {
128
+ --switch-width: 2rem;
129
+ --switch-height: 1.25rem;
130
+ --switch-thumb-size: 0.5rem;
150
131
  }
151
132
 
152
- .switch-primary .switch-input:checked + .switch-track .switch-thumb,
153
- .switch-input:checked + .switch-track.switch-primary .switch-thumb {
154
- background-color: var(--color-primary-content);
133
+ .switch-sm {
134
+ --switch-width: 2.5rem;
135
+ --switch-height: 1.5rem;
136
+ --switch-thumb-size: 0.75rem;
155
137
  }
156
138
 
157
- .switch-secondary .switch-input:checked + .switch-track,
158
- .switch-input:checked + .switch-track.switch-secondary {
159
- background-color: var(--color-secondary);
160
- border-color: var(--color-secondary);
139
+ .switch-md {
140
+ --switch-width: 3.25rem;
141
+ --switch-height: 2rem;
142
+ --switch-thumb-size: 1rem;
161
143
  }
162
144
 
163
- .switch-secondary .switch-input:checked + .switch-track .switch-thumb,
164
- .switch-input:checked + .switch-track.switch-secondary .switch-thumb {
165
- background-color: var(--color-secondary-content);
145
+ .switch-lg {
146
+ --switch-width: 4rem;
147
+ --switch-height: 2.5rem;
148
+ --switch-thumb-size: 1.25rem;
166
149
  }
167
150
 
168
- .switch-tertiary .switch-input:checked + .switch-track,
169
- .switch-input:checked + .switch-track.switch-tertiary {
170
- background-color: var(--color-tertiary);
171
- border-color: var(--color-tertiary);
151
+ .switch-xl {
152
+ --switch-width: 4.75rem;
153
+ --switch-height: 3rem;
154
+ --switch-thumb-size: 1.5rem;
172
155
  }
173
156
 
174
- .switch-tertiary .switch-input:checked + .switch-track .switch-thumb,
175
- .switch-input:checked + .switch-track.switch-tertiary .switch-thumb {
176
- background-color: var(--color-tertiary-content);
177
- }
157
+ /* ========================================
158
+ * Color Variants
159
+ * ======================================== */
178
160
 
179
- .switch-success .switch-input:checked + .switch-track,
180
- .switch-input:checked + .switch-track.switch-success {
181
- background-color: var(--color-success);
182
- border-color: var(--color-success);
161
+ .switch-primary {
162
+ --switch-color: var(--color-primary);
183
163
  }
184
164
 
185
- .switch-error .switch-input:checked + .switch-track,
186
- .switch-input:checked + .switch-track.switch-error {
187
- background-color: var(--color-error);
188
- border-color: var(--color-error);
165
+ .switch-secondary {
166
+ --switch-color: var(--color-secondary);
189
167
  }
190
168
 
191
- /* With Icons */
192
- .switch-icon .switch-thumb {
193
- display: flex;
194
- align-items: center;
195
- justify-content: center;
196
- font-size: 0.625rem;
169
+ .switch-tertiary {
170
+ --switch-color: var(--color-tertiary);
197
171
  }
198
172
 
199
- .switch-icon .switch-input:checked + .switch-track .switch-thumb {
200
- font-size: 0.875rem;
173
+ .switch-success {
174
+ --switch-color: var(--color-success);
201
175
  }
202
176
 
203
- /* Labeled Track */
204
- .switch-labeled-track .switch-track {
205
- width: 4rem;
206
- display: flex;
207
- align-items: center;
208
- justify-content: space-between;
209
- padding: 0 0.5rem;
177
+ .switch-warning {
178
+ --switch-color: var(--color-warning);
210
179
  }
211
180
 
212
- .switch-labeled-track .switch-track-label {
213
- font-size: 0.625rem;
214
- font-weight: 600;
215
- color: var(--color-on-surface-variant);
216
- text-transform: uppercase;
217
- transition: opacity 150ms ease-in-out;
181
+ .switch-error {
182
+ --switch-color: var(--color-error);
218
183
  }
219
184
 
220
- .switch-labeled-track .switch-track-on {
221
- opacity: 0;
185
+ .switch-info {
186
+ --switch-color: var(--color-info);
222
187
  }
223
188
 
224
- .switch-labeled-track .switch-track-off {
225
- opacity: 1;
226
- }
189
+ /* ========================================
190
+ * Switch with Label
191
+ * ======================================== */
227
192
 
228
- .switch-labeled-track .switch-input:checked + .switch-track .switch-track-on {
229
- opacity: 1;
230
- color: var(--color-primary-content);
231
- }
232
-
233
- .switch-labeled-track .switch-input:checked + .switch-track .switch-track-off {
234
- opacity: 0;
193
+ .switch-label {
194
+ display: inline-flex;
195
+ align-items: center;
196
+ gap: 0.75rem;
197
+ cursor: pointer;
198
+ user-select: none;
199
+ font-size: 0.875rem;
200
+ color: var(--color-on-surface);
235
201
  }
236
202
 
237
- /* Loading State */
238
- .switch-loading .switch-thumb::after {
239
- content: '';
240
- position: absolute;
241
- top: 50%;
242
- left: 50%;
243
- width: 0.75rem;
244
- height: 0.75rem;
245
- border: 2px solid transparent;
246
- border-top-color: var(--color-on-surface);
247
- border-radius: 50%;
248
- transform: translate(-50%, -50%);
249
- animation: switch-spin 600ms linear infinite;
203
+ .switch-label:has(.switch:disabled) {
204
+ cursor: not-allowed;
205
+ opacity: 0.38;
250
206
  }
251
207
 
252
- @keyframes switch-spin {
253
- to {
254
- transform: translate(-50%, -50%) rotate(360deg);
255
- }
256
- }
208
+ /* ========================================
209
+ * Switch Group
210
+ * ======================================== */
257
211
 
258
- /* Group */
259
212
  .switch-group {
260
213
  display: flex;
261
214
  flex-direction: column;
262
215
  gap: 0.75rem;
263
216
  }
264
217
 
265
- /* Reduce Motion */
266
- @media (prefers-reduced-motion: reduce) {
267
- .switch-track,
268
- .switch-thumb,
269
- .switch-track::before {
270
- transition: none;
271
- }
218
+ .switch-group-horizontal {
219
+ flex-direction: row;
220
+ flex-wrap: wrap;
221
+ gap: 1.5rem;
222
+ }
223
+
224
+ /* ========================================
225
+ * Reduce Motion
226
+ * ======================================== */
272
227
 
273
- .switch-loading .switch-thumb::after {
274
- animation: none;
228
+ @media (prefers-reduced-motion: reduce) {
229
+ .switch,
230
+ .switch::before {
231
+ transition: none !important;
275
232
  }
276
233
  }
277
234
  }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Textarea Component Styles
3
+ * DuskMoonUI - Material Design 3 inspired multi-line text input
4
+ */
5
+
6
+ @layer components {
7
+ /* Base Textarea */
8
+ .textarea {
9
+ display: block;
10
+ width: 100%;
11
+ min-height: 6rem;
12
+ padding: 0.75rem 1rem;
13
+ font-size: 1rem;
14
+ line-height: 1.5rem;
15
+ font-family: inherit;
16
+ color: var(--color-on-surface);
17
+ background-color: var(--color-surface);
18
+ border: 1px solid var(--color-outline);
19
+ border-radius: 0.5rem;
20
+ outline: none;
21
+ resize: vertical;
22
+ transition: border-color 150ms ease-in-out, box-shadow 150ms ease-in-out;
23
+ }
24
+
25
+ .textarea::placeholder {
26
+ color: var(--color-on-surface-variant);
27
+ opacity: 0.7;
28
+ }
29
+
30
+ .textarea:hover:not(:disabled) {
31
+ border-color: var(--color-on-surface-variant);
32
+ }
33
+
34
+ .textarea:focus {
35
+ outline: none;
36
+ }
37
+
38
+ .textarea:focus-visible {
39
+ border-color: var(--color-primary);
40
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
41
+ }
42
+
43
+ .textarea:disabled {
44
+ cursor: not-allowed;
45
+ opacity: 0.5;
46
+ background-color: var(--color-surface-container);
47
+ resize: none;
48
+ }
49
+
50
+ .textarea[readonly] {
51
+ background-color: var(--color-surface-container);
52
+ cursor: default;
53
+ }
54
+
55
+ /* Filled Variant */
56
+ .textarea-filled {
57
+ background-color: var(--color-surface-container);
58
+ border: none;
59
+ border-bottom: 2px solid var(--color-outline);
60
+ border-radius: 0.5rem 0.5rem 0 0;
61
+ }
62
+
63
+ .textarea-filled:hover:not(:disabled) {
64
+ background-color: var(--color-surface-container-high);
65
+ border-bottom-color: var(--color-on-surface);
66
+ }
67
+
68
+ .textarea-filled:focus-visible {
69
+ border-bottom-color: var(--color-primary);
70
+ box-shadow: none;
71
+ }
72
+
73
+ /* Outlined Variant (default) */
74
+ .textarea-outlined {
75
+ background-color: transparent;
76
+ border: 1px solid var(--color-outline);
77
+ border-radius: 0.5rem;
78
+ }
79
+
80
+ /* Ghost Variant */
81
+ .textarea-ghost {
82
+ background-color: transparent;
83
+ border-color: transparent;
84
+ }
85
+
86
+ .textarea-ghost:hover:not(:disabled) {
87
+ background-color: var(--color-surface-container);
88
+ border-color: transparent;
89
+ }
90
+
91
+ .textarea-ghost:focus-visible {
92
+ background-color: var(--color-surface-container);
93
+ border-color: transparent;
94
+ box-shadow: none;
95
+ }
96
+
97
+ /* Color Variants */
98
+ .textarea-primary:focus-visible {
99
+ border-color: var(--color-primary);
100
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
101
+ }
102
+
103
+ .textarea-secondary:focus-visible {
104
+ border-color: var(--color-secondary);
105
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-secondary) 10%, transparent);
106
+ }
107
+
108
+ .textarea-tertiary:focus-visible {
109
+ border-color: var(--color-tertiary);
110
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-tertiary) 10%, transparent);
111
+ }
112
+
113
+ /* Semantic Colors */
114
+ .textarea-error {
115
+ border-color: var(--color-error);
116
+ }
117
+
118
+ .textarea-error:focus-visible {
119
+ border-color: var(--color-error);
120
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-error) 10%, transparent);
121
+ }
122
+
123
+ .textarea-success {
124
+ border-color: var(--color-success);
125
+ }
126
+
127
+ .textarea-success:focus-visible {
128
+ border-color: var(--color-success);
129
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-success) 10%, transparent);
130
+ }
131
+
132
+ .textarea-warning {
133
+ border-color: var(--color-warning);
134
+ }
135
+
136
+ .textarea-warning:focus-visible {
137
+ border-color: var(--color-warning);
138
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-warning) 10%, transparent);
139
+ }
140
+
141
+ /* Size Variants */
142
+ .textarea-sm {
143
+ min-height: 4rem;
144
+ padding: 0.5rem 0.75rem;
145
+ font-size: 0.875rem;
146
+ line-height: 1.25rem;
147
+ border-radius: 0.375rem;
148
+ }
149
+
150
+ .textarea-lg {
151
+ min-height: 8rem;
152
+ padding: 1rem 1.25rem;
153
+ font-size: 1.125rem;
154
+ line-height: 1.75rem;
155
+ border-radius: 0.625rem;
156
+ }
157
+
158
+ /* Resize Options */
159
+ .textarea-resize-none {
160
+ resize: none;
161
+ }
162
+
163
+ .textarea-resize-horizontal {
164
+ resize: horizontal;
165
+ }
166
+
167
+ .textarea-resize-both {
168
+ resize: both;
169
+ }
170
+
171
+ /* Auto-resize (requires JS) */
172
+ .textarea-autosize {
173
+ resize: none;
174
+ overflow: hidden;
175
+ }
176
+
177
+ /* Character Counter */
178
+ .textarea-wrapper {
179
+ position: relative;
180
+ display: flex;
181
+ flex-direction: column;
182
+ gap: 0.25rem;
183
+ }
184
+
185
+ .textarea-counter {
186
+ font-size: 0.75rem;
187
+ line-height: 1rem;
188
+ color: var(--color-on-surface-variant);
189
+ text-align: right;
190
+ }
191
+
192
+ .textarea-counter-error {
193
+ color: var(--color-error);
194
+ }
195
+
196
+ /* Reduce Motion */
197
+ @media (prefers-reduced-motion: reduce) {
198
+ .textarea {
199
+ transition: none;
200
+ }
201
+ }
202
+ }