@duskmoon-dev/core 1.3.2 → 1.5.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.
@@ -5,7 +5,84 @@ export const css = `/**
5
5
  */
6
6
 
7
7
  @layer components {
8
- /* Base Textarea */
8
+ /* ============================================
9
+ * TEXTAREA CONTAINER
10
+ * ============================================ */
11
+
12
+ .textarea-container {
13
+ position: relative;
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: 0.25rem;
17
+ width: 100%;
18
+ }
19
+
20
+ /* ============================================
21
+ * TEXTAREA LABEL
22
+ * ============================================ */
23
+
24
+ .textarea-label {
25
+ display: block;
26
+ font-size: 0.875rem;
27
+ font-weight: 500;
28
+ line-height: 1.25rem;
29
+ color: var(--color-on-surface);
30
+ margin-bottom: 0.25rem;
31
+ }
32
+
33
+ /* Floating Label */
34
+ .textarea-label-floating {
35
+ position: absolute;
36
+ top: 0.75rem;
37
+ left: 1rem;
38
+ font-size: 1rem;
39
+ font-weight: 400;
40
+ color: var(--color-on-surface-variant);
41
+ pointer-events: none;
42
+ transition: all 150ms ease-in-out;
43
+ transform-origin: left top;
44
+ z-index: 1;
45
+ }
46
+
47
+ /* Floating label active state - when textarea has content or focus */
48
+ .textarea:focus ~ .textarea-label-floating,
49
+ .textarea:not(:placeholder-shown) ~ .textarea-label-floating {
50
+ top: -0.5rem;
51
+ left: 0.75rem;
52
+ font-size: 0.75rem;
53
+ font-weight: 500;
54
+ color: var(--color-primary);
55
+ background-color: var(--color-surface);
56
+ padding: 0 0.25rem;
57
+ }
58
+
59
+ /* Floating label for filled variant */
60
+ .textarea-filled ~ .textarea-label-floating {
61
+ background-color: transparent;
62
+ }
63
+
64
+ .textarea-filled:focus ~ .textarea-label-floating,
65
+ .textarea-filled:not(:placeholder-shown) ~ .textarea-label-floating {
66
+ top: 0.25rem;
67
+ left: 0.75rem;
68
+ background-color: transparent;
69
+ }
70
+
71
+ /* ============================================
72
+ * HELPER TEXT
73
+ * ============================================ */
74
+
75
+ .textarea-helper {
76
+ font-size: 0.75rem;
77
+ line-height: 1rem;
78
+ color: var(--color-on-surface-variant);
79
+ margin-top: 0.25rem;
80
+ }
81
+
82
+ /* ============================================
83
+ * BASE TEXTAREA
84
+ * ============================================ */
85
+
9
86
  .textarea {
10
87
  display: block;
11
88
  width: 100%;
@@ -53,6 +130,15 @@ export const css = `/**
53
130
  cursor: default;
54
131
  }
55
132
 
133
+ /* Full Width */
134
+ .textarea-full {
135
+ width: 100%;
136
+ }
137
+
138
+ /* ============================================
139
+ * VARIANTS
140
+ * ============================================ */
141
+
56
142
  /* Filled Variant */
