@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.
@@ -35,6 +35,72 @@
35
35
  transform: scale(1);
36
36
  }
37
37
 
38
+ /* Direct popover structure (without .popover-content wrapper) */
39
+ .popover.popover-show,
40
+ .popover.show {
41
+ opacity: 1;
42
+ visibility: visible;
43
+ }
44
+
45
+ /* Popover as direct overlay (simpler structure) */
46
+ .popover[class*="popover-top"],
47
+ .popover[class*="popover-bottom"],
48
+ .popover[class*="popover-left"],
49
+ .popover[class*="popover-right"] {
50
+ position: absolute;
51
+ z-index: 1050;
52
+ min-width: 12rem;
53
+ max-width: 20rem;
54
+ padding: 1rem;
55
+ background-color: var(--color-surface);
56
+ border: 1px solid var(--color-outline-variant);
57
+ border-radius: 0.75rem;
58
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
59
+ opacity: 0;
60
+ visibility: hidden;
61
+ transition: opacity 150ms ease-out, visibility 150ms ease-out;
62
+ }
63
+
64
+ .popover[class*="popover-top"].popover-show,
65
+ .popover[class*="popover-bottom"].popover-show,
66
+ .popover[class*="popover-left"].popover-show,
67
+ .popover[class*="popover-right"].popover-show {
68
+ opacity: 1;
69
+ visibility: visible;
70
+ }
71
+
72
+ /* Direct position: Top */
73
+ .popover.popover-top:not(:has(.popover-content)) {
74
+ bottom: 100%;
75
+ left: 50%;
76
+ transform: translateX(-50%);
77
+ margin-bottom: 0.75rem;
78
+ }
79
+
80
+ /* Direct position: Bottom */
81
+ .popover.popover-bottom:not(:has(.popover-content)) {
82
+ top: 100%;
83
+ left: 50%;
84
+ transform: translateX(-50%);
85
+ margin-top: 0.75rem;
86
+ }
87
+
88
+ /* Direct position: Left */
89
+ .popover.popover-left:not(:has(.popover-content)) {
90
+ right: 100%;
91
+ top: 50%;
92
+ transform: translateY(-50%);
93
+ margin-right: 0.75rem;
94
+ }
95
+
96
+ /* Direct position: Right */
97
+ .popover.popover-right:not(:has(.popover-content)) {
98
+ left: 100%;
99
+ top: 50%;
100
+ transform: translateY(-50%);
101
+ margin-left: 0.75rem;
102
+ }
103
+
38
104
  /* Popover Arrow */
39
105
  .popover-arrow {
40
106
  position: absolute;
@@ -211,12 +277,33 @@
211
277
  }
212
278
 
213
279
  /* Color Variants */
280
+ /*
281
+ * Color intensity for themed popovers.
282
+ * Override this variable to adjust how strongly colors appear:
283
+ * - 20% = subtle tint
284
+ * - 30% = moderate (default)
285
+ * - 40% = bold/prominent
286
+ */
287
+ .popover {
288
+ --popover-color-intensity: 30%;
289
+ }
290
+
291
+ /* Dark variant */
214
292
  .popover-dark .popover-content {
215
293
  background-color: var(--color-on-surface);
216
294
  color: var(--color-surface);
217
295
  border-color: transparent;
218
296
  }
219
297
 
298
+ .popover-dark[class*="popover-top"],
299
+ .popover-dark[class*="popover-bottom"],
300
+ .popover-dark[class*="popover-left"],
301
+ .popover-dark[class*="popover-right"] {
302
+ background-color: var(--color-on-surface);
303
+ color: var(--color-surface);
304
+ border-color: transparent;
305
+ }
306
+
220
307
  .popover-dark .popover-arrow {
221
308
  background-color: var(--color-on-surface);
222
309
  border-color: transparent;
@@ -231,16 +318,106 @@
231
318
  opacity: 0.9;
232
319
  }
233
320
 
