@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/overlay.css
CHANGED
|
@@ -78,14 +78,25 @@ dialog.ui-modal[open]::backdrop {
|
|
|
78
78
|
|
|
79
79
|
/* Controlled (non-<dialog>) usage. A portal/React modal that can't be a
|
|
80
80
|
native <dialog> wears the same skin + open layout via `.is-open`.
|
|
81
|
-
Backdrop
|
|
82
|
-
|
|
81
|
+
Backdrop and top-layer stacking are then the consumer's responsibility
|
|
82
|
+
(the native <dialog> path gets them free); focus-trapping no longer is —
|
|
83
|
+
mark the overlay `data-bronto-modal` and run `initModal` for an
|
|
84
|
+
inert-based trap + focus-return + Escape signal. */
|
|
83
85
|
.ui-modal.is-open {
|
|
84
86
|
animation: uiToastIn var(--duration-base) var(--ease-spring) both;
|
|
85
87
|
display: grid;
|
|
86
88
|
grid-template-rows: auto 1fr auto;
|
|
87
89
|
}
|
|
88
90
|
|
|
91
|
+
/* Background scroll-lock. A native <dialog> top layer does NOT stop the page
|
|
92
|
+
behind it scrolling, and the no-JS native path can't lock it in script — so
|
|
93
|
+
lock it in CSS, for both the native [open] dialog and the controlled .is-open
|
|
94
|
+
overlay (incl. the drawer modifier, same base). (component audit C13.) */
|
|
95
|
+
html:has(dialog.ui-modal[open]),
|
|
96
|
+
html:has(.ui-modal.is-open) {
|
|
97
|
+
overflow: hidden;
|
|
98
|
+
}
|
|
99
|
+
|
|
89
100
|
.ui-modal__head {
|
|
90
101
|
align-items: flex-start;
|
|
91
102
|
border-block-end: 1px solid var(--line);
|
|
@@ -150,6 +161,21 @@ dialog.ui-modal[open]::backdrop {
|
|
|
150
161
|
}
|
|
151
162
|
}
|
|
152
163
|
|
|
164
|
+
/* Comfortable hit target on coarse pointers — this bespoke close button measured
|
|
165
|
+
~26px on touch, below the 2.9rem floor the rest of the button family meets
|
|
166
|
+
(WCAG 2.5.8). Scoped to coarse so the fine-pointer (mouse) rendering, which
|
|
167
|
+
already clears 24×24, is unchanged; centre the glyph in the enlarged box.
|
|
168
|
+
(audit C3.) */
|
|
169
|
+
@media (pointer: coarse) {
|
|
170
|
+
.ui-modal__close {
|
|
171
|
+
align-items: center;
|
|
172
|
+
display: inline-flex;
|
|
173
|
+
justify-content: center;
|
|
174
|
+
min-block-size: 2.9rem;
|
|
175
|
+
min-inline-size: 2.9rem;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
153
179
|
/* --- Lightbox — a full-viewport <dialog> wrapping a .ui-carousel. The
|
|
154
180
|
native <dialog> brings the top layer, focus-trap and Escape; initDialog
|
|
155
181
|
adds open (`data-bronto-open`) + focus-return; initCarousel drives the
|
|
@@ -284,6 +310,25 @@ dialog.ui-modal[open]::backdrop {
|
|
|
284
310
|
opacity: 1;
|
|
285
311
|
}
|
|
286
312
|
|
|
313
|
+
/* Menu hover/focus paints var(--bg-accent), which HCM strips, so the hovered row
|
|
314
|
+
is indistinguishable from its siblings (focus keeps the UA ring; hover has no
|
|
315
|
+
such fallback). Re-assert a system Highlight like the combobox/command rows,
|
|
316
|
+
for parity with those sibling surfaces. (component audit C22.) */
|
|
317
|
+
@media (forced-colors: active) {
|
|
318
|
+
.ui-menu__item:hover,
|
|
319
|
+
.ui-menu__item:focus-visible {
|
|
320
|
+
forced-color-adjust: none;
|
|
321
|
+
background: Highlight;
|
|
322
|
+
color: HighlightText;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.ui-menu__item:hover::before,
|
|
326
|
+
.ui-menu__item:focus-visible::before {
|
|
327
|
+
background: HighlightText;
|
|
328
|
+
opacity: 1;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
287
332
|
/* --- Combobox: an input with a filtered listbox popup (APG pattern,
|
|
288
333
|
wired by initCombobox). Reuses the menu surface tokens. --- */
|
|
289
334
|
.ui-combobox {
|
|
@@ -337,6 +382,18 @@ dialog.ui-modal[open]::backdrop {
|
|
|
337
382
|
color: var(--text);
|
|
338
383
|
}
|
|
339
384
|
|
|
385
|
+
/* The active option is tracked by aria-activedescendant — it is NEVER DOM-focused,
|
|
386
|
+
so it gets no UA focus ring under HCM, and its only cue (--bg-accent) is stripped.
|
|
387
|
+
Re-assert a system Highlight like the command palette does. (audit C2.) */
|
|
388
|
+
@media (forced-colors: active) {
|
|
389
|
+
.ui-combobox__option.is-active,
|
|
390
|
+
.ui-combobox__option[aria-selected='true'] {
|
|
391
|
+
forced-color-adjust: none;
|
|
392
|
+
background: Highlight;
|
|
393
|
+
color: HighlightText;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
340
397
|
.ui-combobox__empty {
|
|
341
398
|
color: var(--text-dim);
|
|
342
399
|
font-size: var(--text-2xs);
|
package/css/primitives.css
CHANGED
|
@@ -71,12 +71,15 @@
|
|
|
71
71
|
padding-inline: var(--center-gutter, var(--space-md));
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
/* Intrinsic aspect-ratio box; the media child fills it.
|
|
74
|
+
/* Intrinsic aspect-ratio box; the media child fills it. The contract is ONE
|
|
75
|
+
child (a single <img>/<video>/<iframe>). Scope the fill to :first-child rather
|
|
76
|
+
than every child: a second child would otherwise be forced to 100%/100% +
|
|
77
|
+
object-fit and stack on top, silently breaking the ratio. (audit C34.) */
|
|
75
78
|
.ui-ratio {
|
|
76
79
|
aspect-ratio: var(--ratio, 16 / 9);
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
.ui-ratio >
|
|
82
|
+
.ui-ratio > :first-child {
|
|
80
83
|
block-size: 100%;
|
|
81
84
|
inline-size: 100%;
|
|
82
85
|
object-fit: cover;
|
|
@@ -94,7 +97,11 @@
|
|
|
94
97
|
container: bronto / inline-size;
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
|
|
100
|
+
/* Logical `max-inline-size`, not physical `max-width`: the container is typed
|
|
101
|
+
`inline-size`, so the inline axis is the one actually tracked — the logical
|
|
102
|
+
query matches it in any writing mode (a physical `width` query silently
|
|
103
|
+
misses in a vertical WM). (component audit C34.) */
|
|
104
|
+
@container bronto (max-inline-size: 34rem) {
|
|
98
105
|
.ui-grid {
|
|
99
106
|
--grid-min: 100%;
|
|
100
107
|
}
|
|
@@ -168,7 +175,7 @@
|
|
|
168
175
|
/* Inside an opt-in .ui-cq container, collapse to one column when the
|
|
169
176
|
container (not the viewport) is narrow — keeps tiles usable in a slim
|
|
170
177
|
panel. Inert without .ui-cq, so baselines are unaffected. */
|
|
171
|
-
@container bronto (max-
|
|
178
|
+
@container bronto (max-inline-size: 30rem) {
|
|
172
179
|
.ui-statgrid,
|
|
173
180
|
.ui-app-metrics {
|
|
174
181
|
grid-template-columns: 1fr;
|
|
@@ -182,6 +189,12 @@
|
|
|
182
189
|
border-radius: var(--radius-md);
|
|
183
190
|
display: grid;
|
|
184
191
|
gap: 0.4rem;
|
|
192
|
+
|
|
193
|
+
/* These tiles hold IDs / hashes / big numbers — the unbreakable-token case is
|
|
194
|
+
the common one. A grid item defaults to min-inline-size:auto, so a long
|
|
195
|
+
value pushes the whole statgrid track wider; allow the tile to shrink so the
|
|
196
|
+
value can wrap instead (paired with overflow-wrap on __value). (audit C5.) */
|
|
197
|
+
min-inline-size: 0;
|
|
185
198
|
padding: var(--space-md);
|
|
186
199
|
}
|
|
187
200
|
|
|
@@ -203,6 +216,7 @@
|
|
|
203
216
|
font-weight: var(--display-weight-strong);
|
|
204
217
|
letter-spacing: 0.02em;
|
|
205
218
|
line-height: 1.05;
|
|
219
|
+
overflow-wrap: anywhere; /* break an unspaced ID/hash rather than overflow (audit C5) */
|
|
206
220
|
}
|
|
207
221
|
|
|
208
222
|
.ui-stat__delta,
|
|
@@ -243,6 +257,10 @@
|
|
|
243
257
|
share one P&L vocabulary. Token-identical to the table's is-num/
|
|
244
258
|
is-pos/is-neg (which stay table-local). */
|
|
245
259
|
.ui-num {
|
|
260
|
+
/* inline-block so `text-align: end` actually applies: on a bare inline element
|
|
261
|
+
it computes but never paints (the box is shrink-wrapped), so an author who
|
|
262
|
+
followed the docs to right-align an inline figure saw no effect. (audit C17.) */
|
|
263
|
+
display: inline-block;
|
|
246
264
|
font-variant-numeric: tabular-nums;
|
|
247
265
|
text-align: end;
|
|
248
266
|
}
|
|
@@ -413,13 +431,26 @@
|
|
|
413
431
|
|
|
414
432
|
/* aria-disabled keeps the element in the a11y tree but the browser does NOT
|
|
415
433
|
block activation (a real <a class="ui-button" aria-disabled> still navigates,
|
|
416
|
-
a <button> still fires).
|
|
417
|
-
|
|
434
|
+
a <button> still fires). `pointer-events: none` kills POINTER activation only
|
|
435
|
+
— a focused control still fires on Enter/Space, which CSS cannot intercept. So
|
|
436
|
+
this looks-dead state is NOT keyboard-inert: for that, prefer native
|
|
437
|
+
`<button disabled>`, run `initDisabledGuard()` (it intercepts Enter/Space on
|
|
438
|
+
aria-disabled controls), or add `tabindex="-1"` (and drop `href` on a link).
|
|
439
|
+
See docs/usage.md "Disabled vs aria-disabled". (a11y review C3 / audit C4;
|
|
440
|
+
native :disabled already inert.) */
|
|
418
441
|
.ui-button[aria-disabled='true'],
|
|
419
442
|
.ui-link[aria-disabled='true'] {
|
|
420
443
|
pointer-events: none;
|
|
421
444
|
}
|
|
422
445
|
|
|
446
|
+
/* The button family dims + shows not-allowed via its `:disabled` rule above, but
|
|
447
|
+
a disabled LINK got only pointer-events:none — it looked fully live. Give it
|
|
448
|
+
the same visual disabled cue. (component audit C30.) */
|
|
449
|
+
.ui-link[aria-disabled='true'] {
|
|
450
|
+
cursor: not-allowed;
|
|
451
|
+
opacity: 0.45;
|
|
452
|
+
}
|
|
453
|
+
|
|
423
454
|
.ui-button:active {
|
|
424
455
|
transform: translateY(1px);
|
|
425
456
|
}
|
|
@@ -445,8 +476,13 @@
|
|
|
445
476
|
}
|
|
446
477
|
|
|
447
478
|
@media (prefers-reduced-motion: reduce) {
|
|
479
|
+
/* The global reduced-motion reset freezes the spin on a single frame with an
|
|
480
|
+
!important `animation-duration`, so the old non-important `1.4s` slow-spin
|
|
481
|
+
here was dead code AND left a broken, transparent-topped ring looking like a
|
|
482
|
+
rendering bug. Drop the dead rule; show a STATIC complete ring instead — a
|
|
483
|
+
still busy cue with no implied motion. (component audit C15.) */
|
|
448
484
|
.ui-button[aria-busy='true']::before {
|
|
449
|
-
|
|
485
|
+
border-block-start-color: currentcolor;
|
|
450
486
|
}
|
|
451
487
|
}
|
|
452
488
|
|
|
@@ -492,6 +528,27 @@
|
|
|
492
528
|
inline-size: 0.42rem;
|
|
493
529
|
}
|
|
494
530
|
|
|
531
|
+
/* RTL: the logical borders flip sides, but a fixed `rotate(45deg)` then points
|
|
532
|
+
the chevron UP rather than toward the inline-end (the reading-forward way).
|
|
533
|
+
Mirror the rotation so the resting affordance points forward in RTL too.
|
|
534
|
+
(component audit C14.) */
|
|
535
|
+
[dir='rtl'] .ui-link--arrow::after,
|
|
536
|
+
[dir='rtl'] .ui-link--cta::after {
|
|
537
|
+
transform: rotate(-45deg);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/* Standalone CTA links are tap targets, not inline prose links: on a coarse
|
|
541
|
+
pointer float them to the WCAG 2.5.8 AA 24px floor (the 2.5.8 inline-link
|
|
542
|
+
exception doesn't cover a block-level call-to-action, which is what these
|
|
543
|
+
are). Buttons already auto-grow to ~44px on coarse pointers. (component
|
|
544
|
+
audit C14.) */
|
|
545
|
+
@media (pointer: coarse) {
|
|
546
|
+
.ui-link--arrow,
|
|
547
|
+
.ui-link--cta {
|
|
548
|
+
min-block-size: 1.5rem;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
495
552
|
/* --- Chip / tag --- */
|
|
496
553
|
|
|
497
554
|
.ui-chip {
|
|
@@ -637,6 +694,8 @@
|
|
|
637
694
|
color: var(--text);
|
|
638
695
|
font-family: var(--mono);
|
|
639
696
|
margin: 0;
|
|
697
|
+
min-inline-size: 0;
|
|
698
|
+
overflow-wrap: anywhere; /* IDs/hashes/paths are the common value — break, don't overflow (audit C5) */
|
|
640
699
|
}
|
|
641
700
|
|
|
642
701
|
/* --- Hover (pointer only) --- */
|
|
@@ -679,7 +738,7 @@
|
|
|
679
738
|
|
|
680
739
|
[dir='rtl'] .ui-link--arrow:hover::after,
|
|
681
740
|
[dir='rtl'] .ui-link--cta:hover::after {
|
|
682
|
-
transform: translateX(-0.14rem) rotate(45deg);
|
|
741
|
+
transform: translateX(-0.14rem) rotate(-45deg);
|
|
683
742
|
}
|
|
684
743
|
|
|
685
744
|
.ui-chip--accent:hover {
|
package/css/report.css
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
--report-padding-block: var(--space-2xl);
|
|
13
13
|
--report-gap: var(--space-lg);
|
|
14
14
|
--report-measure: 74ch;
|
|
15
|
-
--report-page-margin: 18mm;
|
|
16
15
|
|
|
17
16
|
color: var(--text-soft);
|
|
18
17
|
display: grid;
|
|
@@ -257,6 +256,46 @@
|
|
|
257
256
|
text-transform: uppercase;
|
|
258
257
|
}
|
|
259
258
|
|
|
259
|
+
/* --- Labelled meter row ---
|
|
260
|
+
A multi-meter block (SLO burn, error budgets, capacity) lays out as
|
|
261
|
+
label | bar | value. The bare `ui-meter` base lives in feedback.css; this is
|
|
262
|
+
the report-document grammar around it so authors stop hand-rolling the grid.
|
|
263
|
+
The bar NEVER carries the reading alone (WCAG 1.4.1) — `ui-meter__value` is
|
|
264
|
+
the data of record, and the bar clamps at 100 so an over-target figure still
|
|
265
|
+
reads correctly in the value text. Collapses to a stack on a narrow screen. */
|
|
266
|
+
.ui-meter__row {
|
|
267
|
+
align-items: center;
|
|
268
|
+
display: grid;
|
|
269
|
+
gap: var(--space-2xs) var(--space-md);
|
|
270
|
+
grid-template-columns: minmax(9rem, 14rem) 1fr auto;
|
|
271
|
+
margin-block: var(--space-2xs);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.ui-meter__row .ui-meter {
|
|
275
|
+
min-inline-size: 8rem;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.ui-meter__label {
|
|
279
|
+
color: var(--text-soft);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.ui-meter__value {
|
|
283
|
+
color: var(--text);
|
|
284
|
+
font-family: var(--mono);
|
|
285
|
+
font-variant-numeric: tabular-nums;
|
|
286
|
+
text-align: end;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
@media (max-width: 32rem) {
|
|
290
|
+
.ui-meter__row {
|
|
291
|
+
grid-template-columns: 1fr;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.ui-meter__value {
|
|
295
|
+
text-align: start;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
260
299
|
/* A chart is NOT a bronto component — it needs scales + data binding, which the
|
|
261
300
|
analytical layer refuses to own. Theme Vega-Lite with `@ponchia/ui/vega`
|
|
262
301
|
(docs/vega.md), or hand-author a token-themed inline `<svg>`, and drop it in a
|
|
@@ -272,10 +311,10 @@
|
|
|
272
311
|
}
|
|
273
312
|
|
|
274
313
|
@media print {
|
|
275
|
-
/*
|
|
276
|
-
|
|
314
|
+
/* Chromium-class print engines do not reliably resolve custom properties in
|
|
315
|
+
@page rules. Keep this literal so reports cannot print edge-to-edge. */
|
|
277
316
|
@page {
|
|
278
|
-
margin:
|
|
317
|
+
margin: 18mm;
|
|
279
318
|
}
|
|
280
319
|
|
|
281
320
|
.ui-report {
|
package/css/sidenote.css
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
sidenote — opt-in Tufte-style margin notes.
|
|
3
|
+
|
|
4
|
+
A numbered `ui-sidenote` and an unnumbered `ui-marginnote` for evidence,
|
|
5
|
+
caveats, and provenance asides that belong beside the text, not in it. Wide
|
|
6
|
+
viewports float the note into the inline-end margin; narrow viewports collapse
|
|
7
|
+
it to an indented inline block. CSS counters number the sidenotes. Not
|
|
8
|
+
imported by core.css.
|
|
9
|
+
|
|
10
|
+
Boundary / wiring: the HOST owns where numbering restarts — set
|
|
11
|
+
`counter-reset: ui-sidenote` on the article (or a section) — and reserves the
|
|
12
|
+
margin gutter by giving that container
|
|
13
|
+
`padding-inline-end: calc(var(--sidenote-width) + var(--sidenote-gap))` at the
|
|
14
|
+
same breakpoint. Place each note in the DOM right after its `.ui-sidenote__ref`.
|
|
15
|
+
========================================================================== */
|
|
16
|
+
|
|
17
|
+
.ui-sidenote,
|
|
18
|
+
.ui-marginnote {
|
|
19
|
+
--sidenote-width: 12rem;
|
|
20
|
+
--sidenote-gap: 2rem;
|
|
21
|
+
|
|
22
|
+
border-inline-start: 2px solid var(--line);
|
|
23
|
+
color: var(--text-dim);
|
|
24
|
+
display: block;
|
|
25
|
+
font-size: var(--text-2xs);
|
|
26
|
+
line-height: 1.5;
|
|
27
|
+
margin-block: var(--space-2xs);
|
|
28
|
+
padding-inline-start: var(--space-md);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* The inline superscript that anchors a numbered sidenote. Use --accent-text,
|
|
32
|
+
not raw --accent: this is readable text and must clear WCAG AA 4.5:1 even
|
|
33
|
+
after a one-knob re-brand to a paler --accent (raw --accent drops to ~1.5:1).
|
|
34
|
+
Same accent-as-text contract as .ui-eyebrow / .ui-link--cta. (audit C6.) */
|
|
35
|
+
.ui-sidenote__ref {
|
|
36
|
+
color: var(--accent-text);
|
|
37
|
+
counter-increment: ui-sidenote;
|
|
38
|
+
font-size: 0.75em;
|
|
39
|
+
vertical-align: super;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.ui-sidenote__ref::after {
|
|
43
|
+
content: counter(ui-sidenote);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* The note repeats its number (display only — the ref already incremented).
|
|
47
|
+
--accent-text for the same WCAG-AA reason as the ref above. (audit C6.) */
|
|
48
|
+
.ui-sidenote::before {
|
|
49
|
+
color: var(--accent-text);
|
|
50
|
+
content: counter(ui-sidenote) '. ';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Wide viewports: float the note into the inline-end margin. The container must
|
|
54
|
+
reserve the gutter (see header). */
|
|
55
|
+
@media (min-width: 60rem) {
|
|
56
|
+
.ui-sidenote,
|
|
57
|
+
.ui-marginnote {
|
|
58
|
+
border-inline-start: 0;
|
|
59
|
+
clear: inline-end;
|
|
60
|
+
float: inline-end;
|
|
61
|
+
inline-size: var(--sidenote-width);
|
|
62
|
+
margin-block: 0;
|
|
63
|
+
margin-inline-end: calc(-1 * (var(--sidenote-width) + var(--sidenote-gap)));
|
|
64
|
+
padding-inline-start: 0;
|
|
65
|
+
text-align: start;
|
|
66
|
+
}
|
|
67
|
+
}
|
package/css/skins.css
CHANGED
|
@@ -11,44 +11,53 @@
|
|
|
11
11
|
/* Amber CRT */
|
|
12
12
|
:root[data-bronto-skin='amber-crt'] {
|
|
13
13
|
--accent: oklch(52% 0.11 67deg);
|
|
14
|
+
--dotmatrix-pulse-min: 0.35;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
:root[data-theme='dark'][data-bronto-skin='amber-crt'] {
|
|
17
18
|
--accent: oklch(82% 0.15 82deg);
|
|
18
19
|
--dotmatrix-glow: 0.4em;
|
|
20
|
+
--dotmatrix-pulse-min: 0.3;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/* E-ink */
|
|
22
24
|
:root[data-bronto-skin='e-ink'] {
|
|
23
25
|
--accent: oklch(34% 0.012 250deg);
|
|
26
|
+
--dotmatrix-reveal-step: 0ms;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
:root[data-theme='dark'][data-bronto-skin='e-ink'] {
|
|
27
30
|
--accent: oklch(84% 0.008 250deg);
|
|
31
|
+
--dotmatrix-reveal-step: 0ms;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
/* Phosphor Green */
|
|
31
35
|
:root[data-bronto-skin='phosphor-green'] {
|
|
32
36
|
--accent: oklch(52% 0.13 150deg);
|
|
37
|
+
--dotmatrix-pulse-min: 0.35;
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
:root[data-theme='dark'][data-bronto-skin='phosphor-green'] {
|
|
36
41
|
--accent: oklch(84% 0.19 150deg);
|
|
37
42
|
--dotmatrix-glow: 0.4em;
|
|
43
|
+
--dotmatrix-pulse-min: 0.3;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
@media (prefers-color-scheme: dark) {
|
|
41
47
|
:root:not([data-theme='light'])[data-bronto-skin='amber-crt'] {
|
|
42
48
|
--accent: oklch(82% 0.15 82deg);
|
|
43
49
|
--dotmatrix-glow: 0.4em;
|
|
50
|
+
--dotmatrix-pulse-min: 0.3;
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
:root:not([data-theme='light'])[data-bronto-skin='e-ink'] {
|
|
47
54
|
--accent: oklch(84% 0.008 250deg);
|
|
55
|
+
--dotmatrix-reveal-step: 0ms;
|
|
48
56
|
}
|
|
49
57
|
|
|
50
58
|
:root:not([data-theme='light'])[data-bronto-skin='phosphor-green'] {
|
|
51
59
|
--accent: oklch(84% 0.19 150deg);
|
|
52
60
|
--dotmatrix-glow: 0.4em;
|
|
61
|
+
--dotmatrix-pulse-min: 0.3;
|
|
53
62
|
}
|
|
54
63
|
}
|
package/css/spark.css
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
spark — opt-in inline datawords (word-sized microcharts).
|
|
3
|
+
|
|
4
|
+
A trend-in-a-sentence for generated reports and dense tables — the inline
|
|
5
|
+
counterpart to the scalar `ui-delta` / `ui-num` / `ui-stat`. Pure CSS, no
|
|
6
|
+
measurement, SSR-static, print-survivable. Not imported by core.css.
|
|
7
|
+
|
|
8
|
+
Boundary: the HOST normalises each point to 0..1 and sets it as `--v` on a
|
|
9
|
+
`.ui-spark__bar`; Bronto only paints the geometry. It refuses raw values and
|
|
10
|
+
min/max/scale computation. A bare spark is opaque to assistive tech, so the
|
|
11
|
+
container MUST carry a host-written `role="img"` + `aria-label` text
|
|
12
|
+
equivalent (e.g. "weekly signups, trending up"). Colour is never the only
|
|
13
|
+
channel — pair it with that label.
|
|
14
|
+
========================================================================== */
|
|
15
|
+
|
|
16
|
+
.ui-spark {
|
|
17
|
+
align-items: flex-end;
|
|
18
|
+
block-size: 1em;
|
|
19
|
+
display: inline-flex;
|
|
20
|
+
gap: 1px;
|
|
21
|
+
inline-size: max-content;
|
|
22
|
+
vertical-align: -0.15em;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.ui-spark__bar {
|
|
26
|
+
background: currentColor;
|
|
27
|
+
block-size: max(1px, calc(var(--v, 0) * 100%));
|
|
28
|
+
border-radius: 0.5px;
|
|
29
|
+
flex: 0 0 0.25em;
|
|
30
|
+
min-inline-size: 2px;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Emphasise / tone a single bar — the rationed accent or a status tone. The
|
|
34
|
+
meaning still has to be in the aria-label (WCAG 1.4.1). */
|
|
35
|
+
.ui-spark__bar--accent {
|
|
36
|
+
background: var(--accent);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.ui-spark__bar--pos {
|
|
40
|
+
background: var(--success);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.ui-spark__bar--neg {
|
|
44
|
+
background: var(--danger);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Dot variant — each bar reads as a stack of dots (the dot-matrix dataword)
|
|
48
|
+
instead of a solid bar, for the signature look in a sentence/table. A vertical
|
|
49
|
+
repeating mask breaks every bar into segments; the host still only sets `--v`
|
|
50
|
+
per bar, so the contract is identical to the solid spark. */
|
|
51
|
+
.ui-spark--dots .ui-spark__bar {
|
|
52
|
+
--_dot: var(--spark-dot, 0.16em);
|
|
53
|
+
--_step: calc(var(--_dot) + var(--spark-dot-gap, 0.06em));
|
|
54
|
+
|
|
55
|
+
border-radius: 0;
|
|
56
|
+
/* stylelint-disable-next-line property-no-vendor-prefix -- Safari still needs the prefixed mask property. */
|
|
57
|
+
-webkit-mask: repeating-linear-gradient(to top, #000 0 var(--_dot), transparent var(--_dot) var(--_step));
|
|
58
|
+
mask: repeating-linear-gradient(to top, #000 0 var(--_dot), transparent var(--_dot) var(--_step));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* Forced colours would force the bar backgrounds to the system surface and the
|
|
62
|
+
chart would vanish — repaint the bars in the system text colour so the shape
|
|
63
|
+
survives (the tone distinction is carried by the required aria-label). */
|
|
64
|
+
@media (forced-colors: active) {
|
|
65
|
+
.ui-spark__bar {
|
|
66
|
+
background: CanvasText;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Print: the bars are currentColor fills the print economy would drop. */
|
|
71
|
+
@media print {
|
|
72
|
+
.ui-spark__bar {
|
|
73
|
+
-webkit-print-color-adjust: exact;
|
|
74
|
+
print-color-adjust: exact;
|
|
75
|
+
}
|
|
76
|
+
}
|
package/css/table.css
CHANGED
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
.ui-table th,
|
|
21
21
|
.ui-table td {
|
|
22
22
|
border-block-end: 1px solid var(--line);
|
|
23
|
-
overflow-wrap:
|
|
23
|
+
overflow-wrap: break-word;
|
|
24
24
|
padding: 0.7rem 0.85rem;
|
|
25
25
|
text-align: start;
|
|
26
26
|
vertical-align: top;
|
|
27
|
+
word-break: normal;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
.ui-table th {
|
|
@@ -35,6 +36,11 @@
|
|
|
35
36
|
position: sticky;
|
|
36
37
|
text-transform: uppercase;
|
|
37
38
|
inset-block-start: 0;
|
|
39
|
+
|
|
40
|
+
/* Keep the sticky header above body cells — cheap insurance for the
|
|
41
|
+
sticky-header + pinned/positioned-column combo, where an un-z-indexed th
|
|
42
|
+
scrolls under a positioned cell. (audit C30.) */
|
|
43
|
+
z-index: 1;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
.ui-table td {
|
|
@@ -74,6 +80,11 @@
|
|
|
74
80
|
border-inline-end: 0;
|
|
75
81
|
}
|
|
76
82
|
|
|
83
|
+
.ui-table--break-anywhere th,
|
|
84
|
+
.ui-table--break-anywhere td {
|
|
85
|
+
overflow-wrap: anywhere;
|
|
86
|
+
}
|
|
87
|
+
|
|
77
88
|
/* Numeric / right-aligned cells. */
|
|
78
89
|
.ui-table .is-num,
|
|
79
90
|
.ui-table th.is-num {
|
|
@@ -154,8 +165,10 @@
|
|
|
154
165
|
padding: var(--space-sm) var(--space-md);
|
|
155
166
|
}
|
|
156
167
|
|
|
157
|
-
/* --- Loading state: set aria-busy + .ui-table--loading on the wrap
|
|
158
|
-
|
|
168
|
+
/* --- Loading state: set aria-busy + .ui-table-wrap--loading on the wrap. The
|
|
169
|
+
modifier is named for the element it goes ON (the wrap), not `.ui-table`, so
|
|
170
|
+
the BEM host matches the documented placement. (component audit C19.) --- */
|
|
171
|
+
.ui-table-wrap--loading {
|
|
159
172
|
opacity: 0.6;
|
|
160
173
|
pointer-events: none;
|
|
161
174
|
}
|
package/css/term.css
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/* ==========================================================================
|
|
2
|
+
term — opt-in inline glossary term + definition, and an end-of-report glossary.
|
|
3
|
+
|
|
4
|
+
The accessible upgrade of the famously touch/keyboard-broken `abbr[title]`: a
|
|
5
|
+
dotted-underline term whose definition lives in real, reachable DOM via the
|
|
6
|
+
native `[popover]` + `popovertarget` pairing, plus a `ui-glossary` `<dl>` block
|
|
7
|
+
that collects every term at the end of a document. Jargon that explains itself
|
|
8
|
+
inline and gathers into a reference — dead-centre on the explanation pillar.
|
|
9
|
+
Pure CSS over native popover, no kernel. Not imported by core.css.
|
|
10
|
+
|
|
11
|
+
Boundary: the HOST owns the wiring. The term is a `<button class="ui-term"
|
|
12
|
+
popovertarget="…">`; the definition is `<div class="ui-def" popover id="…">`.
|
|
13
|
+
That native pairing gives keyboard + touch + light-dismiss for free — no JS.
|
|
14
|
+
The glossary is a plain `<dl>`; the printed document leans on it because
|
|
15
|
+
popovers don't print (see docs/term.md). Anchor positioning is a gated
|
|
16
|
+
enhancement; without it the definition opens centred in the top layer.
|
|
17
|
+
========================================================================== */
|
|
18
|
+
|
|
19
|
+
/* The inline term marker — a real <button> so it is keyboard- and touch-
|
|
20
|
+
reachable (the abbr[title] failure). Reset to inline text, keep a dotted
|
|
21
|
+
underline as the "has a definition" cue. */
|
|
22
|
+
.ui-term {
|
|
23
|
+
background: none;
|
|
24
|
+
border: 0;
|
|
25
|
+
color: inherit;
|
|
26
|
+
cursor: help;
|
|
27
|
+
font: inherit;
|
|
28
|
+
padding: 0;
|
|
29
|
+
text-decoration: underline;
|
|
30
|
+
text-decoration-color: var(--line-strong);
|
|
31
|
+
text-decoration-style: dotted;
|
|
32
|
+
text-underline-offset: 0.2em;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media (hover: hover) {
|
|
36
|
+
.ui-term:hover {
|
|
37
|
+
text-decoration-color: var(--accent);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.ui-term:focus-visible {
|
|
42
|
+
outline: 2px solid var(--focus-ring);
|
|
43
|
+
outline-offset: 2px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* The definition — a native popover. Reset the UA popover chrome to the Bronto
|
|
47
|
+
raised-surface card. */
|
|
48
|
+
.ui-def {
|
|
49
|
+
background: var(--surface-raised);
|
|
50
|
+
border: 1px solid var(--line);
|
|
51
|
+
border-radius: var(--radius-md);
|
|
52
|
+
box-shadow: var(--shadow-raised);
|
|
53
|
+
color: var(--text);
|
|
54
|
+
font-family: var(--sans);
|
|
55
|
+
font-size: var(--text-sm);
|
|
56
|
+
line-height: 1.5;
|
|
57
|
+
margin: 0;
|
|
58
|
+
max-inline-size: 22rem;
|
|
59
|
+
padding: var(--space-sm) var(--space-md);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Progressive enhancement: anchor the definition to its term where CSS anchor
|
|
63
|
+
positioning exists, so it opens beside the word and flips at the viewport
|
|
64
|
+
edge. The host sets the matching `anchor-name` on the term (see docs). Without
|
|
65
|
+
support the popover keeps its centred top-layer fallback. */
|
|
66
|
+
@supports (anchor-name: --x) {
|
|
67
|
+
.ui-def {
|
|
68
|
+
inset: auto;
|
|
69
|
+
margin-block-start: 0.4rem;
|
|
70
|
+
position-area: block-end span-inline-end;
|
|
71
|
+
position-try-fallbacks: flip-block, flip-inline;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* --- Glossary — the end-of-document <dl> the terms collect into. --- */
|
|
76
|
+
|
|
77
|
+
.ui-glossary {
|
|
78
|
+
border-block-start: 1px solid var(--line);
|
|
79
|
+
display: grid;
|
|
80
|
+
gap: var(--space-2xs) var(--space-md);
|
|
81
|
+
grid-template-columns: minmax(6rem, max-content) 1fr;
|
|
82
|
+
margin: 0;
|
|
83
|
+
padding-block-start: var(--space-md);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ui-glossary__term {
|
|
87
|
+
color: var(--text);
|
|
88
|
+
font-family: var(--mono);
|
|
89
|
+
font-size: var(--text-xs);
|
|
90
|
+
font-weight: 600;
|
|
91
|
+
letter-spacing: var(--tracking-wide);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.ui-glossary__def {
|
|
95
|
+
color: var(--text-dim);
|
|
96
|
+
font-size: var(--text-sm);
|
|
97
|
+
margin: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Narrow viewports: stack each term over its definition. */
|
|
101
|
+
@media (max-width: 32rem) {
|
|
102
|
+
.ui-glossary {
|
|
103
|
+
grid-template-columns: 1fr;
|
|
104
|
+
gap: 0.15rem var(--space-md);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.ui-glossary__def {
|
|
108
|
+
margin-block-end: var(--space-xs);
|
|
109
|
+
}
|
|
110
|
+
}
|