@duskmoon-dev/core 1.4.0 → 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.
@@ -69,7 +69,8 @@ export const css = `/**
69
69
  border-color: var(--color-on-surface);
70
70
  }
71
71
 
72
- .checkbox:checked:hover:not(:disabled) {
72
+ .checkbox:checked:hover:not(:disabled),
73
+ .checkbox:indeterminate:hover:not(:disabled) {
73
74
  background-color: color-mix(in oklch, var(--checkbox-color), black 10%);
74
75
  border-color: color-mix(in oklch, var(--checkbox-color), black 10%);
75
76
  }
@@ -36,6 +36,72 @@ export const css = `/**
36
36
  transform: scale(1);
37
37
  }
38
38
 
39
+ /* Direct popover structure (without .popover-content wrapper) */
40
+ .popover.popover-show,
41
+ .popover.show {
42
+ opacity: 1;
43
+ visibility: visible;
44
+ }
45
+
46
+ /* Popover as direct overlay (simpler structure) */
47
+ .popover[class*="popover-top"],
48
+ .popover[class*="popover-bottom"],
49
+ .popover[class*="popover-left"],
50
+ .popover[class*="popover-right"] {
51
+ position: absolute;
52
+ z-index: 1050;
53
+ min-width: 12rem;
54
+ max-width: 20rem;
55
+ padding: 1rem;
56
+ background-color: var(--color-surface);
57
+ border: 1px solid var(--color-outline-variant);
58
+ border-radius: 0.75rem;
59
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
60
+ opacity: 0;
61
+ visibility: hidden;
62
+ transition: opacity 150ms ease-out, visibility 150ms ease-out;
63
+ }
64
+
65
+ .popover[class*="popover-top"].popover-show,
66
+ .popover[class*="popover-bottom"].popover-show,
67
+ .popover[class*="popover-left"].popover-show,
68
+ .popover[class*="popover-right"].popover-show {
69
+ opacity: 1;
70
+ visibility: visible;
71
+ }
72
+
73
+ /* Direct position: Top */
74
+ .popover.popover-top:not(:has(.popover-content)) {
75
+ bottom: 100%;
76
+ left: 50%;
77
+ transform: translateX(-50%);
78
+ margin-bottom: 0.75rem;
79
+ }
80
+
81
+ /* Direct position: Bottom */
82
+ .popover.popover-bottom:not(:has(.popover-content)) {
83
+ top: 100%;
84
+ left: 50%;
85
+ transform: translateX(-50%);
86
+ margin-top: 0.75rem;
87
+ }
88
+
89
+ /* Direct position: Left */
90
+ .popover.popover-left:not(:has(.popover-content)) {
91
+ right: 100%;
92
+ top: 50%;
93
+ transform: translateY(-50%);
94
+ margin-right: 0.75rem;
95
+ }
96
+
97
+ /* Direct position: Right */
98
+ .popover.popover-right:not(:has(.popover-content)) {
99
+ left: 100%;
100
+ top: 50%;
101
+ transform: translateY(-50%);
102
+ margin-left: 0.75rem;
103
+ }
104
+
39
105
  /* Popover Arrow */