321
+ /* Primary variant */
234
322
  .popover-primary .popover-content {
235
- background-color: var(--color-primary-container);
323
+ background-color: color-mix(in oklch, var(--color-primary) var(--popover-color-intensity), var(--color-surface));
324
+ border-color: var(--color-primary);
325
+ }
326
+
327
+ .popover-primary[class*="popover-top"],
328
+ .popover-primary[class*="popover-bottom"],
329
+ .popover-primary[class*="popover-left"],
330
+ .popover-primary[class*="popover-right"] {
331
+ background-color: color-mix(in oklch, var(--color-primary) var(--popover-color-intensity), var(--color-surface));
236
332
  border-color: var(--color-primary);
237
333
  }
238
334
 
239
335
  .popover-primary .popover-arrow {
240
- background-color: var(--color-primary-container);
336
+ background-color: color-mix(in oklch, var(--color-primary) var(--popover-color-intensity), var(--color-surface));
241
337
  border-color: var(--color-primary);
242
338
  }
243
339
 
340
+ .popover-primary .popover-body {
341
+ color: var(--color-on-surface);
342
+ }
343
+
344
+ .popover-primary .popover-title {
345
+ color: var(--color-on-surface);
346
+ }
347
+
348
+ /* Secondary variant */
349
+ .popover-secondary .popover-content {
350
+ background-color: color-mix(in oklch, var(--color-secondary) var(--popover-color-intensity), var(--color-surface));
351
+ border-color: var(--color-secondary);
352
+ }
353
+
354
+ .popover-secondary[class*="popover-top"],
355
+ .popover-secondary[class*="popover-bottom"],
356
+ .popover-secondary[class*="popover-left"],
357
+ .popover-secondary[class*="popover-right"] {
358
+ background-color: color-mix(in oklch, var(--color-secondary) var(--popover-color-intensity), var(--color-surface));
359
+ border-color: var(--color-secondary);
360
+ }
361
+
362
+ .popover-secondary .popover-arrow {
363
+ background-color: color-mix(in oklch, var(--color-secondary) var(--popover-color-intensity), var(--color-surface));
364
+ border-color: var(--color-secondary);
365
+ }
366
+
367
+ .popover-secondary .popover-body {
368
+ color: var(--color-on-surface);
369
+ }
370
+
371
+ .popover-secondary .popover-title {
372
+ color: var(--color-on-surface);
373
+ }
374
+
375
+ /* Tertiary variant */
376
+ .popover-tertiary .popover-content {
377
+ background-color: color-mix(in oklch, var(--color-tertiary) var(--popover-color-intensity), var(--color-surface));
378
+ border-color: var(--color-tertiary);
379
+ }
380
+
381
+ .popover-tertiary[class*="popover-top"],
382
+ .popover-tertiary[class*="popover-bottom"],
383
+ .popover-tertiary[class*="popover-left"],
384
+ .popover-tertiary[class*="popover-right"] {
385
+ background-color: color-mix(in oklch, var(--color-tertiary) var(--popover-color-intensity), var(--color-surface));
386
+ border-color: var(--color-tertiary);
387
+ }
388
+
389
+ .popover-tertiary .popover-arrow {
390
+ background-color: color-mix(in oklch, var(--color-tertiary) var(--popover-color-intensity), var(--color-surface));
391
+ border-color: var(--color-tertiary);
392
+ }
393
+
394
+ .popover-tertiary .popover-body {
395
+ color: var(--color-on-surface);
396
+ }
397
+
398
+ .popover-tertiary .popover-title {
399
+ color: var(--color-on-surface);
400
+ }
401
+
402
+ /* Surface highest variant */
403
+ .popover-surface-highest .popover-content {
404
+ background-color: var(--color-surface-container-highest);
405
+ border-color: var(--color-outline-variant);
406
+ }
407
+
408
+ .popover-surface-highest[class*="popover-top"],
409
+ .popover-surface-highest[class*="popover-bottom"],
410
+ .popover-surface-highest[class*="popover-left"],
411
+ .popover-surface-highest[class*="popover-right"] {
412
+ background-color: var(--color-surface-container-highest);
413
+ border-color: var(--color-outline-variant);
414
+ }
415
+
416
+ .popover-surface-highest .popover-arrow {
417
+ background-color: var(--color-surface-container-highest);
418
+ border-color: var(--color-outline-variant);
419
+ }
420
+
244
421
  /* Hover Trigger */
