@ponchia/ui 0.6.6 → 0.6.8
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 +175 -6
- package/README.md +38 -25
- package/annotations/index.d.ts.map +1 -1
- package/annotations/index.js +21 -3
- package/behaviors/carousel.d.ts.map +1 -1
- package/behaviors/carousel.js +91 -32
- package/behaviors/combobox.d.ts.map +1 -1
- package/behaviors/combobox.js +117 -43
- package/behaviors/command.d.ts.map +1 -1
- package/behaviors/command.js +74 -14
- package/behaviors/connectors.d.ts.map +1 -1
- package/behaviors/connectors.js +92 -9
- package/behaviors/crosshair.d.ts.map +1 -1
- package/behaviors/crosshair.js +47 -1
- package/behaviors/dialog.d.ts.map +1 -1
- package/behaviors/dialog.js +37 -16
- package/behaviors/disclosure.d.ts.map +1 -1
- package/behaviors/disclosure.js +33 -3
- package/behaviors/dismissible.d.ts.map +1 -1
- package/behaviors/dismissible.js +3 -2
- package/behaviors/forms.d.ts.map +1 -1
- package/behaviors/forms.js +78 -5
- package/behaviors/glyph.d.ts.map +1 -1
- package/behaviors/glyph.js +17 -2
- package/behaviors/index.d.ts +2 -0
- package/behaviors/index.d.ts.map +1 -1
- package/behaviors/index.js +2 -0
- package/behaviors/inert.js +3 -2
- package/behaviors/internal.d.ts +2 -1
- package/behaviors/internal.d.ts.map +1 -1
- package/behaviors/internal.js +25 -4
- package/behaviors/legend.d.ts +0 -5
- package/behaviors/legend.d.ts.map +1 -1
- package/behaviors/legend.js +78 -14
- package/behaviors/menu.d.ts.map +1 -1
- package/behaviors/menu.js +13 -8
- package/behaviors/modal.d.ts.map +1 -1
- package/behaviors/modal.js +77 -19
- package/behaviors/popover.d.ts +4 -3
- package/behaviors/popover.d.ts.map +1 -1
- package/behaviors/popover.js +89 -9
- package/behaviors/sources.d.ts.map +1 -1
- package/behaviors/sources.js +14 -2
- package/behaviors/splitter.d.ts +26 -0
- package/behaviors/splitter.d.ts.map +1 -0
- package/behaviors/splitter.js +239 -0
- package/behaviors/spotlight.d.ts.map +1 -1
- package/behaviors/spotlight.js +28 -2
- package/behaviors/table.d.ts.map +1 -1
- package/behaviors/table.js +105 -13
- package/behaviors/tabs.d.ts.map +1 -1
- package/behaviors/tabs.js +82 -18
- package/behaviors/theme.d.ts.map +1 -1
- package/behaviors/theme.js +26 -6
- package/classes/classes.json +230 -4
- package/classes/index.d.ts +64 -3
- package/classes/index.js +56 -2
- package/classes/vscode.css-custom-data.json +1 -1
- package/connectors/index.d.ts +39 -6
- package/connectors/index.d.ts.map +1 -1
- package/connectors/index.js +67 -9
- package/css/analytical.css +3 -1
- package/css/annotations.css +12 -0
- package/css/app.css +4 -4
- package/css/clamp.css +92 -0
- package/css/crosshair.css +27 -2
- package/css/feedback.css +2 -30
- package/css/figure.css +102 -0
- package/css/highlights.css +50 -0
- package/css/interval.css +90 -0
- package/css/navigation.css +12 -0
- package/css/primitives.css +2 -3
- package/css/report-kit.css +38 -0
- package/css/report.css +23 -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 +25 -9
- package/css/workbench.css +101 -8
- package/dist/bronto.css +1 -1
- package/dist/css/analytical.css +1 -1
- package/dist/css/annotations.css +1 -1
- package/dist/css/app.css +1 -1
- package/dist/css/clamp.css +1 -0
- package/dist/css/crosshair.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/figure.css +1 -0
- package/dist/css/highlights.css +1 -0
- package/dist/css/interval.css +1 -0
- package/dist/css/navigation.css +1 -1
- 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/0001-color-system.md +3 -2
- package/docs/adr/0002-scope-and-2026-baseline.md +1 -1
- package/docs/annotations.md +12 -1
- package/docs/architecture.md +105 -48
- package/docs/clamp.md +49 -0
- package/docs/command.md +4 -1
- package/docs/connectors.md +16 -0
- package/docs/contrast.md +34 -24
- package/docs/crosshair.md +1 -1
- package/docs/d2.md +37 -0
- package/docs/dots.md +4 -1
- package/docs/figure.md +71 -0
- package/docs/frontier-primitives.md +25 -24
- package/docs/glyphs.md +11 -0
- 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 +44 -6
- package/docs/reference.md +78 -5
- package/docs/reporting.md +126 -60
- package/docs/sidenote.md +7 -1
- package/docs/sources.md +1 -1
- package/docs/stability.md +23 -5
- package/docs/state.md +67 -10
- package/docs/theming.md +12 -4
- package/docs/usage.md +47 -13
- package/docs/vega.md +4 -4
- package/docs/workbench.md +59 -18
- package/llms.txt +89 -16
- package/package.json +82 -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 +114 -0
- package/svelte/index.d.ts.map +1 -0
- package/svelte/index.js +193 -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 +116 -0
- package/vue/index.d.ts.map +1 -0
- package/vue/index.js +228 -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
|
+
`/svelte`, `/vue`, `/skins`, `/charts`, `/mermaid`, `/d2`, `/vega`). This is
|
|
103
|
+
a permanent, 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. |
|
|
@@ -127,24 +135,47 @@ the result.
|
|
|
127
135
|
|
|
128
136
|
## Drift control
|
|
129
137
|
|
|
130
|
-
Every data mirror is backed by a check wired
|
|
131
|
-
on every push/PR and again by `release.yml`
|
|
132
|
-
gating" below), so a version that fails any
|
|
138
|
+
Every data mirror and public documentation contract is backed by a check wired
|
|
139
|
+
into `npm run check`, run by CI on every push/PR and again by `release.yml`
|
|
140
|
+
before publish (see "Release gating" below), so a version that fails any
|
|
141
|
+
invariant never reaches npm. Public authoring docs are treated as public surface
|
|
142
|
+
too: `check:doc-links` fails stale local paths/anchors across shipped docs,
|
|
143
|
+
GitHub-only docs, and the docs viewer route list, while shipped-doc links must
|
|
144
|
+
also point at files present in the npm tarball. `check:contract` verifies
|
|
145
|
+
documented named imports, `check:doc-recipes` fails copy-paste CDN recipes that
|
|
146
|
+
silently no-op, and `check:report` validates fenced HTML snippets before they
|
|
147
|
+
are copied into consumer reports.
|
|
133
148
|
|
|
134
149
|
| Invariant | Enforced by |
|
|
135
150
|
| ----------------------------------------------- | ------------------- |
|
|
136
|
-
| exports / import graph / `files` consistent
|
|
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` |
|
|
151
|
+
| exports / import graph / source CSS `layer(bronto)` imports / layered-vs-unlayered CSS target map / `files` consistent | `check-exports.mjs` |
|
|
152
|
+
| 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
153
|
| `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` |
|
|
154
|
+
| `connectors`/`annotations`/`react`/`solid`/`qwik`/`svelte`/`vue`/`behaviors` `.d.ts` (+ maps) == fresh `tsc` emit of their JSDoc | `check-dts-emit.mjs` |
|
|
140
155
|
| legend swatch colours ⊆ `charts.js` · opt-in | `check-legend.mjs` |
|
|
141
156
|
| color tokens tiered · no raw chromatic color in components | `check-color-policy.mjs` |
|
|
142
157
|
| `css/skins.css` ⇄ `tokens/skins.js` · colorways opt-in | `check-skins.mjs` |
|
|
143
158
|
| every shipped colorway accent meets its WCAG floor | `check-contrast.mjs` |
|
|
144
159
|
| `dataviz.css`/`charts.json`/`charts.d.ts` ⇄ `tokens/charts.js` · CVD-distinguishable · opt-in | `check-charts.mjs` |
|
|
145
160
|
| `shiki/nothing.json` valid + on rationed palette | `check-shiki.mjs` |
|
|
146
|
-
| `dist/*.css` == fresh build of `css/` + budget
|
|
161
|
+
| `dist/*.css` == fresh single-`@layer bronto` build of `css/` + budget | `check-dist.mjs` |
|
|
147
162
|
| published tarball == intended `files` only | `check-pack.mjs` |
|
|
163
|
+
| packed core JS/JSON public subpaths import without optional framework peers, packed JS named exports exactly match source modules, peer-backed adapters import after peers are linked, concrete CSS/doc/font subpaths resolve, and packed behavior initializers/toast no-op in a clean consumer with no DOM globals | `check-consumer-surface.mjs` |
|
|
164
|
+
| packed typed public subpaths compile through package exports in a clean TypeScript consumer | `check-consumer-types.mjs` |
|
|
165
|
+
| GitHub Actions workflow syntax and embedded shell snippets lint | `check:workflows` (`github-actionlint`) |
|
|
166
|
+
| every shipped CSS leaf is classified as foundation or has explicit docs/demo/e2e ownership | `check-component-matrix.mjs` |
|
|
167
|
+
| every public behavior export has explicit docs, unit-test, and browser-test ownership | `check-behavior-matrix.mjs` |
|
|
168
|
+
| every public helper export in `classes`/`annotations`/`connectors`/`glyphs` has explicit docs, unit-test, and type-test ownership | `check-helper-matrix.mjs` |
|
|
169
|
+
| every delegated behavior has React/Solid/Qwik hook, Svelte action, Vue directive, docs, example, unit, and type ownership | `check-binding-matrix.mjs` |
|
|
170
|
+
| `@playwright/test` version ⇄ pinned Playwright container image ⇄ visual workflows/docs/local runner | `check-playwright-container.mjs` |
|
|
171
|
+
| every shipped JSON schema is exported, documented, validates its public cookbook example, and rejects malformed sidecars | `check-schemas.mjs` |
|
|
172
|
+
| packed public text contains no private terms, local paths, or secret-looking assignments | `check-public-hygiene.mjs` |
|
|
173
|
+
| CSS custom-property references resolve or carry an explicit fallback/host boundary | `check-variables.mjs` |
|
|
174
|
+
| `MIGRATIONS.json` edges have structured rules and matching docs | `check-migrations.mjs` |
|
|
175
|
+
| example inventory ⇄ CI matrix ⇄ browser-smoke list ⇄ README rows ⇄ preview ports | `check-examples.mjs` |
|
|
176
|
+
| demo visual snapshot declarations ⇄ committed Chromium baseline inventory | `check-visual-baselines.mjs` |
|
|
177
|
+
| public authoring docs keep valid local paths/anchors; shipped-doc links resolve inside the tarball; docs viewer routes resolve | `check-doc-links.mjs` |
|
|
178
|
+
| report/docs snippets use valid `ui-*` classes and public authoring snippets keep intact local id/ARIA/behavior references | `check-report.mjs` |
|
|
148
179
|
| published `.d.ts` compile + reject typos | `tsc` (`check:types`) |
|
|
149
180
|
| CSS style/correctness | Stylelint |
|
|
150
181
|
| non-CSS source style | Prettier (`check:format`) |
|
|
@@ -160,32 +191,50 @@ payload contract, raised only intentionally with a CHANGELOG note.
|
|
|
160
191
|
if the generated literal `cls`/token types stopped rejecting typos —
|
|
161
192
|
so the *value* of the generated `.d.ts` is itself gated, not just their
|
|
162
193
|
freshness (`check-fresh`).
|
|
194
|
+
`check:consumer-types` then installs the packed tarball in a clean temp
|
|
195
|
+
project and compiles package-subpath imports, so `exports.types` and
|
|
196
|
+
internal declaration references are proven through consumer resolution.
|
|
163
197
|
|
|
164
198
|
## Release gating
|
|
165
199
|
|
|
166
|
-
`release.yml` (on a pushed `v*` tag) is a
|
|
200
|
+
`release.yml` (on a pushed `v*` tag) is a six-job DAG, serialized by a
|
|
167
201
|
`concurrency: release-publish` group so two tags can't race the dist-tag
|
|
168
202
|
pointer:
|
|
169
203
|
|
|
170
|
-
- `validate` — read-only:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
204
|
+
- `validate` — read-only: verifies the tag commit is reachable from `main`,
|
|
205
|
+
then runs `npm run check` and the tag↔version match. `check`
|
|
206
|
+
includes the node:test unit and contract suite, plus `check:release`;
|
|
207
|
+
for a prerelease tag the base version's CHANGELOG
|
|
208
|
+
section need only exist (`## Unreleased — x.y.z` is fine) — only a stable
|
|
209
|
+
release must carry a dated heading.
|
|
210
|
+
- `e2e` — `needs: validate`: Playwright (visual + axe a11y, both themes,
|
|
211
|
+
demo structural integrity, both themes, cross-engine) in the pinned
|
|
212
|
+
`mcr.microsoft.com/playwright` container. Local cross-engine reproduction
|
|
213
|
+
without screenshot rasterisation is
|
|
214
|
+
`npm run test:e2e:nonpixel`; use `npm run test:e2e` or
|
|
215
|
+
`npm run test:e2e:chromium` only in the pinned container when the pixel
|
|
216
|
+
baseline gate itself is in scope. With Docker running,
|
|
217
|
+
`npm run test:e2e:visual:container` is the local shortcut for the same
|
|
218
|
+
Chromium screenshot environment.
|
|
176
219
|
- `examples` — `needs: validate`: builds the downstream example
|
|
177
220
|
apps against the **packed tarball**, mirroring CI. Catches a broken
|
|
178
221
|
published surface (exports map / missing file / unresolved subpath)
|
|
179
222
|
that `check:pack`'s file-allowlist inspection cannot — so the release
|
|
180
223
|
path runs the same consumer smoke as merge-to-main.
|
|
181
|
-
- `publish-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
224
|
+
- `publish-preflight` — `needs: [validate, e2e, examples]`: installs with
|
|
225
|
+
lifecycle scripts disabled, runs `npm pack --dry-run --ignore-scripts`, and
|
|
226
|
+
writes the pack manifest + size report to the job summary for review before
|
|
227
|
+
the protected publish approval.
|
|
228
|
+
- `publish-npm` — `needs: publish-preflight`: `npm publish --ignore-scripts`
|
|
229
|
+
with provenance. Runs in the `npm-publish` **Environment**
|
|
230
|
+
(required-reviewer protection), so after the gates and preflight pass the run
|
|
231
|
+
pauses for a manual approval in the Actions UI before anything reaches npm —
|
|
232
|
+
a guard against an accidental tag push publishing. Dist-tag is derived from
|
|
233
|
+
the tag: stable (`v0.4.0`) → `latest`; SemVer prerelease (`v0.4.0-rc.1`, any
|
|
234
|
+
hyphenated identifier) → `next`, so the default `npm i @ponchia/ui` never
|
|
235
|
+
moves onto an unstable build (opt in with `@ponchia/ui@next`). Post-publish
|
|
236
|
+
`npm view` registry observation is best-effort only: a registry read flake must
|
|
237
|
+
not fail the job after the immutable publish already succeeded.
|
|
189
238
|
- `release-notes` — `needs: publish-npm`: a GitHub Release for visibility
|
|
190
239
|
(transitively gated on a successful publish, hence on the gates above);
|
|
191
240
|
prerelease tags are flagged so they aren't surfaced as "Latest". The Release
|
|
@@ -194,7 +243,7 @@ pointer:
|
|
|
194
243
|
source of truth, surfaced where readers look.
|
|
195
244
|
|
|
196
245
|
Because the documented install path is the npm package, **the npm publish
|
|
197
|
-
is a real gate**: if `validate`, `e2e`,
|
|
246
|
+
is a real gate**: if `validate`, `e2e`, `examples`, or `publish-preflight` fails,
|
|
198
247
|
`publish-npm` never runs, the version never reaches the registry, and
|
|
199
248
|
consumers never resolve it.
|
|
200
249
|
(Corollary: a flaky `e2e` blocks releases — that is deliberate; fix the
|
|
@@ -210,9 +259,9 @@ Process still applies: bump `package.json`, land on `main`, go green, tag.
|
|
|
210
259
|
## Decision — distribution: npm public `@ponchia/ui`
|
|
211
260
|
|
|
212
261
|
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
|
|
262
|
+
heterogeneous web frontends (Astro, SvelteKit, React, Solid, Qwik, Vue,
|
|
263
|
+
Tailwind, vanilla), several deploying via third-party CI. The only option where
|
|
264
|
+
onboarding a new frontend is `npm i @ponchia/ui` with zero per-consumer config is **npm
|
|
216
265
|
public**, and it uniquely also closes the release-gating gap (publish *is*
|
|
217
266
|
the gate). GitHub Packages was rejected: it requires auth to install even
|
|
218
267
|
public packages, i.e. an `.npmrc` + token on every frontend and CI runner —
|
|
@@ -237,8 +286,16 @@ explained, not surprising.
|
|
|
237
286
|
provenance.
|
|
238
287
|
- Run `npm pack --dry-run --json` locally or from CI logs and confirm the
|
|
239
288
|
intended file count/payload.
|
|
240
|
-
- Build the packed examples matrix
|
|
241
|
-
|
|
289
|
+
- Build the packed examples matrix from the tarball, not a workspace link:
|
|
290
|
+
`npm run test:examples` covers vanilla, Astro, SvelteKit, Vue, React, Solid,
|
|
291
|
+
Qwik, Tailwind, and report-static, with Chromium browser smokes for runtime
|
|
292
|
+
examples. For a deeper consumer pass, `npm run test:examples:cross-browser`
|
|
293
|
+
runs the same packed smokes in Chromium, Firefox, and WebKit; manual CI
|
|
294
|
+
dispatches pass the same cross-browser flag to the reusable examples workflow.
|
|
295
|
+
`npm run test:examples:visual` adds local-safe desktop + mobile
|
|
296
|
+
screenshot/layout health smokes for packed examples; it detects blank,
|
|
297
|
+
under-painted, or horizontally overflowing output but intentionally does not
|
|
298
|
+
author OS-sensitive committed PNG baselines.
|
|
242
299
|
- Confirm the GitHub Release body matches the curated changelog section.
|
|
243
300
|
- If a bad package is published, deprecate that exact version on npm, publish a
|
|
244
301
|
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/command.md
CHANGED
|
@@ -84,7 +84,10 @@ document.querySelector('[data-bronto-command]').addEventListener('bronto:command
|
|
|
84
84
|
);
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
Framework
|
|
87
|
+
Framework adapters: `useCommand()` in `@ponchia/ui/react`, `/solid`, and
|
|
88
|
+
`/qwik`; the `command` / `useCommand` action in `@ponchia/ui/svelte`; and
|
|
89
|
+
`vCommand` (or the `v-bronto-command` directive from `brontoVue`) in
|
|
90
|
+
`@ponchia/ui/vue`.
|
|
88
91
|
|
|
89
92
|
## Accessibility
|
|
90
93
|
|
package/docs/connectors.md
CHANGED
|
@@ -41,6 +41,17 @@ import { initConnectors } from '@ponchia/ui/behaviors';
|
|
|
41
41
|
const stop = initConnectors(); // redraws on resize/scroll; returns a cleanup
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
For static SVG output, author the same parts directly: `.ui-connector__path`
|
|
45
|
+
holds the line path, and `.ui-connector__end` holds an optional arrowhead/dot
|
|
46
|
+
path from `arrowHead()` or `dotMark()`.
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<svg class="ui-connector ui-connector--accent" aria-hidden="true">
|
|
50
|
+
<path class="ui-connector__path" d="M20,10C48,10 72,50 100,50" />
|
|
51
|
+
<path class="ui-connector__end" d="M100,50L92,46L92,54Z" />
|
|
52
|
+
</svg>
|
|
53
|
+
```
|
|
54
|
+
|
|
44
55
|
A connector is decorative — mark it `aria-hidden="true"` and make sure the
|
|
45
56
|
relationship it depicts is also clear from the content/DOM order.
|
|
46
57
|
|
|
@@ -81,6 +92,11 @@ const head = arrowHead(to, angle); // place at the endpoint
|
|
|
81
92
|
- `endTangentAngle(from, to, shape)` — the angle the path *arrives* at `to`
|
|
82
93
|
(chord for `straight`, axis-aligned for `elbow`/`curve`); rotate an end marker
|
|
83
94
|
by this so it points along the path. `connectRects().angle` already uses it.
|
|
95
|
+
- Low-level scalar/SVG kernel helpers are exported for hosts that need the same
|
|
96
|
+
rounding and guard semantics as the path builders: `PRECISION`,
|
|
97
|
+
`roundNumber(value)`, `fmt(value)`, `point(x, y)`, `finite(name, value,
|
|
98
|
+
fallback)`, `dimension(name, value, fallback)`, `clamp(value, min, max)`, and
|
|
99
|
+
`rectPath(left, top, right, bottom)`.
|
|
84
100
|
|
|
85
101
|
## Coordinate model
|
|
86
102
|
|
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/crosshair.md
CHANGED
|
@@ -48,7 +48,7 @@ document.querySelector('[data-bronto-crosshair]').addEventListener(
|
|
|
48
48
|
| `ui-crosshair` | The overlay. Hidden until `.is-active` (set on the first pointer move over the plot). |
|
|
49
49
|
| `ui-crosshair__line` + `--x` / `--y` | Vertical / horizontal rule, positioned by `--crosshair-x` / `--crosshair-y`. |
|
|
50
50
|
| `ui-crosshair__badge` | An axis value chip (you set its text + edge). |
|
|
51
|
-
| `ui-readout` | A pinned readout chip; follows the crosshair point. |
|
|
51
|
+
| `ui-readout` inside `.ui-crosshair` | A pinned readout chip; follows the crosshair point and flips before/above near plot edges via `data-readout-inline` / `data-readout-block`. Standalone `.ui-readout` remains the dot-matrix numeric row from `dots.css`. |
|
|
52
52
|
|
|
53
53
|
`ui-crosshair--muted` is a subtler neutral crosshair. `ui.crosshair({ muted })`
|
|
54
54
|
builds the class string. Include only the lines you need (just `--x` for a
|
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/dots.md
CHANGED
|
@@ -62,7 +62,7 @@ container MUST carry a host-written `role="img"` + `aria-label` with the exact
|
|
|
62
62
|
value — rounding to whole cells is presentation-only, so keep the figure in the
|
|
63
63
|
label (WCAG 1.4.1).
|
|
64
64
|
|
|
65
|
-
### `.ui-
|
|
65
|
+
### `.ui-dotmatrix` cell grid
|
|
66
66
|
|
|
67
67
|
`.ui-dotmatrix` is the raw data-bound grid (the one the glyphs render on): a grid
|
|
68
68
|
of `.ui-dotmatrix__cell` (with `--hot` / `--accent` tones) plus the `--reveal` /
|
|
@@ -125,6 +125,9 @@ Knobs: `--v`, `--gauge-size`, `--gauge-sweep` (default 270deg), `--gauge-from`,
|
|
|
125
125
|
The row wrapper produced by `renderReadout` (see `docs/glyphs.md`) — a Nothing-style
|
|
126
126
|
hero numeric composed from digit glyphs. Tune character spacing with
|
|
127
127
|
`--readout-gap`; spaces render as a `.ui-readout__spacer` of width `--readout-space`.
|
|
128
|
+
Large multi-character readouts are intentionally intrinsic; on narrow report or
|
|
129
|
+
demo pages, either tune the glyph dot size or wrap the readout in a local
|
|
130
|
+
keyboard-focusable horizontal scroller so it does not widen the page.
|
|
128
131
|
|
|
129
132
|
### `.ui-spark--dots`
|
|
130
133
|
|