@ponchia/ui 0.6.0 → 0.6.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.
- package/CHANGELOG.md +82 -4
- package/README.md +1 -1
- package/annotations/index.d.ts.map +1 -1
- package/annotations/index.js +36 -33
- package/behaviors/carousel.d.ts +28 -0
- package/behaviors/carousel.d.ts.map +1 -0
- package/behaviors/carousel.js +3 -0
- package/behaviors/combobox.d.ts +40 -0
- package/behaviors/combobox.d.ts.map +1 -0
- package/behaviors/combobox.js +71 -20
- package/behaviors/command.d.ts +41 -0
- package/behaviors/command.d.ts.map +1 -0
- package/behaviors/command.js +9 -0
- package/behaviors/connectors.d.ts +17 -0
- package/behaviors/connectors.d.ts.map +1 -0
- package/behaviors/connectors.js +3 -0
- package/behaviors/crosshair.d.ts +42 -0
- package/behaviors/crosshair.d.ts.map +1 -0
- package/behaviors/crosshair.js +19 -1
- package/behaviors/dialog.d.ts +20 -0
- package/behaviors/dialog.d.ts.map +1 -0
- package/behaviors/dialog.js +3 -0
- package/behaviors/disclosure.d.ts +10 -0
- package/behaviors/disclosure.d.ts.map +1 -0
- package/behaviors/disclosure.js +3 -0
- package/behaviors/dismissible.d.ts +10 -0
- package/behaviors/dismissible.d.ts.map +1 -0
- package/behaviors/dismissible.js +3 -0
- package/behaviors/forms.d.ts +27 -0
- package/behaviors/forms.d.ts.map +1 -0
- package/behaviors/forms.js +18 -5
- package/behaviors/glyph.d.ts +21 -0
- package/behaviors/glyph.d.ts.map +1 -0
- package/behaviors/glyph.js +82 -4
- package/behaviors/index.d.ts +31 -237
- package/behaviors/index.d.ts.map +1 -0
- package/behaviors/index.js +17 -0
- package/behaviors/inert.d.ts +20 -0
- package/behaviors/inert.d.ts.map +1 -0
- package/behaviors/inert.js +46 -0
- package/behaviors/internal.d.ts +25 -0
- package/behaviors/internal.d.ts.map +1 -0
- package/behaviors/internal.js +30 -1
- package/behaviors/legend.d.ts +35 -0
- package/behaviors/legend.d.ts.map +1 -0
- package/behaviors/legend.js +9 -0
- package/behaviors/menu.d.ts +16 -0
- package/behaviors/menu.d.ts.map +1 -0
- package/behaviors/menu.js +3 -0
- package/behaviors/modal.d.ts +41 -0
- package/behaviors/modal.d.ts.map +1 -0
- package/behaviors/modal.js +124 -0
- package/behaviors/popover.d.ts +28 -0
- package/behaviors/popover.d.ts.map +1 -0
- package/behaviors/popover.js +17 -17
- package/behaviors/spotlight.d.ts +17 -0
- package/behaviors/spotlight.d.ts.map +1 -0
- package/behaviors/spotlight.js +3 -0
- package/behaviors/table.d.ts +36 -0
- package/behaviors/table.d.ts.map +1 -0
- package/behaviors/table.js +48 -8
- package/behaviors/tabs.d.ts +20 -0
- package/behaviors/tabs.d.ts.map +1 -0
- package/behaviors/tabs.js +3 -0
- package/behaviors/theme.d.ts +54 -0
- package/behaviors/theme.d.ts.map +1 -0
- package/behaviors/theme.js +17 -0
- package/behaviors/toast.d.ts +49 -0
- package/behaviors/toast.d.ts.map +1 -0
- package/behaviors/toast.js +34 -2
- package/classes/classes.json +747 -15
- package/classes/index.d.ts +118 -3
- package/classes/index.js +264 -66
- package/connectors/index.d.ts +12 -0
- package/connectors/index.d.ts.map +1 -1
- package/connectors/index.js +23 -2
- package/css/app.css +26 -0
- package/css/bullet.css +108 -0
- package/css/code.css +98 -0
- package/css/content.css +15 -2
- package/css/crosshair.css +7 -7
- package/css/diff.css +153 -0
- package/css/disclosure.css +18 -4
- package/css/dots.css +246 -9
- package/css/feedback.css +39 -7
- package/css/forms.css +71 -3
- package/css/legend.css +5 -2
- package/css/motion.css +79 -14
- package/css/overlay.css +59 -2
- package/css/primitives.css +67 -8
- package/css/report.css +43 -4
- package/css/sidenote.css +67 -0
- package/css/skins.css +9 -0
- package/css/spark.css +76 -0
- package/css/table.css +16 -3
- package/css/term.css +110 -0
- package/css/textref.css +63 -0
- package/css/toc.css +91 -0
- package/css/tokens.css +14 -1
- package/css/tree.css +134 -0
- package/dist/bronto.css +1 -1
- package/dist/css/analytical.css +1 -1
- package/dist/css/app.css +1 -1
- package/dist/css/bullet.css +1 -0
- package/dist/css/code.css +1 -0
- package/dist/css/content.css +1 -1
- package/dist/css/crosshair.css +1 -1
- package/dist/css/diff.css +1 -0
- package/dist/css/disclosure.css +1 -1
- package/dist/css/dots.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/forms.css +1 -1
- package/dist/css/legend.css +1 -1
- package/dist/css/motion.css +1 -1
- package/dist/css/overlay.css +1 -1
- package/dist/css/primitives.css +1 -1
- package/dist/css/report.css +1 -1
- package/dist/css/sidenote.css +1 -0
- package/dist/css/skins.css +1 -1
- package/dist/css/spark.css +1 -0
- package/dist/css/table.css +1 -1
- package/dist/css/term.css +1 -0
- package/dist/css/textref.css +1 -0
- package/dist/css/toc.css +1 -0
- package/dist/css/tokens.css +1 -1
- package/dist/css/tree.css +1 -0
- package/docs/annotations.md +39 -0
- package/docs/architecture.md +2 -3
- package/docs/bullet.md +78 -0
- package/docs/code.md +76 -0
- package/docs/d2.md +4 -3
- package/docs/diff.md +146 -0
- package/docs/dots.md +146 -0
- package/docs/glyphs.md +114 -0
- package/docs/legends.md +8 -4
- package/docs/mermaid.md +21 -4
- package/docs/reference.md +168 -8
- package/docs/reporting.md +49 -17
- package/docs/sidenote.md +64 -0
- package/docs/spark.md +78 -0
- package/docs/stability.md +1 -0
- package/docs/term.md +81 -0
- package/docs/textref.md +78 -0
- package/docs/theming.md +44 -5
- package/docs/toc.md +83 -0
- package/docs/tree.md +74 -0
- package/docs/usage.md +264 -23
- package/docs/vega.md +22 -3
- package/glyphs/glyphs.d.ts +61 -0
- package/glyphs/glyphs.js +600 -31
- package/llms.txt +169 -15
- package/package.json +51 -7
- package/qwik/index.d.ts +4 -2
- package/qwik/index.d.ts.map +1 -1
- package/qwik/index.js +10 -0
- package/react/index.d.ts +4 -2
- package/react/index.d.ts.map +1 -1
- package/react/index.js +6 -0
- package/solid/index.d.ts +6 -2
- package/solid/index.d.ts.map +1 -1
- package/solid/index.js +6 -0
- package/tokens/skins.js +22 -9
package/css/dots.css
CHANGED
|
@@ -348,14 +348,191 @@
|
|
|
348
348
|
animation-delay: 0.63s;
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
-
/*
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
351
|
+
/* ==========================================================================
|
|
352
|
+
Data-bound dot surfaces — the reporting/dashboard family. Each is a thin,
|
|
353
|
+
token-driven leaf over the same lit/dim/accent dot vocabulary: the HOST
|
|
354
|
+
normalises the data (lights `is-on`, sets `data-level`, or writes `--v`
|
|
355
|
+
0..1) and the leaf only lays out + tones. None compute a scale, bin, or
|
|
356
|
+
threshold — that stays with the host (the spark/meter boundary).
|
|
357
|
+
|
|
358
|
+
a11y: every one is opaque to assistive tech, so the container MUST carry a
|
|
359
|
+
host-written `role="img"` + `aria-label` with the exact value/meaning
|
|
360
|
+
(rounding to whole cells is presentation-only; keep the figure in the label).
|
|
361
|
+
========================================================================== */
|
|
362
|
+
|
|
363
|
+
/* Waffle / unit chart — part-to-whole as an N×N field of lit dots ("73 of
|
|
364
|
+
100"). Host marks the lit cells with `is-on`. */
|
|
365
|
+
.ui-waffle {
|
|
366
|
+
display: grid;
|
|
367
|
+
gap: var(--waffle-gap, 0.18em);
|
|
368
|
+
grid-template-columns: repeat(var(--waffle-cols, 10), 1fr);
|
|
369
|
+
inline-size: var(--waffle-size, 7em);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.ui-waffle i {
|
|
373
|
+
aspect-ratio: 1;
|
|
374
|
+
background: var(--field-dot);
|
|
375
|
+
border-radius: var(--dotmatrix-dot-radius, 50%);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.ui-waffle i.is-on {
|
|
379
|
+
background: var(--field-dot-accent);
|
|
380
|
+
box-shadow: 0 0 var(--dotmatrix-glow, 0) var(--field-dot-accent);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/* Activity / contribution grid — density-over-time. A flat list of day cells
|
|
384
|
+
flows down each weekday column via grid-auto-flow; intensity is a 5-step
|
|
385
|
+
ramp on `data-level="0..4"` (the host bins data → level). */
|
|
386
|
+
.ui-activity {
|
|
387
|
+
display: grid;
|
|
388
|
+
gap: var(--activity-gap, 0.18em);
|
|
389
|
+
grid-auto-columns: var(--activity-cell, 0.82em);
|
|
390
|
+
grid-auto-flow: column;
|
|
391
|
+
grid-template-rows: repeat(var(--activity-rows, 7), 1fr);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.ui-activity i {
|
|
395
|
+
aspect-ratio: 1;
|
|
396
|
+
background: var(--field-dot);
|
|
397
|
+
border-radius: var(--dotmatrix-dot-radius, 2px);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.ui-activity i[data-level='1'] {
|
|
401
|
+
background: color-mix(in oklab, var(--field-dot-accent) 30%, var(--field-dot));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.ui-activity i[data-level='2'] {
|
|
405
|
+
background: color-mix(in oklab, var(--field-dot-accent) 55%, var(--field-dot));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.ui-activity i[data-level='3'] {
|
|
409
|
+
background: color-mix(in oklab, var(--field-dot-accent) 78%, var(--field-dot));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.ui-activity i[data-level='4'] {
|
|
413
|
+
background: var(--field-dot-accent);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* LED level meter — a column of discrete segments lit to a threshold (signal /
|
|
417
|
+
load / VU). Fills from the bottom; host marks lit segments with `is-on`.
|
|
418
|
+
`--warn`/`--danger` re-point the lit colour for the whole meter when the host
|
|
419
|
+
crosses a threshold (the host owns the threshold, not the leaf). */
|
|
420
|
+
.ui-level {
|
|
421
|
+
block-size: var(--level-height, 4em);
|
|
422
|
+
display: flex;
|
|
423
|
+
flex-direction: column-reverse;
|
|
424
|
+
gap: var(--level-gap, 2px);
|
|
425
|
+
inline-size: var(--level-size, 0.7em);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.ui-level i {
|
|
429
|
+
background: var(--field-dot);
|
|
430
|
+
border-radius: var(--radius-sm);
|
|
431
|
+
flex: 1;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.ui-level i.is-on {
|
|
435
|
+
background: var(--accent);
|
|
436
|
+
box-shadow: 0 0 var(--dotmatrix-glow, 0) var(--accent);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.ui-level--warn i.is-on {
|
|
440
|
+
background: var(--warning);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.ui-level--danger i.is-on {
|
|
444
|
+
background: var(--danger);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* Dot gauge — a 0..1 reading (`--v`) as a ring of dots filling along an arc.
|
|
448
|
+
A conic-gradient sweep (accent up to --v, dim beyond) intersected with a
|
|
449
|
+
donut-ring × dot-pattern mask, so the lit arc reads as discrete dots. */
|
|
450
|
+
.ui-dotgauge {
|
|
451
|
+
--_v: var(--v, 0);
|
|
452
|
+
--_sweep: var(--gauge-sweep, 270deg);
|
|
453
|
+
|
|
454
|
+
aspect-ratio: 1;
|
|
455
|
+
background: conic-gradient(
|
|
456
|
+
from var(--gauge-from, 135deg),
|
|
457
|
+
var(--field-dot-accent) calc(var(--_v) * var(--_sweep)),
|
|
458
|
+
var(--field-dot) calc(var(--_v) * var(--_sweep)) var(--_sweep),
|
|
459
|
+
transparent var(--_sweep)
|
|
460
|
+
);
|
|
461
|
+
border-radius: 50%;
|
|
462
|
+
inline-size: var(--gauge-size, 5em);
|
|
463
|
+
|
|
464
|
+
/* ring (donut) ∩ dot lattice → a ring of dots */
|
|
465
|
+
/* stylelint-disable property-no-vendor-prefix -- Safari still needs the prefixed mask props. */
|
|
466
|
+
-webkit-mask:
|
|
467
|
+
radial-gradient(closest-side, transparent 58%, #000 59%),
|
|
468
|
+
radial-gradient(circle, #000 34%, transparent 36%) 0 0 / var(--gauge-dot, 0.5em) var(--gauge-dot, 0.5em);
|
|
469
|
+
-webkit-mask-composite: source-in;
|
|
470
|
+
/* stylelint-enable property-no-vendor-prefix */
|
|
471
|
+
mask:
|
|
472
|
+
radial-gradient(closest-side, transparent 58%, #000 59%),
|
|
473
|
+
radial-gradient(circle, #000 34%, transparent 36%) 0 0 / var(--gauge-dot, 0.5em) var(--gauge-dot, 0.5em);
|
|
474
|
+
mask-composite: intersect;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/* Readout — a row of dot-matrix glyphs forming a big numeric (renderReadout).
|
|
478
|
+
The glyphs are decorative; the row carries role=img + the value as its name. */
|
|
479
|
+
.ui-readout {
|
|
480
|
+
align-items: flex-end;
|
|
481
|
+
display: inline-flex;
|
|
482
|
+
gap: var(--readout-gap, 0.12em);
|
|
355
483
|
}
|
|
356
484
|
|
|
357
|
-
.ui-
|
|
358
|
-
|
|
485
|
+
.ui-readout__spacer {
|
|
486
|
+
display: inline-block;
|
|
487
|
+
inline-size: var(--readout-space, 0.5em);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/* Halftone — render host content (an <img> or a box with its own background)
|
|
491
|
+
through a dot lattice so a thumbnail/cover takes on the dot look. A style
|
|
492
|
+
filter, NOT a data viz: the dots are a fixed lattice, not value-modulated. */
|
|
493
|
+
.ui-halftone {
|
|
494
|
+
/* stylelint-disable-next-line property-no-vendor-prefix -- Safari still needs the prefixed mask property. */
|
|
495
|
+
-webkit-mask: radial-gradient(circle, #000 var(--halftone-dot, 38%), transparent calc(var(--halftone-dot, 38%) + 1%)) 0 0 /
|
|
496
|
+
var(--halftone-gap, 0.5em) var(--halftone-gap, 0.5em);
|
|
497
|
+
mask: radial-gradient(circle, #000 var(--halftone-dot, 38%), transparent calc(var(--halftone-dot, 38%) + 1%)) 0 0 /
|
|
498
|
+
var(--halftone-gap, 0.5em) var(--halftone-gap, 0.5em);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/* Density wrapper — make any dot surface inside respond to the CARD it sits in,
|
|
502
|
+
not the viewport, so the same component reads well in a wide hero and a
|
|
503
|
+
narrow tile. Opt-in: wrap the surface and the breakpoint below densifies it. */
|
|
504
|
+
.ui-dotfit {
|
|
505
|
+
container: dotfit / inline-size;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
@container dotfit (width < 18rem) {
|
|
509
|
+
.ui-dotfit .ui-dotgrid {
|
|
510
|
+
--dot-gap: 8px;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.ui-dotfit .ui-activity {
|
|
514
|
+
--activity-cell: 0.6em;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.ui-dotfit .ui-waffle {
|
|
518
|
+
--waffle-size: 5em;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* Matrix-reveal wrapper — content wipes in left→right on .is-in. Gate the
|
|
523
|
+
clipped from-state on `scripting: enabled`: with JS off, `.is-in` is never
|
|
524
|
+
toggled, so without the gate the content stays permanently clipped away and
|
|
525
|
+
invisible to every no-JS/static/print reader. Same graceful default as
|
|
526
|
+
`.ui-reveal`. (component audit C12.) */
|
|
527
|
+
@media (scripting: enabled) {
|
|
528
|
+
.ui-matrix {
|
|
529
|
+
clip-path: inset(0 100% 0 0);
|
|
530
|
+
transition: clip-path var(--duration-slow) var(--ease-out);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.ui-matrix.is-in {
|
|
534
|
+
clip-path: inset(0 0 0 0);
|
|
535
|
+
}
|
|
359
536
|
}
|
|
360
537
|
|
|
361
538
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -404,6 +581,30 @@
|
|
|
404
581
|
background: ButtonText;
|
|
405
582
|
}
|
|
406
583
|
|
|
584
|
+
/* Brand/live dots aren't status tones, but they still encode meaning via
|
|
585
|
+
background-color alone, which HCM flattens. Keep them on a distinct,
|
|
586
|
+
opted-out system colour for completeness. (audit C31.) */
|
|
587
|
+
.ui-dot--accent,
|
|
588
|
+
.ui-dot--live {
|
|
589
|
+
forced-color-adjust: none;
|
|
590
|
+
background: LinkText;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
.ui-dot--live::after {
|
|
594
|
+
border-color: LinkText;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/* The masked one-node icon paints `background: currentcolor` through an SVG
|
|
598
|
+
mask. Under HCM, forced-color-adjust:auto can drop the mask fill so the
|
|
599
|
+
glyph vanishes (white-on-white) — yet .ui-icon is the recommended
|
|
600
|
+
icon-at-scale path AND backs .ui-legend__symbol, and the print block
|
|
601
|
+
already special-cases it. Opt out and pin the fill to the system text
|
|
602
|
+
colour so the glyph stays visible. (audit C1.) */
|
|
603
|
+
.ui-icon {
|
|
604
|
+
forced-color-adjust: none;
|
|
605
|
+
background: CanvasText;
|
|
606
|
+
}
|
|
607
|
+
|
|
407
608
|
.ui-dotmatrix__cell--hot {
|
|
408
609
|
forced-color-adjust: none;
|
|
409
610
|
background: Highlight;
|
|
@@ -413,16 +614,52 @@
|
|
|
413
614
|
forced-color-adjust: none;
|
|
414
615
|
background: LinkText;
|
|
415
616
|
}
|
|
617
|
+
|
|
618
|
+
/* The data-bound dot surfaces encode their value via background-color, which
|
|
619
|
+
HCM flattens. Opt out and pin the lit state to a system colour so the
|
|
620
|
+
reading survives. The activity ramp's 5 steps can't all stay distinct under
|
|
621
|
+
HCM — collapse to present (CanvasText) vs absent (the unstyled track). */
|
|
622
|
+
.ui-waffle i.is-on,
|
|
623
|
+
.ui-level i.is-on {
|
|
624
|
+
forced-color-adjust: none;
|
|
625
|
+
background: LinkText;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
.ui-level--warn i.is-on {
|
|
629
|
+
background: Mark;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.ui-level--danger i.is-on {
|
|
633
|
+
background: Highlight;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
.ui-activity i[data-level='1'],
|
|
637
|
+
.ui-activity i[data-level='2'],
|
|
638
|
+
.ui-activity i[data-level='3'],
|
|
639
|
+
.ui-activity i[data-level='4'] {
|
|
640
|
+
forced-color-adjust: none;
|
|
641
|
+
background: CanvasText;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/* The gauge is a masked gradient; keep its paint rather than let HCM drop it. */
|
|
645
|
+
.ui-dotgauge {
|
|
646
|
+
forced-color-adjust: none;
|
|
647
|
+
}
|
|
416
648
|
}
|
|
417
649
|
|
|
418
650
|
/* Print: the dot surfaces carry data (heatmap cells, the segmented meter, the
|
|
419
|
-
status dot, the masked glyph
|
|
420
|
-
"economy" default that drops
|
|
651
|
+
status dot, the masked glyph, the waffle/activity/level/gauge readings), so
|
|
652
|
+
their painted fills must survive the print "economy" default that drops
|
|
653
|
+
backgrounds. */
|
|
421
654
|
@media print {
|
|
422
655
|
.ui-dotmatrix__cell,
|
|
423
656
|
.ui-dotbar i,
|
|
424
657
|
.ui-dot,
|
|
425
|
-
.ui-icon
|
|
658
|
+
.ui-icon,
|
|
659
|
+
.ui-waffle i,
|
|
660
|
+
.ui-activity i,
|
|
661
|
+
.ui-level i,
|
|
662
|
+
.ui-dotgauge {
|
|
426
663
|
-webkit-print-color-adjust: exact;
|
|
427
664
|
print-color-adjust: exact;
|
|
428
665
|
}
|
package/css/feedback.css
CHANGED
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
never a fill. Nothing-flat, hairline, sharp.
|
|
5
5
|
========================================================================== */
|
|
6
6
|
|
|
7
|
+
/* `--value` drives the progress/meter fill width: a UNITLESS number 0–100,
|
|
8
|
+
never a percentage. Registering it as `<number>` makes that contract real —
|
|
9
|
+
a stray `--value: 50%` is invalid against the typed syntax and falls back to
|
|
10
|
+
the initial `0` (empty bar) instead of poisoning the `clamp()` and painting a
|
|
11
|
+
FULL bar (the old failure mode). It inherits so the value set on the host
|
|
12
|
+
`.ui-meter` / `.ui-progress` cascades to the inner `__fill`/`__bar`.
|
|
13
|
+
(component audit C8.) */
|
|
14
|
+
@property --value {
|
|
15
|
+
syntax: '<number>';
|
|
16
|
+
inherits: true;
|
|
17
|
+
initial-value: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
7
20
|
/* --- Alert / callout — inline, dismissible-compatible --- */
|
|
8
21
|
|
|
9
22
|
.ui-alert {
|
|
@@ -50,7 +63,7 @@
|
|
|
50
63
|
margin: 0;
|
|
51
64
|
}
|
|
52
65
|
|
|
53
|
-
.ui-
|
|
66
|
+
.ui-alert__close {
|
|
54
67
|
background: transparent;
|
|
55
68
|
border: 0;
|
|
56
69
|
color: var(--text-dim);
|
|
@@ -62,7 +75,7 @@
|
|
|
62
75
|
padding: 0.1rem 0.3rem;
|
|
63
76
|
}
|
|
64
77
|
|
|
65
|
-
.ui-alert:has(.ui-
|
|
78
|
+
.ui-alert:has(.ui-alert__close) {
|
|
66
79
|
grid-template-columns: auto 1fr auto;
|
|
67
80
|
}
|
|
68
81
|
|
|
@@ -107,7 +120,7 @@
|
|
|
107
120
|
}
|
|
108
121
|
|
|
109
122
|
@media (hover: hover) {
|
|
110
|
-
.ui-
|
|
123
|
+
.ui-alert__close:hover {
|
|
111
124
|
color: var(--text);
|
|
112
125
|
}
|
|
113
126
|
}
|
|
@@ -216,7 +229,7 @@
|
|
|
216
229
|
tap-target floor (2.9rem, as in primitives.css / forms.css) without changing
|
|
217
230
|
the desktop glyph size. The box is centred so the glyph stays put. */
|
|
218
231
|
@media (pointer: coarse) {
|
|
219
|
-
.ui-
|
|
232
|
+
.ui-alert__close,
|
|
220
233
|
.ui-toast__close {
|
|
221
234
|
display: inline-grid;
|
|
222
235
|
place-items: center;
|
|
@@ -224,7 +237,7 @@
|
|
|
224
237
|
min-inline-size: 2.9rem;
|
|
225
238
|
}
|
|
226
239
|
|
|
227
|
-
.ui-
|
|
240
|
+
.ui-alert__close {
|
|
228
241
|
padding: 0;
|
|
229
242
|
}
|
|
230
243
|
}
|
|
@@ -453,6 +466,18 @@
|
|
|
453
466
|
@media (prefers-reduced-motion: reduce) {
|
|
454
467
|
.ui-progress--indeterminate .ui-progress__bar {
|
|
455
468
|
animation-duration: 0.01ms;
|
|
469
|
+
|
|
470
|
+
/* A still, solid, full-width bar reads as "100% complete" — the opposite of
|
|
471
|
+
indeterminate. Fall back to a static diagonal hatch that fills the track
|
|
472
|
+
(so it's clearly active) but doesn't read as done. AT is covered via
|
|
473
|
+
aria-busy. (audit C26.) */
|
|
474
|
+
background: repeating-linear-gradient(
|
|
475
|
+
-45deg,
|
|
476
|
+
var(--accent) 0,
|
|
477
|
+
var(--accent) 0.3rem,
|
|
478
|
+
color-mix(in srgb, var(--accent) 35%, transparent) 0.3rem,
|
|
479
|
+
color-mix(in srgb, var(--accent) 35%, transparent) 0.6rem
|
|
480
|
+
);
|
|
456
481
|
inset-inline-start: 0;
|
|
457
482
|
inline-size: 100%;
|
|
458
483
|
}
|
|
@@ -467,7 +492,9 @@
|
|
|
467
492
|
(task progress, can be indeterminate), a meter shows a measured static
|
|
468
493
|
value for data display (coverage, capacity, a KPI against a target).
|
|
469
494
|
Drive the fill with the same --value knob as progress; tone the fill by
|
|
470
|
-
threshold. Author role="meter" + aria-valuenow/min/max for AT
|
|
495
|
+
threshold. Author role="meter" + aria-valuenow/min/max for AT — but role=meter
|
|
496
|
+
has uneven AT support, so keep the visible .ui-meter__label/__value (they are
|
|
497
|
+
the real channel, not just decoration). (component audit C25.) --- */
|
|
471
498
|
|
|
472
499
|
.ui-meter {
|
|
473
500
|
background: var(--panel-soft);
|
|
@@ -534,7 +561,12 @@
|
|
|
534
561
|
font-size: var(--text-xs);
|
|
535
562
|
gap: 0.5rem;
|
|
536
563
|
letter-spacing: var(--tracking-wide);
|
|
537
|
-
|
|
564
|
+
|
|
565
|
+
/* Prefer the natural one-line width, but never wider than the container: a
|
|
566
|
+
long step label at `max-content` couldn't shrink and overflowed the page on
|
|
567
|
+
narrow viewports (tabs scroll; steps didn't). Capping at 100% lets an
|
|
568
|
+
over-long label wrap instead of overflowing. (component audit C18.) */
|
|
569
|
+
min-inline-size: min(100%, max-content);
|
|
538
570
|
text-transform: uppercase;
|
|
539
571
|
}
|
|
540
572
|
|
package/css/forms.css
CHANGED
|
@@ -77,6 +77,16 @@
|
|
|
77
77
|
opacity: 0.5;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/* Read-only is editable-looking but not editable; give it a distinct, quieter
|
|
81
|
+
cue (muted fill + default cursor) so it doesn't read as a live field. Not
|
|
82
|
+
disabled — value still submits and the field stays focusable/selectable.
|
|
83
|
+
(component audit C28.) */
|
|
84
|
+
.ui-input:read-only:not(:disabled),
|
|
85
|
+
.ui-textarea:read-only:not(:disabled) {
|
|
86
|
+
background: var(--panel-soft);
|
|
87
|
+
cursor: default;
|
|
88
|
+
}
|
|
89
|
+
|
|
80
90
|
/* Disabled affordance parity. The text inputs above style :disabled directly;
|
|
81
91
|
the controls that WRAP a native input (switch/check/segmented) showed no
|
|
82
92
|
disabled cue and their label kept cursor:pointer — a lie. Mirror the cue via
|
|
@@ -94,6 +104,8 @@
|
|
|
94
104
|
/* Keep autofilled fields on-theme — the UA's yellow fill otherwise paints over
|
|
95
105
|
the monochrome surface and breaks the contrast story. (forms review C24.) */
|
|
96
106
|
.ui-input:autofill,
|
|
107
|
+
.ui-select:autofill,
|
|
108
|
+
.ui-textarea:autofill,
|
|
97
109
|
.ui-search input:autofill {
|
|
98
110
|
-webkit-text-fill-color: var(--text);
|
|
99
111
|
box-shadow: inset 0 0 0 100rem var(--bg-elevated);
|
|
@@ -106,6 +118,58 @@
|
|
|
106
118
|
border-color: var(--danger);
|
|
107
119
|
}
|
|
108
120
|
|
|
121
|
+
/* Wrapper controls (switch / check / segmented) hide their native <input>, so
|
|
122
|
+
the `[aria-invalid]` the validator sets on it paints nothing — a sighted,
|
|
123
|
+
non-AT user couldn't see the error (WCAG 1.4.1). Mirror the invalid cue onto
|
|
124
|
+
the visible surface via :has(), the same way the disabled cue is mirrored.
|
|
125
|
+
(component audit C7.) */
|
|
126
|
+
.ui-check:has(input[aria-invalid='true']) input {
|
|
127
|
+
outline: 2px solid var(--danger);
|
|
128
|
+
outline-offset: 1px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.ui-switch:has(input[aria-invalid='true']) .ui-switch__track {
|
|
132
|
+
border-color: var(--danger);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.ui-segmented:has(input[aria-invalid='true']) {
|
|
136
|
+
border-color: var(--danger);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Forced colours flatten `var(--danger)` to a system colour identical to the
|
|
140
|
+
resting border, so the invalid border becomes indistinguishable from a valid
|
|
141
|
+
one and sighted HCM users lose the only error cue (WCAG 1.4.1). The switch got
|
|
142
|
+
a forced-colors block; the error family did not. Re-assert the state on a
|
|
143
|
+
NON-colour channel — a thicker, doubled border — that survives HCM, and prefix
|
|
144
|
+
the error hint with a glyph so the message itself carries the error.
|
|
145
|
+
(component audit C5.) */
|
|
146
|
+
@media (forced-colors: active) {
|
|
147
|
+
.ui-input[aria-invalid='true'],
|
|
148
|
+
.ui-select[aria-invalid='true'],
|
|
149
|
+
.ui-textarea[aria-invalid='true'] {
|
|
150
|
+
border-style: double;
|
|
151
|
+
border-width: 3px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* Same NON-colour re-assertion for the wrapper controls (C7): a thicker,
|
|
155
|
+
doubled outline/border survives the HCM colour flattening. */
|
|
156
|
+
.ui-check:has(input[aria-invalid='true']) input {
|
|
157
|
+
outline-width: 3px;
|
|
158
|
+
outline-style: double;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.ui-switch:has(input[aria-invalid='true']) .ui-switch__track,
|
|
162
|
+
.ui-segmented:has(input[aria-invalid='true']) {
|
|
163
|
+
border-style: double;
|
|
164
|
+
border-width: 3px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.ui-hint--error::before,
|
|
168
|
+
.ui-error-summary__title::before {
|
|
169
|
+
content: '⚠ ';
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
109
173
|
.ui-hint {
|
|
110
174
|
color: var(--text-dim);
|
|
111
175
|
font-size: var(--text-2xs);
|
|
@@ -139,14 +203,18 @@
|
|
|
139
203
|
border-end-end-radius: var(--radius-md);
|
|
140
204
|
}
|
|
141
205
|
|
|
142
|
-
.ui-input-group > .ui-input:focus-visible
|
|
143
|
-
|
|
206
|
+
.ui-input-group > .ui-input:focus-visible,
|
|
207
|
+
.ui-input-group > .ui-select:focus-visible {
|
|
208
|
+
z-index: 1; /* keep the focus ring above the adjacent addon border (select too — audit C29) */
|
|
144
209
|
}
|
|
145
210
|
|
|
146
211
|
.ui-input-group__addon {
|
|
147
212
|
align-items: center;
|
|
148
213
|
background: var(--panel-soft);
|
|
149
|
-
|
|
214
|
+
|
|
215
|
+
/* Match the wrapped control's `--line-strong` border so the prefix/suffix seam
|
|
216
|
+
isn't a fainter cap than the field it abuts. (component audit C33.) */
|
|
217
|
+
border: 1px solid var(--line-strong);
|
|
150
218
|
color: var(--text-dim);
|
|
151
219
|
display: flex;
|
|
152
220
|
font-size: var(--text-sm);
|
package/css/legend.css
CHANGED
|
@@ -90,10 +90,13 @@
|
|
|
90
90
|
inline-size: 1.1rem;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
/* Glyph/symbol swatch — fill an `.ui-icon` mask with the series colour.
|
|
93
|
+
/* Glyph/symbol swatch — fill an `.ui-icon` mask with the series colour. Match the
|
|
94
|
+
__swatch fallback chain exactly (--chart-color → --chart-1 → --accent): without
|
|
95
|
+
the --chart-1 layer, a symbol and a swatch for the SAME series diverge to two
|
|
96
|
+
colours whenever a theme overrides --chart-1. (audit C22.) */
|
|
94
97
|
.ui-legend__symbol {
|
|
95
98
|
block-size: 0.95rem;
|
|
96
|
-
color: var(--chart-color, var(--accent));
|
|
99
|
+
color: var(--chart-color, var(--chart-1, var(--accent)));
|
|
97
100
|
flex: 0 0 auto;
|
|
98
101
|
inline-size: 0.95rem;
|
|
99
102
|
}
|
package/css/motion.css
CHANGED
|
@@ -52,6 +52,28 @@
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/* Scroll-driven reveal (`.ui-scroll-reveal`). Unlike the time-based uiRise, a
|
|
56
|
+
scroll timeline can FREEZE part-way: an element near the document bottom can't
|
|
57
|
+
scroll far enough to finish its range, so a fade-to-opacity-1 would strand the
|
|
58
|
+
content permanently transparent. Reach full opacity early (35%) and hold it,
|
|
59
|
+
so even a partially-driven reveal is fully legible — only the last few px of
|
|
60
|
+
the rise are left unfinished. (component audit C9.) */
|
|
61
|
+
@keyframes uiScrollReveal {
|
|
62
|
+
0% {
|
|
63
|
+
opacity: 0;
|
|
64
|
+
transform: translateY(10px);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
35% {
|
|
68
|
+
opacity: 1;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
100% {
|
|
72
|
+
opacity: 1;
|
|
73
|
+
transform: translateY(0);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
55
77
|
@keyframes uiDotIn {
|
|
56
78
|
0% {
|
|
57
79
|
opacity: 0;
|
|
@@ -158,10 +180,13 @@
|
|
|
158
180
|
animation: uiMatrixReveal var(--duration-slow) var(--ease-out) both;
|
|
159
181
|
}
|
|
160
182
|
|
|
161
|
-
/* Stagger children: set --i on each child (or use nth-child cap).
|
|
183
|
+
/* Stagger children: set --i on each child (or use nth-child cap). Cap the manual
|
|
184
|
+
--i path at index 6 (360ms) to match the .ui-stagger--auto ceiling — a long
|
|
185
|
+
list with --i:30 would otherwise hold the last child at opacity:0 for 1.8s
|
|
186
|
+
before popping in. (audit C32.) */
|
|
162
187
|
.ui-stagger > * {
|
|
163
188
|
animation: uiRise var(--duration-slow) var(--ease-spring) both;
|
|
164
|
-
animation-delay: calc(var(--i, 0) * 60ms);
|
|
189
|
+
animation-delay: calc(min(var(--i, 0), 6) * 60ms);
|
|
165
190
|
}
|
|
166
191
|
|
|
167
192
|
.ui-stagger--auto > *:nth-child(1) {
|
|
@@ -193,11 +218,17 @@
|
|
|
193
218
|
}
|
|
194
219
|
|
|
195
220
|
/* Reveal-on-scroll: add .ui-reveal, then toggle .is-visible (e.g. from an
|
|
196
|
-
IntersectionObserver you own) to play it in.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
on `scripting: enabled
|
|
200
|
-
|
|
221
|
+
IntersectionObserver you own) to play it in.
|
|
222
|
+
|
|
223
|
+
⚠ `.ui-reveal` requires you to wire that observer. The hidden initial state is
|
|
224
|
+
gated only on `scripting: enabled` — it can't detect whether an observer is
|
|
225
|
+
actually attached, so merely loading bronto.js (or any script) hides every
|
|
226
|
+
`.ui-reveal` until YOUR code adds `.is-visible`. If you add the class and never
|
|
227
|
+
wire the toggle, the content stays invisible. For an LLM-authored or no-build
|
|
228
|
+
report, DON'T use `.ui-reveal` — use `.ui-scroll-reveal` below (scroll-driven,
|
|
229
|
+
CSS-only, no observer, and it can never strand content invisible). With
|
|
230
|
+
scripting OFF, `.ui-reveal` content is fully visible and never silently hidden
|
|
231
|
+
behind a script that will never run. */
|
|
201
232
|
@media (prefers-reduced-motion: no-preference) and (scripting: enabled) {
|
|
202
233
|
.ui-reveal {
|
|
203
234
|
opacity: 0;
|
|
@@ -246,10 +277,12 @@
|
|
|
246
277
|
|
|
247
278
|
/* --- Scroll-driven (progressive enhancement) — the scroll/view timeline
|
|
248
279
|
IS the engine, no JS. Everything is gated on `@supports
|
|
249
|
-
(animation-timeline: …)` so
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
280
|
+
(animation-timeline: …)` so an engine without it keeps the static end state
|
|
281
|
+
(as of 2026 Chrome 115+ and Safari 18.4+ drive scroll()/view(); Firefox is
|
|
282
|
+
the remaining holdout, still graceful via the @supports gate), and on
|
|
283
|
+
`prefers-reduced-motion: no-preference` (a scroll timeline ignores
|
|
284
|
+
animation-duration, so the global reduced-motion reset below can't neutralise
|
|
285
|
+
it — it must be gated here). --- */
|
|
253
286
|
|
|
254
287
|
/* Reading-progress bar. Fixed hairline that fills with document scroll;
|
|
255
288
|
unsupported → a static, empty (scaleX(0)) bar. Pair with role="progressbar"
|
|
@@ -270,6 +303,30 @@
|
|
|
270
303
|
transform-origin: 100% 50%;
|
|
271
304
|
}
|
|
272
305
|
|
|
306
|
+
/* Forced colours (HCM) flatten the spinner's accent top-border and its --line
|
|
307
|
+
rest to one system colour, so the ring looks uniform with no visible sweep,
|
|
308
|
+
and the scroll-progress bar can vanish into the canvas. Re-assert distinct
|
|
309
|
+
system colours so each keeps a visible channel; AT is already covered via
|
|
310
|
+
aria-busy / role="status" / role="progressbar". (component audit C35.) */
|
|
311
|
+
@media (forced-colors: active) {
|
|
312
|
+
.ui-spinner {
|
|
313
|
+
border-color: CanvasText;
|
|
314
|
+
border-block-start-color: Highlight;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.ui-scroll-progress {
|
|
318
|
+
background: Highlight;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* HCM strips the shimmer gradient, leaving an invisible box where a loading
|
|
322
|
+
placeholder should be. Give it a system-colour border so the skeleton
|
|
323
|
+
still reads as present. Same decorative-loading family as the spinner +
|
|
324
|
+
scroll-progress above. (audit C15.) */
|
|
325
|
+
.ui-skeleton {
|
|
326
|
+
border: 1px solid CanvasText;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
273
330
|
@supports (animation-timeline: scroll()) {
|
|
274
331
|
@media (prefers-reduced-motion: no-preference) {
|
|
275
332
|
.ui-scroll-progress {
|
|
@@ -284,13 +341,21 @@
|
|
|
284
341
|
|
|
285
342
|
/* Reveal-on-scroll with no JS and no IntersectionObserver: the element
|
|
286
343
|
rises + fades as it scrolls into view. Unsupported → fully visible
|
|
287
|
-
(the
|
|
344
|
+
(the keyframe end state), same graceful default as `.ui-reveal`.
|
|
345
|
+
|
|
346
|
+
The range is `entry 0% entry 100%`, NOT the old `cover 40%`: `cover` only
|
|
347
|
+
completes once the element has scrolled most of the way THROUGH the viewport,
|
|
348
|
+
which an element near the document bottom can never do — it froze part-way,
|
|
349
|
+
leaving a conclusion section permanently semi-transparent (C9). `entry`
|
|
350
|
+
completes the moment the element is fully in view, which any scroll-to-bottom
|
|
351
|
+
reaches. Paired with the early-opacity uiScrollReveal keyframe, an element
|
|
352
|
+
taller than the viewport is legible even if its range never fully completes. */
|
|
288
353
|
@supports (animation-timeline: view()) {
|
|
289
354
|
@media (prefers-reduced-motion: no-preference) {
|
|
290
355
|
.ui-scroll-reveal {
|
|
291
|
-
animation:
|
|
356
|
+
animation: uiScrollReveal linear both;
|
|
292
357
|
animation-timeline: view();
|
|
293
|
-
animation-range: entry 0%
|
|
358
|
+
animation-range: entry 0% entry 100%;
|
|
294
359
|
}
|
|
295
360
|
}
|
|
296
361
|
}
|