@ponchia/ui 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +552 -8
- package/MIGRATIONS.json +106 -0
- package/README.md +34 -8
- package/annotations/index.d.ts +402 -0
- package/annotations/index.d.ts.map +1 -0
- package/annotations/index.js +792 -0
- package/behaviors/carousel.js +198 -0
- package/behaviors/combobox.js +226 -0
- package/behaviors/command.js +190 -0
- package/behaviors/connectors.js +95 -0
- package/behaviors/crosshair.js +57 -0
- package/behaviors/dialog.js +74 -0
- package/behaviors/disclosure.js +26 -0
- package/behaviors/dismissible.js +25 -0
- package/behaviors/forms.js +186 -0
- package/behaviors/glyph.js +108 -0
- package/behaviors/index.d.ts +79 -0
- package/behaviors/index.js +18 -1409
- package/behaviors/internal.js +97 -0
- package/behaviors/legend.js +67 -0
- package/behaviors/menu.js +47 -0
- package/behaviors/popover.js +179 -0
- package/behaviors/spotlight.js +52 -0
- package/behaviors/table.js +136 -0
- package/behaviors/tabs.js +103 -0
- package/behaviors/theme.js +84 -0
- package/behaviors/toast.js +164 -0
- package/classes/classes.json +1857 -0
- package/classes/index.d.ts +306 -13
- package/classes/index.js +339 -12
- package/classes/vscode.css-custom-data.json +12 -0
- package/connectors/index.d.ts +191 -0
- package/connectors/index.d.ts.map +1 -0
- package/connectors/index.js +275 -0
- package/css/analytical.css +21 -0
- package/css/annotations.css +292 -0
- package/css/app.css +43 -13
- package/css/base.css +15 -10
- package/css/command.css +97 -0
- package/css/connectors.css +110 -0
- package/css/content.css +7 -1
- package/css/crosshair.css +100 -0
- package/css/dataviz.css +5 -1
- package/css/disclosure.css +38 -6
- package/css/dots.css +57 -0
- package/css/feedback.css +111 -2
- package/css/fonts.css +11 -7
- package/css/forms.css +42 -1
- package/css/generated.css +117 -0
- package/css/legend.css +272 -0
- package/css/marks.css +174 -0
- package/css/motion.css +24 -44
- package/css/navigation.css +7 -0
- package/css/overlay.css +31 -1
- package/css/primitives.css +109 -5
- package/css/report.css +39 -81
- package/css/selection.css +46 -0
- package/css/site.css +16 -2
- package/css/sources.css +221 -0
- package/css/spotlight.css +104 -0
- package/css/state.css +121 -0
- package/css/tokens.css +60 -37
- package/css/workbench.css +83 -0
- package/dist/bronto.css +1 -1
- package/dist/css/analytical.css +1 -0
- package/dist/css/annotations.css +1 -0
- package/dist/css/app.css +1 -1
- package/dist/css/base.css +1 -1
- package/dist/css/command.css +1 -0
- package/dist/css/connectors.css +1 -0
- package/dist/css/content.css +1 -1
- package/dist/css/crosshair.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/fonts.css +1 -1
- package/dist/css/forms.css +1 -1
- package/dist/css/generated.css +1 -0
- package/dist/css/legend.css +1 -0
- package/dist/css/marks.css +1 -0
- package/dist/css/motion.css +1 -1
- package/dist/css/navigation.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/selection.css +1 -0
- package/dist/css/site.css +1 -1
- package/dist/css/sources.css +1 -0
- package/dist/css/spotlight.css +1 -0
- package/dist/css/state.css +1 -0
- package/dist/css/tokens.css +1 -1
- package/dist/css/workbench.css +1 -0
- package/docs/adr/0003-theme-model.md +7 -4
- package/docs/annotations.md +425 -0
- package/docs/architecture.md +246 -0
- package/docs/command.md +95 -0
- package/docs/connectors.md +91 -0
- package/docs/contrast.md +116 -92
- package/docs/crosshair.md +63 -0
- package/docs/d2.md +195 -0
- package/docs/generated.md +91 -0
- package/docs/legends.md +184 -0
- package/docs/marks.md +93 -0
- package/docs/mermaid.md +152 -0
- package/docs/reference.md +385 -23
- package/docs/reporting.md +436 -63
- package/docs/selection.md +40 -0
- package/docs/sources.md +137 -0
- package/docs/spotlight.md +78 -0
- package/docs/stability.md +24 -2
- package/docs/state.md +85 -0
- package/docs/usage.md +123 -4
- package/docs/vega.md +225 -0
- package/docs/workbench.md +78 -0
- package/fonts/doto-400.woff2 +0 -0
- package/fonts/doto-500.woff2 +0 -0
- package/fonts/doto-600.woff2 +0 -0
- package/fonts/doto-700.woff2 +0 -0
- package/fonts/doto-800.woff2 +0 -0
- package/fonts/doto-900.woff2 +0 -0
- package/glyphs/glyphs.js +6 -4
- package/llms.txt +362 -14
- package/package.json +115 -12
- package/qwik/index.d.ts +42 -54
- package/qwik/index.d.ts.map +1 -0
- package/qwik/index.js +75 -3
- package/react/index.d.ts +39 -56
- package/react/index.d.ts.map +1 -0
- package/react/index.js +67 -3
- package/solid/index.d.ts +64 -56
- package/solid/index.d.ts.map +1 -0
- package/solid/index.js +70 -3
- package/tokens/d2.d.ts +38 -0
- package/tokens/d2.js +71 -0
- package/tokens/d2.json +43 -0
- package/tokens/index.d.ts +5 -5
- package/tokens/index.js +23 -5
- package/tokens/index.json +9 -0
- package/tokens/mermaid.d.ts +23 -0
- package/tokens/mermaid.js +181 -0
- package/tokens/mermaid.json +163 -0
- package/tokens/resolved.json +45 -1
- package/tokens/skins.js +3 -2
- package/tokens/tokens.dtcg.json +26 -0
- package/tokens/vega.d.ts +34 -0
- package/tokens/vega.js +155 -0
- package/tokens/vega.json +179 -0
- package/fonts/doto-400.ttf +0 -0
- package/fonts/doto-500.ttf +0 -0
- package/fonts/doto-600.ttf +0 -0
- package/fonts/doto-700.ttf +0 -0
- package/fonts/doto-800.ttf +0 -0
- package/fonts/doto-900.ttf +0 -0
package/docs/d2.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# D2
|
|
2
|
+
|
|
3
|
+
[D2](https://d2lang.com) compiles a diagram script to **SVG** (Go CLI or the
|
|
4
|
+
`@terrastruct/d2` WASM build). Like the [Mermaid integration](./mermaid.md),
|
|
5
|
+
`@ponchia/ui` doesn't render diagrams — it **themes** them from your tokens and
|
|
6
|
+
lets you **annotate** the result. Two things ship:
|
|
7
|
+
|
|
8
|
+
- `@ponchia/ui/d2` — helpers that produce on-brand D2 theme overrides.
|
|
9
|
+
- `@ponchia/ui/d2.json` — the resolved slot → hex maps, for any consumer.
|
|
10
|
+
|
|
11
|
+
D2 stays the consumer's renderer; this is config only, and D2 is **not** a
|
|
12
|
+
dependency.
|
|
13
|
+
|
|
14
|
+
## Theme a diagram
|
|
15
|
+
|
|
16
|
+
D2's theme is a compact set of named colour slots. Override them with the bronto
|
|
17
|
+
palette by prepending a `vars` block to your diagram source — `brontoD2Vars()`
|
|
18
|
+
returns exactly that block (both light and dark):
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
import { brontoD2Vars } from '@ponchia/ui/d2';
|
|
22
|
+
|
|
23
|
+
const source = brontoD2Vars() + `
|
|
24
|
+
api -> db: query
|
|
25
|
+
db: Store { shape: cylinder }
|
|
26
|
+
`;
|
|
27
|
+
// → render `source` with the D2 CLI or @terrastruct/d2
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
If you render through D2's Go or WASM API instead of source, pass the slot map to
|
|
31
|
+
its `themeOverrides` / `darkThemeOverrides`:
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
import { brontoD2Overrides } from '@ponchia/ui/d2';
|
|
35
|
+
brontoD2Overrides('dark'); // { N1: '#e6e6e6', B1: '#555555', … }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For a build step or non-JS host, read `@ponchia/ui/d2.json` directly.
|
|
39
|
+
|
|
40
|
+
> **file:// portability.** A report opened from disk (`file://`) cannot
|
|
41
|
+
> `import` the `@ponchia/ui/d2` module **nor** `fetch('…/d2.json')` — the browser
|
|
42
|
+
> blocks both across the `null`/file origin (CORS), exactly as with
|
|
43
|
+
> [Vega](./vega.md#from-a-cdn-no-bundler) and [Mermaid](./mermaid.md#theme-a-diagram).
|
|
44
|
+
> This is rarely an issue for D2, whose native path is **build-time
|
|
45
|
+
> pre-rendering** to a frozen SVG (no client runtime — see below), but if you do
|
|
46
|
+
> theme D2 in the browser over `file://`, **inline** the slot map (paste the
|
|
47
|
+
> object from `d2.json`) rather than importing it. Over `http(s)` the
|
|
48
|
+
> `import`/`fetch` forms both work.
|
|
49
|
+
|
|
50
|
+
### Why resolved colours, not `var(--x)`
|
|
51
|
+
|
|
52
|
+
D2 compiles to a **frozen SVG** in Go/WASM — there is no client CSS cascade, so a
|
|
53
|
+
`var(--accent)` could never resolve. The maps therefore ship **resolved hex per
|
|
54
|
+
theme**, projected from the same token source as
|
|
55
|
+
[`tokens/resolved.json`](./architecture.md) / `charts.json`. Each slot is set
|
|
56
|
+
explicitly (D2 does not derive a ramp from one seed). Because the output is a
|
|
57
|
+
static SVG, **build-time pre-rendering is D2's native path** — ideal for the
|
|
58
|
+
[report layer](./reporting.md).
|
|
59
|
+
|
|
60
|
+
### What the slots paint
|
|
61
|
+
|
|
62
|
+
The mapping keeps a diagram **monochrome by default** — the rationed accent is
|
|
63
|
+
not spent on borders, edges, or shapes the author never marked:
|
|
64
|
+
|
|
65
|
+
| Slots | Paint | bronto mapping |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `N1`–`N3` | Text · muted · subtle | `--text` · `--text-soft` · `--text-dim` |
|
|
68
|
+
| `N4`–`N5` | Strong / regular lines | `--line-strong` · `--line` |
|
|
69
|
+
| `N6`–`N7` | Subtle background · canvas | `--panel-soft` · `--bg` |
|
|
70
|
+
| `B1` | Shape borders **and** connections (edges) | `--line-strong` |
|
|
71
|
+
| `B2`–`B3` | Muted borders | `--line` |
|
|
72
|
+
| `B4`–`B6` | Container fills (outer → leaf) | `--panel-soft` · `--bg-elevated` · `--panel` |
|
|
73
|
+
| `AA*` / `AB*` | Alternative-accent ramps (special-shape fills, class/sql headers) | kept **neutral** |
|
|
74
|
+
|
|
75
|
+
The alt-accent ramps are deliberately neutral: D2 spends them on special shapes
|
|
76
|
+
(cylinders, class/sql-table headers) by default, so mapping them to the accent
|
|
77
|
+
would colour shapes you never marked. Keeping them neutral is what holds the
|
|
78
|
+
monochrome default.
|
|
79
|
+
|
|
80
|
+
### Spending the accent
|
|
81
|
+
|
|
82
|
+
To emphasise a node, opt it into a class that sets the accent explicitly — the
|
|
83
|
+
accent appears only where you ask for it:
|
|
84
|
+
|
|
85
|
+
```d2
|
|
86
|
+
classes: {
|
|
87
|
+
accent: {
|
|
88
|
+
style: { fill: "#d71921"; font-color: "#ffffff"; stroke: "#b2151b" }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
alert: Alert { class: accent }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Use the resolved `--accent` / `--accent-strong` hex (from
|
|
96
|
+
[`tokens/resolved.json`](./architecture.md), per theme) for the `fill` / `stroke`
|
|
97
|
+
so the emphasis matches the rest of the surface. Reserve it for the one thing a
|
|
98
|
+
reader must not miss.
|
|
99
|
+
|
|
100
|
+
> **The label on an accent fill uses `--on-accent`, not `--accent-text`.** A
|
|
101
|
+
> filled-accent node needs **on-accent ink** for its `font-color` — the resolved
|
|
102
|
+
> `--on-accent` token (white on the light accent, black on the dark accent;
|
|
103
|
+
> ≥ 4.5:1, gated in [contrast.md](./contrast.md)). Do **not** reach for
|
|
104
|
+
> `--accent-text` by name: that is the inverse token — _accent-coloured text for
|
|
105
|
+
> a neutral background_ (it resolves to `--accent-strong`, ~1.3:1 on the accent
|
|
106
|
+
> fill, an unreadable label). The literal `#ffffff` above is `--on-accent` for
|
|
107
|
+
> the light theme; pull the per-theme hex from `tokens/resolved.json`.
|
|
108
|
+
|
|
109
|
+
### Frozen inline SVG (no D2 runtime)
|
|
110
|
+
|
|
111
|
+
A static, PDF-first report often has no D2 binary in its pipeline. When you only
|
|
112
|
+
need a few boxes and arrows, **hand-author a token-themed inline `<svg>`** instead
|
|
113
|
+
of running D2 — the same frozen-figure route the [report chart
|
|
114
|
+
recipe](./reporting.md#chart-figure-recipe) uses. Paint each element from the
|
|
115
|
+
token that the live theme map would have resolved, so the frozen diagram still
|
|
116
|
+
re-skins (drive fills from `var(--token)` in the inline `style`/attributes, or
|
|
117
|
+
paste the resolved hex from [`tokens/resolved.json`](./architecture.md) for a
|
|
118
|
+
`file://` PDF):
|
|
119
|
+
|
|
120
|
+
| Diagram element | bronto token | (live D2 slot it mirrors) |
|
|
121
|
+
| --- | --- | --- |
|
|
122
|
+
| Node fill | `--panel` | `B4`–`B6` container fills |
|
|
123
|
+
| Node border | `--line-strong` | `B1` |
|
|
124
|
+
| Node label | `--text` | `N1` |
|
|
125
|
+
| Edge / connector + arrowhead | `--line-strong` | `B1` |
|
|
126
|
+
| Edge label | `--text-soft` | `N2` |
|
|
127
|
+
| Cluster / container fill | `--panel-soft` | `B4` |
|
|
128
|
+
| Accent (emphasised) node fill · its label | `--accent` · `--on-accent` | the `accent` class above |
|
|
129
|
+
|
|
130
|
+
```html
|
|
131
|
+
<svg viewBox="0 0 320 120" role="img" aria-labelledby="flow-title">
|
|
132
|
+
<title id="flow-title">Ingest → Queue → Worker</title>
|
|
133
|
+
<!-- edge -->
|
|
134
|
+
<line x1="92" y1="40" x2="128" y2="40" stroke="var(--line-strong)" marker-end="url(#arrow)" />
|
|
135
|
+
<line x1="220" y1="40" x2="256" y2="40" stroke="var(--line-strong)" marker-end="url(#arrow)" />
|
|
136
|
+
<defs>
|
|
137
|
+
<marker id="arrow" viewBox="0 0 8 8" refX="7" refY="4" markerWidth="6" markerHeight="6" orient="auto">
|
|
138
|
+
<path d="M0,0 L8,4 L0,8 z" fill="var(--line-strong)" />
|
|
139
|
+
</marker>
|
|
140
|
+
</defs>
|
|
141
|
+
<!-- neutral node -->
|
|
142
|
+
<rect x="16" y="22" width="76" height="36" rx="6" fill="var(--panel)" stroke="var(--line-strong)" />
|
|
143
|
+
<text x="54" y="44" text-anchor="middle" fill="var(--text)" font-size="12">Ingest</text>
|
|
144
|
+
<!-- accent (emphasised) node: on-accent ink on the accent fill -->
|
|
145
|
+
<rect x="128" y="22" width="92" height="36" rx="6" fill="var(--accent)" stroke="var(--accent-strong)" />
|
|
146
|
+
<text x="174" y="44" text-anchor="middle" fill="var(--on-accent)" font-size="12">Queue</text>
|
|
147
|
+
<rect x="256" y="22" width="64" height="36" rx="6" fill="var(--panel)" stroke="var(--line-strong)" />
|
|
148
|
+
<text x="288" y="44" text-anchor="middle" fill="var(--text)" font-size="12">Worker</text>
|
|
149
|
+
</svg>
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
For anything larger or graph-laid-out, run D2 with the theme map and freeze its
|
|
153
|
+
output — don't hand-lay a complex graph.
|
|
154
|
+
|
|
155
|
+
### Fit to small screens
|
|
156
|
+
|
|
157
|
+
D2 emits an SVG with explicit `width`/`height` from its layout, so on a narrow
|
|
158
|
+
screen it overflows unless you make it fluid. Two build-time options:
|
|
159
|
+
|
|
160
|
+
- **Scale to fit** — drop the fixed `width`/`height` attributes (keep the
|
|
161
|
+
`viewBox`) so the SVG shrinks to its container. Best for diagrams that stay
|
|
162
|
+
legible at smaller sizes.
|
|
163
|
+
- **Scroll a wide diagram** — wrap it in `overflow-x: auto` so a large diagram
|
|
164
|
+
scrolls inside its box instead of pushing the page wide, the same pattern the
|
|
165
|
+
report layer uses for wide figures.
|
|
166
|
+
|
|
167
|
+
```css
|
|
168
|
+
.diagram-scroll {
|
|
169
|
+
overflow-x: auto;
|
|
170
|
+
}
|
|
171
|
+
.diagram-scroll svg {
|
|
172
|
+
max-inline-size: 100%;
|
|
173
|
+
block-size: auto;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Render **non-sketch** at a fixed `scale` for predictable boxes.
|
|
178
|
+
|
|
179
|
+
## Annotate a diagram
|
|
180
|
+
|
|
181
|
+
D2 output is SVG, so the [annotation layer](./annotations.md) composes onto it
|
|
182
|
+
exactly as in the [Mermaid recipe](./mermaid.md#annotate-a-diagram): render to a
|
|
183
|
+
frozen SVG (the D2 CLI `d2 in.d2 out.svg`, or `@terrastruct/d2` in a build
|
|
184
|
+
script), read the target shape's box, and paste a `<g class="ui-annotation">`
|
|
185
|
+
computed with `@ponchia/ui/annotations`. The same caveats apply — D2's internal
|
|
186
|
+
SVG (element ids, the root transform) is not a public contract, so pin your D2
|
|
187
|
+
version, render **non-sketch**, account for `pad` / `scale`, and avoid
|
|
188
|
+
`animate-interval` (a multi-board animated SVG would not track a single overlay).
|
|
189
|
+
|
|
190
|
+
## Scope
|
|
191
|
+
|
|
192
|
+
bronto owns the theme map (gated: every slot resolves to a colour, both themes,
|
|
193
|
+
no `var()` leaks) and the annotation geometry. It does not own D2's rendering,
|
|
194
|
+
its internal SVG, or its CLI — those stay D2's, and the overlay is a documented
|
|
195
|
+
composition, not a shipped runtime binding.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Generated content & AI trust
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/generated.css` is an opt-in layer of **trust surfaces for AI /
|
|
4
|
+
system-generated content**. Bronto is not a chat framework — it does not ship
|
|
5
|
+
chat bubbles. It ships the surfaces that make generated content legible and
|
|
6
|
+
accountable: a marked region, an origin label, and quiet collapsible reasoning
|
|
7
|
+
and tool-call logs. It pairs with the [source & provenance layer](./sources.md).
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
@import '@ponchia/ui';
|
|
11
|
+
@import '@ponchia/ui/css/generated.css';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Bronto styles the disclosure, origin, and trace surfaces. The host owns model
|
|
15
|
+
metadata, reasoning-visibility policy, tool execution, redaction, and safety.
|
|
16
|
+
Prefer evidence and source links over decorative "AI sparkle" styling. Not in
|
|
17
|
+
the core bundle.
|
|
18
|
+
|
|
19
|
+
## Generated region — `.ui-generated`
|
|
20
|
+
|
|
21
|
+
Wraps a block of machine-authored content with a quiet accent edge and an
|
|
22
|
+
optional origin `__label`.
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<section class="ui-generated">
|
|
26
|
+
<p class="ui-generated__label">Model output</p>
|
|
27
|
+
<p>The migration cut p95 latency by 38% in the first hour.</p>
|
|
28
|
+
</section>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Origin label — `.ui-origin-label`
|
|
32
|
+
|
|
33
|
+
A small tag for "AI assisted", "model output", "tool output", or "human
|
|
34
|
+
reviewed". `--ai` accent-tints it for model-generated origin; the default is a
|
|
35
|
+
neutral tag. The words carry the meaning — the tint is reinforcement.
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<span class="ui-origin-label ui-origin-label--ai">AI generated</span>
|
|
39
|
+
<span class="ui-origin-label">Human reviewed</span>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Reasoning — `.ui-reasoning`
|
|
43
|
+
|
|
44
|
+
A quiet, collapsible "how this was produced" block on a native `<details>` (zero
|
|
45
|
+
JS). Keep it closed by default; the host decides what reasoning is safe to show.
|
|
46
|
+
|
|
47
|
+
```html
|
|
48
|
+
<details class="ui-reasoning">
|
|
49
|
+
<summary>Reasoning</summary>
|
|
50
|
+
<p class="ui-reasoning__body">Compared the Q2 and Q3 latency tables, then…</p>
|
|
51
|
+
</details>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Tool log — `.ui-tool-log` / `.ui-tool-call`
|
|
55
|
+
|
|
56
|
+
A document-grade record of tool invocations. Each `.ui-tool-call` is a
|
|
57
|
+
collapsible `<details>`: a `__name`, an optional `__status` (put a `.ui-dot` or
|
|
58
|
+
text), and a `__body` with the arguments or output.
|
|
59
|
+
|
|
60
|
+
```html
|
|
61
|
+
<div class="ui-tool-log">
|
|
62
|
+
<details class="ui-tool-call">
|
|
63
|
+
<summary>
|
|
64
|
+
<span class="ui-tool-call__name">search_docs(query)</span>
|
|
65
|
+
<span class="ui-tool-call__status"><span class="ui-dot ui-dot--live"></span> ok</span>
|
|
66
|
+
</summary>
|
|
67
|
+
<pre class="ui-tool-call__body">{ "query": "p95 latency", "hits": 3 }</pre>
|
|
68
|
+
</details>
|
|
69
|
+
</div>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Recipe
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
import { ui } from '@ponchia/ui/classes';
|
|
76
|
+
|
|
77
|
+
ui.originLabel({ ai: true }); // "ui-origin-label ui-origin-label--ai"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The other parts are class-only (`cls.generated`, `cls.toolCall`, …) — they have
|
|
81
|
+
no options, so apply the class directly.
|
|
82
|
+
|
|
83
|
+
## Accessibility
|
|
84
|
+
|
|
85
|
+
- The reasoning and tool-call disclosures are native `<details>` — keyboard- and
|
|
86
|
+
screen-reader-accessible with no JS.
|
|
87
|
+
- An origin label must read as text ("AI generated"); the accent tint is not the
|
|
88
|
+
only signal.
|
|
89
|
+
- A confidence / verdict widget is intentionally **not** shipped: don't
|
|
90
|
+
fabricate precision. Add a confidence surface only when the product has a real
|
|
91
|
+
signal to show.
|
package/docs/legends.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Legends & data keys
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/legend.css` is an opt-in layer of **data keys** for charts,
|
|
4
|
+
reports, and analytical figures. A legend maps a visual encoding (colour, a
|
|
5
|
+
pattern, a shape, a gradient) to its meaning. It reads the same Tier-4
|
|
6
|
+
`--chart-*` tokens the [data-viz palette](./reference.md) ships, so a key never
|
|
7
|
+
drifts from the series it describes.
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
@import '@ponchia/ui';
|
|
11
|
+
@import '@ponchia/ui/css/dataviz.css';
|
|
12
|
+
@import '@ponchia/ui/css/legend.css';
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Bronto **paints and positions** the key; it owns no scales, no data→pixel
|
|
16
|
+
mapping, and no series state. Pair it with any chart — a token-themed inline
|
|
17
|
+
SVG, or an external engine like [Vega-Lite](./vega.md) — the host owns the
|
|
18
|
+
chart.
|
|
19
|
+
|
|
20
|
+
## What it is not
|
|
21
|
+
|
|
22
|
+
- Not a chart engine. It does not compute tick positions, "nice" numbers,
|
|
23
|
+
bin thresholds, or bubble radii — you supply the tick/range **text**; the
|
|
24
|
+
CSS positions the slots you give it.
|
|
25
|
+
- Not a colour source. Swatch colour always comes from a `--chart-*` token
|
|
26
|
+
(enforced by `check:legend`), never a hand-rolled hex.
|
|
27
|
+
|
|
28
|
+
## Accessibility: colour is never the only channel
|
|
29
|
+
|
|
30
|
+
A legend that distinguishes series by **colour alone** fails
|
|
31
|
+
[WCAG 1.4.1 Use of Color](https://www.w3.org/WAI/WCAG21/Understanding/use-of-color.html).
|
|
32
|
+
In a legend, the **text label** is the required non-colour channel — every
|
|
33
|
+
entry carries its name, so the meaning survives even if colour is lost
|
|
34
|
+
(`forced-colors`, monochrome print, colour-vision deficiency). Where the chart
|
|
35
|
+
*mark itself* is colour-only (a bare area or line), also pair the swatch with
|
|
36
|
+
its `--chart-pattern-*` so the figure — not just the legend — stays readable.
|
|
37
|
+
|
|
38
|
+
Recommended structure: wrap the figure and its key in a `<figure>` with a
|
|
39
|
+
`<figcaption>`, and give the legend its own group label.
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<figure role="group" aria-labelledby="fig-1-title">
|
|
43
|
+
<figcaption id="fig-1-title">Fig 1 — Weekly focus split</figcaption>
|
|
44
|
+
|
|
45
|
+
<!-- … the chart … -->
|
|
46
|
+
|
|
47
|
+
<ul class="ui-legend" aria-label="Series">
|
|
48
|
+
<li class="ui-legend__item">
|
|
49
|
+
<span
|
|
50
|
+
class="ui-legend__swatch"
|
|
51
|
+
style="--chart-color: var(--chart-1); --chart-pattern: var(--chart-pattern-1)"
|
|
52
|
+
aria-hidden="true"
|
|
53
|
+
></span>
|
|
54
|
+
<span class="ui-legend__label">Research</span>
|
|
55
|
+
</li>
|
|
56
|
+
<li class="ui-legend__item">
|
|
57
|
+
<span class="ui-legend__swatch ui-legend__swatch--2" aria-hidden="true"></span>
|
|
58
|
+
<span class="ui-legend__label">Delivery</span>
|
|
59
|
+
</li>
|
|
60
|
+
</ul>
|
|
61
|
+
</figure>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The swatch is decorative (`aria-hidden="true"`) — the meaning is the label
|
|
65
|
+
text. Set its colour either inline (`--chart-color`) to mirror exactly what the
|
|
66
|
+
chart mark uses, or with a `--N` index helper for the categorical palette.
|
|
67
|
+
|
|
68
|
+
## Parts
|
|
69
|
+
|
|
70
|
+
| Class | Role |
|
|
71
|
+
| --- | --- |
|
|
72
|
+
| `ui-legend` | The container (a wrapping inline row by default). |
|
|
73
|
+
| `ui-legend__title` | Optional heading for the key. |
|
|
74
|
+
| `ui-legend__item` | One entry — swatch + label (+ value). |
|
|
75
|
+
| `ui-legend__swatch` | The colour/pattern chip. |
|
|
76
|
+
| `ui-legend__symbol` | A glyph/shape chip (fill an `.ui-icon` mask). |
|
|
77
|
+
| `ui-legend__label` | The series name (the non-colour channel). |
|
|
78
|
+
| `ui-legend__value` | Optional trailing value/range. |
|
|
79
|
+
| `ui-legend__caption` | Optional footnote (units, source). |
|
|
80
|
+
| `ui-legend__track` | The gradient bar (continuous keys). |
|
|
81
|
+
| `ui-legend__ticks` / `ui-legend__tick` | Tick labels under the track. |
|
|
82
|
+
|
|
83
|
+
### Swatch colour
|
|
84
|
+
|
|
85
|
+
| Approach | Use |
|
|
86
|
+
| --- | --- |
|
|
87
|
+
| `style="--chart-color: var(--chart-3)"` | Mirror any token, any order; add `--chart-pattern` to match a patterned mark. |
|
|
88
|
+
| `class="… ui-legend__swatch--3"` | Categorical palette series 1–8 — sets `--chart-3` for you. |
|
|
89
|
+
|
|
90
|
+
`ui-legend__swatch--circle` and `ui-legend__swatch--line` change the chip shape
|
|
91
|
+
(dot series, line series).
|
|
92
|
+
|
|
93
|
+
A `ui-legend__symbol` chip is an `.ui-icon` mask — it needs a `--icon-mask`
|
|
94
|
+
(e.g. `style="--icon-mask: var(--glyph-dot)"`) or it paints a solid square, like
|
|
95
|
+
any [icon](./reference.md). And an **interactive** legend entry must be a real
|
|
96
|
+
`<button>` (as in the example below) — a non-button `ui-legend__item` carrying
|
|
97
|
+
`data-series` is not keyboard-reachable.
|
|
98
|
+
|
|
99
|
+
**Keying the de-emphasised series.** In an accent-rationed chart — one mark
|
|
100
|
+
painted with [`brontoVegaAccent`](./vega.md#spending-the-accent), the rest left
|
|
101
|
+
quiet with `brontoVegaNeutral` — the quiet neutral **is** the last categorical
|
|
102
|
+
series, `--chart-8` (`#4d5358`). So a legend that honestly mirrors that chart
|
|
103
|
+
uses `ui-legend__swatch--1` for the highlighted entry and
|
|
104
|
+
`ui-legend__swatch--8` for the "everything else" entry — both are real palette
|
|
105
|
+
tokens, so the key never drifts from the marks and `check:legend` stays happy.
|
|
106
|
+
Don't hand-roll a grey: `--chart-8` is the neutral by construction.
|
|
107
|
+
|
|
108
|
+
## Variants
|
|
109
|
+
|
|
110
|
+
| Modifier | Effect |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| `ui-legend--vertical` | Stack entries instead of the wrapping row. |
|
|
113
|
+
| `ui-legend--compact` | Denser type and gaps. |
|
|
114
|
+
| `ui-legend--with-values` | Align a trailing `__value` column across rows. |
|
|
115
|
+
| `ui-legend--gradient` | Continuous colour ramp (`__track` + `__ticks`). |
|
|
116
|
+
| `ui-legend--diverging` | Use the 7-stop diverging ramp (with `--gradient`). |
|
|
117
|
+
| `ui-legend--threshold` | Binned `swatch │ range-label` grid. |
|
|
118
|
+
| `ui-legend--interactive` | Entries are toggle controls (see below). |
|
|
119
|
+
|
|
120
|
+
The recipe mirrors this surface:
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
import { ui } from '@ponchia/ui/classes';
|
|
124
|
+
|
|
125
|
+
ui.legend({ type: 'gradient', diverging: true });
|
|
126
|
+
// "ui-legend ui-legend--gradient ui-legend--diverging"
|
|
127
|
+
ui.legendSwatch({ series: 3, shape: 'circle' });
|
|
128
|
+
// "ui-legend__swatch ui-legend__swatch--3 ui-legend__swatch--circle"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Continuous ramp
|
|
132
|
+
|
|
133
|
+
Supply the min/mid/max tick **text**; the track interpolates the sequential
|
|
134
|
+
ramp in OKLCH (`--diverging` swaps in the diverging ramp around its neutral
|
|
135
|
+
centre).
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<div class="ui-legend ui-legend--gradient" role="group" aria-label="Density">
|
|
139
|
+
<span class="ui-legend__track" aria-hidden="true"></span>
|
|
140
|
+
<span class="ui-legend__ticks">
|
|
141
|
+
<span class="ui-legend__tick">0</span>
|
|
142
|
+
<span class="ui-legend__tick">50</span>
|
|
143
|
+
<span class="ui-legend__tick">100</span>
|
|
144
|
+
</span>
|
|
145
|
+
</div>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Interactive legends (optional)
|
|
149
|
+
|
|
150
|
+
An interactive legend toggles a series on or off. Bronto ships the **control
|
|
151
|
+
surface** only; the **host owns the data**. The split is deliberate (it keeps
|
|
152
|
+
the legend out of chart-engine territory):
|
|
153
|
+
|
|
154
|
+
- **Bronto:** each entry is a `<button aria-pressed>`. The optional
|
|
155
|
+
`initLegend` behavior flips `aria-pressed`, toggles `.is-inactive`, and
|
|
156
|
+
dispatches `bronto:legend:toggle` with `{ detail: { series, active } }`.
|
|
157
|
+
- **You:** listen for the event, hide/show your own series, and announce the
|
|
158
|
+
change through an `aria-live` region you own.
|
|
159
|
+
|
|
160
|
+
```html
|
|
161
|
+
<ul class="ui-legend ui-legend--interactive" data-bronto-legend aria-label="Series">
|
|
162
|
+
<li>
|
|
163
|
+
<button type="button" class="ui-legend__item" aria-pressed="true" data-series="research">
|
|
164
|
+
<span class="ui-legend__swatch ui-legend__swatch--1" aria-hidden="true"></span>
|
|
165
|
+
<span class="ui-legend__label">Research</span>
|
|
166
|
+
</button>
|
|
167
|
+
</li>
|
|
168
|
+
</ul>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
import { initLegend } from '@ponchia/ui/behaviors';
|
|
173
|
+
const stop = initLegend(); // returns a cleanup fn
|
|
174
|
+
document.addEventListener('bronto:legend:toggle', (e) => {
|
|
175
|
+
const { series, active } = e.detail;
|
|
176
|
+
// hide/show your series, then announce it in your own aria-live region
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Convention: `aria-pressed="true"` means the series is **shown** (the default).
|
|
181
|
+
The entry's label never changes on toggle — only `aria-pressed` and
|
|
182
|
+
`.is-inactive` flip, so a screen reader reads a stable name with a clear
|
|
183
|
+
pressed state. React/Solid/Qwik consumers can use `useLegend()` instead of
|
|
184
|
+
calling `initLegend` directly.
|
package/docs/marks.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Text marks & evidence
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/marks.css` is an opt-in layer of **evidence and emphasis marks
|
|
4
|
+
for running text** — the prose counterpart to the [SVG annotations](./annotations.md)
|
|
5
|
+
layer. Annotations call out a figure; marks call out a sentence. The look is
|
|
6
|
+
sober and report-grade (good for docs, audits, and generated/LLM reports), not
|
|
7
|
+
a hand-drawn highlighter.
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
@import '@ponchia/ui';
|
|
11
|
+
@import '@ponchia/ui/css/marks.css';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Monochrome by default — the rationed accent is opt-in via `--accent`, and the
|
|
15
|
+
status tones are only for status-bearing emphasis.
|
|
16
|
+
|
|
17
|
+
## Inline marks — `.ui-mark`
|
|
18
|
+
|
|
19
|
+
Put `.ui-mark` on a `<mark>` so the emphasis is semantic, not just visual.
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<p>
|
|
23
|
+
The migration <mark class="ui-mark ui-mark--accent">cut p95 latency by 38%</mark>,
|
|
24
|
+
but <mark class="ui-mark ui-mark--danger ui-mark--underline">error rate doubled</mark>
|
|
25
|
+
in the first hour.
|
|
26
|
+
</p>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| Style | Effect |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| _(default)_ | Highlighter fill. |
|
|
32
|
+
| `ui-mark--underline` | Coloured underline (no fill). |
|
|
33
|
+
| `ui-mark--box` | Outlined box. |
|
|
34
|
+
| `ui-mark--strike` | Strikethrough (removed/superseded text). |
|
|
35
|
+
|
|
36
|
+
| Tone | Use |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| _(default)_ | Neutral ink emphasis (monochrome). |
|
|
39
|
+
| `ui-mark--accent` | The rationed accent — "this is the proof". |
|
|
40
|
+
| `ui-mark--success` / `--warning` / `--danger` / `--info` | Status-bearing emphasis only. |
|
|
41
|
+
| `ui-mark--muted` | De-emphasis. |
|
|
42
|
+
|
|
43
|
+
`ui-mark--draw` sweeps the highlight in once on load; it respects
|
|
44
|
+
`prefers-reduced-motion` (reduced motion shows the resting full highlight). It
|
|
45
|
+
applies to the highlight fill, so pair it with the default (no style modifier).
|
|
46
|
+
|
|
47
|
+
> **A mark is a behind-text highlight, not a filled chip.** The fill is a
|
|
48
|
+
> low-alpha gradient *behind* the running text, so the text keeps its normal ink
|
|
49
|
+
> and the contrast stays text-on-background — even `ui-mark--accent` is
|
|
50
|
+
> contrast-safe by construction. Don't reach for `--on-accent` here (that ink is
|
|
51
|
+
> for a *solid* accent fill, like a button or a D2 node); a `<mark>` never needs
|
|
52
|
+
> it.
|
|
53
|
+
|
|
54
|
+
## Passage bracket — `.ui-bracket-note`
|
|
55
|
+
|
|
56
|
+
Brackets a whole block and optionally labels it — the prose analogue of
|
|
57
|
+
`ui-annotation--bracket`. Useful for "this paragraph is the evidence/caveat".
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<blockquote class="ui-bracket-note ui-bracket-note--accent">
|
|
61
|
+
<span class="ui-bracket-note__label">Source</span>
|
|
62
|
+
Q3 incident review, §4 — sustained for 47 minutes before rollback.
|
|
63
|
+
</blockquote>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Tones: `--accent` (the rationed accent), `--success`, `--warning`, `--danger`,
|
|
67
|
+
`--info`. The default is a neutral bracket.
|
|
68
|
+
|
|
69
|
+
## Recipes
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
import { ui } from '@ponchia/ui/classes';
|
|
73
|
+
|
|
74
|
+
ui.mark({ tone: 'accent', motion: 'draw' });
|
|
75
|
+
// "ui-mark ui-mark--accent ui-mark--draw"
|
|
76
|
+
ui.mark({ style: 'underline', tone: 'danger' });
|
|
77
|
+
// "ui-mark ui-mark--underline ui-mark--danger"
|
|
78
|
+
ui.bracketNote({ tone: 'warning' });
|
|
79
|
+
// "ui-bracket-note ui-bracket-note--warning"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Accessibility
|
|
83
|
+
|
|
84
|
+
- A `.ui-mark` is **visual emphasis**; it does not announce itself to screen
|
|
85
|
+
readers. When the emphasis carries meaning that the surrounding words don't,
|
|
86
|
+
state it in the text (a screen-reader user can't see the highlight) — the same
|
|
87
|
+
rule as colour (WCAG 1.4.1). Use a native `<mark>` so the relationship is at
|
|
88
|
+
least semantic.
|
|
89
|
+
- In `forced-colors` mode a highlight `background` is dropped, so marks add an
|
|
90
|
+
underline to keep the emphasis visible; the `--box`/`--underline`/`--strike`
|
|
91
|
+
styles already survive as a system colour.
|
|
92
|
+
- `.ui-bracket-note` is a plain block; wrap a quotation in `<blockquote>` (or a
|
|
93
|
+
region with its own heading/label) so its role is conveyed without the border.
|