57
143
  .textarea-filled {
58
144
  background-color: var(--color-surface-container);
@@ -95,23 +181,68 @@ export const css = `/**
95
181
  box-shadow: none;
96
182
  }
97
183
 
98
- /* Color Variants */
184
+ /* ============================================
185
+ * COLOR VARIANTS
186
+ * ============================================ */
187
+
188
+ /* Primary */
189
+ .textarea-primary {
190
+ border-color: var(--color-primary);
191
+ }
192
+
99
193
  .textarea-primary:focus-visible {
100
194
  border-color: var(--color-primary);
101
195
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
102
196
  }
103
197
 
198
+ .textarea-filled.textarea-primary {
199
+ border-bottom-color: var(--color-primary);
200
+ }
201
+
202
+ .textarea-filled.textarea-primary:focus-visible {
203
+ border-bottom-color: var(--color-primary);
204
+ }
205
+
206
+ /* Secondary */
207
+ .textarea-secondary {
208
+ border-color: var(--color-secondary);
209
+ }
210
+
104
211
  .textarea-secondary:focus-visible {
105
212
  border-color: var(--color-secondary);
106
213
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-secondary) 10%, transparent);
107
214
  }
108
215
 
216
+ .textarea-filled.textarea-secondary {
217
+ border-bottom-color: var(--color-secondary);
218
+ }
219
+
220
+ .textarea-filled.textarea-secondary:focus-visible {
221
+ border-bottom-color: var(--color-secondary);
222
+ }
223
+
224
+ /* Tertiary */
225
+ .textarea-tertiary {
226
+ border-color: var(--color-tertiary);
227
+ }
228
+
109
229
  .textarea-tertiary:focus-visible {
110
230
  border-color: var(--color-tertiary);
111
231
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-tertiary) 10%, transparent);
112
232
  }
113
233
 
114
- /* Semantic Colors */
234
+ .textarea-filled.textarea-tertiary {
235
+ border-bottom-color: var(--color-tertiary);
236
+ }
237
+
238
+ .textarea-filled.textarea-tertiary:focus-visible {
239
+ border-bottom-color: var(--color-tertiary);
240
+ }
241
+
242
+ /* ============================================
243
+ * SEMANTIC COLORS
244
+ * ============================================ */
245
+
115
246
  .textarea-error {
116
247
  border-color: var(--color-error);
117
248
  }
@@ -121,6 +252,10 @@ export const css = `/**
121
252
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-error) 10%, transparent);
122
253
  }
123
254
 
255
+ .textarea-filled.textarea-error {
256
+ border-bottom-color: var(--color-error);
257
+ }
258
+
124
259
  .textarea-success {
125
260
  border-color: var(--color-success);
126
261
  }
@@ -130,6 +265,10 @@ export const css = `/**
130
265
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-success) 10%, transparent);
131
266
  }
132
267
 
268
+ .textarea-filled.textarea-success {
269
+ border-bottom-color: var(--color-success);
270
+ }
271
+
133
272
  .textarea-warning {
134
273
  border-color: var(--color-warning);
135
274
  }
@@ -139,7 +278,14 @@ export const css = `/**
139
278
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-warning) 10%, transparent);
140
279
  }
141
280
 
142
- /* Size Variants */
281
+ .textarea-filled.textarea-warning {
282
+ border-bottom-color: var(--color-warning);
283
+ }
284
+
285
+ /* ============================================
286
+ * SIZE VARIANTS
287
+ * ============================================ */
288
+
143
289
  .textarea-sm {
144
290
  min-height: 4rem;
145
291
  padding: 0.5rem 0.75rem;
@@ -156,11 +302,18 @@ export const css = `/**
156
302
  border-radius: 0.625rem;
157
303
  }
158
304
 
159
- /* Resize Options */
305
+ /* ============================================
306
+ * RESIZE OPTIONS
307
+ * ============================================ */
308
+
160
309
  .textarea-resize-none {
161
310
  resize: none;
162
311
  }
163
312
 
313
+ .textarea-resize-vertical {
314
+ resize: vertical;
315
+ }
316
+
164
317
  .textarea-resize-horizontal {
165
318
  resize: horizontal;
166
319
  }
@@ -169,19 +322,21 @@ export const css = `/**
169
322
  resize: both;
170
323
  }
171
324
 
172
- /* Auto-resize (requires JS) */
325
+ /* ============================================
326
+ * AUTO-RESIZE (requires JS)
327
+ * ============================================ */
328
+
329
+ .textarea-auto-resize,
173
330
  .textarea-autosize {
174
331
  resize: none;
175
332
  overflow: hidden;
333
+ min-height: 3rem;
334
+ field-sizing: content; /* Modern CSS - auto-grows without JS in supported browsers */
176
335
  }
177
336
 
178
- /* Character Counter */
179
- .textarea-wrapper {
180
- position: relative;
181
- display: flex;
182
- flex-direction: column;
183
- gap: 0.25rem;
184
- }
337
+ /* ============================================
338
+ * CHARACTER COUNTER
339
+ * ============================================ */
185
340
 
186
341
  .textarea-counter {
187
342
  font-size: 0.75rem;
@@ -190,13 +345,54 @@ export const css = `/**
190
345
  text-align: right;
191
346
  }
192
347
 
193
- .textarea-counter-error {
348
+ .textarea-counter-error,
349
+ .textarea-counter-exceeded {
350
+ color: var(--color-error);
351
+ }
352
+
353
+ /* ============================================
354
+ * CONTAINER STATES
355
+ * ============================================ */
356
+
357
+ .textarea-container-error .textarea-label {
358
+ color: var(--color-error);
359
+ }
360
+
361
+ .textarea-container-error .textarea-helper {
362
+ color: var(--color-error);
363
+ }
364
+
365
+ .textarea-container-error .textarea-label-floating {
366
+ color: var(--color-error);
367
+ }
368
+
369
+ .textarea-container-error .textarea:focus ~ .textarea-label-floating {
194
370
  color: var(--color-error);
195
371
  }
196
372
 
197
- /* Reduce Motion */
373
+ .textarea-container-success .textarea-label {
374
+ color: var(--color-success);
375
+ }
376
+
377
+ .textarea-container-success .textarea-helper {
378
+ color: var(--color-success);
379
+ }
380
+
381
+ .textarea-container-success .textarea-label-floating {
382
+ color: var(--color-success);
383
+ }
384
+
385
+ .textarea-container-success .textarea:focus ~ .textarea-label-floating {
386
+ color: var(--color-success);
387
+ }
388
+
389
+ /* ============================================
390
+ * REDUCE MOTION
391
+ * ============================================ */
392
+
198
393
  @media (prefers-reduced-motion: reduce) {
199
- .textarea {
394
+ .textarea,
395
+ .textarea-label-floating {
200
396
  transition: none;
201
397
  }
202
398
  }
@@ -40,12 +40,24 @@ export const css = `/**
40
40
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
41
41
  }
42
42
 
43
- .tree-select-trigger:disabled {
43
+ .tree-select-trigger:disabled,
44
+ .tree-select-trigger[aria-disabled="true"] {
44
45
  cursor: not-allowed;
45
46
  opacity: 0.5;
46
47
  background-color: var(--color-surface-container);
47
48
  }
48
49
 
50
+ /* Support for div-based trigger (when using clear button) */
51
+ div.tree-select-trigger {
52
+ user-select: none;
53
+ }
54
+
55
+ div.tree-select-trigger:focus-visible {
56
+ outline: none;
57
+ border-color: var(--color-primary);
58
+ box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
59
+ }
60
+
49
61
  /* Value Display */
50
62
  .tree-select-value {
51
63
  flex: 1;
@@ -106,6 +118,11 @@ export const css = `/**
106
118
  background-color: var(--color-surface-container-high);
107
119
  }
108
120
 
121
+ .tree-select-clear svg {
122
+ width: 1rem;
123
+ height: 1rem;
124
+ }
125
+
109
126
  /* Tree Select Dropdown */
110
127
  .tree-select-dropdown {
111
128
  position: absolute;
@@ -128,9 +145,10 @@ export const css = `/**
128
145
  display: block;
129
146
  }
130
147
 
131
- /* Popover API Support - Browser handles visibility, JS handles positioning */
148
+ /* Popover API Support */
132
149
  .tree-select-dropdown[popover] {
133
- /* Reset browser defaults - positioning handled by JavaScript */
150
+ /* Reset browser defaults for proper positioning */
151
+ position: absolute;
134
152
  inset: unset;
135
153
  margin: 0;
136
154
  border: 1px solid var(--color-outline-variant);
@@ -140,6 +158,21 @@ export const css = `/**
140
158
  display: block;
141
159
  }
142
160
 
161
+ /* CSS Anchor Positioning for modern browsers */
162
+ @supports (anchor-name: --tree-select) {
163
+ .tree-select {
164
+ anchor-name: --tree-select;
165
+ }
166
+
167
+ .tree-select-dropdown[popover] {
168
+ position-anchor: --tree-select;
169
+ top: anchor(bottom);
170
+ left: anchor(left);
171
+ right: anchor(right);
172
+ position-try-fallbacks: flip-block;
173
+ }
174
+ }
175
+
143
176
  /* Search Input in Dropdown */
144
177
  .tree-select-search {
145
178
  display: flex;