@ponchia/ui 0.6.0 → 0.6.3
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 +64 -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 +14 -0
- package/behaviors/glyph.d.ts.map +1 -0
- package/behaviors/glyph.js +24 -0
- 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 +683 -13
- package/classes/index.d.ts +106 -2
- package/classes/index.js +249 -65
- 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 +37 -7
- 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 +40 -0
- package/css/sidenote.css +67 -0
- package/css/spark.css +62 -0
- package/css/table.css +9 -2
- 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/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/legends.md +8 -4
- package/docs/mermaid.md +21 -4
- package/docs/reference.md +127 -8
- package/docs/reporting.md +35 -14
- 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.js +7 -1
- package/llms.txt +159 -13
- package/package.json +47 -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/docs/sidenote.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Sidenote
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/sidenote.css` is an opt-in **margin-note** grammar in the Tufte
|
|
4
|
+
tradition — the channel for evidence, caveats, and provenance asides that belong
|
|
5
|
+
*beside* the prose, not interrupting it. `ui-sidenote` is numbered;
|
|
6
|
+
`ui-marginnote` is the unnumbered variant.
|
|
7
|
+
|
|
8
|
+
```css
|
|
9
|
+
@import '@ponchia/ui';
|
|
10
|
+
@import '@ponchia/ui/css/sidenote.css';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## How it behaves
|
|
14
|
+
|
|
15
|
+
- **Wide viewports** (≥ 60rem) float the note into the inline-end margin.
|
|
16
|
+
- **Narrow viewports** collapse it to an indented inline block right after its
|
|
17
|
+
anchor, so nothing is lost on a phone or in a narrow column.
|
|
18
|
+
- **Numbering** is a CSS counter — no host bookkeeping.
|
|
19
|
+
|
|
20
|
+
## Wiring — two things the host owns
|
|
21
|
+
|
|
22
|
+
1. **Where numbering restarts.** Put `counter-reset: ui-sidenote` on the article
|
|
23
|
+
(or a section) so the count starts at 1 there.
|
|
24
|
+
2. **The margin gutter.** At the same breakpoint, give that container
|
|
25
|
+
`padding-inline-end: calc(var(--sidenote-width) + var(--sidenote-gap))` so the
|
|
26
|
+
floated note has room. `--sidenote-width` defaults to `12rem`.
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<article style="counter-reset: ui-sidenote">
|
|
30
|
+
<p>
|
|
31
|
+
The migration cut p95 latency by 38%<label class="ui-sidenote__ref"></label>.
|
|
32
|
+
<span class="ui-sidenote">
|
|
33
|
+
Measured over the first 24h after rollout; see the incident review §4.
|
|
34
|
+
</span>
|
|
35
|
+
The error budget held<span class="ui-marginnote">No paging events fired.</span>.
|
|
36
|
+
</p>
|
|
37
|
+
</article>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The `.ui-sidenote__ref` renders the superscript number and increments the
|
|
41
|
+
counter; place the `.ui-sidenote` immediately after it in the DOM so it reads
|
|
42
|
+
the same number.
|
|
43
|
+
|
|
44
|
+
## Class reference
|
|
45
|
+
|
|
46
|
+
| Class | Role |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `.ui-sidenote` | A numbered margin note. |
|
|
49
|
+
| `.ui-marginnote` | An unnumbered margin note (no `__ref`). |
|
|
50
|
+
| `.ui-sidenote__ref` | The inline superscript anchor; increments + prints the number. |
|
|
51
|
+
|
|
52
|
+
| Custom property | On | Meaning |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| `--sidenote-width` | `.ui-sidenote` / `.ui-marginnote` | Floated width on wide viewports (default `12rem`). |
|
|
55
|
+
| `--sidenote-gap` | same | Gap between the text column and the note (default `2rem`). |
|
|
56
|
+
|
|
57
|
+
## Accessibility & robustness
|
|
58
|
+
|
|
59
|
+
- The note is **real DOM in reading order**, so a screen reader encounters it
|
|
60
|
+
right after the anchor — no off-screen trickery.
|
|
61
|
+
- On narrow viewports the note is always visible (an indented block with an
|
|
62
|
+
inline-start rule), so there is no hidden-content trap.
|
|
63
|
+
- Keep the note's meaning in its text; the number is a wayfinding aid, not the
|
|
64
|
+
content.
|
package/docs/spark.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Spark
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/spark.css` is an opt-in **inline dataword** — a word-sized
|
|
4
|
+
microchart you drop into a sentence or a dense table cell to show a trend
|
|
5
|
+
in-line. It is the inline counterpart to the scalar `ui-delta` / `ui-num` /
|
|
6
|
+
`ui-stat`, and it fills a gap a block-level chart cannot.
|
|
7
|
+
|
|
8
|
+
```css
|
|
9
|
+
@import '@ponchia/ui';
|
|
10
|
+
@import '@ponchia/ui/css/spark.css';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Boundary — the host owns the numbers
|
|
14
|
+
|
|
15
|
+
Bronto paints bars; it does **not** compute scales. The host normalises each
|
|
16
|
+
data point to `0..1` and sets it as `--v` on a `.ui-spark__bar`. Bronto refuses
|
|
17
|
+
raw values and min/max — that is the same boundary that keeps the analytical
|
|
18
|
+
layer from owning chart state.
|
|
19
|
+
|
|
20
|
+
## Markup
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<!-- "weekly signups, trending up" — the label IS the accessible content -->
|
|
24
|
+
<span class="ui-spark" role="img" aria-label="weekly signups, trending up">
|
|
25
|
+
<span class="ui-spark__bar" style="--v: 0.2"></span>
|
|
26
|
+
<span class="ui-spark__bar" style="--v: 0.4"></span>
|
|
27
|
+
<span class="ui-spark__bar" style="--v: 0.35"></span>
|
|
28
|
+
<span class="ui-spark__bar" style="--v: 0.7"></span>
|
|
29
|
+
<span class="ui-spark__bar ui-spark__bar--accent" style="--v: 1"></span>
|
|
30
|
+
</span>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
A **win/loss** strip uses fixed-height bars with `--pos` / `--neg` tones:
|
|
34
|
+
|
|
35
|
+
```html
|
|
36
|
+
<span class="ui-spark" role="img" aria-label="last 5 deploys: 4 passed, 1 failed">
|
|
37
|
+
<span class="ui-spark__bar ui-spark__bar--pos" style="--v: 1"></span>
|
|
38
|
+
<span class="ui-spark__bar ui-spark__bar--pos" style="--v: 1"></span>
|
|
39
|
+
<span class="ui-spark__bar ui-spark__bar--neg" style="--v: 1"></span>
|
|
40
|
+
<span class="ui-spark__bar ui-spark__bar--pos" style="--v: 1"></span>
|
|
41
|
+
<span class="ui-spark__bar ui-spark__bar--pos" style="--v: 1"></span>
|
|
42
|
+
</span>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Class reference
|
|
46
|
+
|
|
47
|
+
| Class | Role |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| `.ui-spark` | The inline container (sizes to ~1em tall, `max-content` wide). |
|
|
50
|
+
| `.ui-spark__bar` | One bar — height from `--v` (`0..1`). Defaults to `currentColor`. |
|
|
51
|
+
| `.ui-spark__bar--accent` | Emphasise one bar with the rationed accent. |
|
|
52
|
+
| `.ui-spark__bar--pos` | Success tone (win). |
|
|
53
|
+
| `.ui-spark__bar--neg` | Danger tone (loss). |
|
|
54
|
+
|
|
55
|
+
| Custom property | On | Meaning |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| `--v` | `.ui-spark__bar` | **Required.** Normalised height `0..1`; the host computes it. A bar with no `--v` collapses to a 1px floor. |
|
|
58
|
+
|
|
59
|
+
## Recipes
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import { ui } from '@ponchia/ui/classes';
|
|
63
|
+
|
|
64
|
+
ui.sparkBar({ tone: 'accent' }); // "ui-spark__bar ui-spark__bar--accent"
|
|
65
|
+
ui.sparkBar({ tone: 'pos' }); // "ui-spark__bar ui-spark__bar--pos"
|
|
66
|
+
ui.sparkBar(); // "ui-spark__bar"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Accessibility & robustness
|
|
70
|
+
|
|
71
|
+
- **A bare spark is opaque.** The `.ui-spark` container **must** carry
|
|
72
|
+
`role="img"` and an `aria-label` that states the trend in words — colour and
|
|
73
|
+
shape are never the only channel (WCAG 1.4.1, 1.1.1).
|
|
74
|
+
- **Forced colours:** the bars repaint in the system text colour so the shape
|
|
75
|
+
survives (the tone distinction lives in the label).
|
|
76
|
+
- **Print:** bars are forced through `print-color-adjust: exact`.
|
|
77
|
+
- No JS, no measurement — it renders identically server-side and in a static
|
|
78
|
+
report.
|
package/docs/stability.md
CHANGED
|
@@ -40,6 +40,7 @@ until `1.0.0` is tagged. This matrix defines what counts as public API.
|
|
|
40
40
|
| Generated / AI-trust (`css/generated.css`, `.ui-generated*`, `.ui-origin-label*`, `.ui-reasoning*`, `.ui-tool-log`, `.ui-tool-call*`) | Stable additive | The generated-content, origin-label (incl. `--ai`), reasoning-trace and tool-log/tool-call class names and the `ui.originLabel` recipe are public. Opt-in, not in the default bundle. Not a chat kit; no confidence widget. |
|
|
41
41
|
| Workbench (`css/workbench.css`, `.ui-inspector*`, `.ui-property*`, `.ui-selectionbar*`) | Stable additive | Inspector, property-row and selection-bar class + BEM part names are public (class-only — no recipe). Opt-in, not in the default bundle. Splitters/drag handles are out of scope. |
|
|
42
42
|
| Command palette (`css/command.css`, `.ui-command*`, `initCommand`, `useCommand`) | Stable additive | Command class/part names, the `data-bronto-command` attribute, and the event contract — `bronto:command:select` (`detail: { value, label }`) and `bronto:command:close` — are public, plus the `useCommand` binding hook. Bronto filters + navigates (APG combobox/listbox); the host owns the action registry/execution. Opt-in, not in the default bundle, no global hotkey. |
|
|
43
|
+
| Controlled-modal focus trap (`initModal`, `useModal`, `data-bronto-modal`) | Stable additive | For the `.ui-modal.is-open` (non-`<dialog>`) path: the `data-bronto-modal` opt-in marker, the `inert`-based focus trap + focus-return, and the cancelable `bronto:modal:close` (`detail: { reason }`) event are public. The consumer still owns the `is-open` class; the behavior never changes visibility. The native `<dialog>` path (`initDialog`) is the default and gets the trap for free. |
|
|
43
44
|
| Keyboard-shortcut hint (`.ui-shortcut`, `.ui-shortcut__sep`) | Stable additive | Class names for the chord/sequence hint over `.ui-kbd` are public. Ships in the core layer (class-only, no recipe). |
|
|
44
45
|
| Generated docs shipped in npm | Stable paths | `llms.txt` and exported docs paths stay shipped and resolvable within a compatible minor. Markdown/text assets are for reading unless your runtime has a loader. Generated content may change with the source contract. |
|
|
45
46
|
| Demo, examples, tests, scripts | Internal | Useful for learning and verification, but not shipped runtime API unless a path is explicitly exported in `package.json`. |
|
package/docs/term.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Term & Glossary
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/term.css` is an opt-in **inline glossary** — the accessible
|
|
4
|
+
upgrade of the famously touch/keyboard-broken `abbr[title]`. A dotted-underline
|
|
5
|
+
term whose definition lives in real, reachable DOM via the native `[popover]` +
|
|
6
|
+
`popovertarget` pairing, plus a `ui-glossary` `<dl>` that collects every term at
|
|
7
|
+
the end of a document. Jargon that explains itself inline and gathers into a
|
|
8
|
+
reference.
|
|
9
|
+
|
|
10
|
+
```css
|
|
11
|
+
@import '@ponchia/ui';
|
|
12
|
+
@import '@ponchia/ui/css/term.css';
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## How it behaves
|
|
16
|
+
|
|
17
|
+
- The term is a real `<button>`, so it is keyboard- and touch-reachable — the
|
|
18
|
+
exact failure of `abbr[title]`, which only reveals on desktop hover.
|
|
19
|
+
- The definition is a native popover: `popovertarget` gives open/close, Esc, and
|
|
20
|
+
light-dismiss for free, with no JavaScript.
|
|
21
|
+
- Where CSS anchor positioning exists the definition opens beside the word and
|
|
22
|
+
flips at the viewport edge; otherwise it falls back to a centred top-layer card.
|
|
23
|
+
|
|
24
|
+
## Wiring — native popover pairing
|
|
25
|
+
|
|
26
|
+
The host owns the `popovertarget` ↔ `id` wiring. No Bronto kernel is involved.
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<p>
|
|
30
|
+
We held the
|
|
31
|
+
<button class="ui-term" popovertarget="def-eb">error budget</button>
|
|
32
|
+
through the change.
|
|
33
|
+
<span class="ui-def" popover id="def-eb">
|
|
34
|
+
The amount of unreliability a service is allowed before its SLO is breached.
|
|
35
|
+
</span>
|
|
36
|
+
</p>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
To anchor the definition to its term where supported, set a matching
|
|
40
|
+
`anchor-name` on the button and `position-anchor` on the `.ui-def` (a
|
|
41
|
+
progressive enhancement; the centred fallback works everywhere):
|
|
42
|
+
|
|
43
|
+
```css
|
|
44
|
+
#def-eb {
|
|
45
|
+
position-anchor: --eb;
|
|
46
|
+
}
|
|
47
|
+
.ui-term[popovertarget='def-eb'] {
|
|
48
|
+
anchor-name: --eb;
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The end-of-document glossary is a plain `<dl>`:
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<dl class="ui-glossary">
|
|
56
|
+
<dt class="ui-glossary__term">Error budget</dt>
|
|
57
|
+
<dd class="ui-glossary__def">The unreliability a service may spend before its SLO breaches.</dd>
|
|
58
|
+
<dt class="ui-glossary__term">p95</dt>
|
|
59
|
+
<dd class="ui-glossary__def">The 95th-percentile value of a latency distribution.</dd>
|
|
60
|
+
</dl>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Class reference
|
|
64
|
+
|
|
65
|
+
| Class | Role |
|
|
66
|
+
| --------------------- | ---------------------------------------------------------------- |
|
|
67
|
+
| `.ui-term` | The inline term `<button>` (dotted underline "has a definition"). |
|
|
68
|
+
| `.ui-def` | The definition body — a native `[popover]` styled as a raised card. |
|
|
69
|
+
| `.ui-glossary` | The end-of-document `<dl>` two-column glossary block. |
|
|
70
|
+
| `.ui-glossary__term` | A glossary `<dt>` (mono, the term). |
|
|
71
|
+
| `.ui-glossary__def` | A glossary `<dd>` (the definition). |
|
|
72
|
+
|
|
73
|
+
## Accessibility & robustness
|
|
74
|
+
|
|
75
|
+
- Because the term is a `<button popovertarget>`, the definition is reachable by
|
|
76
|
+
**keyboard and touch**, not just hover — the whole point versus `abbr[title]`.
|
|
77
|
+
- The definition is real DOM in reading order, so a screen reader encounters it
|
|
78
|
+
right after the term.
|
|
79
|
+
- Popovers don't print; the **printed document leans on the `ui-glossary` block**,
|
|
80
|
+
so include it at the end of any report that uses inline terms.
|
|
81
|
+
- On narrow viewports the glossary stacks each term over its definition.
|
package/docs/textref.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Textref
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/textref.css` is an opt-in **deep-link-to-the-cited-sentence**
|
|
4
|
+
provenance primitive. A citation whose `href` is a URL [Text
|
|
5
|
+
Fragment](https://developer.mozilla.org/en-US/docs/Web/Text_fragments)
|
|
6
|
+
(`#…:~:text=`): the browser scrolls to the exact quoted text and highlights it,
|
|
7
|
+
and Bronto owns the on-brand `::target-text` paint. It is the inline counterpart
|
|
8
|
+
to the static `ui-src` / `ui-citation` trust layer (`sources.css`), which can
|
|
9
|
+
label a source but cannot point *inside* it.
|
|
10
|
+
|
|
11
|
+
```css
|
|
12
|
+
@import '@ponchia/ui';
|
|
13
|
+
@import '@ponchia/ui/css/textref.css';
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## How it behaves
|
|
17
|
+
|
|
18
|
+
- The link navigates to the document and the browser scrolls to + highlights the
|
|
19
|
+
first match of the quoted text — no script, no anchors to pre-place.
|
|
20
|
+
- Bronto repaints that browser highlight (`::target-text`) in the rationed
|
|
21
|
+
accent wash so it matches the rest of the trust layer.
|
|
22
|
+
- On engines without Text Fragments the link still navigates to the page (or its
|
|
23
|
+
`#section` anchor); the highlight is purely additive, so nothing breaks.
|
|
24
|
+
|
|
25
|
+
## Wiring — the host builds the fragment URL
|
|
26
|
+
|
|
27
|
+
`::target-text` highlighting is driven entirely by the URL; Bronto ships no
|
|
28
|
+
runtime for it. Build the `href` with a three-line pure helper and drop it on the
|
|
29
|
+
`.ui-textref` link:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
// Encode a quote as a URL Text Fragment directive.
|
|
33
|
+
// encodeTextFragment('p95 latency fell 38%') -> '#:~:text=p95%20latency%20fell%2038%25'
|
|
34
|
+
export function encodeTextFragment(quote, { prefix } = {}) {
|
|
35
|
+
const enc = (s) => encodeURIComponent(s).replace(/-/g, '%2D');
|
|
36
|
+
const text = prefix ? `${enc(prefix)}-,${enc(quote)}` : enc(quote);
|
|
37
|
+
return `#:~:text=${text}`;
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<p>
|
|
43
|
+
The migration cut p95 latency by 38%
|
|
44
|
+
<a
|
|
45
|
+
class="ui-textref"
|
|
46
|
+
href="https://example.com/incident-review#:~:text=p95%20latency%20fell%2038%25"
|
|
47
|
+
>jump to the source</a
|
|
48
|
+
>.
|
|
49
|
+
</p>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Notes for an autonomous author:
|
|
53
|
+
|
|
54
|
+
- Text Fragment navigation requires a **user activation** (a real click) on most
|
|
55
|
+
engines — it will not fire from a programmatic `location.assign`.
|
|
56
|
+
- Keep the quote short and verbatim; a fuzzy or paraphrased quote won't match.
|
|
57
|
+
- Use the optional `prefix` (a `prefix-,` directive) to disambiguate a quote that
|
|
58
|
+
appears more than once on the target page.
|
|
59
|
+
|
|
60
|
+
## Class reference
|
|
61
|
+
|
|
62
|
+
| Class | Role |
|
|
63
|
+
| ------------- | ------------------------------------------------------------------- |
|
|
64
|
+
| `.ui-textref` | A citation link whose `href` is a `#:~:text=` fragment; dotted underline + quote-jump cue. |
|
|
65
|
+
|
|
66
|
+
| Custom property | On | Meaning |
|
|
67
|
+
| ---------------------- | ------------- | ------------------------------------------------------------------------- |
|
|
68
|
+
| `--textref-highlight` | `.ui-textref` | The `::target-text` wash for the matched sentence (default `var(--accent-soft)`). |
|
|
69
|
+
|
|
70
|
+
## Accessibility & robustness
|
|
71
|
+
|
|
72
|
+
- The link is a real `<a href>` — keyboard- and screen-reader-reachable, and it
|
|
73
|
+
degrades to ordinary navigation everywhere.
|
|
74
|
+
- `::target-text` is repainted with the system highlight colours under
|
|
75
|
+
`forced-colors`, so the landed-on sentence stays visible in high-contrast mode.
|
|
76
|
+
- The highlight is **global** once this leaf is imported: any text-fragment
|
|
77
|
+
landing on the page gets the brand wash, which keeps provenance highlighting
|
|
78
|
+
consistent across a report.
|
package/docs/theming.md
CHANGED
|
@@ -19,17 +19,40 @@ theme-owned neutral ramp endpoint:
|
|
|
19
19
|
| `--field-dot-accent` | `--accent` at 78% / 82% | form dot indicators |
|
|
20
20
|
| `--focus-ring` | `var(--accent)` (solid) | **every focus outline** — override to tune the ring alone |
|
|
21
21
|
|
|
22
|
-
So a full re-brand is one declaration
|
|
22
|
+
So a full re-brand is one declaration **at `:root` (or a theme root)**:
|
|
23
23
|
|
|
24
24
|
```css
|
|
25
25
|
:root { --accent: #2f6df6; } /* brand the whole app blue */
|
|
26
|
-
.promo { --accent: #16a34a; } /* …or just this section green */
|
|
27
26
|
:root[data-theme='dark'] { --accent: #6ea8ff; } /* per-theme tuning */
|
|
28
27
|
```
|
|
29
28
|
|
|
30
29
|
Everything — buttons, focus rings, dot motifs, accent borders, soft
|
|
31
30
|
fills — follows automatically, in both light and dark.
|
|
32
31
|
|
|
32
|
+
> **Re-branding a subtree (not `:root`) is only a *partial* re-brand.**
|
|
33
|
+
> The derived family (`--accent-soft`, `--accent-strong`, `--accent-text`,
|
|
34
|
+
> `--bg-accent`, the `--accent-1…6` ramp) is computed from `--accent` via
|
|
35
|
+
> `color-mix()` **at `:root`**, where it resolves once. A custom property's
|
|
36
|
+
> value is substituted where it's declared, so overriding only `--accent`
|
|
37
|
+
> on a `.promo` subtree re-brands the surfaces that read raw `var(--accent)`
|
|
38
|
+
> (focus rings, dot motifs, some borders) but leaves every *derived* surface
|
|
39
|
+
> (soft fills, accent-as-text, the ramp) at the root hue — a visibly broken
|
|
40
|
+
> half-rebrand. To re-brand a subtree fully, set the derived tokens you use
|
|
41
|
+
> too, e.g.:
|
|
42
|
+
>
|
|
43
|
+
> ```css
|
|
44
|
+
> .promo {
|
|
45
|
+
> --accent: #16a34a;
|
|
46
|
+
> --accent-strong: color-mix(in srgb, var(--accent) 83%, #000);
|
|
47
|
+
> --accent-text: var(--accent-strong);
|
|
48
|
+
> --accent-soft: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
49
|
+
> --bg-accent: color-mix(in srgb, var(--accent) 6%, transparent);
|
|
50
|
+
> }
|
|
51
|
+
> ```
|
|
52
|
+
>
|
|
53
|
+
> When in doubt, re-brand at `:root`/`[data-theme]` — that path re-derives
|
|
54
|
+
> the whole family for you.
|
|
55
|
+
|
|
33
56
|
> **Two contrast obligations when you change `--accent`:**
|
|
34
57
|
>
|
|
35
58
|
> 1. **Buttons** — pick an `--accent` with ≥ 4.5:1 against `--button-text`
|
|
@@ -46,6 +69,13 @@ fills — follows automatically, in both light and dark.
|
|
|
46
69
|
> ring is solid `--accent` (≥ 3:1 non-text) — re-brand to a near-`--bg`
|
|
47
70
|
> hue and you must also raise `--focus-ring` (it's an independent knob).
|
|
48
71
|
|
|
72
|
+
> **Re-inking accent fills: use `--button-text`, not `--on-accent`.** In-DOM
|
|
73
|
+
> components (buttons, chips, accent fills) paint their on-accent ink from
|
|
74
|
+
> `--button-text`. `--on-accent` is a **read-only export** of that resolved ink
|
|
75
|
+
> for *foreign renderers* (charts, canvas, a non-DOM target reading the token) —
|
|
76
|
+
> overriding it in CSS validates but does **not** change any in-DOM component. To
|
|
77
|
+
> change the ink on a re-brand, set `--button-text`.
|
|
78
|
+
|
|
49
79
|
## Other supported knobs
|
|
50
80
|
|
|
51
81
|
- **Spacing** — override the `--space-2xs … --space-2xl` scale, or use a
|
|
@@ -182,9 +212,12 @@ you have, so the one-accent discipline holds.
|
|
|
182
212
|
skin on a subtree would leave that family stale, so the selectors are
|
|
183
213
|
`:root`-anchored and a subtree skin simply no-ops.
|
|
184
214
|
- **Phosphor bloom.** The `amber-crt` / `phosphor-green` skins set
|
|
185
|
-
`--dotmatrix-glow`
|
|
186
|
-
|
|
187
|
-
|
|
215
|
+
`--dotmatrix-glow` in dark, so the dot-matrix gains a CRT-style glow. It is a
|
|
216
|
+
Tier-3 *display knob* — a `--dotmatrix-*` CSS custom property with an inline
|
|
217
|
+
default of `0` (off), like `--dotmatrix-dot`/`--dotmatrix-gap`, **not** a token
|
|
218
|
+
in the palette export (Tier-3 display expression lives as `--dotmatrix-*` knobs
|
|
219
|
+
in `css/dots.css`, by ADR-0001). Set it yourself on any `.ui-dotmatrix` to tune
|
|
220
|
+
the bloom.
|
|
188
221
|
- **Contrast-gated.** Every shipped skin accent meets the same WCAG AA / 3:1
|
|
189
222
|
floors as the core palette — see [contrast.md](contrast.md) → "Display
|
|
190
223
|
colorways". (Your _own_ `--accent` re-brand is still your obligation; the
|
|
@@ -210,6 +243,12 @@ const series = charts.dark.categorical; // ['#ff3b41', '#e69f00', …] — serie
|
|
|
210
243
|
`var(--accent)` (your brand leads), series 2–8 are the Okabe-Ito
|
|
211
244
|
colourblind-safe set. The set is **gated for mutual distinguishability under
|
|
212
245
|
normal + simulated protanopia/deuteranopia/tritanopia** (OKLab ΔE).
|
|
246
|
+
**Caveat — the CVD gate measures the SHIPPED default accent.** Series 1 is
|
|
247
|
+
`var(--accent)`, so if you re-skin `--accent` you change series 1 but not the
|
|
248
|
+
Okabe-Ito 2–8, and the gate never re-checks your custom hue: a brand close to
|
|
249
|
+
series 3's orange can collide for a deuteranope. If a re-brand drives data-viz,
|
|
250
|
+
re-verify your accent against the set, or pin `--chart-1` to a fixed Okabe-Ito
|
|
251
|
+
value (`--chart-1: #0072b2`) and let your brand lead the UI only.
|
|
213
252
|
- **Sequential `--chart-seq-1..6`** — single-hue light→dark, for
|
|
214
253
|
heatmaps/intensity. **Diverging `--chart-div-1..7`** — blue↔neutral↔orange,
|
|
215
254
|
for ±/gains-losses.
|
package/docs/toc.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Table of contents (scrollspy)
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/toc.css` is an opt-in **sticky contents rail** for long
|
|
4
|
+
generated reports. The entry for the section currently in view is highlighted, so
|
|
5
|
+
a reader always knows where they are. It degrades to a plain anchored list with
|
|
6
|
+
zero JavaScript.
|
|
7
|
+
|
|
8
|
+
```css
|
|
9
|
+
@import '@ponchia/ui';
|
|
10
|
+
@import '@ponchia/ui/css/toc.css';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## How it behaves
|
|
14
|
+
|
|
15
|
+
- The rail sticks within its scroll container (`--toc-top` sets the inset).
|
|
16
|
+
- The active entry keys on the standard `aria-current="true"` hook — the same
|
|
17
|
+
rule every other nav surface here uses.
|
|
18
|
+
- Nested lists indent for sub-sections.
|
|
19
|
+
|
|
20
|
+
## Wiring — the host mirrors the in-view section
|
|
21
|
+
|
|
22
|
+
CSS alone cannot know which section is on screen, so the host sets
|
|
23
|
+
`aria-current="true"` on the link for the current section. Two ways:
|
|
24
|
+
|
|
25
|
+
1. **Static** — server-render `aria-current` on the section you're rendering a
|
|
26
|
+
"current page" for. No script at all.
|
|
27
|
+
2. **Live scrollspy** — a tiny `IntersectionObserver` (~15 lines, copy-paste
|
|
28
|
+
below) mirrors the in-view section onto its link. No Bronto kernel ships for
|
|
29
|
+
this; the rail is fully useful as a static sticky list without it.
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<nav class="ui-toc" aria-label="Contents">
|
|
33
|
+
<p class="ui-toc__title">Contents</p>
|
|
34
|
+
<ul class="ui-toc__list">
|
|
35
|
+
<li><a class="ui-toc__link" href="#intro" aria-current="true">Introduction</a></li>
|
|
36
|
+
<li><a class="ui-toc__link" href="#method">Method</a></li>
|
|
37
|
+
<li><a class="ui-toc__link" href="#results">Results</a></li>
|
|
38
|
+
</ul>
|
|
39
|
+
</nav>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
// Optional live scrollspy — mirror the in-view section onto its TOC link.
|
|
44
|
+
const links = new Map(
|
|
45
|
+
[...document.querySelectorAll('.ui-toc__link')].map((a) => [a.hash.slice(1), a]),
|
|
46
|
+
);
|
|
47
|
+
const spy = new IntersectionObserver(
|
|
48
|
+
(entries) => {
|
|
49
|
+
for (const e of entries) {
|
|
50
|
+
if (!e.isIntersecting) continue;
|
|
51
|
+
for (const a of links.values()) a.removeAttribute('aria-current');
|
|
52
|
+
links.get(e.target.id)?.setAttribute('aria-current', 'true');
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
{ rootMargin: '0px 0px -70% 0px' },
|
|
56
|
+
);
|
|
57
|
+
for (const id of links.keys()) {
|
|
58
|
+
const section = document.getElementById(id);
|
|
59
|
+
if (section) spy.observe(section);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Class reference
|
|
64
|
+
|
|
65
|
+
| Class | Role |
|
|
66
|
+
| ---------------- | ---------------------------------------------------------- |
|
|
67
|
+
| `.ui-toc` | The sticky contents rail (`<nav>`). |
|
|
68
|
+
| `.ui-toc__title` | Optional eyebrow heading for the rail. |
|
|
69
|
+
| `.ui-toc__list` | The list of entries (`<ul>`/`<ol>`; nests for sub-sections). |
|
|
70
|
+
| `.ui-toc__link` | An entry link; `aria-current="true"` marks the active one. |
|
|
71
|
+
|
|
72
|
+
| Custom property | On | Meaning |
|
|
73
|
+
| --------------- | --------- | ------------------------------------------------------------- |
|
|
74
|
+
| `--toc-top` | `.ui-toc` | Sticky inset from the top of the scroll container (default `var(--space-md)`). |
|
|
75
|
+
|
|
76
|
+
## Accessibility & robustness
|
|
77
|
+
|
|
78
|
+
- The active cue is repainted with the system highlight under `forced-colors`,
|
|
79
|
+
so it survives high-contrast mode.
|
|
80
|
+
- Wrap the rail in a `<nav aria-label="Contents">` so it is announced as a
|
|
81
|
+
navigation landmark.
|
|
82
|
+
- Without the optional observer the rail is a normal anchored list — every link
|
|
83
|
+
still jumps to its section.
|
package/docs/tree.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Tree (hierarchy outline)
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/tree.css` is an opt-in **hierarchy outline** — a depth-indented
|
|
4
|
+
view of nested structure (file trees, report contents, nested generated-content
|
|
5
|
+
provenance, object graphs). It is built on nested native `<details>` (optionally
|
|
6
|
+
`name` exclusive-accordion groups), so open/close, keyboard toggling and the
|
|
7
|
+
open/close animation come from the platform. This leaf is the hierarchy *layer*
|
|
8
|
+
(rails, indent, chevron); it does **not** reinvent the disclosure grammar
|
|
9
|
+
`ui-accordion` already owns.
|
|
10
|
+
|
|
11
|
+
```css
|
|
12
|
+
@import '@ponchia/ui';
|
|
13
|
+
@import '@ponchia/ui/css/tree.css';
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## How it behaves
|
|
17
|
+
|
|
18
|
+
- A **branch** is a `<details class="ui-tree__branch">`; a **leaf** is a plain
|
|
19
|
+
`.ui-tree__leaf` row with no disclosure.
|
|
20
|
+
- Nested rows indent and carry a hairline guide rail back to their parent.
|
|
21
|
+
- The summary's chevron rotates open; auto-height open/close is gated exactly like
|
|
22
|
+
`ui-accordion` (Chrome 131+/Safari 18.4+; Firefox snaps).
|
|
23
|
+
|
|
24
|
+
## Wiring
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<div class="ui-tree">
|
|
28
|
+
<details class="ui-tree__branch" open>
|
|
29
|
+
<summary class="ui-tree__summary"><span class="ui-tree__label">src</span></summary>
|
|
30
|
+
<details class="ui-tree__branch">
|
|
31
|
+
<summary class="ui-tree__summary"><span class="ui-tree__label">components</span></summary>
|
|
32
|
+
<div class="ui-tree__leaf"><span class="ui-tree__label">button.css</span></div>
|
|
33
|
+
<div class="ui-tree__leaf"><span class="ui-tree__label">card.css</span></div>
|
|
34
|
+
</details>
|
|
35
|
+
<div class="ui-tree__leaf"><span class="ui-tree__label">index.js</span></div>
|
|
36
|
+
</details>
|
|
37
|
+
</div>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Add `name="…"` to sibling `<details class="ui-tree__branch">` to make them an
|
|
41
|
+
exclusive-accordion group (only one open at a time) — a platform behaviour, no
|
|
42
|
+
script.
|
|
43
|
+
|
|
44
|
+
## A11y honesty — disclosure group, not an ARIA tree
|
|
45
|
+
|
|
46
|
+
A native `<details>` group is a **disclosure group**, not an ARIA `tree`. Do not
|
|
47
|
+
bolt `role="tree"` / `role="treeitem"` / `aria-level` onto this markup: those
|
|
48
|
+
roles imply a roving-focus keyboard model (Up/Down to move, Left/Right to
|
|
49
|
+
collapse/expand, typeahead) that native `<details>` does not provide, so the
|
|
50
|
+
result would announce a contract the keyboard can't honour.
|
|
51
|
+
|
|
52
|
+
That roving-focus kernel is intentionally **not shipped** with this leaf. It
|
|
53
|
+
lands behind a real consumer that needs the full APG tree interaction (e.g. a
|
|
54
|
+
workbench file pane). Until then, the disclosure semantics are correct and
|
|
55
|
+
honest as-is.
|
|
56
|
+
|
|
57
|
+
## Class reference
|
|
58
|
+
|
|
59
|
+
| Class | Role |
|
|
60
|
+
| -------------------- | ---------------------------------------------------------- |
|
|
61
|
+
| `.ui-tree` | The outline container. |
|
|
62
|
+
| `.ui-tree__branch` | A `<details>` node with children (twist chevron). |
|
|
63
|
+
| `.ui-tree__leaf` | A leaf row with no disclosure (chevron-aligned spacer). |
|
|
64
|
+
| `.ui-tree__summary` | The branch's `<summary>` toggle row. |
|
|
65
|
+
| `.ui-tree__label` | The row label (truncates with ellipsis). |
|
|
66
|
+
|
|
67
|
+
## Accessibility & robustness
|
|
68
|
+
|
|
69
|
+
- Open/close, keyboard toggling (Enter/Space on the summary) and the exclusive
|
|
70
|
+
`name` grouping are all native `<details>` behaviour.
|
|
71
|
+
- The chevron is `currentColor` geometry, so it survives `forced-colors`; the
|
|
72
|
+
open-branch accent cue is re-asserted with the system highlight.
|
|
73
|
+
- The chevron spin and the auto-height animation both respect
|
|
74
|
+
`prefers-reduced-motion`.
|