@ponchia/ui 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/CHANGELOG.md +552 -8
  2. package/MIGRATIONS.json +106 -0
  3. package/README.md +34 -8
  4. package/annotations/index.d.ts +402 -0
  5. package/annotations/index.d.ts.map +1 -0
  6. package/annotations/index.js +792 -0
  7. package/behaviors/carousel.js +198 -0
  8. package/behaviors/combobox.js +226 -0
  9. package/behaviors/command.js +190 -0
  10. package/behaviors/connectors.js +95 -0
  11. package/behaviors/crosshair.js +57 -0
  12. package/behaviors/dialog.js +74 -0
  13. package/behaviors/disclosure.js +26 -0
  14. package/behaviors/dismissible.js +25 -0
  15. package/behaviors/forms.js +186 -0
  16. package/behaviors/glyph.js +108 -0
  17. package/behaviors/index.d.ts +79 -0
  18. package/behaviors/index.js +18 -1409
  19. package/behaviors/internal.js +97 -0
  20. package/behaviors/legend.js +67 -0
  21. package/behaviors/menu.js +47 -0
  22. package/behaviors/popover.js +179 -0
  23. package/behaviors/spotlight.js +52 -0
  24. package/behaviors/table.js +136 -0
  25. package/behaviors/tabs.js +103 -0
  26. package/behaviors/theme.js +84 -0
  27. package/behaviors/toast.js +164 -0
  28. package/classes/classes.json +1857 -0
  29. package/classes/index.d.ts +306 -13
  30. package/classes/index.js +339 -12
  31. package/classes/vscode.css-custom-data.json +12 -0
  32. package/connectors/index.d.ts +191 -0
  33. package/connectors/index.d.ts.map +1 -0
  34. package/connectors/index.js +275 -0
  35. package/css/analytical.css +21 -0
  36. package/css/annotations.css +292 -0
  37. package/css/app.css +43 -13
  38. package/css/base.css +15 -10
  39. package/css/command.css +97 -0
  40. package/css/connectors.css +110 -0
  41. package/css/content.css +7 -1
  42. package/css/crosshair.css +100 -0
  43. package/css/dataviz.css +5 -1
  44. package/css/disclosure.css +38 -6
  45. package/css/dots.css +57 -0
  46. package/css/feedback.css +111 -2
  47. package/css/fonts.css +11 -7
  48. package/css/forms.css +42 -1
  49. package/css/generated.css +117 -0
  50. package/css/legend.css +272 -0
  51. package/css/marks.css +174 -0
  52. package/css/motion.css +24 -44
  53. package/css/navigation.css +7 -0
  54. package/css/overlay.css +31 -1
  55. package/css/primitives.css +109 -5
  56. package/css/report.css +39 -81
  57. package/css/selection.css +46 -0
  58. package/css/site.css +16 -2
  59. package/css/sources.css +221 -0
  60. package/css/spotlight.css +104 -0
  61. package/css/state.css +121 -0
  62. package/css/tokens.css +60 -37
  63. package/css/workbench.css +83 -0
  64. package/dist/bronto.css +1 -1
  65. package/dist/css/analytical.css +1 -0
  66. package/dist/css/annotations.css +1 -0
  67. package/dist/css/app.css +1 -1
  68. package/dist/css/base.css +1 -1
  69. package/dist/css/command.css +1 -0
  70. package/dist/css/connectors.css +1 -0
  71. package/dist/css/content.css +1 -1
  72. package/dist/css/crosshair.css +1 -0
  73. package/dist/css/disclosure.css +1 -1
  74. package/dist/css/dots.css +1 -1
  75. package/dist/css/feedback.css +1 -1
  76. package/dist/css/fonts.css +1 -1
  77. package/dist/css/forms.css +1 -1
  78. package/dist/css/generated.css +1 -0
  79. package/dist/css/legend.css +1 -0
  80. package/dist/css/marks.css +1 -0
  81. package/dist/css/motion.css +1 -1
  82. package/dist/css/navigation.css +1 -1
  83. package/dist/css/overlay.css +1 -1
  84. package/dist/css/primitives.css +1 -1
  85. package/dist/css/report.css +1 -1
  86. package/dist/css/selection.css +1 -0
  87. package/dist/css/site.css +1 -1
  88. package/dist/css/sources.css +1 -0
  89. package/dist/css/spotlight.css +1 -0
  90. package/dist/css/state.css +1 -0
  91. package/dist/css/tokens.css +1 -1
  92. package/dist/css/workbench.css +1 -0
  93. package/docs/adr/0003-theme-model.md +7 -4
  94. package/docs/annotations.md +425 -0
  95. package/docs/architecture.md +246 -0
  96. package/docs/command.md +95 -0
  97. package/docs/connectors.md +91 -0
  98. package/docs/contrast.md +116 -92
  99. package/docs/crosshair.md +63 -0
  100. package/docs/d2.md +195 -0
  101. package/docs/generated.md +91 -0
  102. package/docs/legends.md +184 -0
  103. package/docs/marks.md +93 -0
  104. package/docs/mermaid.md +152 -0
  105. package/docs/reference.md +385 -23
  106. package/docs/reporting.md +436 -63
  107. package/docs/selection.md +40 -0
  108. package/docs/sources.md +137 -0
  109. package/docs/spotlight.md +78 -0
  110. package/docs/stability.md +24 -2
  111. package/docs/state.md +85 -0
  112. package/docs/usage.md +123 -4
  113. package/docs/vega.md +225 -0
  114. package/docs/workbench.md +78 -0
  115. package/fonts/doto-400.woff2 +0 -0
  116. package/fonts/doto-500.woff2 +0 -0
  117. package/fonts/doto-600.woff2 +0 -0
  118. package/fonts/doto-700.woff2 +0 -0
  119. package/fonts/doto-800.woff2 +0 -0
  120. package/fonts/doto-900.woff2 +0 -0
  121. package/glyphs/glyphs.js +6 -4
  122. package/llms.txt +362 -14
  123. package/package.json +115 -12
  124. package/qwik/index.d.ts +42 -54
  125. package/qwik/index.d.ts.map +1 -0
  126. package/qwik/index.js +75 -3
  127. package/react/index.d.ts +39 -56
  128. package/react/index.d.ts.map +1 -0
  129. package/react/index.js +67 -3
  130. package/solid/index.d.ts +64 -56
  131. package/solid/index.d.ts.map +1 -0
  132. package/solid/index.js +70 -3
  133. package/tokens/d2.d.ts +38 -0
  134. package/tokens/d2.js +71 -0
  135. package/tokens/d2.json +43 -0
  136. package/tokens/index.d.ts +5 -5
  137. package/tokens/index.js +23 -5
  138. package/tokens/index.json +9 -0
  139. package/tokens/mermaid.d.ts +23 -0
  140. package/tokens/mermaid.js +181 -0
  141. package/tokens/mermaid.json +163 -0
  142. package/tokens/resolved.json +45 -1
  143. package/tokens/skins.js +3 -2
  144. package/tokens/tokens.dtcg.json +26 -0
  145. package/tokens/vega.d.ts +34 -0
  146. package/tokens/vega.js +155 -0
  147. package/tokens/vega.json +179 -0
  148. package/fonts/doto-400.ttf +0 -0
  149. package/fonts/doto-500.ttf +0 -0
  150. package/fonts/doto-600.ttf +0 -0
  151. package/fonts/doto-700.ttf +0 -0
  152. package/fonts/doto-800.ttf +0 -0
  153. package/fonts/doto-900.ttf +0 -0