245
422
  .popover-hover:hover .popover-content,
246
423
  .popover-hover:focus-within .popover-content {
@@ -315,11 +492,160 @@
315
492
  justify-content: center;
316
493
  }
317
494
 
495
+ /* ========================================
496
+ * HTML Popover API Support
497
+ * Uses native [popover] attribute with :popover-open pseudo-class
498
+ * ======================================== */
499
+
500
+ /* Native popover base styles */
501
+ .popover[popover] {
502
+ position: fixed;
503
+ inset: unset;
504
+ z-index: 1050;
505
+ min-width: 12rem;
506
+ max-width: 20rem;
507
+ padding: 1rem;
508
+ margin: 0;
509
+ background-color: var(--color-surface);
510
+ border: 1px solid var(--color-outline-variant);
511
+ border-radius: 0.75rem;
512
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
513
+ opacity: 0;
514
+ transform: scale(0.95);
515
+ transition: opacity 150ms ease-out, transform 150ms ease-out, overlay 150ms ease-out allow-discrete, display 150ms ease-out allow-discrete;
516
+ }
517
+
518
+ /* Popover open state */
519
+ .popover[popover]:popover-open {
520
+ opacity: 1;
521
+ transform: scale(1);
522
+ }
523
+
524
+ /* Starting state for entry animation */
525
+ @starting-style {
526
+ .popover[popover]:popover-open {
527
+ opacity: 0;
528
+ transform: scale(0.95);
529
+ }
530
+ }
531
+
532
+ /* Native popover backdrop */
533
+ .popover[popover]::backdrop {
534
+ background-color: rgb(0 0 0 / 0);
535
+ transition: background-color 150ms ease-out, overlay 150ms ease-out allow-discrete, display 150ms ease-out allow-discrete;
536
+ }
537
+
538
+ .popover[popover]:popover-open::backdrop {
539
+ background-color: rgb(0 0 0 / 0.1);
540
+ }
541
+
542
+ @starting-style {
543
+ .popover[popover]:popover-open::backdrop {
544
+ background-color: rgb(0 0 0 / 0);
545
+ }
546
+ }
547
+
548
+ /* Native popover with modal backdrop */
549
+ .popover-modal[popover]::backdrop {
550
+ background-color: rgb(0 0 0 / 0);
551
+ transition: background-color 150ms ease-out, overlay 150ms ease-out allow-discrete, display 150ms ease-out allow-discrete;
552
+ }
553
+
554
+ .popover-modal[popover]:popover-open::backdrop {
555
+ background-color: rgb(0 0 0 / 0.3);
556
+ }
557
+
558
+ @starting-style {
559
+ .popover-modal[popover]:popover-open::backdrop {
560
+ background-color: rgb(0 0 0 / 0);
561
+ }
562
+ }
563
+
564
+ /* Native popover color variants */
565
+ .popover-primary[popover] {
566
+ background-color: color-mix(in oklch, var(--color-primary) var(--popover-color-intensity), var(--color-surface));
567
+ border-color: var(--color-primary);
568
+ }
569
+
570
+ .popover-primary[popover] .popover-body {
571
+ color: var(--color-on-surface);
572
+ }
573
+
574
+ .popover-primary[popover] .popover-title {
575
+ color: var(--color-on-surface);
576
+ }
577
+
578
+ .popover-secondary[popover] {
579
+ background-color: color-mix(in oklch, var(--color-secondary) var(--popover-color-intensity), var(--color-surface));
580
+ border-color: var(--color-secondary);
581
+ }
582
+
583
+ .popover-secondary[popover] .popover-body {
584
+ color: var(--color-on-surface);
585
+ }
586
+
587
+ .popover-secondary[popover] .popover-title {
588
+ color: var(--color-on-surface);
589
+ }
590
+
591
+ .popover-tertiary[popover] {
592
+ background-color: color-mix(in oklch, var(--color-tertiary) var(--popover-color-intensity), var(--color-surface));
593
+ border-color: var(--color-tertiary);
594
+ }
595
+
596
+ .popover-tertiary[popover] .popover-body {
597
+ color: var(--color-on-surface);
598
+ }
599
+
600
+ .popover-tertiary[popover] .popover-title {
601
+ color: var(--color-on-surface);
602
+ }
603
+
604
+ .popover-dark[popover] {
605
+ background-color: var(--color-on-surface);
606
+ border-color: transparent;
607
+ }
608
+
609
+ .popover-dark[popover] .popover-body {
610
+ color: var(--color-surface);
611
+ opacity: 0.9;
612
+ }
613
+
614
+ .popover-dark[popover] .popover-title {
615
+ color: var(--color-surface);
616
+ }
617
+
618
+ .popover-surface-highest[popover] {
619
+ background-color: var(--color-surface-container-highest);
620
+ border-color: var(--color-outline-variant);
621
+ }
622
+
623
+ /* Native popover size variants */
624
+ .popover-sm[popover] {
625
+ min-width: 8rem;
626
+ max-width: 14rem;
627
+ padding: 0.75rem;
628
+ }
629
+
630
+ .popover-lg[popover] {
631
+ min-width: 16rem;
632
+ max-width: 28rem;
633
+ padding: 1.25rem;
634
+ }
635
+
636
+ .popover-full[popover] {
637
+ min-width: 0;
638
+ max-width: none;
639
+ width: max-content;
640
+ }
641
+
318
642
  /* Reduce Motion */