40
106
  .popover-arrow {
41
107
  position: absolute;
@@ -212,12 +278,33 @@ export const css = `/**
212
278
  }
213
279
 
214
280
  /* Color Variants */
281
+ /*
282
+ * Color intensity for themed popovers.
283
+ * Override this variable to adjust how strongly colors appear:
284
+ * - 20% = subtle tint
285
+ * - 30% = moderate (default)
286
+ * - 40% = bold/prominent
287
+ */
288
+ .popover {
289
+ --popover-color-intensity: 30%;
290
+ }
291
+
292
+ /* Dark variant */
215
293
  .popover-dark .popover-content {
216
294
  background-color: var(--color-on-surface);
217
295
  color: var(--color-surface);
218
296
  border-color: transparent;
219
297
  }
220
298
 
299
+ .popover-dark[class*="popover-top"],
300
+ .popover-dark[class*="popover-bottom"],
301
+ .popover-dark[class*="popover-left"],
302
+ .popover-dark[class*="popover-right"] {
303
+ background-color: var(--color-on-surface);
304
+ color: var(--color-surface);
305
+ border-color: transparent;
306
+ }
307
+
221
308
  .popover-dark .popover-arrow {
222
309
  background-color: var(--color-on-surface);
223
310
  border-color: transparent;
@@ -232,16 +319,106 @@ export const css = `/**
232
319
  opacity: 0.9;
233
320
  }
234
321
 
322
+ /* Primary variant */
235
323
  .popover-primary .popover-content {
236
- background-color: var(--color-primary-container);
324
+ background-color: color-mix(in oklch, var(--color-primary) var(--popover-color-intensity), var(--color-surface));
325
+ border-color: var(--color-primary);
326
+ }
327
+
328
+ .popover-primary[class*="popover-top"],
329
+ .popover-primary[class*="popover-bottom"],
330
+ .popover-primary[class*="popover-left"],
331
+ .popover-primary[class*="popover-right"] {
332
+ background-color: color-mix(in oklch, var(--color-primary) var(--popover-color-intensity), var(--color-surface));
237
333
  border-color: var(--color-primary);
238
334
  }
239
335
 
240
336
  .popover-primary .popover-arrow {
241
- background-color: var(--color-primary-container);
337
+ background-color: color-mix(in oklch, var(--color-primary) var(--popover-color-intensity), var(--color-surface));
242
338
  border-color: var(--color-primary);
243
339
  }
244
340
 
341
+ .popover-primary .popover-body {
342
+ color: var(--color-on-surface);
343
+ }
344
+
345
+ .popover-primary .popover-title {
346
+ color: var(--color-on-surface);
347
+ }
348
+
349
+ /* Secondary variant */
350
+ .popover-secondary .popover-content {
351
+ background-color: color-mix(in oklch, var(--color-secondary) var(--popover-color-intensity), var(--color-surface));
352
+ border-color: var(--color-secondary);
353
+ }
354
+
355
+ .popover-secondary[class*="popover-top"],
356
+ .popover-secondary[class*="popover-bottom"],
357
+ .popover-secondary[class*="popover-left"],
358
+ .popover-secondary[class*="popover-right"] {
359
+ background-color: color-mix(in oklch, var(--color-secondary) var(--popover-color-intensity), var(--color-surface));
360
+ border-color: var(--color-secondary);
361
+ }
362
+
363
+ .popover-secondary .popover-arrow {
364
+ background-color: color-mix(in oklch, var(--color-secondary) var(--popover-color-intensity), var(--color-surface));
365
+ border-color: var(--color-secondary);
366
+ }
367
+
368
+ .popover-secondary .popover-body {
369
+ color: var(--color-on-surface);
370
+ }
371
+
372
+ .popover-secondary .popover-title {
373
+ color: var(--color-on-surface);
374
+ }
375
+
376
+ /* Tertiary variant */
377
+ .popover-tertiary .popover-content {
378
+ background-color: color-mix(in oklch, var(--color-tertiary) var(--popover-color-intensity), var(--color-surface));
379
+ border-color: var(--color-tertiary);
380
+ }
381
+
382
+ .popover-tertiary[class*="popover-top"],
383
+ .popover-tertiary[class*="popover-bottom"],
384
+ .popover-tertiary[class*="popover-left"],
385
+ .popover-tertiary[class*="popover-right"] {
386
+ background-color: color-mix(in oklch, var(--color-tertiary) var(--popover-color-intensity), var(--color-surface));
387
+ border-color: var(--color-tertiary);
388
+ }
389
+
390
+ .popover-tertiary .popover-arrow {
391
+ background-color: color-mix(in oklch, var(--color-tertiary) var(--popover-color-intensity), var(--color-surface));
392
+ border-color: var(--color-tertiary);
393
+ }
394
+
395
+ .popover-tertiary .popover-body {
396
+ color: var(--color-on-surface);
397
+ }
398
+
399
+ .popover-tertiary .popover-title {
400
+ color: var(--color-on-surface);
401
+ }
402
+
403
+ /* Surface highest variant */
404
+ .popover-surface-highest .popover-content {
405
+ background-color: var(--color-surface-container-highest);
406
+ border-color: var(--color-outline-variant);
407
+ }
408
+
409
+ .popover-surface-highest[class*="popover-top"],
410
+ .popover-surface-highest[class*="popover-bottom"],
411
+ .popover-surface-highest[class*="popover-left"],
412
+ .popover-surface-highest[class*="popover-right"] {
413
+ background-color: var(--color-surface-container-highest);
414
+ border-color: var(--color-outline-variant);
415
+ }
416
+
417
+ .popover-surface-highest .popover-arrow {
418
+ background-color: var(--color-surface-container-highest);
419
+ border-color: var(--color-outline-variant);
420
+ }
421
+
245
422
  /* Hover Trigger */
246
423
  .popover-hover:hover .popover-content,
247
424
  .popover-hover:focus-within .popover-content {
@@ -316,11 +493,160 @@ export const css = `/**
316
493
  justify-content: center;
317
494
  }
318
495
 
496
+ /* ========================================
497
+ * HTML Popover API Support
498
+ * Uses native [popover] attribute with :popover-open pseudo-class
499
+ * ======================================== */
500
+
501
+ /* Native popover base styles */
502
+ .popover[popover] {
503
+ position: fixed;
504
+ inset: unset;
505
+ z-index: 1050;
506
+ min-width: 12rem;
507
+ max-width: 20rem;
508
+ padding: 1rem;
509
+ margin: 0;
510
+ background-color: var(--color-surface);
511
+ border: 1px solid var(--color-outline-variant);
512
+ border-radius: 0.75rem;
513
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
514
+ opacity: 0;
515
+ transform: scale(0.95);
516
+ transition: opacity 150ms ease-out, transform 150ms ease-out, overlay 150ms ease-out allow-discrete, display 150ms ease-out allow-discrete;
517
+ }
518
+
519
+ /* Popover open state */
520
+ .popover[popover]:popover-open {
521
+ opacity: 1;
522
+ transform: scale(1);
523
+ }
524
+
525
+ /* Starting state for entry animation */
526
+ @starting-style {
527
+ .popover[popover]:popover-open {
528
+ opacity: 0;
529
+ transform: scale(0.95);
530
+ }
531
+ }
532
+
533
+ /* Native popover backdrop */
534
+ .popover[popover]::backdrop {
535
+ background-color: rgb(0 0 0 / 0);
536
+ transition: background-color 150ms ease-out, overlay 150ms ease-out allow-discrete, display 150ms ease-out allow-discrete;
537
+ }
538
+
539
+ .popover[popover]:popover-open::backdrop {
540
+ background-color: rgb(0 0 0 / 0.1);
541
+ }
542
+
543
+ @starting-style {
544
+ .popover[popover]:popover-open::backdrop {
545
+ background-color: rgb(0 0 0 / 0);
546
+ }
547
+ }
548
+
549
+ /* Native popover with modal backdrop */
550
+ .popover-modal[popover]::backdrop {
551
+ background-color: rgb(0 0 0 / 0);
552
+ transition: background-color 150ms ease-out, overlay 150ms ease-out allow-discrete, display 150ms ease-out allow-discrete;
553
+ }
554
+
555
+ .popover-modal[popover]:popover-open::backdrop {
556
+ background-color: rgb(0 0 0 / 0.3);
557
+ }
558
+
559
+ @starting-style {
560
+ .popover-modal[popover]:popover-open::backdrop {
561
+ background-color: rgb(0 0 0 / 0);
562
+ }
563
+ }
564
+
565
+ /* Native popover color variants */
566
+ .popover-primary[popover] {
567
+ background-color: color-mix(in oklch, var(--color-primary) var(--popover-color-intensity), var(--color-surface));
568
+ border-color: var(--color-primary);
569
+ }
570
+
571
+ .popover-primary[popover] .popover-body {
572
+ color: var(--color-on-surface);
573
+ }
574
+
575
+ .popover-primary[popover] .popover-title {
576
+ color: var(--color-on-surface);
577
+ }
578
+
579
+ .popover-secondary[popover] {
580
+ background-color: color-mix(in oklch, var(--color-secondary) var(--popover-color-intensity), var(--color-surface));
581
+ border-color: var(--color-secondary);
582
+ }
583
+
584
+ .popover-secondary[popover] .popover-body {
585
+ color: var(--color-on-surface);
586
+ }
587
+
588
+ .popover-secondary[popover] .popover-title {
589
+ color: var(--color-on-surface);
590
+ }
591
+
592
+ .popover-tertiary[popover] {
593
+ background-color: color-mix(in oklch, var(--color-tertiary) var(--popover-color-intensity), var(--color-surface));
594
+ border-color: var(--color-tertiary);
595
+ }
596
+
597
+ .popover-tertiary[popover] .popover-body {
598
+ color: var(--color-on-surface);
599
+ }
600
+
601
+ .popover-tertiary[popover] .popover-title {
602
+ color: var(--color-on-surface);
603
+ }
604
+
605
+ .popover-dark[popover] {
606
+ background-color: var(--color-on-surface);
607
+ border-color: transparent;
608
+ }
609
+
610
+ .popover-dark[popover] .popover-body {
611
+ color: var(--color-surface);
612
+ opacity: 0.9;
613
+ }
614
+
615
+ .popover-dark[popover] .popover-title {
616
+ color: var(--color-surface);
617
+ }
618
+
619
+ .popover-surface-highest[popover] {
620
+ background-color: var(--color-surface-container-highest);
621
+ border-color: var(--color-outline-variant);
622
+ }
623
+
624
+ /* Native popover size variants */
625
+ .popover-sm[popover] {
626
+ min-width: 8rem;
627
+ max-width: 14rem;
628
+ padding: 0.75rem;
629
+ }
630
+
631
+ .popover-lg[popover] {
632
+ min-width: 16rem;
633
+ max-width: 28rem;
634
+ padding: 1.25rem;
635
+ }
636
+
637
+ .popover-full[popover] {
638
+ min-width: 0;
639
+ max-width: none;
640
+ width: max-content;
641
+ }
642
+
319
643
  /* Reduce Motion */
320
644
  @media (prefers-reduced-motion: reduce) {
321
645
  .popover-content,
322
646
  .popover-close,
323
- .popover-menu-item {
647
+ .popover-menu-item,
648
+ .popover[popover],
649
+ .popover[popover]::backdrop {
324
650
  transition: none;
325
651
  }
326
652
  }
@@ -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
  }