package/CHANGELOG.md CHANGED
@@ -1,9 +1,556 @@
1
1
  # Changelog
2
2
 
3
- > **Versioning:** pre-1.0, breaking changes ship in the _minor_. Pin
4
- > `~0.x`; `^0.x` does **not** protect you. See README → Versioning, and
3
+ > **Versioning:** pre-1.0, breaking changes ship in the _minor_. Pin to the
4
+ > minor — `~0.4.0` (equivalently `^0.4.0`) resolves to `>=0.4.0 <0.5.0`; a bare
5
+ > `^0` / `*` wildcard does **not** protect you. See README → Versioning, and
5
6
  > the deprecation policy in CONTRIBUTING.md.
6
7
 
8
+ ## 0.6.0 — 2026-06-03
9
+
10
+ Accumulates the post-0.5.0 work: a multi-agent audit pass (accessibility
11
+ hardening, a behavior/binding scope-safety fix, codegen/gate tightening) plus a
12
+ **breaking** charting realignment. The local static-bar renderer
13
+ (`.ui-chart*`) is **removed** — a chart needs scales + data binding, which the
14
+ analytical layer refuses to own. In its place, bronto becomes a themeable target
15
+ for **Vega-Lite** (`@ponchia/ui/vega`), the same tokens-as-data path as Mermaid
16
+ and D2. The data-viz **palette** (`--chart-*`, `tokens/charts.json`) and the
17
+ **legend** layer are unchanged. Pin `~0.5` → re-pin `~0.6`; see
18
+ [`MIGRATIONS.json`](./MIGRATIONS.json) (`0.5`→`0.6`).
19
+
20
+ ### Added
21
+
22
+ - **`@ponchia/ui/vega`** (+ `vega.json`) — an on-brand Vega-Lite / Vega
23
+ [`config`](https://vega.github.io/vega-lite/docs/config.html) resolved per
24
+ theme (the idiomatic `vega-themes` shape): monochrome chrome + one rationed
25
+ accent, `range.category/ordinal/ramp/heatmap/diverging` from the CVD-safe
26
+ data-viz palette. `brontoVegaConfig(theme)`. Resolved hex (Vega bakes colours
27
+ into SVG/canvas, can't read `var()`); gated structurally **and** by a headless
28
+ render-probe that asserts the colours land on a rendered chart. Vega is the
29
+ consumer's renderer — config only, not a dependency. See `docs/vega.md`.
30
+ - **`ui-delta`** — a standalone trend/change indicator (core primitive): an
31
+ arrow glyph (the non-colour channel) plus the figure, with
32
+ `--up`/`--down`/`--flat`, and `--invert` to swap only the tone when "up" is
33
+ the bad direction (latency, error rate, cost). `ui.delta({ dir, invert })`.
34
+ - **`ui-compare`** — a fluid side-by-side / before-after layout for the report
35
+ layer (`css/report.css`): `__col`, `__head`, and `--2up`.
36
+ `ui.compare({ cols })`.
37
+ - **`@ponchia/ui/classes.json`** — the class vocabulary as language-neutral
38
+ data (`groups`/`classes`/`states`/`customProperties`), so a non-JS/non-TS
39
+ host or an external linter can validate emitted markup without executing the
40
+ ESM `cls` map or parsing the `.d.ts`. Generated from `cls`; drift-checked and
41
+ its `states`/`customProperties` gated against the stylesheet.
42
+ - **`tokens/resolved.json` `scale` block** — the resolved non-colour scales
43
+ (spacing/radius/type/z/motion, `var()` chains flattened), completing the
44
+ token contract for non-CSS hosts (previously colour-only).
45
+ - **`--display-weight` / `--display-weight-strong`** (700 / 800) — the weight of
46
+ the Doto dot-matrix display face, now a token. Themes/skins can re-tune how
47
+ heavy display text renders in one place.
48
+ - On-brand **Mermaid** (`@ponchia/ui/mermaid`, `mermaid.json`) and **D2**
49
+ (`@ponchia/ui/d2`, `d2.json`) theme maps — resolved per-theme palettes
50
+ projected from the same tokens, gated. Diagrams stay the consumer's renderer;
51
+ these are config only.
52
+ - Annotation geometry options: `connectorElbow({ mid })` (turn position along the
53
+ dominant axis), `notePlacement({ inset })` (reserve the title stroke-halo so a
54
+ placement that "fits" doesn't clip), and a `spread` half-angle on both
55
+ `connectorEndArrow` and the shared `arrowHead` kernel.
56
+ - **`brontoVegaAccent(theme)` / `brontoVegaNeutral(theme)`** (`@ponchia/ui/vega`)
57
+ — the exact per-theme hexes for `range.category`'s accent (series 1) and
58
+ neutral (last series), so spending the accent on one emphasised mark needs no
59
+ palette-index reverse-engineering.
60
+ - **`--on-accent`** token — the readable ink for a label on **any accent fill**
61
+ (button, badge, themed chart bar, a Vega/D2 node). Resolves to `--button-text`
62
+ (white on the light accent, black on the dark) and is gated ≥ 4.5:1 in
63
+ `docs/contrast.md`. Use it instead of `--accent-text`, which is the inverse
64
+ (accent-coloured text for a *neutral* background, ~1.3:1 on an accent fill).
65
+ - **`.ui-src`** standalone trust pill (`cls.src`, `css/sources.css`) — wears a
66
+ `.ui-src--*` tone (verified / reviewed / generated / unverified / stale /
67
+ conflict) on its own, for a bare trust label outside a citation or source card.
68
+ Previously the `.ui-src--*` modifiers only painted a `--src-tone` with no
69
+ standalone host, so a lone pill validated against `classes.json` yet rendered
70
+ nothing.
71
+
72
+ ### Removed
73
+
74
+ - **BREAKING: the local static bar-chart renderer (`.ui-chart`, `.ui-chart__plot`,
75
+ `__bar`, `__label`, `__track`, `__fill`, `__fallback`, `__caption`).** A chart
76
+ needs scales and data binding — out of scope for a CSS-first analytical layer
77
+ (ADR-0002). Replace with a Vega-Lite chart themed via `@ponchia/ui/vega`, or a
78
+ hand-authored token-themed inline `<svg>`, inside a `.ui-report__figure` with a
79
+ `.ui-report__caption` and a `.ui-legend` key. The `--chart-value` inline knob
80
+ is gone; the `--chart-color`/`--chart-pattern` swatch knobs remain (legend).
81
+ See `MIGRATIONS.json` (`0.5`→`0.6`) and `docs/vega.md`.
82
+
83
+ ### Changed
84
+
85
+ - **Annotation connectors are crisper.** `connectorEndArrow` now defaults to a
86
+ sharper head (half-angle 0.32 ≈ 37°, size 8 vs the former blunt 0.45 / 7).
87
+ Author-facing geometry only; the `arrowHead` kernel default is unchanged, so
88
+ node-connector arrowheads don't move.
89
+
90
+ ### Accessibility
91
+
92
+ - **Coarse-pointer tap-target floors extended to navigation.** The 2.9 rem
93
+ touch floor (already on primitives/forms/feedback) now also covers
94
+ `.ui-sitenav a`, `.ui-app-nav a`, `.ui-sitemenu > summary`, and
95
+ `.ui-themetoggle__button` under `@media (pointer: coarse)` — the primary nav
96
+ affordances were below the 44 px target on touch.
97
+ - **App shell uses dynamic viewport units.** `100vh` → `100dvh` (shell/body) and
98
+ the scrolling rail → `100svh`, so the rail and its pinned account/footer no
99
+ longer fall under the mobile URL bar.
100
+ - **Forced-colors status dots stay distinct.** `.ui-dot--success/--warning/--danger/--info`
101
+ and `.ui-dotmatrix__cell--hot/--accent` now map to distinct system colors
102
+ under Windows High Contrast instead of collapsing to one — the only signal
103
+ these carry is colour.
104
+ - **Keyboard affordance parity.** `.ui-menu__item:focus-visible` gets the same
105
+ row highlight as hover; the segmented control's focus ring is now inset so the
106
+ container's `overflow: hidden` no longer clips it.
107
+ - **Reduced-motion skeleton.** `.ui-skeleton` flattens to a solid placeholder
108
+ under `prefers-reduced-motion` instead of freezing mid-shimmer.
109
+
110
+ ### Fixed
111
+
112
+ - **Published-type drift (code-quality audit).** `ui.meter({ tone: 'info' })` and
113
+ `ui.bracketNote({ tone: 'success' })` emit real classes at runtime, but the
114
+ generated `.d.ts` tone unions (hand-mirrored in `gen-dts.mjs`) omitted them, so
115
+ a TS consumer got a spurious type error for a value that renders. The unions
116
+ now match the factory; a new `check:recipe-types` gate cross-checks every
117
+ factory's string-literal options against its `*Opts` union so this whole class
118
+ of drift fails CI.
119
+ - **Component-library audit (16-agent dogfood pass) — the validates-but-no-ops
120
+ cluster.** A whole-surface audit found the meter-style trap (a class/token that
121
+ validates and paints but silently does nothing without an undocumented
122
+ precondition) recurring across components. Fixed:
123
+ - `aria-disabled="true"` on `.ui-button` / `.ui-link` now sets
124
+ `pointer-events: none` — it looked dead but a real `<a>` still navigated.
125
+ - Disabled affordance reaches the controls that wrap a native input
126
+ (`.ui-switch` / `.ui-check` / `.ui-segmented__option` via `:has(input:disabled)`,
127
+ plus `.ui-range` / `.ui-file`) — they previously looked operable and their
128
+ label kept `cursor: pointer`.
129
+ - Bare `[aria-current]` selectors (`.ui-sitenav`, `.ui-breadcrumb__item`) now
130
+ scope `:not([aria-current='false'])`, so a correctly-authored
131
+ `aria-current="false"` link is no longer styled as current.
132
+ - The active-tab forced-colors re-assert moved from `base.css` to
133
+ `disclosure.css` (after the default rule) — an earlier bundle leaf let the
134
+ accent default override it, so the selected tab lost its only HC cue.
135
+ - `.ui-meter__fill` / `.ui-progress__bar` get a system colour under
136
+ `forced-colors`, so the measured proportion stays visible.
137
+ - `.ui-search` gains a 2px keyboard focus ring to match every sibling input
138
+ (it had only a 1px border-colour shift).
139
+ - `.ui-prose` gets `overflow-wrap: break-word` — long tokens in
140
+ machine-generated Markdown forced horizontal page scroll.
141
+ - `.ui-mark--draw` is scoped to fill styles (`:not(--underline, --box, --strike)`)
142
+ so it no longer looks applied while doing nothing.
143
+ - `.ui-cq` hardcodes its container-name (the `@container bronto` collapse
144
+ queries hardcode it, so a `--cq-name` override silently killed the collapse).
145
+ - `initPopover()` seeds resting ARIA (`aria-haspopup`, `aria-controls`,
146
+ `aria-expanded`) and syncs `aria-expanded` when the UA closes a native
147
+ popover; `toast()` validates `tone` (an unknown string rendered an unstyled
148
+ neutral toast) and warns; the combobox listbox gets an accessible name.
149
+ - `.ui-error-summary__title` uses the legible sans, not the low-legibility Doto
150
+ display face. `.ui-input` / `.ui-search` autofill stays on-theme.
151
+ - `.ui-reveal` hidden state is gated on `scripting: enabled` (genuinely degrades
152
+ visible with no JS; the prior comment lied) — and `ui-scroll-reveal` is the
153
+ documented zero-JS path.
154
+ - Parity modifiers added: `.ui-meter--info`, `.ui-bracket-note--success`.
155
+ - Responsive/mobile hardening across the framework: `rem`-rooted type for WCAG
156
+ 1.4.4, coarse-pointer tap-target floors, combobox/tour-note viewport clamps,
157
+ and `@media (hover)` gating — with a new responsive e2e sweep.
158
+ - **Faint numbers on stat cards.** `.ui-stat__value` / `.ui-app-metric__value`
159
+ (and the report cover/section titles, rail brand, panel titles, `.ui-display`,
160
+ `.ui-quote`) set the Doto display face but no weight, so they rendered at the
161
+ thinnest cut (400). They now apply `--display-weight(-strong)` — visibly bolder
162
+ and more legible, on screen and in print.
163
+ - **Painted data surfaces dropped in the PDF.** Headless-Chromium print drops
164
+ backgrounds by default, silently blanking the data-bearing fills. Dot-matrix
165
+ cells, the segmented meter, status dots, masked glyphs, highlight marks,
166
+ connector lines, and progress/meter fills now carry `print-color-adjust: exact`
167
+ so they survive the A4 print/PDF that the report kit targets.
168
+ - **Dark-theme cards/tables printed dark-on-white.** The dark→ink token remap was
169
+ scoped to `.ui-report`; it is now lifted to the print `:root` (in the exempt
170
+ token-definition file), so a bare `.ui-card` / `.ui-statgrid` / `.ui-table` —
171
+ the markup an external LLM emits — also prints legibly.
172
+ - Inline `ui-citation` no longer dumps its full URL mid-sentence when printed
173
+ (the reference list carries the URL); `ui-legend--with-values` values are
174
+ right-aligned for a clean tabular column.
175
+ - **Annotation elbow connector was a 45° chamfer, not a dogleg.**
176
+ `connectorElbow` turned by `min(|dx|,|dy|)`, drawing a diagonal stub the
177
+ `stroke-linejoin` bevel never matched. It now delegates to the connectors
178
+ geometry kernel's right-angle `elbowPath` (H/V/H), so an annotation leader and
179
+ a node connector draw the same elbow.
180
+ - **Scoped behaviors no longer hijack the whole document on a null root.**
181
+ `init*({ root })` with an explicitly-provided-but-unready root (a framework
182
+ ref still `null` at mount, a conditional that hasn't rendered) now no-ops
183
+ instead of silently widening to document-wide delegation. The react/solid/qwik
184
+ bindings emit `root: null` for the not-ready case so the distinction survives
185
+ the boundary; passing no `root` still delegates from `document` exactly as
186
+ before. Affects every delegated behavior (dialog, menu, combobox, …).
187
+ - **`--report-width` / `--report-padding-block` are now declared defaults** on
188
+ `.ui-report` — they were read with inline fallbacks but never declared, so the
189
+ override surface was undiscoverable and `--report-measure` looked like the
190
+ width knob when it isn't.
191
+ - Carousel's IntersectionObserver is now set up and torn down in lockstep with
192
+ its event binding, removing a one-tick window where a re-init left two
193
+ observers on the same slides.
194
+ - **`ui-meter` / `ui-progress` fill painted a 0×0 box.** `.ui-meter__fill` and
195
+ `.ui-progress__bar` set `block-size`/`inline-size` but no `display`, so on the
196
+ documented `<span>` fill (an inline box ignores width/height) the bar rendered
197
+ empty — a "validates-but-renders-nothing" trap the registry and docs both
198
+ hid. They are now `display: block`. Found by a second multi-agent dogfood
199
+ pass; guarded going forward by a render-geometry e2e (below).
200
+
201
+ ### Documentation
202
+
203
+ - LLM-authored static reports: a prominent CSS-loading note (bundler vs
204
+ `node_modules` vs CDN) and a copy-pasteable CDN report in
205
+ `docs/reporting.md`; clarified that `dist/bronto.css` does **not** include the
206
+ opt-in report/chart/legend/annotation layers; number/date formatting
207
+ guidance; and a standalone, no-build report reference
208
+ (`demo/report-standalone.html`).
209
+ - Resolved the `is-*` self-contradiction: the framework's own
210
+ `is-num`/`is-pos`/`is-neg`/`is-key`/`is-open` state hooks are valid even
211
+ though they deliberately live outside `cls` (documented in
212
+ `docs/reference.md` and `classes.json`).
213
+ - Clarified two standing contracts in `docs/architecture.md`: `css/analytical.css`
214
+ is the roll-up of exactly the seven figure leaves (annotations, legend, marks,
215
+ connectors, spotlight, crosshair, selection) — `sources`/`state`/`generated`/
216
+ `workbench`/`command` are adjacent leaves imported individually — and the root
217
+ `.` export is CSS-only (no runtime JS at the root). Pre-1.0 stability/pinning
218
+ spelled out in `docs/stability.md`. `docs/workbench.md` notes that
219
+ `.ui-selectionbar` is unrelated to the `.ui-sel--*` selection-emphasis classes.
220
+ - Honest JSDoc limits: combobox/command read options from the DOM at init
221
+ (re-run after replacing them); popover restores focus on Escape but not on
222
+ outside-click; the table sorter is locale-naive display-text; mask-mode glyphs
223
+ are single-tone.
224
+ - **Foreign-renderer recipes hardened after a multi-agent dogfooding pass**
225
+ (build five real reports across the whole stack, review from every POV). The
226
+ Vega CDN recipe now pins the `/build/*.min.js` UMD bundles and `renderer:'svg'`
227
+ (a bare `cdn.jsdelivr.net/npm/vega@6` tag has no `window.vega`, so the previous
228
+ recipe rendered nothing); the file://-portable path (inline the config — an
229
+ imported/fetched config is CORS-blocked from disk) is now explicit. New
230
+ `docs/reporting.md` recipes: "Theming a live report" (the theme-toggle/re-embed
231
+ foot-guns — clear the host, container-width-while-hidden, Mermaid source vs
232
+ output), live charts are `ui-screen-only` while the table prints (a kept live
233
+ chart bakes the on-screen theme), `ui-meter`/`ui-quote` markup, and the
234
+ sequential/diverging frozen-figure ramp. `docs/d2.md` gains a frozen
235
+ inline-`<svg>`-from-slots recipe and on-accent-ink guidance; `docs/vega.md`
236
+ documents the theme-inverting ramp and the OKLCH-vs-d3 gradient-key drift.
237
+ - `docs/annotations.md` states the rule in both directions: a data annotation
238
+ must stay readable (not `aria-hidden`), a decorative one must be hidden.
239
+ - **A second dogfood pass closed the foreign-renderer/contract gaps it found.**
240
+ `docs/sources.md` + `llms.txt` now document the standalone `.ui-src` trust
241
+ pill and state that a `ui-src--*` tone class **needs a host** (a bare
242
+ `<span class="ui-src--verified">` validates but renders nothing), and name the
243
+ source-card body part as `__excerpt` (not `__detail`). `docs/mermaid.md`:
244
+ `gantt`/`timeline` are **not** covered by the base `themeVariables` (they
245
+ render with Mermaid's own defaults — prefer the native `ui-timeline` for a
246
+ report). `docs/mermaid.md` + `docs/d2.md` gain the same `file://` CORS caveat
247
+ Vega carries (inline the map or pre-render). `docs/vega.md`: select the themed
248
+ ramp with `scale: { range: 'heatmap' }` — **not** `scheme:`, which throws — and
249
+ the accent/neutral series map to `--chart-1` / `--chart-8`, so a legend keys
250
+ them with `ui-legend__swatch--1`/`--8` (`docs/legends.md`). `docs/reporting.md`:
251
+ the live-theme recipe now `finalize()`s the prior Vega view before re-embed
252
+ (was leaking a view per toggle), and notes `ui-meter --value` clamps at 100
253
+ (put an over-target figure in the written label). `docs/marks.md`: `ui-mark`
254
+ is a behind-text highlight (contrast-safe; never needs `--on-accent`).
255
+
256
+ ### Internal
257
+
258
+ - **New `check:versions` gate** — every `@ponchia/ui@X.Y.Z` literal in a shipped
259
+ doc (`llms.txt`, `docs/reporting.md`, …) must equal `package.json`, so a stale
260
+ CDN pin can't ship to LLM/copy-paste consumers on the next bump.
261
+ - **Dev-dependency Vega bumped to the v6 stack** — the render-probe now runs on
262
+ `vega@^6.2.0` + `vega-lite@^6.4.3` (Vega-Lite 6 peers Vega 6; a Vega-Lite-6 ÷
263
+ Vega-5 mix is incoherent). The theme `config` is version-independent resolved
264
+ hex, so the artifacts and the probe assertions are unchanged; the documented
265
+ CDN recipe is re-pinned to the matching majors (`vega@6.2.0` / `vega-lite@6.4.3`
266
+ / `vega-embed@7.1.0`, all still shipping a UMD `/build/*.min.js`). Vega remains
267
+ the consumer's renderer, not a runtime dependency.
268
+ - **New `check:doc-recipes` gate** — a `<script src>` CDN recipe in a shipped doc
269
+ must pin a jsDelivr `/build/*.min.js` UMD bundle, never a bare
270
+ `cdn.jsdelivr.net/npm/<pkg>@N` redirect (which serves a module bundle with no
271
+ global and renders nothing). Docs are otherwise an untested surface; this is
272
+ the structural guard that closes the broken-recipe class the dogfood pass
273
+ found. `<link href>` CSS and prose mentions are exempt.
274
+ - **`classes.json` `customProperties` expanded** to cover the load-bearing,
275
+ no-op-without-it knobs the audit found undocumented: the **required**
276
+ `--icon-mask` (a bare `.ui-icon` paints a solid square without it) and
277
+ `--ui-vt-name` (`.ui-vt` is inert without it), plus `--icon-size`. The
278
+ `states` manifest comment now explicitly names the runtime-managed hooks it
279
+ deliberately excludes (`is-leaving`/`is-visible`/`is-in`/`is-on`) so the
280
+ omission reads as intentional, not a gap. `--on-accent` is annotated at its
281
+ token source as a read-only export for foreign renderers (in-DOM ink is
282
+ `--button-text`). `contrast.md` now prints APCA `Lc` to one decimal so an
283
+ advisory shortfall (e.g. `Lc 44.9`) no longer rounds to a passing-looking `45`.
284
+ - Raw bundle budget 81 → 82 kB for the component-audit accessibility/state
285
+ blocks (gzip held ~14.1 kB — the additions are repetitive media-query and
286
+ `:has()`/`:not()` rules that compress well).
287
+ - **Code-quality audit (16-agent) — two new gates + targeted dedup, no churn.**
288
+ A code-health pass (complexity / duplication / AI-slop / missing-best-practice)
289
+ that deliberately left working, gate-protected code alone. Added:
290
+ `check:recipe-types` (factory↔`.d.ts` option parity, above) and `check:chain`
291
+ (every `check:*` script is wired into the aggregate `check` chain — closes the
292
+ silent-coverage-drop class; it would have caught a forgotten gate). Reconciled
293
+ a latent bug — `clamp()` had silently diverged between `connectors` and
294
+ `annotations`; the two now share one scalar/geometry kernel (the guarded form).
295
+ Dedup that removed real duplication: a shared `collectHosts()` /
296
+ `scrollIntoViewSafe()` / `wrapIndex()` in `behaviors/internal.js` (~9 behaviors),
297
+ a `freshnessErrors()` helper reused by 7 drift gates, the shared `CSS_COLOR`
298
+ regex across the 3 foreign-renderer gates, `check-report`'s opt-in list as a
299
+ loop, `check-pack`'s shipped-docs derived from `pkg.files`, and a looser
300
+ `check-classes` recipe-scrape. README hero de-densified; `srcTone` matched to
301
+ `stateTone`'s idiom; the intentional badge accent-mix (45% vs 40%) documented.
302
+ - **`check:dist` now asserts source-coverage** — every `css/*.css` leaf must be
303
+ bundled, an opt-in `EXTRA_LEAVES` entry, or a roll-up; an orphaned leaf that
304
+ would ship nothing now fails loudly (the inverse of the existing stale-dist
305
+ guard).
306
+ - **`check:dts-emit` now compares `.d.ts.map`** mapping data (volatile `sources`
307
+ path normalized), closing a drift hole the code comment had acknowledged.
308
+ - DTCG export types `--display-weight*` as the spec `fontWeight` type (was
309
+ `number`). Corrected stale `check-tokens.mjs` doc references (the real gate is
310
+ `check:fresh`).
311
+ - Tests: binding hook-surface parity is now **derived** from the modules (the old
312
+ hard-coded list silently omitted the five analytical hooks); a new
313
+ `analytical-boundary` test makes the "no scales/state/fetch/global-hotkey"
314
+ contract executable; a new behavior test pins the null-root no-op.
315
+ - Removed four dead keyframes (`scan`/`growBar`/`drawLine`/`pulseNode`) from
316
+ `motion.css`. Raw bundle budget 80 → 81 kB for the accessibility blocks (gzip
317
+ held ~14.0 kB).
318
+ - **`classes.json` `--value` retargeted** to `.ui-meter__fill, .ui-progress__bar`
319
+ (was the `.ui-meter, .ui-progress` track parent) — the custom property is read
320
+ on the fill child, so the machine-readable manifest now matches where an author
321
+ actually sets it.
322
+ - **New render-geometry e2e** (`test/e2e/render-geometry.spec.mjs`) — launches a
323
+ browser at the demo's real report primitives and asserts the `.ui-meter__fill`
324
+ / `.ui-progress__bar` fills and the standalone `.ui-src` pill paint a non-zero
325
+ box (via `getBoundingClientRect`, not the inline-box-lying
326
+ `getComputedStyle().inlineSize`). Closes the validates-but-renders-nothing
327
+ category that hid the meter regression. The demo gains a standalone `.ui-src`
328
+ pill row to exercise it.
329
+
330
+ ## 0.5.0 — 2026-06-02
331
+
332
+ A **minor** that builds out the "analytical & generated-report UI" identity: a
333
+ full suite of opt-in **communication primitives** — SVG annotations, legends,
334
+ text/evidence marks, leader-line connectors, a guided-focus spotlight, a
335
+ crosshair/readout, a selection-state vocabulary, label declutter + direct labels
336
+ (`declutterLabels`/`directLabels`), and a source/citation/provenance **trust
337
+ layer** — plus a consolidation pass over them. Each owns its visual grammar and
338
+ pure geometry and refuses to own scales/state/hit-testing (no chart engine).
339
+
340
+ Per the project's versioning policy, breaking changes ship in the minor. This
341
+ release carries three: the opt-in report kit's chart data key moved into the new
342
+ legend layer (`.ui-chart__legend`/`__swatch` removed — see Changed and
343
+ [`MIGRATIONS.json`](MIGRATIONS.json)), annotation arrowheads now render via
344
+ the shared connectors geometry kernel (a small path-shape change), and the
345
+ opt-in marks' rationed-accent tone was renamed `evidence`→`accent` to match the
346
+ rest of the analytical tone vocabulary. Everything else is additive and opt-in,
347
+ save for the tiny `.ui-shortcut` keyboard-hint primitive that joins the core
348
+ layer; the rest of the default `dist/bronto.css` is unchanged. Also folds in the
349
+ 0.4.x maintenance hardening that had not yet been released.
350
+
351
+ ### Added
352
+
353
+ - **SVG annotations** (`@ponchia/ui/css/annotations.css`,
354
+ `@ponchia/ui/annotations`, `.ui-annotation*`, `ui.annotation()`): an opt-in
355
+ annotation layer for charts, reports, and analytical figures, following the
356
+ d3-annotation grammar (a **subject** marks the thing, a **connector** points
357
+ away, a **note** carries the text). Ships a class grammar (variants for
358
+ label/callout/elbow/curve/circle/rect/threshold/badge/bracket/band/slope/
359
+ compare/cluster/axis/timeline/evidence, six tones, and opt-in
360
+ `draw`/`reveal`/`pulse`/`focus` motion that respects `prefers-reduced-motion`)
361
+ plus tiny geometry helpers that return SVG strings only — they own no chart
362
+ scales, mutate no DOM, and provide no edit mode. Documented in
363
+ [`docs/annotations.md`](docs/annotations.md) and gated by `check:report`.
364
+ - **Legends / data keys** (`@ponchia/ui/css/legend.css`, `.ui-legend*`,
365
+ `ui.legend()`/`ui.legendItem()`/`ui.legendSwatch()`, `initLegend`): an opt-in,
366
+ standalone data-key layer that reads the `--chart-*` palette tokens.
367
+ Categorical, continuous gradient (sequential + `--diverging`), threshold, and
368
+ pattern keys; swatch colour set inline (`--chart-color`) or via
369
+ `.ui-legend__swatch--1..8` index helpers; vertical/compact/with-values
370
+ layouts. WCAG 1.4.1 by construction (the text label is the non-colour
371
+ channel), with `forced-colors` and print care. Optional interactive
372
+ (series-toggling) entries are `<button aria-pressed>` controls: `initLegend`
373
+ flips `aria-pressed`/`.is-inactive` and emits `bronto:legend:toggle`
374
+ (`{ series, active }`) — the host owns hiding the series and any `aria-live`
375
+ announcement (it is never a chart engine). Optional `useLegend` hook in the
376
+ React/Solid/Qwik bindings. New `check:legend` gate proves swatch colours are a
377
+ subset of `tokens/charts.js` and never a raw hex. Documented in
378
+ [`docs/legends.md`](docs/legends.md).
379
+ - **Text marks / evidence** (`@ponchia/ui/css/marks.css`, `.ui-mark*`,
380
+ `.ui-bracket-note*`, `ui.mark()`/`ui.bracketNote()`): an opt-in layer of
381
+ sober, report-grade emphasis for running prose — the counterpart to SVG
382
+ annotations (annotations call out a figure, marks call out a sentence). Inline
383
+ `.ui-mark` (highlight/underline/box/strike; `--accent` + status
384
+ tones; `--draw` reduced-motion-safe sweep) for use on `<mark>`, and
385
+ `.ui-bracket-note` for bracketing a whole passage. Pure CSS on semantic
386
+ tokens, monochrome by default, with `forced-colors` care. Documented in
387
+ [`docs/marks.md`](docs/marks.md).
388
+ - **Connectors / leader lines** (`@ponchia/ui/css/connectors.css`,
389
+ `@ponchia/ui/connectors`, `.ui-connector*`, `initConnectors`, `ui.connector()`):
390
+ an opt-in layer that draws a line between two DOM elements (the
391
+ page-coordinate cousin of annotations). Pure geometry helpers
392
+ (`connectRects`/`connectorPath`/`arrowHead`/…) that return SVG strings and own
393
+ no DOM, an `.ui-connector` overlay grammar (straight/elbow/curve, arrow/dot
394
+ ends, tones, dashed, `--draw`), and an optional `initConnectors` behavior that
395
+ draws + tracks on resize/scroll. `useConnectors` in the bindings. Documented in
396
+ [`docs/connectors.md`](docs/connectors.md).
397
+ - **Spotlight / guided focus** (`@ponchia/ui/css/spotlight.css`, `.ui-spotlight*`,
398
+ `.ui-tour-note*`, `initSpotlight`, `ui.spotlight()`): an opt-in guided-focus
399
+ overlay — a box-shadow cutout over a target element, optional ring, and a
400
+ callout note. `initSpotlight` positions the cutout (`--spot-x/y/w/h`) and
401
+ re-places on resize/scroll and when `data-target` changes. Deliberately **not**
402
+ a tour engine — the host owns step order/advancing/visibility. `useSpotlight`
403
+ in the bindings. Documented in [`docs/spotlight.md`](docs/spotlight.md).
404
+ - **Crosshair / readout** (`@ponchia/ui/css/crosshair.css`, `.ui-crosshair*`,
405
+ `.ui-readout`, `initCrosshair`, `ui.crosshair()`): an opt-in plot ruler +
406
+ pinned readout. `initCrosshair` tracks the pointer over a
407
+ `[data-bronto-crosshair]` plot, sets `--crosshair-x/y`, and dispatches
408
+ `bronto:crosshair:move` with px + 0–1 fractions — it reports position only and
409
+ never maps pixels to data (that needs the host's scales). `useCrosshair` in the
410
+ bindings. Documented in [`docs/crosshair.md`](docs/crosshair.md).
411
+ - **Selection states** (`@ponchia/ui/css/selection.css`, `.ui-sel*`,
412
+ `ui.sel()`): a tiny cross-cutting selection-emphasis vocabulary
413
+ (`--on`/`--off`/`--maybe`) reusable on chart marks, table rows, list items, or
414
+ map regions. The carve-out from brush/lasso — Bronto styles the states; the
415
+ host owns the selection/hit-test logic. Documented in
416
+ [`docs/selection.md`](docs/selection.md).
417
+ - **Sources, citations & provenance** (`@ponchia/ui/css/sources.css`,
418
+ `.ui-citation`/`.ui-source-card`/`.ui-source-list`/`.ui-provenance`,
419
+ `ui.citation()`/`ui.source()`/`ui.provenance()`): an opt-in, CSS-only **trust
420
+ layer** for generated reports and AI output — the grammar for "where did this
421
+ come from?". A cross-cutting `.ui-src--*` state (verified/reviewed/generated/
422
+ unverified/stale/conflict) sets a rationed tone, always paired with an
423
+ author-written label (never colour alone). Bronto owns the grammar + states;
424
+ the host owns fetching, citation numbering, and trust. The first
425
+ frontier-primitive beyond the analytical suite. Documented in
426
+ [`docs/sources.md`](docs/sources.md).
427
+ - **Keyboard-shortcut hint** (`.ui-shortcut` + `.ui-shortcut__sep`, core): a tiny
428
+ universal-chrome primitive that lays out one or more `.ui-kbd` keys as a chord
429
+ (`⌘`+`K`) or sequence (`G` then `I`) with a dim connective. The command tier's
430
+ smallest piece, broadly useful outside a palette (menu items, buttons,
431
+ tooltips). Class-only, like `.ui-kbd`.
432
+ - **Lifecycle / system state** (`@ponchia/ui/css/state.css`, `.ui-state`
433
+ (+`__label`/`__detail`/`--busy`) with canonical state modifiers
434
+ (saving/saved/queued/offline/stale/conflict/error/locked/reviewed/
435
+ needs-review), `.ui-syncbar`, `ui.state()`): an opt-in, CSS-only vocabulary for
436
+ the states apps actually live in — a labelled state object with a rationed tone
437
+ and a page/document sync bar. The label is the state (never colour alone);
438
+ `--busy` pulses the indicator (reduced-motion-safe). Bronto ships the visual
439
+ states + canonical wording; the host owns the state machine, retry, and
440
+ persistence. Frontier candidate #2. Documented in [`docs/state.md`](docs/state.md).
441
+ - **Generated content & AI trust** (`@ponchia/ui/css/generated.css`,
442
+ `.ui-generated`/`.ui-origin-label`/`.ui-reasoning`/`.ui-tool-log`/`.ui-tool-call`,
443
+ `ui.originLabel()`): an opt-in, CSS-only set of **trust surfaces** for AI /
444
+ system-generated content — a marked region, an origin label, and quiet
445
+ native-`<details>` reasoning + tool-call logs. Not a chat kit; no
446
+ fabricated-confidence widget. Bronto styles disclosure/origin/trace, the host
447
+ owns model metadata, redaction, and safety. Pairs with the source layer.
448
+ Documented in [`docs/generated.md`](docs/generated.md).
449
+ - **Workbench** (`@ponchia/ui/css/workbench.css`, `.ui-inspector`/`.ui-property`/
450
+ `.ui-selectionbar`): an opt-in, CSS-only core for tool UIs — a selected-object
451
+ inspector panel, denser property rows, and a raised selection action bar.
452
+ Layout + affordances only; resizable split panes and drag handles are
453
+ deferred. Documented in [`docs/workbench.md`](docs/workbench.md).
454
+ - **Command palette** (`@ponchia/ui/css/command.css`, `.ui-command` (+
455
+ `__input`/`__list`/`__group`/`__item`/`__shortcut`/`__meta`/`__empty`),
456
+ `initCommand`, `useCommand`): an opt-in CSS shell + behavior — filter +
457
+ keyboard-navigate a DOM-authored command list (roving focus, group hiding,
458
+ full keyboard), emitting `bronto:command:select` ({ value, label }) and
459
+ `bronto:command:close`. Bronto navigates; the host owns the action registry,
460
+ routing, and execution. No global Cmd/Ctrl+K. Completes the command tier
461
+ (frontier #3) atop the shipped `ui-shortcut`. Documented in
462
+ [`docs/command.md`](docs/command.md).
463
+ - **Label declutter** (`@ponchia/ui/annotations` `declutterLabels`): a
464
+ deterministic, order-preserving **1-D** label de-overlap helper (sort, push
465
+ apart by `size + gap`, slide to fit `max`) — pure, no DOM/scales. Not a 2-D
466
+ collision solver. Documented in [`docs/annotations.md`](docs/annotations.md).
467
+ - **Direct labels** (`@ponchia/ui/annotations` `directLabels`): the
468
+ direct-labeling companion to `declutterLabels` — it declutters labels along an
469
+ axis **and** draws the leader from each anchor to its placed label, reusing the
470
+ connectors geometry kernel. Returns `[{ x, y, anchor, key, d }]` (the `d` feeds
471
+ a `ui-annotation__connector`). Deterministic and pure: no scales, no DOM, no
472
+ 2-D placement (the 1-D core of Labella, completed with leaders). Documented in
473
+ [`docs/annotations.md`](docs/annotations.md).
474
+ - **Connectors** (`@ponchia/ui/connectors`, `@ponchia/ui/css/connectors.css`,
475
+ `initConnectors`, `useConnectors`, `ui.connector()`) and **Spotlight**
476
+ (`css/spotlight.css`, `initSpotlight`, `ui.spotlight()`) — leader lines between
477
+ DOM elements and a guided-focus overlay; both opt-in, geometry/visual only
478
+ (the host owns layout/tour state).
479
+ - **Crosshair / readout** (`css/crosshair.css`, `initCrosshair`,
480
+ `ui.crosshair()`) and **selection states** (`css/selection.css`, `ui.sel()`) —
481
+ a plot ruler that reports pointer position (not data), and a cross-cutting
482
+ `.ui-sel--on/off/maybe` emphasis vocabulary (the host owns brush/hit-test).
483
+ - **`@ponchia/ui/css/analytical.css`** — a convenience roll-up that bundles the
484
+ seven analytical leaves (annotations, legend, marks, connectors, spotlight,
485
+ crosshair, selection) into one import. Add `dataviz.css`/`report.css`
486
+ separately as needed.
487
+
488
+ ### Fixed
489
+
490
+ - The optional Qwik binding (`@ponchia/ui/qwik`) is now built from the packed
491
+ tarball in CI **and** release, alongside React/Solid — closing a coverage gap
492
+ (it was documented as optimizer-proven but no job actually built it). Also
493
+ covered by `check:pack`, the size report, and the dead-code config.
494
+
495
+ ### Changed
496
+
497
+ - **Breaking (opt-in report kit):** the chart data key moved out of
498
+ `css/report.css` into the standalone `css/legend.css`. `.ui-chart__legend` →
499
+ `.ui-legend` (now with `.ui-legend__item`/`.ui-legend__label` rows) and
500
+ `.ui-chart__swatch` → `.ui-legend__swatch`. Import `@ponchia/ui/css/legend.css`
501
+ beside the report kit; see [`MIGRATIONS.json`](MIGRATIONS.json) and
502
+ [`docs/legends.md`](docs/legends.md). The `--chart-color`/`--chart-pattern`
503
+ swatch contract is unchanged, so the rename is mechanical.
504
+ - **Breaking (opt-in marks):** the rationed-accent **tone** on `.ui-mark` and
505
+ `.ui-bracket-note` was renamed `evidence` → `accent` (`ui-mark--evidence` →
506
+ `ui-mark--accent`, `ui-bracket-note--evidence` → `ui-bracket-note--accent`;
507
+ `ui.mark({ tone: 'accent' })` / `ui.bracketNote({ tone: 'accent' })`) so the
508
+ accent tone reads the same across every analytical primitive (it already was
509
+ `accent` on `ui.connector`/`ui.annotation`). `.ui-annotation--evidence` is
510
+ **unchanged** — it is a marker _variant_ (a proof/source shape), not a tone.
511
+ Mechanical whole-token rename; see [`MIGRATIONS.json`](MIGRATIONS.json).
512
+ - **Consolidation:** the SVG geometry is single-sourced in the `connectors`
513
+ kernel — `@ponchia/ui/annotations` now builds its connectors on it, so a
514
+ line/curve/arrow/dot is drawn one way across both. `connectorLine`/`Curve`/
515
+ `EndDot` output is byte-identical; **`connectorEndArrow` is the one
516
+ (minor-breaking) shape change** — the arrowhead now matches the connectors
517
+ arrowhead. New `check:helpers-dts` gate keeps the hand-maintained
518
+ `annotations`/`connectors` `.d.ts` in parity with their runtime exports.
519
+ - The Doto webfont now ships as **woff2 only** (Brotli) instead of uncompressed
520
+ TTF: ~5.7 kB per weight vs ~137 kB, cutting the six-weight payload from ~823 kB
521
+ to ~35 kB (the dot-matrix glyphs compress ~96%) and shrinking the unpacked
522
+ tarball by roughly the same. No TTF fallback is carried — woff2 is supported by
523
+ the entire browser floor (ADR-0002: Chrome 125 / Safari 18 / Firefox 129).
524
+ `@font-face` is internal, so this is transparent to consumers; only self-hosts
525
+ that referenced `fonts/doto-*.ttf` directly need to point at `*.woff2`.
526
+ - `docs/architecture.md` now ships in the package, so the offline rationale the
527
+ shipped ADRs link to resolves inside the tarball.
528
+ - `docs/stability.md` clarifies that `data-surface`/`data-density`/
529
+ `data-contrast` are **convenience presets**, not part of the stability
530
+ contract; `data-theme` (light/dark) remains the contractual base.
531
+
532
+ ### Internal
533
+
534
+ - Token values are single-sourced in `tokens/index.js` (`cssVars`); the
535
+ `css/tokens.css` palette is generated from it, so the dark palette is authored
536
+ once instead of in three places (the shipped CSS is byte-identical).
537
+ - `behaviors/index.js` is split into per-behavior modules behind the same public
538
+ barrel (no surface change).
539
+ - Drift-gate consolidation (`assertFresh`), a Qwik type smoke + stronger
540
+ class-recipe wiring test, the APCA advisory widened to the accent text across
541
+ the core palette and every colorway (still advisory; WCAG 2.1 AA stays the
542
+ hard gate), an OLED computed-style smoke test, and several doc reconciliations.
543
+ - New `demos.spec` e2e sweep runs the console-error / uncaught-exception /
544
+ failed-response guards **and** an axe scan over every per-feature demo page
545
+ (annotations, legends, marks, connectors, spotlight, crosshair, selection,
546
+ report) in both themes and cross-browser — previously only `/demo/` was
547
+ guarded, so a throw or 404 on those SVG-heavy pages could not fail CI.
548
+ - The `check:dist` payload ceiling was raised to 80 kB raw / 14.5 kB gzip (from
549
+ 78 kB / 13.5 kB). The default bundle was sitting ~21 bytes under the old gzip
550
+ gate — the analytical primitives are opt-in leaves and stay out of it, so this
551
+ is residual prior growth; the bump restores a real ~3% raw / ~7% gzip margin
552
+ so an ordinary token addition no longer trips an unrelated PR.
553
+
7
554
  ## 0.4.1 — 2026-06-01
8
555
 
9
556
  Patch hardening for the public framework surface, plus the first step of the
@@ -79,6 +626,9 @@ modern-platform motion direction (see [ADR-0002](docs/adr/0002-scope-and-2026-ba
79
626
  - **[ADR-0003](docs/adr/0003-theme-model.md)** records the theme model: a binary
80
627
  light/dark base × one-knob derivation × orthogonal axes (colorway, surface,
81
628
  contrast, density), and why a flat named-theme catalog is rejected.
629
+ - React and Solid Vite examples, CI/release matrix coverage for those examples,
630
+ runtime binding tests, public API stability docs, a release runbook, and
631
+ `npm run size:report`.
82
632
 
83
633
  ### Changed
84
634
 
@@ -117,12 +667,6 @@ modern-platform motion direction (see [ADR-0002](docs/adr/0002-scope-and-2026-ba
117
667
  and the OKLCH accent ramp uses an explicit white/black neutral endpoint for
118
668
  cross-engine browser parity.
119
669
 
120
- ### Added
121
-
122
- - React and Solid Vite examples, CI/release matrix coverage for those examples,
123
- runtime binding tests, public API stability docs, a release runbook, and
124
- `npm run size:report`.
125
-
126
670
  ## 0.4.0 — 2026-05-31
127
671
 
128
672
  The color-system release — [ADR-0001](docs/adr/0001-color-system.md) steps 1–8.