v0.5.0
A minor that builds out the "analytical & generated-report UI" identity: a
full suite of opt-in communication primitives — SVG annotations, legends,
text/evidence marks, leader-line connectors, a guided-focus spotlight, a
crosshair/readout, a selection-state vocabulary, label declutter + direct labels
(declutterLabels/directLabels), and a source/citation/provenance trust
layer — plus a consolidation pass over them. Each owns its visual grammar and
pure geometry and refuses to own scales/state/hit-testing (no chart engine).
Per the project's versioning policy, breaking changes ship in the minor. This
release carries three: the opt-in report kit's chart data key moved into the new
legend layer (.ui-chart__legend/__swatch removed — see Changed and
MIGRATIONS.json), annotation arrowheads now render via
the shared connectors geometry kernel (a small path-shape change), and the
opt-in marks' rationed-accent tone was renamed evidence→accent to match the
rest of the analytical tone vocabulary. Everything else is additive and opt-in,
save for the tiny .ui-shortcut keyboard-hint primitive that joins the core
layer; the rest of the default dist/bronto.css is unchanged. Also folds in the
0.4.x maintenance hardening that had not yet been released.
Added
- SVG annotations (
@ponchia/ui/css/annotations.css,
@ponchia/ui/annotations,.ui-annotation*,ui.annotation()): an opt-in
annotation layer for charts, reports, and analytical figures, following the
d3-annotation grammar (a subject marks the thing, a connector points
away, a note carries the text). Ships a class grammar (variants for
label/callout/elbow/curve/circle/rect/threshold/badge/bracket/band/slope/
compare/cluster/axis/timeline/evidence, six tones, and opt-in
draw/reveal/pulse/focusmotion that respectsprefers-reduced-motion)
plus tiny geometry helpers that return SVG strings only — they own no chart
scales, mutate no DOM, and provide no edit mode. Documented in
docs/annotations.mdand gated bycheck:report. - Legends / data keys (
@ponchia/ui/css/legend.css,.ui-legend*,
ui.legend()/ui.legendItem()/ui.legendSwatch(),initLegend): an opt-in,
standalone data-key layer that reads the--chart-*palette tokens.
Categorical, continuous gradient (sequential +--diverging), threshold, and
pattern keys; swatch colour set inline (--chart-color) or via
.ui-legend__swatch--1..8index helpers; vertical/compact/with-values
layouts. WCAG 1.4.1 by construction (the text label is the non-colour
channel), withforced-colorsand print care. Optional interactive
(series-toggling) entries are<button aria-pressed>controls:initLegend
flipsaria-pressed/.is-inactiveand emitsbronto:legend:toggle
({ series, active }) — the host owns hiding the series and anyaria-live
announcement (it is never a chart engine). OptionaluseLegendhook in the
React/Solid/Qwik bindings. Newcheck:legendgate proves swatch colours are a
subset oftokens/charts.jsand never a raw hex. Documented in
docs/legends.md. - Text marks / evidence (
@ponchia/ui/css/marks.css,.ui-mark*,
.ui-bracket-note*,ui.mark()/ui.bracketNote()): an opt-in layer of
sober, report-grade emphasis for running prose — the counterpart to SVG
annotations (annotations call out a figure, marks call out a sentence). Inline
.ui-mark(highlight/underline/box/strike;--accent+ status
tones;--drawreduced-motion-safe sweep) for use on<mark>, and
.ui-bracket-notefor bracketing a whole passage. Pure CSS on semantic
tokens, monochrome by default, withforced-colorscare. Documented in
docs/marks.md. - Connectors / leader lines (
@ponchia/ui/css/connectors.css,
@ponchia/ui/connectors,.ui-connector*,initConnectors,ui.connector()):
an opt-in layer that draws a line between two DOM elements (the
page-coordinate cousin of annotations). Pure geometry helpers
(connectRects/connectorPath/arrowHead/…) that return SVG strings and own
no DOM, an.ui-connectoroverlay grammar (straight/elbow/curve, arrow/dot
ends, tones, dashed,--draw), and an optionalinitConnectorsbehavior that
draws + tracks on resize/scroll.useConnectorsin the bindings. Documented in
docs/connectors.md. - Spotlight / guided focus (
@ponchia/ui/css/spotlight.css,.ui-spotlight*,
.ui-tour-note*,initSpotlight,ui.spotlight()): an opt-in guided-focus
overlay — a box-shadow cutout over a target element, optional ring, and a
callout note.initSpotlightpositions the cutout (--spot-x/y/w/h) and
re-places on resize/scroll and whendata-targetchanges. Deliberately not
a tour engine — the host owns step order/advancing/visibility.useSpotlight
in the bindings. Documented indocs/spotlight.md. - Crosshair / readout (
@ponchia/ui/css/crosshair.css,.ui-crosshair*,
.ui-readout,initCrosshair,ui.crosshair()): an opt-in plot ruler +
pinned readout.initCrosshairtracks the pointer over a
[data-bronto-crosshair]plot, sets--crosshair-x/y, and dispatches
bronto:crosshair:movewith px + 0–1 fractions — it reports position only and
never maps pixels to data (that needs the host's scales).useCrosshairin the
bindings. Documented indocs/crosshair.md. - Selection states (
@ponchia/ui/css/selection.css,.ui-sel*,
ui.sel()): a tiny cross-cutting selection-emphasis vocabulary
(--on/--off/--maybe) reusable on chart marks, table rows, list items, or
map regions. The carve-out from brush/lasso — Bronto styles the states; the
host owns the selection/hit-test logic. Documented in
docs/selection.md. - Sources, citations & provenance (
@ponchia/ui/css/sources.css,
.ui-citation/.ui-source-card/.ui-source-list/.ui-provenance,
ui.citation()/ui.source()/ui.provenance()): an opt-in, CSS-only trust
layer for generated reports and AI output — the grammar for "where did this
come from?". A cross-cutting.ui-src--*state (verified/reviewed/generated/
unverified/stale/conflict) sets a rationed tone, always paired with an
author-written label (never colour alone). Bronto owns the grammar + states;
the host owns fetching, citation numbering, and trust. The first
frontier-primitive beyond the analytical suite. Documented in
docs/sources.md. - Keyboard-shortcut hint (
.ui-shortcut+.ui-shortcut__sep, core): a tiny
universal-chrome primitive that lays out one or more.ui-kbdkeys as a chord
(⌘+K) or sequence (GthenI) with a dim connective. The command tier's
smallest piece, broadly useful outside a palette (menu items, buttons,
tooltips). Class-only, like.ui-kbd. - Lifecycle / system state (
@ponchia/ui/css/state.css,.ui-state
(+__label/__detail/--busy) with canonical state modifiers
(saving/saved/queued/offline/stale/conflict/error/locked/reviewed/
needs-review),.ui-syncbar,ui.state()): an opt-in, CSS-only vocabulary for
the states apps actually live in — a labelled state object with a rationed tone
and a page/document sync bar. The label is the state (never colour alone);
--busypulses the indicator (reduced-motion-safe). Bronto ships the visual
states + canonical wording; the host owns the state machine, retry, and
persistence. Frontier candidate #2. Documented indocs/state.md. - Generated content & AI trust (
@ponchia/ui/css/generated.css,
.ui-generated/.ui-origin-label/.ui-reasoning/.ui-tool-log/.ui-tool-call,
ui.originLabel()): an opt-in, CSS-only set of trust surfaces for AI /
system-generated content — a marked region, an origin label, and quiet
native-<details>reasoning + tool-call logs. Not a chat kit; no
fabricated-confidence widget. Bronto styles disclosure/origin/trace, the host
owns model metadata, redaction, and safety. Pairs with the source layer.
Documented indocs/generated.md. - Workbench (
@ponchia/ui/css/workbench.css,.ui-inspector/.ui-property/
.ui-selectionbar): an opt-in, CSS-only core for tool UIs — a selected-object
inspector panel, denser property rows, and a raised selection action bar.
Layout + affordances only; resizable split panes and drag handles are
deferred. Documented indocs/workbench.md. - Command palette (
@ponchia/ui/css/command.css,.ui-command(+
__input/__list/__group/__item/__shortcut/__meta/__empty),
initCommand,useCommand): an opt-in CSS shell + behavior — filter +
keyboard-navigate a DOM-authored command list (roving focus, group hiding,
full keyboard), emittingbronto:command:select({ value, label }) and
bronto:command:close. Bronto navigates; the host owns the action registry,
routing, and execution. No global Cmd/Ctrl+K. Completes the command tier
(frontier #3) atop the shippedui-shortcut. Documented in
docs/command.md. - Label declutter (
@ponchia/ui/annotationsdeclutterLabels): a
deterministic, order-preserving 1-D label de-overlap helper (sort, push
apart bysize + gap, slide to fitmax) — pure, no DOM/scales. Not a 2-D
collision solver. Documented indocs/annotations.md. - Direct labels (
@ponchia/ui/annotationsdirectLabels): the
direct-labeling companion todeclutterLabels— it declutters labels along an
axis and draws the leader from each anchor to its placed label, reusing the
connectors geometry kernel. Returns[{ x, y, anchor, key, d }](thedfeeds
aui-annotation__connector). Deterministic and pure: no scales, no DOM, no
2-D placement (the 1-D core of Labella, completed with leaders). Documented in
docs/annotations.md. - Connectors (
@ponchia/ui/connectors,@ponchia/ui/css/connectors.css,
initConnectors,useConnectors,ui.connector()) and Spotlight
(css/spotlight.css,initSpotlight,ui.spotlight()) — leader lines between
DOM elements and a guided-focus overlay; both opt-in, geometry/visual only
(the host owns layout/tour state). - Crosshair / readout (
css/crosshair.css,initCrosshair,
ui.crosshair()) and selection states (css/selection.css,ui.sel()) —
a plot ruler that reports pointer position (not data), and a cross-cutting
.ui-sel--on/off/maybeemphasis vocabulary (the host owns brush/hit-test). @ponchia/ui/css/analytical.css— a convenience roll-up that bundles the
seven analytical leaves (annotations, legend, marks, connectors, spotlight,
crosshair, selection) into one import. Adddataviz.css/report.css
separately as needed.
Fixed
- The optional Qwik binding (
@ponchia/ui/qwik) is now built from the packed
tarball in CI and release, alongside React/Solid — closing a coverage gap
(it was documented as optimizer-proven but no job actually built it). Also
covered bycheck:pack, the size report, and the dead-code config.
Changed
- Breaking (opt-in report kit): the chart data key moved out of
css/report.cssinto the standalonecss/legend.css..ui-chart__legend→
.ui-legend(now with.ui-legend__item/.ui-legend__labelrows) and
.ui-chart__swatch→.ui-legend__swatch. Import@ponchia/ui/css/legend.css
beside the report kit; seeMIGRATIONS.jsonand
docs/legends.md. The--chart-color/--chart-pattern
swatch contract is unchanged, so the rename is mechanical. - Breaking (opt-in marks): the rationed-accent tone on
.ui-markand
.ui-bracket-notewas renamedevidence→accent(ui-mark--evidence→
ui-mark--accent,ui-bracket-note--evidence→ui-bracket-note--accent;
ui.mark({ tone: 'accent' })/ui.bracketNote({ tone: 'accent' })) so the
accent tone reads the same across every analytical primitive (it already was
accentonui.connector/ui.annotation)..ui-annotation--evidenceis
unchanged — it is a marker variant (a proof/source shape), not a tone.
Mechanical whole-token rename; seeMIGRATIONS.json. - Consolidation: the SVG geometry is single-sourced in the
connectors
kernel —@ponchia/ui/annotationsnow builds its connectors on it, so a
line/curve/arrow/dot is drawn one way across both.connectorLine/Curve/
EndDotoutput is byte-identical;connectorEndArrowis the one
(minor-breaking) shape change — the arrowhead now matches the connectors
arrowhead. Newcheck:helpers-dtsgate keeps the hand-maintained
annotations/connectors.d.tsin parity with their runtime exports. - The Doto webfont now ships as woff2 only (Brotli) instead of uncompressed
TTF: ~5.7 kB per weight vs ~137 kB, cutting the six-weight payload from ~823 kB
to ~35 kB (the dot-matrix glyphs compress ~96%) and shrinking the unpacked
tarball by roughly the same. No TTF fallback is carried — woff2 is supported by
the entire browser floor (ADR-0002: Chrome 125 / Safari 18 / Firefox 129).
@font-faceis internal, so this is transparent to consumers; only self-hosts
that referencedfonts/doto-*.ttfdirectly need to point at*.woff2. docs/architecture.mdnow ships in the package, so the offline rationale the
shipped ADRs link to resolves inside the tarball.docs/stability.mdclarifies thatdata-surface/data-density/
data-contrastare convenience presets, not part of the stability
contract;data-theme(light/dark) remains the contractual base.
Internal
- Token values are single-sourced in
tokens/index.js(cssVars); the
css/tokens.csspalette is generated from it, so the dark palette is authored
once instead of in three places (the shipped CSS is byte-identical). behaviors/index.jsis split into per-behavior modules behind the same public
barrel (no surface change).- Drift-gate consolidation (
assertFresh), a Qwik type smoke + stronger
class-recipe wiring test, the APCA advisory widened to the accent text across
the core palette and every colorway (still advisory; WCAG 2.1 AA stays the
hard gate), an OLED computed-style smoke test, and several doc reconciliations. - New
demos.spece2e sweep runs the console-error / uncaught-exception /
failed-response guards and an axe scan over every per-feature demo page
(annotations, legends, marks, connectors, spotlight, crosshair, selection,
report) in both themes and cross-browser — previously only/demo/was
guarded, so a throw or 404 on those SVG-heavy pages could not fail CI. - The
check:distpayload ceiling was raised to 80 kB raw / 14.5 kB gzip (from
78 kB / 13.5 kB). The default bundle was sitting ~21 bytes under the old gzip
gate — the analytical primitives are opt-in leaves and stay out of it, so this
is residual prior growth; the bump restores a real ~3% raw / ~7% gzip margin
so an ordinary token addition no longer trips an unrelated PR.