@ponchia/ui 0.6.5 → 0.6.7
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 +170 -0
- package/README.md +43 -23
- package/behaviors/carousel.d.ts.map +1 -1
- package/behaviors/carousel.js +3 -0
- package/behaviors/dialog.d.ts.map +1 -1
- package/behaviors/dialog.js +14 -8
- package/behaviors/forms.d.ts.map +1 -1
- package/behaviors/forms.js +11 -5
- package/behaviors/index.d.ts +2 -0
- package/behaviors/index.d.ts.map +1 -1
- package/behaviors/index.js +2 -0
- package/behaviors/internal.d.ts +2 -1
- package/behaviors/internal.d.ts.map +1 -1
- package/behaviors/internal.js +23 -3
- package/behaviors/legend.d.ts.map +1 -1
- package/behaviors/legend.js +41 -9
- package/behaviors/splitter.d.ts +26 -0
- package/behaviors/splitter.d.ts.map +1 -0
- package/behaviors/splitter.js +200 -0
- package/behaviors/table.js +3 -3
- package/behaviors/theme.js +2 -2
- package/classes/classes.json +230 -4
- package/classes/index.d.ts +49 -1
- package/classes/index.js +56 -1
- package/classes/vscode.css-custom-data.json +1 -1
- package/css/analytical.css +3 -1
- package/css/app.css +4 -4
- package/css/clamp.css +92 -0
- package/css/figure.css +102 -0
- package/css/highlights.css +50 -0
- package/css/interval.css +90 -0
- package/css/primitives.css +2 -3
- package/css/report-kit.css +38 -0
- package/css/report.css +51 -4
- package/css/sidenote.css +12 -2
- package/css/site.css +2 -1
- package/css/sources.css +5 -0
- package/css/state.css +120 -1
- package/css/table.css +4 -0
- package/css/tokens.css +9 -9
- package/css/workbench.css +101 -8
- package/dist/bronto.css +1 -1
- package/dist/css/analytical.css +1 -1
- package/dist/css/app.css +1 -1
- package/dist/css/clamp.css +1 -0
- package/dist/css/figure.css +1 -0
- package/dist/css/highlights.css +1 -0
- package/dist/css/interval.css +1 -0
- package/dist/css/primitives.css +1 -1
- package/dist/css/report-kit.css +1 -0
- package/dist/css/report.css +1 -1
- package/dist/css/sidenote.css +1 -1
- package/dist/css/site.css +1 -1
- package/dist/css/sources.css +1 -1
- package/dist/css/state.css +1 -1
- package/dist/css/table.css +1 -1
- package/dist/css/tokens.css +1 -1
- package/dist/css/workbench.css +1 -1
- package/docs/adr/0002-scope-and-2026-baseline.md +1 -1
- package/docs/architecture.md +67 -43
- package/docs/clamp.md +49 -0
- package/docs/contrast.md +34 -24
- package/docs/d2.md +37 -0
- package/docs/figure.md +71 -0
- package/docs/frontier-primitives.md +48 -23
- package/docs/highlights.md +52 -0
- package/docs/interop/tailwind.md +148 -0
- package/docs/interval.md +55 -0
- package/docs/legends.md +3 -2
- package/docs/mermaid.md +6 -0
- package/docs/migrations/0.2-to-0.3.md +80 -0
- package/docs/migrations/0.3-to-0.4.md +48 -0
- package/docs/migrations/0.4-to-0.5.md +96 -0
- package/docs/migrations/0.5-to-0.6.md +82 -0
- package/docs/package-contract.md +40 -2
- package/docs/reference.md +79 -6
- package/docs/reporting.md +132 -56
- package/docs/sidenote.md +7 -1
- package/docs/sources.md +1 -1
- package/docs/stability.md +5 -3
- package/docs/state.md +67 -10
- package/docs/theming.md +10 -2
- package/docs/usage.md +31 -11
- package/docs/workbench.md +59 -18
- package/llms.txt +82 -14
- package/package.json +68 -6
- package/qwik/index.d.ts +1 -0
- package/qwik/index.d.ts.map +1 -1
- package/qwik/index.js +26 -21
- package/react/index.d.ts +1 -0
- package/react/index.d.ts.map +1 -1
- package/react/index.js +4 -1
- package/schemas/report-claims.v1.schema.json +137 -0
- package/solid/index.d.ts +2 -0
- package/solid/index.d.ts.map +1 -1
- package/solid/index.js +3 -0
- package/svelte/index.d.ts +88 -0
- package/svelte/index.d.ts.map +1 -0
- package/svelte/index.js +166 -0
- package/tailwind.css +87 -0
- package/tokens/figma.variables.json +2241 -0
- package/tokens/index.js +1 -1
- package/tokens/index.json +2 -2
- package/tokens/resolved.json +3 -3
- package/tokens/tokens.dtcg.json +1 -1
- package/vue/index.d.ts +79 -0
- package/vue/index.d.ts.map +1 -0
- package/vue/index.js +197 -0
package/docs/architecture.md
CHANGED
|
@@ -39,9 +39,12 @@ on top of the CSS, none of which require a framework commitment**:
|
|
|
39
39
|
├── connectors/ pure SVG leader-line geometry kernel (no DOM) [optional]
|
|
40
40
|
├── annotations/ pure SVG callout geometry (builds on connectors) [optional]
|
|
41
41
|
├── glyphs/ dot-matrix glyph registry/renderers [optional]
|
|
42
|
+
├── schemas/ declarative JSON contracts for report/tooling data [optional]
|
|
42
43
|
├── react/ thin React hooks over behaviors [optional peer]
|
|
43
44
|
├── solid/ thin Solid primitives over behaviors [optional peer]
|
|
44
|
-
|
|
45
|
+
├── qwik/ thin Qwik hooks over behaviors (useVisibleTask$) [optional peer]
|
|
46
|
+
├── svelte/ thin Svelte actions over behaviors [optional]
|
|
47
|
+
└── vue/ thin Vue directives over behaviors [optional]
|
|
45
48
|
```
|
|
46
49
|
|
|
47
50
|
### Consequences of each layer
|
|
@@ -57,12 +60,12 @@ on top of the CSS, none of which require a framework commitment**:
|
|
|
57
60
|
- **tokens/** — `index.js` (`cssVars`) is the single source of truth for token
|
|
58
61
|
values. The four `:root` palette blocks of `css/tokens.css` are **generated**
|
|
59
62
|
from it (`scripts/gen-tokens-css.mjs`), as are the JSON artifacts (`index.json`,
|
|
60
|
-
`tokens.dtcg.json`, `resolved.json`). So the dark
|
|
61
|
-
not in three places (the two CSS dark blocks are now
|
|
62
|
-
construction), resolving the duplication ADR-0003 flagged. The
|
|
63
|
-
presets (density / contrast / OLED) stay hand-authored below a marker
|
|
64
|
-
preserved across regeneration. `scripts/check-fresh.mjs` fails CI if
|
|
65
|
-
|
|
63
|
+
`tokens.dtcg.json`, `resolved.json`, `figma.variables.json`). So the dark
|
|
64
|
+
palette is authored once, not in three places (the two CSS dark blocks are now
|
|
65
|
+
identical by construction), resolving the duplication ADR-0003 flagged. The
|
|
66
|
+
CSS-only presets (density / contrast / OLED) stay hand-authored below a marker
|
|
67
|
+
and are preserved across regeneration. `scripts/check-fresh.mjs` fails CI if a
|
|
68
|
+
generated mirror drifts from the model.
|
|
66
69
|
- **classes/** — `cls` is the flat registry; recipes only emit from it;
|
|
67
70
|
`scripts/check-classes.mjs` enforces a bidirectional match with the
|
|
68
71
|
stylesheet's `.ui-*` selectors. The class contract cannot silently rot.
|
|
@@ -76,24 +79,28 @@ on top of the CSS, none of which require a framework commitment**:
|
|
|
76
79
|
- **glyphs/** — static bitmap data and SSR-safe render helpers. The
|
|
77
80
|
256-cell DOM renderers are for display and solid inline icons; the `.ui-icon`
|
|
78
81
|
mask renderer is for dense icon-at-scale use.
|
|
79
|
-
- **react/** / **solid/** / **qwik/** — optional lifecycle
|
|
80
|
-
They do not define markup, own state, or fork
|
|
81
|
-
the vanilla initializers on mount and cleanup
|
|
82
|
+
- **react/** / **solid/** / **qwik/** / **svelte/** / **vue/** — optional lifecycle
|
|
83
|
+
adapters over `behaviors/`. They do not define markup, own state, or fork
|
|
84
|
+
behavior logic; they only run the vanilla initializers on mount and cleanup
|
|
85
|
+
on unmount/dispose. The Svelte and Vue adapters are plain action/directive
|
|
86
|
+
objects, so they do not add runtime dependencies to the package.
|
|
82
87
|
- **`css/analytical.css` — the analytical roll-up.** This convenience file
|
|
83
|
-
`@import`s exactly **
|
|
84
|
-
`legend`, `marks`, `connectors`, `spotlight`, `crosshair`,
|
|
85
|
-
The adjacent opt-in leaves — `sources`,
|
|
86
|
-
|
|
87
|
-
**not** part of the
|
|
88
|
-
|
|
88
|
+
`@import`s exactly **nine** analytical figure/evidence leaves: `figure`,
|
|
89
|
+
`annotations`, `legend`, `marks`, `connectors`, `spotlight`, `crosshair`,
|
|
90
|
+
`selection`, and `highlights`. The adjacent opt-in leaves — `sources`,
|
|
91
|
+
`interval`, `clamp`, `state`, `generated`, `workbench`, and `command` — are
|
|
92
|
+
report/tooling/trust surfaces that are intentionally **not** part of the
|
|
93
|
+
analytical roll-up and must be imported individually. Importing
|
|
94
|
+
`analytical.css` does not pull in any of those seven.
|
|
89
95
|
- **Root export (`.`) is CSS-only.** `exports["."]` resolves to the CSS
|
|
90
96
|
bundle (`dist/bronto.css`). It is a CSS side-effect import for CSS-aware
|
|
91
97
|
bundlers (`@import '@ponchia/ui'` in CSS, or a side-effect
|
|
92
98
|
`import '@ponchia/ui'` in Vite/Astro/SvelteKit). There is no runtime JS at
|
|
93
99
|
the package root — Node/runtime JS imports of `.` are not supported. All JS
|
|
94
100
|
entrypoints are explicit subpaths (`/behaviors`, `/classes`, `/tokens`,
|
|
95
|
-
`/glyphs`, `/
|
|
96
|
-
|
|
101
|
+
`/glyphs`, `/annotations`, `/connectors`, `/react`, `/solid`, `/qwik`,
|
|
102
|
+
`/skins`, `/charts`, `/mermaid`, `/d2`, `/vega`). This is a permanent,
|
|
103
|
+
intentional contract.
|
|
97
104
|
|
|
98
105
|
## Repository layout
|
|
99
106
|
|
|
@@ -108,9 +115,10 @@ generator overwrites them and a drift gate fails CI).
|
|
|
108
115
|
| --- | --- | --- | --- |
|
|
109
116
|
| `css/` | source | yes | The framework. Hand-authored `@layer bronto` CSS. (`css/tokens.css` palette blocks and `css/generated.css` are generated — see below.) |
|
|
110
117
|
| `tokens/index.js` | source | yes | The single source of truth for token **values** (`cssVars`). |
|
|
111
|
-
| `classes/index.js`, `behaviors/`, `annotations/`, `connectors/`, `react/`, `solid/`, `qwik/`, `glyphs/`, `shiki/` | source · published-subpath (path-frozen) | yes — but **do not move** | Authored ESM shipped as-is; the dir name is the public import path. The `.d.ts` beside them are generated/drift-checked: `connectors`/`annotations`/`react`/`solid`/`qwik`/`behaviors` are emitted from JSDoc by `tsc` (`npm run dts:emit`), `classes`/`tokens`/`glyphs` from the runtime. No leaf `.d.ts` is hand-maintained. |
|
|
118
|
+
| `classes/index.js`, `behaviors/`, `annotations/`, `connectors/`, `react/`, `solid/`, `qwik/`, `svelte/`, `vue/`, `glyphs/`, `shiki/` | source · published-subpath (path-frozen) | yes — but **do not move** | Authored ESM shipped as-is; the dir name is the public import path. The `.d.ts` beside them are generated/drift-checked: `connectors`/`annotations`/`react`/`solid`/`qwik`/`svelte`/`vue`/`behaviors` are emitted from JSDoc by `tsc` (`npm run dts:emit`), `classes`/`tokens`/`glyphs` from the runtime. No leaf `.d.ts` is hand-maintained. |
|
|
119
|
+
| `schemas/*.schema.json` | source · published schema files (path-frozen) | yes — but **do not move exported files** | Declarative JSON Schema contracts for sidecars/tooling data. Each exported schema file path is public; the directory itself is not a wildcard import. No validator runtime ships. |
|
|
112
120
|
| `dist/` | generated | no | Build of `css/` (`npm run dist:build`); byte-checked by `check:dist`. |
|
|
113
|
-
| `tokens/index.json`, `tokens/resolved.json`, `tokens/tokens.dtcg.json`, `tokens/charts.json`, `classes/index.d.ts`, `tokens/index.d.ts`, `tokens/{skins,charts}.d.ts`, `glyphs/glyphs.d.ts`, `classes/vscode.css-custom-data.json`, `docs/reference.md` | generated | no | Committed build artifacts; regenerate with `npm run prepack`, never hand-edit. Drift-checked in `npm run check`. |
|
|
121
|
+
| `tokens/index.json`, `tokens/resolved.json`, `tokens/tokens.dtcg.json`, `tokens/figma.variables.json`, `tokens/charts.json`, `classes/index.d.ts`, `tokens/index.d.ts`, `tokens/{skins,charts}.d.ts`, `glyphs/glyphs.d.ts`, `classes/vscode.css-custom-data.json`, `docs/reference.md` | generated | no | Committed build artifacts; regenerate with `npm run prepack`, never hand-edit. Drift-checked in `npm run check`. |
|
|
114
122
|
| `fonts/` | vendored | — | The Doto webfont (woff2) + its OFL license. |
|
|
115
123
|
| `scripts/` | tooling | yes | `gen-*` regenerate artifacts, `check-*` are the drift/contract gates wired into `npm run check`, plus `build-dist`, `serve`, `size-report`. |
|
|
116
124
|
| `docs/` | source (mostly) | yes | Hand-authored docs + ADRs; the curated subset in `package.json` `files` ships in the tarball. `docs/reference.md` is generated. |
|
|
@@ -134,9 +142,9 @@ gating" below), so a version that fails any invariant never reaches npm.
|
|
|
134
142
|
| Invariant | Enforced by |
|
|
135
143
|
| ----------------------------------------------- | ------------------- |
|
|
136
144
|
| exports / import graph / `files` consistent | `check-exports.mjs` |
|
|
137
|
-
| pure generated mirrors fresh — `tokens.css`/`index.json`, `dtcg.json`, `resolved.json`, `classes`/`tokens` `.d.ts`, `reference.md`, vscode data — each byte-equal to its generator (registry: `scripts/lib/artifacts.mjs`) | `check-fresh.mjs` |
|
|
145
|
+
| pure generated mirrors fresh — `tokens.css`/`index.json`, `dtcg.json`, `resolved.json`, `figma.variables.json`, `classes`/`tokens` `.d.ts`, `reference.md`, vscode data — each byte-equal to its generator (registry: `scripts/lib/artifacts.mjs`) | `check-fresh.mjs` |
|
|
138
146
|
| `classes` `cls` ⇄ `.ui-*` selectors | `check-classes.mjs` |
|
|
139
|
-
| `connectors`/`annotations`/`react`/`solid`/`qwik`/`behaviors` `.d.ts` (+ maps) == fresh `tsc` emit of their JSDoc | `check-dts-emit.mjs` |
|
|
147
|
+
| `connectors`/`annotations`/`react`/`solid`/`qwik`/`svelte`/`vue`/`behaviors` `.d.ts` (+ maps) == fresh `tsc` emit of their JSDoc | `check-dts-emit.mjs` |
|
|
140
148
|
| legend swatch colours ⊆ `charts.js` · opt-in | `check-legend.mjs` |
|
|
141
149
|
| color tokens tiered · no raw chromatic color in components | `check-color-policy.mjs` |
|
|
142
150
|
| `css/skins.css` ⇄ `tokens/skins.js` · colorways opt-in | `check-skins.mjs` |
|
|
@@ -145,6 +153,10 @@ gating" below), so a version that fails any invariant never reaches npm.
|
|
|
145
153
|
| `shiki/nothing.json` valid + on rationed palette | `check-shiki.mjs` |
|
|
146
154
|
| `dist/*.css` == fresh build of `css/` + budget | `check-dist.mjs` |
|
|
147
155
|
| published tarball == intended `files` only | `check-pack.mjs` |
|
|
156
|
+
| packed public text contains no private terms, local paths, or secret-looking assignments | `check-public-hygiene.mjs` |
|
|
157
|
+
| CSS custom-property references resolve or carry an explicit fallback/host boundary | `check-variables.mjs` |
|
|
158
|
+
| `MIGRATIONS.json` edges have structured rules and matching docs | `check-migrations.mjs` |
|
|
159
|
+
| example inventory ⇄ CI matrix ⇄ browser-smoke list ⇄ README rows ⇄ preview ports | `check-examples.mjs` |
|
|
148
160
|
| published `.d.ts` compile + reject typos | `tsc` (`check:types`) |
|
|
149
161
|
| CSS style/correctness | Stylelint |
|
|
150
162
|
| non-CSS source style | Prettier (`check:format`) |
|
|
@@ -163,29 +175,40 @@ freshness (`check-fresh`).
|
|
|
163
175
|
|
|
164
176
|
## Release gating
|
|
165
177
|
|
|
166
|
-
`release.yml` (on a pushed `v*` tag) is a
|
|
178
|
+
`release.yml` (on a pushed `v*` tag) is a six-job DAG, serialized by a
|
|
167
179
|
`concurrency: release-publish` group so two tags can't race the dist-tag
|
|
168
180
|
pointer:
|
|
169
181
|
|
|
170
|
-
- `validate` — read-only:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
only
|
|
174
|
-
|
|
175
|
-
|
|
182
|
+
- `validate` — read-only: verifies the tag commit is reachable from `main`,
|
|
183
|
+
then runs `npm run check`, `npm test`, and the tag↔version match. `check`
|
|
184
|
+
includes `check:release`; for a prerelease tag the base version's CHANGELOG
|
|
185
|
+
section need only exist (`## Unreleased — x.y.z` is fine) — only a stable
|
|
186
|
+
release must carry a dated heading.
|
|
187
|
+
- `e2e` — `needs: validate`: Playwright (visual + axe a11y, both themes,
|
|
188
|
+
cross-engine) in the pinned `mcr.microsoft.com/playwright` container. Local
|
|
189
|
+
cross-engine reproduction without screenshot rasterisation is
|
|
190
|
+
`npm run test:e2e:nonpixel`; use `npm run test:e2e` or
|
|
191
|
+
`npm run test:e2e:chromium` only in the pinned container when the pixel
|
|
192
|
+
baseline gate itself is in scope.
|
|
176
193
|
- `examples` — `needs: validate`: builds the downstream example
|
|
177
194
|
apps against the **packed tarball**, mirroring CI. Catches a broken
|
|
178
195
|
published surface (exports map / missing file / unresolved subpath)
|
|
179
196
|
that `check:pack`'s file-allowlist inspection cannot — so the release
|
|
180
197
|
path runs the same consumer smoke as merge-to-main.
|
|
181
|
-
- `publish-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
198
|
+
- `publish-preflight` — `needs: [validate, e2e, examples]`: installs with
|
|
199
|
+
lifecycle scripts disabled, runs `npm pack --dry-run --ignore-scripts`, and
|
|
200
|
+
writes the pack manifest + size report to the job summary for review before
|
|
201
|
+
the protected publish approval.
|
|
202
|
+
- `publish-npm` — `needs: publish-preflight`: `npm publish --ignore-scripts`
|
|
203
|
+
with provenance. Runs in the `npm-publish` **Environment**
|
|
204
|
+
(required-reviewer protection), so after the gates and preflight pass the run
|
|
205
|
+
pauses for a manual approval in the Actions UI before anything reaches npm —
|
|
206
|
+
a guard against an accidental tag push publishing. Dist-tag is derived from
|
|
207
|
+
the tag: stable (`v0.4.0`) → `latest`; SemVer prerelease (`v0.4.0-rc.1`, any
|
|
208
|
+
hyphenated identifier) → `next`, so the default `npm i @ponchia/ui` never
|
|
209
|
+
moves onto an unstable build (opt in with `@ponchia/ui@next`). Post-publish
|
|
210
|
+
`npm view` registry observation is best-effort only: a registry read flake must
|
|
211
|
+
not fail the job after the immutable publish already succeeded.
|
|
189
212
|
- `release-notes` — `needs: publish-npm`: a GitHub Release for visibility
|
|
190
213
|
(transitively gated on a successful publish, hence on the gates above);
|
|
191
214
|
prerelease tags are flagged so they aren't surfaced as "Latest". The Release
|
|
@@ -194,7 +217,7 @@ pointer:
|
|
|
194
217
|
source of truth, surfaced where readers look.
|
|
195
218
|
|
|
196
219
|
Because the documented install path is the npm package, **the npm publish
|
|
197
|
-
is a real gate**: if `validate`, `e2e`,
|
|
220
|
+
is a real gate**: if `validate`, `e2e`, `examples`, or `publish-preflight` fails,
|
|
198
221
|
`publish-npm` never runs, the version never reaches the registry, and
|
|
199
222
|
consumers never resolve it.
|
|
200
223
|
(Corollary: a flaky `e2e` blocks releases — that is deliberate; fix the
|
|
@@ -210,9 +233,9 @@ Process still applies: bump `package.json`, land on `main`, go green, tag.
|
|
|
210
233
|
## Decision — distribution: npm public `@ponchia/ui`
|
|
211
234
|
|
|
212
235
|
Decided 2026-05-15. The framework is consumed by a growing set of
|
|
213
|
-
heterogeneous web frontends (Astro, SvelteKit, React, Solid, Qwik,
|
|
214
|
-
several deploying via third-party CI. The only option where
|
|
215
|
-
frontend is `npm i @ponchia/ui` with zero per-consumer config is **npm
|
|
236
|
+
heterogeneous web frontends (Astro, SvelteKit, React, Solid, Qwik, Vue,
|
|
237
|
+
Tailwind, vanilla), several deploying via third-party CI. The only option where
|
|
238
|
+
onboarding a new frontend is `npm i @ponchia/ui` with zero per-consumer config is **npm
|
|
216
239
|
public**, and it uniquely also closes the release-gating gap (publish *is*
|
|
217
240
|
the gate). GitHub Packages was rejected: it requires auth to install even
|
|
218
241
|
public packages, i.e. an `.npmrc` + token on every frontend and CI runner —
|
|
@@ -237,8 +260,9 @@ explained, not surprising.
|
|
|
237
260
|
provenance.
|
|
238
261
|
- Run `npm pack --dry-run --json` locally or from CI logs and confirm the
|
|
239
262
|
intended file count/payload.
|
|
240
|
-
- Build the packed examples matrix
|
|
241
|
-
|
|
263
|
+
- Build the packed examples matrix from the tarball, not a workspace link:
|
|
264
|
+
`npm run test:examples` covers vanilla, Astro, SvelteKit, Vue, React, Solid,
|
|
265
|
+
Qwik, Tailwind, and report-static, with browser smokes for runtime examples.
|
|
242
266
|
- Confirm the GitHub Release body matches the curated changelog section.
|
|
243
267
|
- If a bad package is published, deprecate that exact version on npm, publish a
|
|
244
268
|
patched version, and link the deprecation note to the changelog/security
|
package/docs/clamp.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Clamp
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/clamp.css` is an opt-in bounded excerpt primitive for source
|
|
4
|
+
excerpts, claim basis, caveats, and evidence text that should scan compactly but
|
|
5
|
+
remain reachable.
|
|
6
|
+
|
|
7
|
+
```css
|
|
8
|
+
@import '@ponchia/ui';
|
|
9
|
+
@import '@ponchia/ui/css/clamp.css';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Bounded excerpt
|
|
13
|
+
|
|
14
|
+
```html
|
|
15
|
+
<div class="ui-clamp" style="--clamp-lines: 3">
|
|
16
|
+
<input class="ui-clamp__toggle" id="source-excerpt" type="checkbox" />
|
|
17
|
+
<p class="ui-clamp__body">
|
|
18
|
+
The source excerpt remains real text in the DOM. The visible block is
|
|
19
|
+
clamped for scanning, but the full passage is available through the reveal
|
|
20
|
+
control and is expanded for print.
|
|
21
|
+
</p>
|
|
22
|
+
<label class="ui-clamp__control" for="source-excerpt">
|
|
23
|
+
<span class="ui-clamp__more">Show more</span>
|
|
24
|
+
<span class="ui-clamp__less">Show less</span>
|
|
25
|
+
</label>
|
|
26
|
+
</div>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If the host should not offer expansion, omit the toggle and control and keep
|
|
30
|
+
only `ui-clamp` plus `ui-clamp__body`.
|
|
31
|
+
|
|
32
|
+
## Contract
|
|
33
|
+
|
|
34
|
+
| Class | Role |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| `.ui-clamp` | Wrapper and `--clamp-lines` host. |
|
|
37
|
+
| `.ui-clamp__body` | The clamped text block. |
|
|
38
|
+
| `.ui-clamp__toggle` | Optional checkbox state for CSS-only reveal. |
|
|
39
|
+
| `.ui-clamp__control` | Optional visible reveal control. |
|
|
40
|
+
| `.ui-clamp__more` / `.ui-clamp__less` | Explicit labels for closed/open state. |
|
|
41
|
+
|
|
42
|
+
| Custom property | On | Meaning |
|
|
43
|
+
| --- | --- | --- |
|
|
44
|
+
| `--clamp-lines` | `.ui-clamp` | Number of lines before the excerpt is clamped. Default `4`. |
|
|
45
|
+
|
|
46
|
+
## Print
|
|
47
|
+
|
|
48
|
+
Print expands the body and hides the toggle/control. Do not use `ui-clamp` to
|
|
49
|
+
hide information from archived or PDF output.
|
package/docs/contrast.md
CHANGED
|
@@ -15,7 +15,8 @@ model (`tokens/resolved.json`) so it cannot drift from the palette, and
|
|
|
15
15
|
|
|
16
16
|
- **Body / UI text** pairings are guaranteed **WCAG 2.1 AA — 4.5:1**
|
|
17
17
|
(1.4.3). This covers `--text`, `--text-soft`, `--text-dim`,
|
|
18
|
-
`--accent-text`,
|
|
18
|
+
`--accent-text`, neutral text on soft accent-tint components, and the
|
|
19
|
+
primary-button label.
|
|
19
20
|
- **Non-text UI** (focus ring, accent fill, status colour) is guaranteed
|
|
20
21
|
**3:1** (1.4.11 non-text contrast / the large-text bar). These are
|
|
21
22
|
deliberately *not* held to 4.5:1 — a focus
|
|
@@ -32,7 +33,8 @@ model (`tokens/resolved.json`) so it cannot drift from the palette, and
|
|
|
32
33
|
further but is out of scope for this gated baseline.
|
|
33
34
|
|
|
34
35
|
Translucent foregrounds (soft fills) are alpha-flattened over their
|
|
35
|
-
background before measuring
|
|
36
|
+
background before measuring. When a translucent background is a component
|
|
37
|
+
tint, the table names the neutral base it is composited over.
|
|
36
38
|
|
|
37
39
|
Overall: **all contractual pairings meet their floor ✅**.
|
|
38
40
|
|
|
@@ -51,6 +53,7 @@ Overall: **all contractual pairings meet their floor ✅**.
|
|
|
51
53
|
| `--text-dim` | `--surface-muted` | Dim/meta text on a muted panel | AA text (4.5:1) | 4.74:1 | Lc 66.7 | ✅ pass |
|
|
52
54
|
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.32:1 | Lc 75.4 | ✅ pass |
|
|
53
55
|
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 6.96:1 | Lc 82.1 | ✅ pass |
|
|
56
|
+
| `--text-soft` | `--accent-soft` over `--surface-muted` | Neutral tag/badge text on an accent tint | AA text (4.5:1) | 8.88:1 | Lc 77.0 | ✅ pass |
|
|
54
57
|
| `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 5.91:1 | Lc 71.0 | ℹ️ not gated |
|
|
55
58
|
| `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 6.31:1 | Lc 75.3 | ℹ️ not gated |
|
|
56
59
|
| `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.18:1 | Lc 78.9 | ✅ pass |
|
|
@@ -77,10 +80,11 @@ Overall: **all contractual pairings meet their floor ✅**.
|
|
|
77
80
|
| `--text-dim` | `--bg` | Dim/meta text on page background | AA text (4.5:1) | 7.16:1 | Lc 50.3 | ✅ pass |
|
|
78
81
|
| `--text-dim` | `--surface` | Dim/meta text on a card | AA text (4.5:1) | 6.52:1 | Lc 49.3 | ✅ pass |
|
|
79
82
|
| `--text-dim` | `--surface-muted` | Dim/meta text on a muted panel | AA text (4.5:1) | 5.94:1 | Lc 48.1 | ✅ pass |
|
|
80
|
-
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.
|
|
81
|
-
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 5.
|
|
82
|
-
| `--
|
|
83
|
-
| `--accent-text` | `--
|
|
83
|
+
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.42:1 | Lc 46.6 | ✅ pass |
|
|
84
|
+
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 5.84:1 | Lc 45.6 | ✅ pass |
|
|
85
|
+
| `--text-soft` | `--accent-soft` over `--surface-muted` | Neutral tag/badge text on an accent tint | AA text (4.5:1) | 8.05:1 | Lc 68.4 | ✅ pass |
|
|
86
|
+
| `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 2.42:1 | Lc 42.2 | ℹ️ not gated |
|
|
87
|
+
| `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 2.63:1 | Lc 47.4 | ℹ️ not gated |
|
|
84
88
|
| `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.95:1 | Lc 42.9 | ✅ pass |
|
|
85
89
|
| `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 5.95:1 | Lc 42.9 | ✅ pass |
|
|
86
90
|
| `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 5.31:1 | Lc 40.0 | ✅ pass |
|
|
@@ -109,8 +113,9 @@ palette untouched). Accents are authored in OKLCH; `--accent-text` is the
|
|
|
109
113
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
110
114
|
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.75:1 | Lc 78.9 | ✅ pass |
|
|
111
115
|
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 7.44:1 | Lc 85.6 | ✅ pass |
|
|
112
|
-
| `--
|
|
113
|
-
| `--accent-text` | `--
|
|
116
|
+
| `--text-soft` | `--accent-soft` over `--surface-muted` | Neutral tag/badge text on an accent tint | AA text (4.5:1) | 9.13:1 | Lc 78.7 | ✅ pass |
|
|
117
|
+
| `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 6.48:1 | Lc 76.1 | ℹ️ not gated |
|
|
118
|
+
| `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 6.85:1 | Lc 79.9 | ℹ️ not gated |
|
|
114
119
|
| `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.66:1 | Lc 83.3 | ✅ pass |
|
|
115
120
|
| `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 5.66:1 | Lc 83.3 | ✅ pass |
|
|
116
121
|
| `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 5.14:1 | Lc 71.4 | ✅ pass |
|
|
@@ -121,10 +126,11 @@ palette untouched). Accents are authored in OKLCH; `--accent-text` is the
|
|
|
121
126
|
|
|
122
127
|
| Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
|
|
123
128
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
124
|
-
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 11.
|
|
125
|
-
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 10.
|
|
126
|
-
| `--
|
|
127
|
-
| `--accent-text` | `--
|
|
129
|
+
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 11.84:1 | Lc 76.1 | ✅ pass |
|
|
130
|
+
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 10.77:1 | Lc 75.1 | ✅ pass |
|
|
131
|
+
| `--text-soft` | `--accent-soft` over `--surface-muted` | Neutral tag/badge text on an accent tint | AA text (4.5:1) | 6.83:1 | Lc 65.5 | ✅ pass |
|
|
132
|
+
| `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 1.46:1 | Lc 20.8 | ℹ️ not gated |
|
|
133
|
+
| `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 1.51:1 | Lc 23.2 | ℹ️ not gated |
|
|
128
134
|
| `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 11.88:1 | Lc 71.6 | ✅ pass |
|
|
129
135
|
| `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 11.88:1 | Lc 71.6 | ✅ pass |
|
|
130
136
|
| `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 10.60:1 | Lc 69.9 | ✅ pass |
|
|
@@ -137,8 +143,9 @@ palette untouched). Accents are authored in OKLCH; `--accent-text` is the
|
|
|
137
143
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
138
144
|
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 12.23:1 | Lc 93.3 | ✅ pass |
|
|
139
145
|
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 13.47:1 | Lc 99.9 | ✅ pass |
|
|
140
|
-
| `--
|
|
141
|
-
| `--accent-text` | `--
|
|
146
|
+
| `--text-soft` | `--accent-soft` over `--surface-muted` | Neutral tag/badge text on an accent tint | AA text (4.5:1) | 8.78:1 | Lc 76.3 | ✅ pass |
|
|
147
|
+
| `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 11.31:1 | Lc 88.2 | ℹ️ not gated |
|
|
148
|
+
| `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 12.15:1 | Lc 92.8 | ℹ️ not gated |
|
|
142
149
|
| `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 11.74:1 | Lc 100.6 | ✅ pass |
|
|
143
150
|
| `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 11.74:1 | Lc 100.6 | ✅ pass |
|
|
144
151
|
| `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 10.66:1 | Lc 90.4 | ✅ pass |
|
|
@@ -149,10 +156,11 @@ palette untouched). Accents are authored in OKLCH; `--accent-text` is the
|
|
|
149
156
|
|
|
150
157
|
| Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
|
|
151
158
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
152
|
-
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 12.
|
|
153
|
-
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 11.
|
|
154
|
-
| `--
|
|
155
|
-
| `--accent-text` | `--
|
|
159
|
+
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 12.72:1 | Lc 80.3 | ✅ pass |
|
|
160
|
+
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 11.57:1 | Lc 79.3 | ✅ pass |
|
|
161
|
+
| `--text-soft` | `--accent-soft` over `--surface-muted` | Neutral tag/badge text on an accent tint | AA text (4.5:1) | 6.66:1 | Lc 65.0 | ✅ pass |
|
|
162
|
+
| `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 1.38:1 | Lc 18.0 | ℹ️ not gated |
|
|
163
|
+
| `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 1.42:1 | Lc 19.9 | ℹ️ not gated |
|
|
156
164
|
| `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 12.86:1 | Lc 75.6 | ✅ pass |
|
|
157
165
|
| `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 12.86:1 | Lc 75.6 | ✅ pass |
|
|
158
166
|
| `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 11.48:1 | Lc 74.1 | ✅ pass |
|
|
@@ -165,8 +173,9 @@ palette untouched). Accents are authored in OKLCH; `--accent-text` is the
|
|
|
165
173
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
166
174
|
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.22:1 | Lc 76.6 | ✅ pass |
|
|
167
175
|
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 6.85:1 | Lc 83.3 | ✅ pass |
|
|
168
|
-
| `--
|
|
169
|
-
| `--accent-text` | `--
|
|
176
|
+
| `--text-soft` | `--accent-soft` over `--surface-muted` | Neutral tag/badge text on an accent tint | AA text (4.5:1) | 9.16:1 | Lc 78.9 | ✅ pass |
|
|
177
|
+
| `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 5.98:1 | Lc 74.1 | ℹ️ not gated |
|
|
178
|
+
| `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 6.32:1 | Lc 77.7 | ℹ️ not gated |
|
|
170
179
|
| `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.19:1 | Lc 80.7 | ✅ pass |
|
|
171
180
|
| `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 5.19:1 | Lc 80.7 | ✅ pass |
|
|
172
181
|
| `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 4.71:1 | Lc 68.6 | ✅ pass |
|
|
@@ -177,10 +186,11 @@ palette untouched). Accents are authored in OKLCH; `--accent-text` is the
|
|
|
177
186
|
|
|
178
187
|
| Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
|
|
179
188
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
180
|
-
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) |
|
|
181
|
-
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 11.
|
|
182
|
-
| `--
|
|
183
|
-
| `--accent-text` | `--
|
|
189
|
+
| `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 13.18:1 | Lc 82.8 | ✅ pass |
|
|
190
|
+
| `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 11.99:1 | Lc 81.8 | ✅ pass |
|
|
191
|
+
| `--text-soft` | `--accent-soft` over `--surface-muted` | Neutral tag/badge text on an accent tint | AA text (4.5:1) | 6.66:1 | Lc 65.0 | ✅ pass |
|
|
192
|
+
| `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 1.33:1 | Lc 15.3 | ℹ️ not gated |
|
|
193
|
+
| `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 1.37:1 | Lc 17.3 | ℹ️ not gated |
|
|
184
194
|
| `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 13.75:1 | Lc 79.7 | ✅ pass |
|
|
185
195
|
| `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 13.75:1 | Lc 79.7 | ✅ pass |
|
|
186
196
|
| `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 12.27:1 | Lc 78.5 | ✅ pass |
|
package/docs/d2.md
CHANGED
|
@@ -153,6 +153,43 @@ paste the resolved hex from [`tokens/resolved.json`](./architecture.md) for a
|
|
|
153
153
|
For anything larger or graph-laid-out, run D2 with the theme map and freeze its
|
|
154
154
|
output — don't hand-lay a complex graph.
|
|
155
155
|
|
|
156
|
+
### Tokenize D2 output — one inline SVG that re-skins live
|
|
157
|
+
|
|
158
|
+
A frozen D2 SVG carries **resolved hex**, so a dynamic (screen-only) report
|
|
159
|
+
would need a light SVG and a dark SVG and JS/CSS to swap them — and the hidden
|
|
160
|
+
twin is dead weight. Instead, post-process the rendered SVG's colours back into
|
|
161
|
+
tokens, and ONE inline SVG re-skins live when `data-theme` flips (this is the
|
|
162
|
+
inverse of the resolved-hex rule above: it only works for **inline** SVG in a
|
|
163
|
+
themed page, never for `file://`/PDF artifacts or `<img>` embeds):
|
|
164
|
+
|
|
165
|
+
1. Render **light only** with the theme map (`brontoD2Vars()` prepended).
|
|
166
|
+
2. D2 emits each colour twice: as inline `fill="#hex"`/`stroke="#hex"` AND as
|
|
167
|
+
class rules in an embedded `<style>` block — **not just `.fill-*` /
|
|
168
|
+
`.stroke-*`: there are also `.color-*` and `.background-color-*` rules**
|
|
169
|
+
(they carry the same hex and trip any raw-colour gate). **The style rules
|
|
170
|
+
win over the inline attributes**, so strip ALL hex-bearing rules from the
|
|
171
|
+
`<style>` first — only then do attribute rewrites take effect.
|
|
172
|
+
3. Rewrite the inline hex → `var(--token)` using the slot table above
|
|
173
|
+
(`N1`→`--text`, `N4`/`B1`→`--line-strong`, `N6`/`B4`→`--panel-soft`,
|
|
174
|
+
`B6`→`--panel`, accent class fill→`--accent`, its ink→`--on-accent`, …).
|
|
175
|
+
4. Leave `<mask>` `fill="black"`/`"white"` keywords alone — that is a
|
|
176
|
+
luminance mask, not a colour.
|
|
177
|
+
5. Make the outer `<svg>` fluid (drop `width`/`height`, keep `viewBox`) and
|
|
178
|
+
inject `<title>` + `<desc>` with `role="img" aria-labelledby` before
|
|
179
|
+
inlining.
|
|
180
|
+
|
|
181
|
+
The result follows the page theme with zero swap machinery, and avoids the
|
|
182
|
+
visual-QA traps of the two-SVG approach (a `display:none` twin is easy to
|
|
183
|
+
flag as a blank figure).
|
|
184
|
+
|
|
185
|
+
> **Avoid `tooltip:` and `|md` markdown shapes in frozen report SVGs.** Both
|
|
186
|
+
> make D2 embed GitHub-Primer styling that survives tokenization: tooltips
|
|
187
|
+
> render Octicon info-icons and markdown text ships Primer CSS, each full of
|
|
188
|
+
> foreign `var(--color-*)` references and extra hex (`#2e3346`-class values
|
|
189
|
+
> outside the theme map). Fold tooltip text into the node label and use plain
|
|
190
|
+
> labels or `shape: text` instead — or strip the tooltip appendix from the
|
|
191
|
+
> SVG before inlining.
|
|
192
|
+
|
|
156
193
|
### Fit to small screens
|
|
157
194
|
|
|
158
195
|
D2 emits an SVG with explicit `width`/`height` from its layout, so on a narrow
|
package/docs/figure.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Figure
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/figure.css` is an opt-in analytical/report figure stage. It
|
|
4
|
+
does not render charts. It gives charts, diagrams, screenshots, and annotated
|
|
5
|
+
SVGs a stable frame: caption, media stage, optional overlay, optional key, and
|
|
6
|
+
fallback data.
|
|
7
|
+
|
|
8
|
+
```css
|
|
9
|
+
@import '@ponchia/ui';
|
|
10
|
+
@import '@ponchia/ui/css/figure.css';
|
|
11
|
+
@import '@ponchia/ui/css/legend.css';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Use it with `ui-report__figure` when the figure sits in a report. Use it alone
|
|
15
|
+
when the same stage appears in a dashboard, doc page, or generated artifact.
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<figure class="ui-figure ui-report__figure ui-print-exact" role="group" aria-labelledby="fig-title">
|
|
19
|
+
<figcaption id="fig-title" class="ui-figure__caption ui-report__caption">
|
|
20
|
+
Fig 1 - Weekly focus split
|
|
21
|
+
</figcaption>
|
|
22
|
+
<div class="ui-figure__body ui-figure__body--key-right">
|
|
23
|
+
<div class="ui-figure__stage" style="--figure-max-inline: 30rem; --figure-min-block: 12rem">
|
|
24
|
+
<svg class="ui-figure__media" viewBox="0 0 320 120" role="img" aria-labelledby="svg-title svg-desc">
|
|
25
|
+
<title id="svg-title">Weekly focus split</title>
|
|
26
|
+
<desc id="svg-desc">Research is the largest category.</desc>
|
|
27
|
+
<rect x="80" y="24" width="180" height="20" fill="var(--chart-1)" />
|
|
28
|
+
<rect x="80" y="64" width="110" height="20" fill="var(--chart-2)" />
|
|
29
|
+
</svg>
|
|
30
|
+
<svg class="ui-figure__overlay" viewBox="0 0 320 120" aria-hidden="true">
|
|
31
|
+
<path class="ui-annotation__connector" d="M260,34L292,14" />
|
|
32
|
+
</svg>
|
|
33
|
+
</div>
|
|
34
|
+
<div class="ui-figure__key">
|
|
35
|
+
<ul class="ui-legend" aria-label="Series">...</ul>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<div class="ui-figure__data ui-table-wrap">
|
|
39
|
+
<table class="ui-table ui-table--dense">...</table>
|
|
40
|
+
</div>
|
|
41
|
+
</figure>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Contract
|
|
45
|
+
|
|
46
|
+
| Class | Role |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `.ui-figure` | Figure wrapper. |
|
|
49
|
+
| `.ui-figure__caption` | Caption text; can compose with `ui-report__caption`. |
|
|
50
|
+
| `.ui-figure__body` | Stage/key layout wrapper. |
|
|
51
|
+
| `.ui-figure__body--key-right` | Two-column body: visual stage plus right-side key. |
|
|
52
|
+
| `.ui-figure__stage` | Stable, centered media stage; `position: relative` for overlays. |
|
|
53
|
+
| `.ui-figure__media` | Primary SVG, image, canvas, or rendered figure output. |
|
|
54
|
+
| `.ui-figure__overlay` | Absolute, pointer-transparent overlay for annotations or guides. |
|
|
55
|
+
| `.ui-figure__key` | Legend/key slot. |
|
|
56
|
+
| `.ui-figure__data` | Fallback data slot, usually a `ui-table-wrap`. |
|
|
57
|
+
|
|
58
|
+
| Custom property | On | Meaning |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| `--figure-max-inline` | `.ui-figure__stage` | Maximum stage width, default `42rem`. |
|
|
61
|
+
| `--figure-min-block` | `.ui-figure__stage` | Reserved stage height for late-rendered media. |
|
|
62
|
+
| `--figure-key-width` | `.ui-figure__body--key-right` | Right key column width before mobile collapse. |
|
|
63
|
+
|
|
64
|
+
## Boundary
|
|
65
|
+
|
|
66
|
+
- Bronto owns layout, responsive collapse, overlay positioning, print spacing,
|
|
67
|
+
and class names.
|
|
68
|
+
- The host owns scales, data binding, SVG/canvas/chart rendering, fallback table
|
|
69
|
+
rows, annotation text, and accessibility labels.
|
|
70
|
+
- A figure should always have a `<figcaption>`. Data-bearing SVGs still need
|
|
71
|
+
`<title>` and `<desc>`.
|
|
@@ -25,7 +25,7 @@ The pattern that worked for annotations should stay the rule:
|
|
|
25
25
|
- Prefer CSS and markup first. Add JS only when the browser cannot express the
|
|
26
26
|
behavior without measuring, filtering, keyboard state, or pointer tracking.
|
|
27
27
|
|
|
28
|
-
This keeps Bronto useful across Astro, SvelteKit, React, Solid, Qwik, plain
|
|
28
|
+
This keeps Bronto useful across Astro, SvelteKit, Vue, React, Solid, Qwik, plain
|
|
29
29
|
HTML, and generated static reports without becoming a framework component kit.
|
|
30
30
|
|
|
31
31
|
## Already aligned in 0.5.0
|
|
@@ -164,19 +164,21 @@ and footnotes, but not a trust grammar. The shipped surface and its trust-state
|
|
|
164
164
|
vocabulary are documented with the component; richer preview popovers remain
|
|
165
165
|
host-owned.
|
|
166
166
|
|
|
167
|
-
### 2. Lifecycle and system-state UI — 🟡 `ui-state` family shipped in 0.5.0
|
|
167
|
+
### 2. Lifecycle and system-state UI — 🟡 `ui-state` family shipped in 0.5.0, `ui-job` added for 0.6.7
|
|
168
168
|
|
|
169
169
|
Shipped as `@ponchia/ui/css/state.css` (`ui-state` + the canonical state matrix
|
|
170
|
-
+ `ui-syncbar`), matching the "good first build" below.
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
+ `ui-syncbar`), matching the "good first build" below. The 0.6.7 local pass
|
|
171
|
+
adds `ui-job`: a persistent background-job row with determinate progress,
|
|
172
|
+
written status, and action slots, while polling/retry/cancel semantics stay in
|
|
173
|
+
the host. `ui-conflict` (resolution affordances) remains deferred until a
|
|
174
|
+
consumer needs it; `ui-review-state` is covered by the reviewed/needs-review
|
|
173
175
|
state modifiers.
|
|
174
176
|
|
|
175
177
|
Why it matters: serious apps spend a lot of time in states like saving, saved,
|
|
176
178
|
queued, offline, stale, retrying, conflicted, locked, reviewed, and background
|
|
177
179
|
job running. These states are usually improvised per product, so even good apps
|
|
178
|
-
feel inconsistent. Still deferred: `ui-
|
|
179
|
-
|
|
180
|
+
feel inconsistent. Still deferred: `ui-conflict` (resolution affordances), until
|
|
181
|
+
a consumer needs it.
|
|
180
182
|
|
|
181
183
|
### 3. Command-first UI — ✅ shipped in 0.5.0
|
|
182
184
|
|
|
@@ -193,20 +195,19 @@ design-system contract: shortcuts, actions, groups, disabled reasons, context,
|
|
|
193
195
|
and command result feedback. The host still owns the action registry and
|
|
194
196
|
execution; global Cmd/Ctrl+K stays opt-in by design.
|
|
195
197
|
|
|
196
|
-
### 4. Workbench UI — 🟡 inspector / property / selectionbar shipped in 0.5.0
|
|
198
|
+
### 4. Workbench UI — 🟡 inspector / property / selectionbar shipped in 0.5.0, splitter added in 0.6.7
|
|
197
199
|
|
|
198
200
|
Shipped as `@ponchia/ui/css/workbench.css` (`ui-inspector`, `ui-property`,
|
|
199
|
-
`ui-selectionbar`) — the low-risk
|
|
200
|
-
|
|
201
|
-
|
|
201
|
+
`ui-selectionbar`, `ui-splitter`) plus `initSplitter` — the low-risk workbench
|
|
202
|
+
core below. Splitters own the focusable ARIA separator, keyboard/pointer resize,
|
|
203
|
+
`--splitter-pos`, and `aria-valuenow`; the host owns pane contents, persistence,
|
|
204
|
+
collapse policy, and saved layout state.
|
|
202
205
|
|
|
203
206
|
Why it matters: real tools need inspectors, object action bars, split panes,
|
|
204
207
|
resize handles, property rows, dense trees, and selected-object affordances.
|
|
205
208
|
Generic UI kits tend to stop at cards/tables/forms, leaving every app to build
|
|
206
|
-
its own half-consistent workbench. Still open:
|
|
207
|
-
|
|
208
|
-
arrow-key resize) and drag/drop affordances — both deferred, and Bronto should
|
|
209
|
-
style drag handles, not become a drag-and-drop framework.
|
|
209
|
+
its own half-consistent workbench. Still open: drag/drop affordances. Bronto
|
|
210
|
+
should style drag handles and drop targets, not become a drag-and-drop framework.
|
|
210
211
|
|
|
211
212
|
### 5. Generated-content and AI trust primitives — 🟡 shipped in 0.5.0
|
|
212
213
|
|
|
@@ -226,14 +227,38 @@ precision signal the product does not have).
|
|
|
226
227
|
|
|
227
228
|
The CSS cores of candidates 1–5 shipped in 0.5.0, the 2026 scout batch shipped
|
|
228
229
|
in PRs #97/#100, and scout batch #2 (textref / bullet / term / toc / tree)
|
|
229
|
-
shipped 2026-06-04.
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
230
|
+
shipped 2026-06-04.
|
|
231
|
+
|
|
232
|
+
**The proven lane is report / provenance / explanation** — it is the only lane
|
|
233
|
+
with a real consumer (LLM-authored reports). The command, workbench, and
|
|
234
|
+
durable-state lanes shipped their CSS cores and have had **no non-demo
|
|
235
|
+
consumer since**; their follow-ons are demand-gated, not queued. Active work
|
|
236
|
+
is therefore consolidation of the report lane (hub routing, print/PDF
|
|
237
|
+
fidelity, consumer-contract gates), not new surfaces.
|
|
238
|
+
|
|
239
|
+
### Report-lane primitives shipped in 0.6.7
|
|
240
|
+
|
|
241
|
+
From the 2026-06-09 local scout. These were kept on merit, then shipped only
|
|
242
|
+
after `docs/reporting.md` carried routing rows so the leaves are discoverable:
|
|
243
|
+
|
|
244
|
+
1. `ui-interval` — honest low–high uncertainty span + ± chip, inline in
|
|
245
|
+
reports; the error-bar grammar generic kits never ship.
|
|
246
|
+
2. `ui-clamp` — N-line clamp + fade + native show-more for claim-basis and
|
|
247
|
+
source excerpts (`-webkit-line-clamp` + `mask-image` + `<details>`).
|
|
248
|
+
3. `ui-highlights` — cited-evidence / search-hit spans painted from host
|
|
249
|
+
Ranges via the CSS Custom Highlight API (progressive enhancement; clean
|
|
250
|
+
no-op below the floor).
|
|
251
|
+
4. `ui-figure` — stable chart/diagram/screenshot stage with overlay/key/fallback
|
|
252
|
+
slots; it composes with report figures but still refuses chart scales.
|
|
253
|
+
|
|
254
|
+
### Dormant (build with the first real app consumer, not before)
|
|
255
|
+
|
|
256
|
+
- `ui-conflict` lifecycle surface (candidate 2).
|
|
257
|
+
- Drag/drop workbench affordances (candidate 4) and any gating consumer for
|
|
258
|
+
`ui-tree`'s deferred roving-focus tree kernel.
|
|
259
|
+
- Any command/workbench follow-ons beyond the shipped cores.
|
|
260
|
+
|
|
261
|
+
Dormant items stay gated on a real consumer needing them. This posture keeps
|
|
237
262
|
Bronto differentiated while staying inside its core philosophy: small,
|
|
238
263
|
framework-agnostic primitives that make complex interfaces clearer.
|
|
239
264
|
|