319
643
  @media (prefers-reduced-motion: reduce) {
320
644
  .popover-content,
321
645
  .popover-close,
322
- .popover-menu-item {
646
+ .popover-menu-item,
647
+ .popover[popover],
648
+ .popover[popover]::backdrop {
323
649
  transition: none;
324
650
  }
325
651
  }
@@ -4,7 +4,84 @@
4
4
  */
5
5
 
6
6
  @layer components {
7
- /* Base Textarea */
7
+ /* ============================================
8
+ * TEXTAREA CONTAINER
9
+ * ============================================ */
10
+
11
+ .textarea-container {
12
+ position: relative;
13
+ display: flex;
14
+ flex-direction: column;
15
+ gap: 0.25rem;
16
+ width: 100%;
17
+ }
18
+
19
+ /* ============================================
20
+ * TEXTAREA LABEL
21
+ * ============================================ */
22
+
23
+ .textarea-label {
24
+ display: block;
25
+ font-size: 0.875rem;
26
+ font-weight: 500;
27
+ line-height: 1.25rem;
28
+ color: var(--color-on-surface);
29
+ margin-bottom: 0.25rem;
30
+ }
31
+
32
+ /* Floating Label */
33
+ .textarea-label-floating {
34
+ position: absolute;
35
+ top: 0.75rem;
36
+ left: 1rem;
37
+ font-size: 1rem;
38
+ font-weight: 400;
39
+ color: var(--color-on-surface-variant);
40
+ pointer-events: none;
41
+ transition: all 150ms ease-in-out;
42
+ transform-origin: left top;
43
+ z-index: 1;
44
+ }
45
+
46
+ /* Floating label active state - when textarea has content or focus */
47
+ .textarea:focus ~ .textarea-label-floating,
48
+ .textarea:not(:placeholder-shown) ~ .textarea-label-floating {
49
+ top: -0.5rem;
50
+ left: 0.75rem;
51
+ font-size: 0.75rem;
52
+ font-weight: 500;
53
+ color: var(--color-primary);
54
+ background-color: var(--color-surface);
55
+ padding: 0 0.25rem;
56
+ }
57
+
58
+ /* Floating label for filled variant */
59
+ .textarea-filled ~ .textarea-label-floating {
60
+ background-color: transparent;
61
+ }
62
+
63
+ .textarea-filled:focus ~ .textarea-label-floating,
64
+ .textarea-filled:not(:placeholder-shown) ~ .textarea-label-floating {
65
+ top: 0.25rem;
66
+ left: 0.75rem;
67
+ background-color: transparent;
68
+ }
69
+
70
+ /* ============================================
71
+ * HELPER TEXT
72
+ * ============================================ */
73
+
74
+ .textarea-helper {
75
+ font-size: 0.75rem;
76
+ line-height: 1rem;
77
+ color: var(--color-on-surface-variant);
78
+ margin-top: 0.25rem;
79
+ }
80
+
81
+ /* ============================================
82
+ * BASE TEXTAREA
83
+ * ============================================ */
84
+
8
85
  .textarea {
9
86
  display: block;
10
87
  width: 100%;
@@ -52,6 +129,15 @@
52
129
  cursor: default;
53
130
  }
54
131
 
132
+ /* Full Width */
133
+ .textarea-full {
134
+ width: 100%;
135
+ }
136
+
137
+ /* ============================================
138
+ * VARIANTS
139
+ * ============================================ */
140
+
55
141
  /* Filled Variant */
56
142
  .textarea-filled {
57
143
  background-color: var(--color-surface-container);
@@ -94,23 +180,68 @@
94
180
  box-shadow: none;
95
181
  }
96
182
 
97
- /* Color Variants */
183
+ /* ============================================
184
+ * COLOR VARIANTS
185
+ * ============================================ */
186
+
187
+ /* Primary */
188
+ .textarea-primary {
189
+ border-color: var(--color-primary);
190
+ }
191
+
98
192
  .textarea-primary:focus-visible {
99
193
  border-color: var(--color-primary);
100
194
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-primary) 10%, transparent);
101
195
  }
102
196
 
197
+ .textarea-filled.textarea-primary {
198
+ border-bottom-color: var(--color-primary);
199
+ }
200
+
201
+ .textarea-filled.textarea-primary:focus-visible {
202
+ border-bottom-color: var(--color-primary);
203
+ }
204
+
205
+ /* Secondary */
206
+ .textarea-secondary {
207
+ border-color: var(--color-secondary);
208
+ }
209
+
103
210
  .textarea-secondary:focus-visible {
104
211
  border-color: var(--color-secondary);
105
212
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-secondary) 10%, transparent);
106
213
  }
107
214
 
215
+ .textarea-filled.textarea-secondary {
216
+ border-bottom-color: var(--color-secondary);
217
+ }
218
+
219
+ .textarea-filled.textarea-secondary:focus-visible {
220
+ border-bottom-color: var(--color-secondary);
221
+ }
222
+
223
+ /* Tertiary */
224
+ .textarea-tertiary {
225
+ border-color: var(--color-tertiary);
226
+ }
227
+
108
228
  .textarea-tertiary:focus-visible {
109
229
  border-color: var(--color-tertiary);
110
230
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-tertiary) 10%, transparent);
111
231
  }
112
232
 
113
- /* Semantic Colors */
233
+ .textarea-filled.textarea-tertiary {
234
+ border-bottom-color: var(--color-tertiary);
235
+ }
236
+
237
+ .textarea-filled.textarea-tertiary:focus-visible {
238
+ border-bottom-color: var(--color-tertiary);
239
+ }
240
+
241
+ /* ============================================
242
+ * SEMANTIC COLORS
243
+ * ============================================ */
244
+
114
245
  .textarea-error {
115
246
  border-color: var(--color-error);
116
247
  }
@@ -120,6 +251,10 @@
120
251
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-error) 10%, transparent);
121
252
  }
122
253
 
254
+ .textarea-filled.textarea-error {
255
+ border-bottom-color: var(--color-error);
256
+ }
257
+
123
258
  .textarea-success {
124
259
  border-color: var(--color-success);
125
260
  }
@@ -129,6 +264,10 @@
129
264
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-success) 10%, transparent);
130
265
  }
131
266
 
267
+ .textarea-filled.textarea-success {
268
+ border-bottom-color: var(--color-success);
269
+ }
270
+
132
271
  .textarea-warning {
133
272
  border-color: var(--color-warning);
134
273
  }
@@ -138,7 +277,14 @@
138
277
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--color-warning) 10%, transparent);
139
278
  }
140
279
 
141
- /* Size Variants */
280
+ .textarea-filled.textarea-warning {
281
+ border-bottom-color: var(--color-warning);
282
+ }
283
+
284
+ /* ============================================
285
+ * SIZE VARIANTS
286
+ * ============================================ */
287
+
142
288
  .textarea-sm {
143
289
  min-height: 4rem;
144
290
  padding: 0.5rem 0.75rem;
@@ -155,11 +301,18 @@
155
301
  border-radius: 0.625rem;
156
302
  }
157
303
 
158
- /* Resize Options */
304
+ /* ============================================
305
+ * RESIZE OPTIONS
306
+ * ============================================ */
307
+
159
308
  .textarea-resize-none {
160
309
  resize: none;
161
310
  }
162
311
 
312
+ .textarea-resize-vertical {
313
+ resize: vertical;
314
+ }
315
+
163
316
  .textarea-resize-horizontal {
164
317
  resize: horizontal;
165
318
  }
@@ -168,19 +321,21 @@
168
321
  resize: both;
169
322
  }
170
323
 
171
- /* Auto-resize (requires JS) */
324
+ /* ============================================
325
+ * AUTO-RESIZE (requires JS)
326
+ * ============================================ */
327
+
328
+ .textarea-auto-resize,
172
329
  .textarea-autosize {
173
330
  resize: none;
174
331
  overflow: hidden;
332
+ min-height: 3rem;
333
+ field-sizing: content; /* Modern CSS - auto-grows without JS in supported browsers */
175
334
  }
176
335
 
177
- /* Character Counter */
178
- .textarea-wrapper {
179
- position: relative;
180
- display: flex;
181
- flex-direction: column;
182
- gap: 0.25rem;
183
- }
336
+ /* ============================================
337
+ * CHARACTER COUNTER
338
+ * ============================================ */
184
339
 
185
340
  .textarea-counter {
186
341
  font-size: 0.75rem;
@@ -189,13 +344,54 @@
189
344
  text-align: right;
190
345
  }
191
346
 
192
- .textarea-counter-error {
347
+ .textarea-counter-error,
348
+ .textarea-counter-exceeded {
349
+ color: var(--color-error);
350
+ }
351
+
352
+ /* ============================================
353
+ * CONTAINER STATES
354
+ * ============================================ */
355
+
356
+ .textarea-container-error .textarea-label {
357
+ color: var(--color-error);
358
+ }
359
+
360
+ .textarea-container-error .textarea-helper {
361
+ color: var(--color-error);
362
+ }
363
+
364
+ .textarea-container-error .textarea-label-floating {
365
+ color: var(--color-error);
366
+ }
367
+
368
+ .textarea-container-error .textarea:focus ~ .textarea-label-floating {
193
369
  color: var(--color-error);
194
370
  }
195
371
 
196
- /* Reduce Motion */
372
+ .textarea-container-success .textarea-label {
373
+ color: var(--color-success);
374
+ }
375
+
376
+ .textarea-container-success .textarea-helper {
377
+ color: var(--color-success);
378
+ }
379
+
380
+ .textarea-container-success .textarea-label-floating {
381
+ color: var(--color-success);
382
+ }
383
+
384
+ .textarea-container-success .textarea:focus ~ .textarea-label-floating {
385
+ color: var(--color-success);
386
+ }
387
+
388
+ /* ============================================
389
+ * REDUCE MOTION
390
+ * ============================================ */
391
+
197
392
  @media (prefers-reduced-motion: reduce) {
198
- .textarea {
393
+ .textarea,
394
+ .textarea-label-floating {
199
395
  transition: none;
200
396
  }
201
397
  }