@ponchia/ui 0.4.0 → 0.5.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 (123) hide show
  1. package/CHANGELOG.md +352 -12
  2. package/MIGRATIONS.json +92 -0
  3. package/README.md +27 -17
  4. package/annotations/index.d.ts +280 -0
  5. package/annotations/index.js +522 -0
  6. package/behaviors/carousel.js +197 -0
  7. package/behaviors/combobox.js +195 -0
  8. package/behaviors/command.js +187 -0
  9. package/behaviors/connectors.js +96 -0
  10. package/behaviors/crosshair.js +58 -0
  11. package/behaviors/dialog.js +73 -0
  12. package/behaviors/disclosure.js +25 -0
  13. package/behaviors/dismissible.js +24 -0
  14. package/behaviors/forms.js +158 -0
  15. package/behaviors/glyph.js +109 -0
  16. package/behaviors/index.d.ts +82 -1
  17. package/behaviors/index.js +18 -1336
  18. package/behaviors/internal.js +50 -0
  19. package/behaviors/legend.js +46 -0
  20. package/behaviors/menu.js +46 -0
  21. package/behaviors/popover.js +108 -0
  22. package/behaviors/spotlight.js +53 -0
  23. package/behaviors/table.js +109 -0
  24. package/behaviors/tabs.js +103 -0
  25. package/behaviors/theme.js +82 -0
  26. package/behaviors/toast.js +152 -0
  27. package/classes/index.d.ts +318 -0
  28. package/classes/index.js +352 -0
  29. package/classes/vscode.css-custom-data.json +18 -14
  30. package/connectors/index.d.ts +71 -0
  31. package/connectors/index.js +179 -0
  32. package/css/analytical.css +21 -0
  33. package/css/annotations.css +292 -0
  34. package/css/command.css +97 -0
  35. package/css/connectors.css +93 -0
  36. package/css/crosshair.css +100 -0
  37. package/css/disclosure.css +29 -0
  38. package/css/dots.css +2 -0
  39. package/css/feedback.css +104 -0
  40. package/css/fonts.css +11 -7
  41. package/css/generated.css +117 -0
  42. package/css/legend.css +268 -0
  43. package/css/marks.css +144 -0
  44. package/css/motion.css +88 -0
  45. package/css/overlay.css +52 -1
  46. package/css/primitives.css +18 -0
  47. package/css/report.css +363 -0
  48. package/css/selection.css +46 -0
  49. package/css/sources.css +179 -0
  50. package/css/spotlight.css +104 -0
  51. package/css/state.css +121 -0
  52. package/css/tokens.css +78 -60
  53. package/css/workbench.css +83 -0
  54. package/dist/bronto.css +1 -1
  55. package/dist/css/analytical.css +1 -0
  56. package/dist/css/annotations.css +1 -0
  57. package/dist/css/command.css +1 -0
  58. package/dist/css/connectors.css +1 -0
  59. package/dist/css/crosshair.css +1 -0
  60. package/dist/css/disclosure.css +1 -1
  61. package/dist/css/dots.css +1 -1
  62. package/dist/css/feedback.css +1 -1
  63. package/dist/css/fonts.css +1 -1
  64. package/dist/css/generated.css +1 -0
  65. package/dist/css/legend.css +1 -0
  66. package/dist/css/marks.css +1 -0
  67. package/dist/css/motion.css +1 -1
  68. package/dist/css/overlay.css +1 -1
  69. package/dist/css/primitives.css +1 -1
  70. package/dist/css/report.css +1 -0
  71. package/dist/css/selection.css +1 -0
  72. package/dist/css/sources.css +1 -0
  73. package/dist/css/spotlight.css +1 -0
  74. package/dist/css/state.css +1 -0
  75. package/dist/css/tokens.css +1 -1
  76. package/dist/css/workbench.css +1 -0
  77. package/docs/adr/0001-color-system.md +26 -27
  78. package/docs/adr/0002-scope-and-2026-baseline.md +104 -0
  79. package/docs/adr/0003-theme-model.md +97 -0
  80. package/docs/annotations.md +345 -0
  81. package/docs/architecture.md +202 -0
  82. package/docs/command.md +95 -0
  83. package/docs/connectors.md +91 -0
  84. package/docs/contrast.md +42 -42
  85. package/docs/crosshair.md +63 -0
  86. package/docs/generated.md +91 -0
  87. package/docs/legends.md +168 -0
  88. package/docs/marks.md +86 -0
  89. package/docs/reference.md +418 -15
  90. package/docs/reporting.md +305 -0
  91. package/docs/selection.md +40 -0
  92. package/docs/sources.md +110 -0
  93. package/docs/spotlight.md +78 -0
  94. package/docs/stability.md +52 -0
  95. package/docs/state.md +85 -0
  96. package/docs/theming.md +18 -6
  97. package/docs/usage.md +43 -3
  98. package/docs/workbench.md +72 -0
  99. package/fonts/doto-400.woff2 +0 -0
  100. package/fonts/doto-500.woff2 +0 -0
  101. package/fonts/doto-600.woff2 +0 -0
  102. package/fonts/doto-700.woff2 +0 -0
  103. package/fonts/doto-800.woff2 +0 -0
  104. package/fonts/doto-900.woff2 +0 -0
  105. package/llms.txt +288 -8
  106. package/package.json +95 -5
  107. package/qwik/index.d.ts +60 -0
  108. package/qwik/index.js +149 -0
  109. package/react/index.d.ts +40 -14
  110. package/react/index.js +32 -5
  111. package/solid/index.d.ts +40 -14
  112. package/solid/index.js +31 -3
  113. package/tokens/index.d.ts +3 -3
  114. package/tokens/index.js +25 -19
  115. package/tokens/index.json +32 -28
  116. package/tokens/resolved.json +34 -32
  117. package/tokens/tokens.dtcg.json +22 -14
  118. package/fonts/doto-400.ttf +0 -0
  119. package/fonts/doto-500.ttf +0 -0
  120. package/fonts/doto-600.ttf +0 -0
  121. package/fonts/doto-700.ttf +0 -0
  122. package/fonts/doto-800.ttf +0 -0
  123. package/fonts/doto-900.ttf +0 -0
package/docs/state.md ADDED
@@ -0,0 +1,85 @@
1
+ # Lifecycle & system state
2
+
3
+ `@ponchia/ui/css/state.css` is an opt-in vocabulary for the states real apps
4
+ spend their time in — saving, saved, queued, offline, stale, conflicted, locked,
5
+ reviewed. These are usually improvised per product, so even good apps feel
6
+ inconsistent. This is the canonical set: a labelled state object with a rationed
7
+ tone, plus a page/document sync bar.
8
+
9
+ ```css
10
+ @import '@ponchia/ui';
11
+ @import '@ponchia/ui/css/state.css';
12
+ ```
13
+
14
+ Bronto ships the visual states and the canonical wording. The host owns the
15
+ state machine, retry policy, persistence, and announcements. **Persistent state
16
+ deserves persistent UI** — a toast is secondary, not the answer. The tone is a
17
+ second channel; the **label is the state**, so it survives forced-colors and
18
+ screen readers (WCAG 1.4.1).
19
+
20
+ ## `.ui-state`
21
+
22
+ A leading tone dot, a `__label`, and an optional `__detail`. Add `--busy` to
23
+ pulse the indicator for an in-progress state (reduced-motion-safe).
24
+
25
+ ```html
26
+ <span class="ui-state ui-state--saving ui-state--busy">
27
+ <span class="ui-state__label">Saving…</span>
28
+ </span>
29
+
30
+ <span class="ui-state ui-state--saved">
31
+ <span class="ui-state__label">Saved</span>
32
+ <span class="ui-state__detail">2m ago</span>
33
+ </span>
34
+ ```
35
+
36
+ ## State matrix
37
+
38
+ Use the canonical label; the modifier bakes in the tone.
39
+
40
+ | State | Class | Canonical label | Tone | Busy? |
41
+ | --- | --- | --- | --- | --- |
42
+ | Saving | `ui-state--saving` | "Saving…" | accent | yes |
43
+ | Saved | `ui-state--saved` | "Saved" / "All changes saved" | success | — |
44
+ | Queued | `ui-state--queued` | "Queued" / "Pending" | muted | — |
45
+ | Offline | `ui-state--offline` | "Offline" | warning | — |
46
+ | Stale | `ui-state--stale` | "Out of date" | warning | — |
47
+ | Conflict | `ui-state--conflict` | "Conflict" | danger | — |
48
+ | Error | `ui-state--error` | "Failed" / "Couldn't save" | danger | — |
49
+ | Locked | `ui-state--locked` | "Locked" / "Read-only" | muted | — |
50
+ | Reviewed | `ui-state--reviewed` | "Reviewed" | success | — |
51
+ | Needs review | `ui-state--needs-review` | "Needs review" | warning | — |
52
+
53
+ "Syncing" and "Retrying" are the saving tone with their own label — use
54
+ `ui-state--saving ui-state--busy` and write the word.
55
+
56
+ ## `.ui-syncbar`
57
+
58
+ A page- or document-level status strip: a state on one side, optional actions on
59
+ the other.
60
+
61
+ ```html
62
+ <div class="ui-syncbar">
63
+ <span class="ui-state ui-state--saved">
64
+ <span class="ui-state__label">All changes saved</span>
65
+ <span class="ui-state__detail">just now</span>
66
+ </span>
67
+ <button class="ui-button ui-button--subtle ui-button--sm" type="button">View history</button>
68
+ </div>
69
+ ```
70
+
71
+ ## Recipe
72
+
73
+ ```js
74
+ import { ui } from '@ponchia/ui/classes';
75
+
76
+ ui.state({ state: 'saving', busy: true }); // "ui-state ui-state--saving ui-state--busy"
77
+ ui.state({ state: 'conflict' }); // "ui-state ui-state--conflict"
78
+ ```
79
+
80
+ ## Scope
81
+
82
+ CSS only — there is no JS yet. Auto-updating elapsed time ("2m ago") or live
83
+ progress text is the host's job; a small optional behavior may come later if a
84
+ real consumer needs it. Background-job progress and conflict-resolution
85
+ affordances are deliberately deferred until then.
package/docs/theming.md CHANGED
@@ -4,13 +4,15 @@
4
4
  This is the **stable, supported surface** for re-branding without forking.
5
5
  Anything not listed here is internal and may change between minor versions.
6
6
 
7
- ## The one knob: `--accent`
7
+ ## The brand knob: `--accent`
8
8
 
9
- The whole accent family derives from `--accent` via `color-mix()`:
9
+ The accent family derives from `--accent` via `color-mix()` and the
10
+ theme-owned neutral ramp endpoint:
10
11
 
11
12
  | Token | Derivation (light / dark) | Role |
12
13
  | --------------------- | -------------------------------------------- | ---- |
13
14
  | `--accent-strong` | `--accent` mixed 83% with black / 84% white | darker/lighter accent for hover, emphasis |
15
+ | `--accent-ramp-end` | white / black | neutral endpoint for the low-chroma OKLCH ramp |
14
16
  | `--accent-text` | `var(--accent-strong)` (alias) | **accent used as foreground text** — the on-surface, AA-safe one |
15
17
  | `--accent-soft` | `--accent` at 10% / 14% over transparent | tinted fills |
16
18
  | `--bg-accent` | `--accent` at 6% / 8% | faint accent backgrounds |
@@ -49,6 +51,14 @@ fills — follows automatically, in both light and dark.
49
51
  - **Spacing** — override the `--space-2xs … --space-2xl` scale, or use a
50
52
  preset: `data-density="compact"` / `data-density="comfortable"` on any
51
53
  element (defaults to the middle scale).
54
+ - **Dark surface** — the dark theme's base is a deliberately *elevated*
55
+ near-black (`--bg: #121212`) for readability: pure black + bright text
56
+ causes halation, and near-black-on-black surface steps are imperceptible.
57
+ For OLED power-saving or the original true-black "Nothing" look, opt in with
58
+ `data-surface="oled"` on `:root` (a root-level attribute like `data-theme`).
59
+ It only affects the dark theme and is a CSS-only preset (not in the JS token
60
+ model), so it never blacks out the light theme. See
61
+ [ADR-0003](adr/0003-theme-model.md) for the theme-model rationale.
52
62
  - **Radius** — `--radius-sm … --radius-xl`, `--radius-pill`. The Nothing
53
63
  default is near-sharp; raise these for a softer brand.
54
64
  - **Type** — `--display` (dot-matrix face), `--mono`, `--sans`. Override
@@ -115,7 +125,7 @@ is not. The values above are *measured* (white label on `#a8431a` =
115
125
  the same check. Don't set one global `--accent` and hope — that is why
116
126
  this example is split per theme.
117
127
 
118
- ## Token tiers (0.3.1)
128
+ ## Token tiers
119
129
 
120
130
  Three additive, non-breaking tiers sit on top of the primitives. The
121
131
  short legacy names (`--panel`, `--line`, `--accent`, …) keep working
@@ -131,9 +141,11 @@ coarser-grained handles.
131
141
  primitives, so light/dark still Just Works.
132
142
  - **Accent ramp — `--accent-1 … --accent-6`.** A stepped family
133
143
  (subtle → bold) derived from the single `--accent` knob via
134
- `color-mix` against the theme background. Re-brands and theme-adapts
135
- automatically. This is the palette for charts / data-viz / multi-state
136
- surfaces (the use case the JS token export advertises).
144
+ `color-mix(in oklch, …)` against a per-theme white/black endpoint
145
+ (`--accent-ramp-end`). Re-brands and theme-adapts automatically. Steps
146
+ 1–4 are subtle surfaces; steps 5–6 are the accent and strong accent.
147
+ Exact hex outputs are visual tuning, but the token names and roles are
148
+ stable.
137
149
  - **Neutral ramp — `--surface-1 … --surface-6`** (low → high contrast
138
150
  against `--bg`) for layered surfaces without hand-picking greys.
139
151
  - **Stacking scale — `--z-base / -raised / -sticky / -overlay /
package/docs/usage.md CHANGED
@@ -60,6 +60,22 @@ Rule of thumb: state → dot, classification → badge, user-controlled value
60
60
  `.ui-card` itself, so card padding/border stays the card's and prose
61
61
  rhythm stays the content's. One responsibility per element.
62
62
 
63
+ ## Static reports
64
+
65
+ Use the opt-in `@ponchia/ui/css/report.css` layer for static, PDF-first
66
+ reports. A report composes `ui-report` + existing primitives: `ui-statgrid`
67
+ for KPIs, `ui-alert` for persistent notices, `ui-table` for evidence,
68
+ `ui-timeline` for events, `ui-meter` for measured values, and `ui-prose` only
69
+ for narrative body content you do not fully control.
70
+
71
+ Do not turn every report block into a card. Use `ui-report__summary`,
72
+ `ui-report__finding`, and `ui-report__evidence` for document structure; use
73
+ `ui-card` only when the block is genuinely a repeated card item. Charts use
74
+ the report `ui-chart*` wrappers plus the opt-in data-viz tokens; simple static
75
+ bar charts can use `ui-chart__plot`, `ui-chart__bar`, and `ui-chart__fill`.
76
+ Always include a caption and fallback data. Full LLM/static report cookbook:
77
+ [reporting.md](reporting.md).
78
+
63
79
  ## Buttons: variant and size
64
80
 
65
81
  - **primary** — the single most important action in a view. Aim for one.
@@ -148,12 +164,14 @@ infinite-clone track). It's a gallery: the user drives it.
148
164
 
149
165
  ## Display glyphs: when (and when not)
150
166
 
151
- `@ponchia/ui/glyphs` is a 43-glyph dot-matrix icon set — navigation
167
+ `@ponchia/ui/glyphs` is a 48-glyph dot-matrix icon set — navigation
152
168
  (`arrow-*`, `chevron-*`), actions (`check`, `close`, `plus`, `minus`,
153
169
  `search`, `menu`, `gear`), status (`info`, `warning`, `bell`, `lock`) and
154
- common marks (`home`, `user`, `heart`, `star`, `spark`) — rendered on the
170
+ common marks (`home`, `user`, `heart`, `star`, `spark`, circle-family marks) — rendered on the
155
171
  `.ui-dotmatrix` primitive, so they re-skin with the same `--field-dot*`
156
- tokens as every other dot surface (no SVG, no icon font).
172
+ tokens as every other dot surface. The default and `solid` renderers emit
173
+ dot-matrix DOM, not an icon font; the dense `.ui-icon` renderer uses an
174
+ internal SVG data URL as a CSS mask so it can stay one DOM node.
157
175
 
158
176
  **Two rendering modes — pick by size.** The dots need physical room to
159
177
  read, so the default _dot_ look is for **display** sizes (~40px up: hero
@@ -245,6 +263,28 @@ dashboards: `--chart-1..8` (categorical), `--chart-seq-*` (sequential),
245
263
  Cap a chart at ~8 series. Full detail in [theming.md](theming.md) →
246
264
  "Data-viz palette".
247
265
 
266
+ ## SVG annotations: subject, connector, note
267
+
268
+ `@ponchia/ui/css/annotations.css` (opt-in) adds Bronto-styled SVG annotations
269
+ for reports and chart figures. It is a visual grammar, not a charting or
270
+ authoring engine.
271
+
272
+ - Compose each callout from `ui-annotation` plus a subject
273
+ (`ui-annotation__subject`), connector (`ui-annotation__connector`), and note
274
+ (`ui-annotation__note`, `ui-annotation__title`, `ui-annotation__label`).
275
+ - Use `ui.annotation({ variant, tone, motion })` when building class strings in
276
+ JS. The default is a callout in the accent tone; motion is always opt-in.
277
+ - Use `@ponchia/ui/annotations` when you want deterministic SVG path strings
278
+ for circle, rect, threshold, bracket, band, slope, comparison, cluster, axis,
279
+ timeline, line, elbow, or curve annotations. Use `notePlacement()` for a
280
+ single bounded note when you need a conservative first placement pass.
281
+ - Status tones are only for status-bearing callouts; otherwise use `accent` for
282
+ the main insight and `muted` for secondary labels.
283
+ - Keep annotated charts sparse. Dense figures need a scrollable SVG, a
284
+ simplified mobile SVG, or complete caption/fallback text.
285
+ - Annotation text must be visible or represented in the figure caption, SVG
286
+ `<desc>`, or fallback table. Full detail in [annotations.md](annotations.md).
287
+
248
288
  ## When to add a behavior
249
289
 
250
290
  The CSS is the framework; `@ponchia/ui/behaviors` is the *sanctioned*
@@ -0,0 +1,72 @@
1
+ # Workbench — inspector, properties, selection bar
2
+
3
+ `@ponchia/ui/css/workbench.css` is an opt-in set of primitives for **real
4
+ tools**: an inspector panel, property rows for a selected object, and a bar of
5
+ actions on the current selection. Generic kits stop at cards/tables/forms, so
6
+ every app builds its own half-consistent workbench. This is the low-risk CSS
7
+ core — layout and affordances only.
8
+
9
+ ```css
10
+ @import '@ponchia/ui';
11
+ @import '@ponchia/ui/css/workbench.css';
12
+ ```
13
+
14
+ Resizable split panes (a focusable ARIA window-splitter behavior) and drag
15
+ handles are deliberately deferred until a consumer needs them. Not in the core
16
+ bundle.
17
+
18
+ ## Inspector — `.ui-inspector`
19
+
20
+ A panel of details for the selected object: a `__header` (title + actions) over
21
+ a `__body` of property rows.
22
+
23
+ ```html
24
+ <aside class="ui-inspector">
25
+ <div class="ui-inspector__header">
26
+ <h2 class="ui-eyebrow">Rectangle</h2>
27
+ <button class="ui-button ui-button--subtle ui-button--sm" type="button">Reset</button>
28
+ </div>
29
+ <div class="ui-inspector__body">
30
+ <!-- property rows -->
31
+ </div>
32
+ </aside>
33
+ ```
34
+
35
+ ## Property row — `.ui-property`
36
+
37
+ A label/value pair, denser than `ui-key-value` and tuned for an inspector. The
38
+ `__value` can hold a static read-out or an input.
39
+
40
+ ```html
41
+ <div class="ui-property">
42
+ <span class="ui-property__label">Width</span>
43
+ <span class="ui-property__value">240 px</span>
44
+ </div>
45
+ <div class="ui-property">
46
+ <span class="ui-property__label">Fill</span>
47
+ <span class="ui-property__value"><input class="ui-input" value="#121212" /></span>
48
+ </div>
49
+ ```
50
+
51
+ ## Selection bar — `.ui-selectionbar`
52
+
53
+ A raised bar of actions on the current selection: a `__count` on one side,
54
+ `__actions` on the other. The host owns what is selected and what the actions do.
55
+
56
+ ```html
57
+ <div class="ui-selectionbar">
58
+ <span class="ui-selectionbar__count">3 selected</span>
59
+ <span class="ui-selectionbar__actions">
60
+ <button class="ui-button ui-button--subtle ui-button--sm" type="button">Group</button>
61
+ <button class="ui-button ui-button--subtle ui-button--sm" type="button">Align</button>
62
+ <button class="ui-button ui-button--danger ui-button--sm" type="button">Delete</button>
63
+ </span>
64
+ </div>
65
+ ```
66
+
67
+ ## Scope
68
+
69
+ CSS only, no recipes — these are structural containers and rows; apply the
70
+ classes directly (or read them from `cls.inspector`, `cls.property`, …). Pair
71
+ the selection bar with the cross-cutting [`ui-sel`](./selection.md) states on the
72
+ selected items themselves; Bronto styles both, the host owns the hit-testing.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/llms.txt CHANGED
@@ -16,10 +16,16 @@
16
16
  CSS (pick one; do not import both):
17
17
 
18
18
  ```css
19
- @import '@ponchia/ui'; /* dist/bronto.css — flattened, minified, one file (recommended) */
20
- @import '@ponchia/ui/css'; /* css/core.css — @import fan-out, needs a bundler */
19
+ @import '@ponchia/ui'; /* dist/bronto.css — flattened, minified, one file (recommended) */
20
+ @import '@ponchia/ui/css'; /* css/core.css — @import fan-out, needs a bundler */
21
21
  ```
22
22
 
23
+ `import '@ponchia/ui'` is supported only as a CSS side-effect import in a
24
+ CSS-aware bundler. Do not import the package root from Node/runtime JS; use the
25
+ explicit JS subpaths below.
26
+
27
+ JS subpaths are ESM-only. In CommonJS, use dynamic `import()`.
28
+
23
29
  Everything lands in `@layer bronto`. Consumer styles in no layer (or a
24
30
  later layer) always win over framework styles regardless of selector
25
31
  specificity — this is the intended override mechanism; do not fight it
@@ -46,11 +52,13 @@ Optional vanilla behaviors (theme toggle, etc. — SSR-safe, tree-shakeable):
46
52
  import { applyStoredTheme } from '@ponchia/ui/behaviors';
47
53
  ```
48
54
 
49
- For React/Solid, optional thin hook bindings over those behaviors (peer deps
50
- `react` / `solid-js`, optional — core stays zero-dep):
55
+ For React/Solid/Qwik, optional thin hook bindings over those behaviors (peer
56
+ deps `react` / `solid-js` / `@builder.io/qwik`, optional — core stays zero-dep):
51
57
 
52
58
  ```js
53
- import { useDialog, useToast } from '@ponchia/ui/react'; // or '@ponchia/ui/solid'
59
+ import { useDialog, useToast } from '@ponchia/ui/react'; // or '@ponchia/ui/solid' or '@ponchia/ui/qwik'
60
+ // React: scope with { root: reactRef } or a resolver, not { root: ref.current } during render.
61
+ // Qwik: hooks run in useVisibleTask$; scope with a Qwik signal — useDialog({ root: useSignal() }).
54
62
  ```
55
63
 
56
64
  Optional display glyphs — dot-matrix bitmaps on the `.ui-dotmatrix` primitive
@@ -66,7 +74,7 @@ like `data-theme` (the derived accent family only recomputes at `:root`):
66
74
 
67
75
  ```html
68
76
  <link rel="stylesheet" href="@ponchia/ui/css/skins.css" />
69
- <html data-bronto-skin="amber-crt | phosphor-green | e-ink">
77
+ <html data-bronto-skin="amber-crt | phosphor-green | e-ink"></html>
70
78
  ```
71
79
 
72
80
  Each re-points the one `--accent` (OKLCH, per-theme, contrast-gated); status
@@ -89,6 +97,265 @@ simulated protan/deutan/tritan), `--chart-seq-*` (sequential), `--chart-div-*`
89
97
  (diverging), and `--chart-pattern-1..8` (dot-matrix fills — pair colour N with
90
98
  pattern N; colour is never the sole signal). Details in `docs/theming.md`.
91
99
 
100
+ Building analytical / generated-report UI? Import the roll-up
101
+ `@ponchia/ui/css/analytical.css` (bundles annotations + legend + marks +
102
+ connectors + spotlight + crosshair + selection) instead of the seven leaves;
103
+ add `css/dataviz.css` for chart colours and `css/report.css` for the document
104
+ grammar. The individual layers (each opt-in, none in the default bundle):
105
+
106
+ Optional SVG annotations for charts/reports — opt-in, never in the default
107
+ bundle. This is Bronto's subject / connector / note grammar inspired by
108
+ d3-annotation, not an authoring engine:
109
+
110
+ ```html
111
+ <link rel="stylesheet" href="@ponchia/ui/css/annotations.css" />
112
+ ```
113
+
114
+ ```js
115
+ import { annotationParts, connectorLine, circleSubjectPath } from '@ponchia/ui/annotations';
116
+ ```
117
+
118
+ Use `ui-annotation` groups inside SVG with `ui-annotation__subject`,
119
+ `ui-annotation__connector`, `ui-annotation__note`, `ui-annotation__title`, and
120
+ `ui-annotation__label`. `ui.annotation({ variant, tone, motion })` builds the
121
+ base class string. Variants cover labels, callouts, elbow/curve connectors,
122
+ circle/rect/threshold subjects, badges, brackets, bands, slopes, comparisons,
123
+ clusters, axis marks, timeline pins, and evidence markers. Motion is opt-in
124
+ (`draw`, `reveal`, `pulse`, `focus`) and respects reduced-motion preferences.
125
+ Use `notePlacement()` for one-note bounded placement, but do not treat it as a
126
+ whole-chart collision solver. `declutterLabels(items,{gap,min,max})` is a
127
+ deterministic 1-D declutter; `directLabels(items,{axis,cross,gap,min,max,shape})`
128
+ adds the leader line (it declutters labels along an axis and returns each placed
129
+ point plus a leader path `d` for `ui-annotation__connector`) — neither owns
130
+ scales, DOM, or 2-D placement. Status tones are only for status-bearing callouts.
131
+ Keep charts sparse; dense mobile figures need horizontal scrolling, a simplified
132
+ SVG, or complete caption/fallback text. Details: `docs/annotations.md`.
133
+
134
+ Optional legends / data keys for charts and figures — opt-in, never in the
135
+ default bundle. Reads the `--chart-*` tokens, so a key never drifts from its
136
+ series:
137
+
138
+ ```html
139
+ <link rel="stylesheet" href="@ponchia/ui/css/legend.css" />
140
+ ```
141
+
142
+ Use a `ui-legend` container with `ui-legend__item` rows, each holding a
143
+ `ui-legend__swatch` (decorative, `aria-hidden`) and a `ui-legend__label` (the
144
+ required non-colour channel — WCAG 1.4.1; colour alone never carries meaning).
145
+ Set the swatch colour inline (`style="--chart-color: var(--chart-3)"`, plus
146
+ `--chart-pattern` to match a patterned mark) or with a `ui-legend__swatch--1..8`
147
+ index helper. Variants: `ui-legend--vertical`/`--compact`/`--with-values`,
148
+ `ui-legend--gradient` (+`--diverging`, with `ui-legend__track`/`__ticks`/`__tick`),
149
+ and `ui-legend--threshold`. `ui.legend()`/`ui.legendItem()`/`ui.legendSwatch()`
150
+ build the class strings. Interactive (series-toggling) legends are opt-in:
151
+ author entries as `<button aria-pressed>` under `[data-bronto-legend]`, run
152
+ `initLegend()` (or `useLegend()`), and listen for `bronto:legend:toggle`
153
+ (`{ series, active }`) — Bronto flips the control state; you hide the series and
154
+ own the `aria-live` announcement. Details: `docs/legends.md`.
155
+
156
+ Optional text marks / evidence emphasis for running prose — opt-in, never in
157
+ the default bundle. The prose counterpart to annotations (annotations call out a
158
+ figure; marks call out a sentence):
159
+
160
+ ```html
161
+ <link rel="stylesheet" href="@ponchia/ui/css/marks.css" />
162
+ ```
163
+
164
+ Put `ui-mark` on a `<mark>` for inline emphasis (semantic, not just visual).
165
+ Styles: default highlight, `ui-mark--underline`, `ui-mark--box`,
166
+ `ui-mark--strike`. Tones: `ui-mark--accent` (the rationed accent),
167
+ `--success`/`--warning`/`--danger`/`--info` (status-bearing only), `--muted`.
168
+ `ui-mark--draw` sweeps the highlight in (reduced-motion-safe). `ui-bracket-note`
169
+ (+ `ui-bracket-note__label`, tones `--accent`/`--warning`/`--danger`/`--info`)
170
+ brackets a whole passage — the prose analogue of `ui-annotation--bracket`.
171
+ `ui.mark({ style, tone, motion })` / `ui.bracketNote({ tone })` build the class
172
+ strings. A mark is visual only; if the emphasis carries meaning the words don't,
173
+ state it in the text (WCAG 1.4.1). Details: `docs/marks.md`.
174
+
175
+ Optional connectors / leader lines between two DOM elements — opt-in, never in
176
+ the default bundle. Pure geometry + an optional draw/track behavior; owns no
177
+ layout:
178
+
179
+ ```html
180
+ <link rel="stylesheet" href="@ponchia/ui/css/connectors.css" />
181
+ ```
182
+
183
+ Place a `ui-connector` SVG (`data-bronto-connector`, `aria-hidden`) as an
184
+ overlay inside a `position: relative` container holding both endpoints; set
185
+ `data-from`/`data-to` (element ids), optional `data-shape`
186
+ (straight|elbow|curve), `data-from-side`/`data-to-side`, and `data-end`
187
+ (arrow|dot|none). `initConnectors()` (or `useConnectors()`) draws the path and
188
+ redraws on resize/scroll. Tones `ui-connector--accent`/`--muted`/status,
189
+ `ui-connector--dashed`, `ui-connector--draw`. Pure helpers from
190
+ `@ponchia/ui/connectors` (`connectRects`, `connectorPath`, `arrowHead`,
191
+ `anchorPoint`, …) return SVG strings for SSR/canvas. Details: `docs/connectors.md`.
192
+
193
+ Optional spotlight / guided-focus overlay — opt-in, never in the default
194
+ bundle. The visual language of a tour, NOT a tour engine (the host owns step
195
+ order/advancing/visibility):
196
+
197
+ ```html
198
+ <link rel="stylesheet" href="@ponchia/ui/css/spotlight.css" />
199
+ ```
200
+
201
+ A `ui-spotlight` overlay (`data-bronto-spotlight`, `data-target=<element id>`)
202
+ contains a `ui-spotlight__hole` (the box-shadow cutout) and a `ui-tour-note`
203
+ callout (`__step`/`__title`/`__body`/`__actions`). `initSpotlight()` (or
204
+ `useSpotlight()`) measures the target and sets `--spot-x/y/w/h`, re-placing on
205
+ resize/scroll and when `data-target` changes — so advance a tour by updating
206
+ `data-target`. `ui-spotlight--ring` adds a ring. The overlay is non-blocking by
207
+ design; put the real guidance text in the focusable `ui-tour-note`. Details:
208
+ `docs/spotlight.md`.
209
+
210
+ Optional crosshair / readout for reading a value off a plot — opt-in, never in
211
+ the default bundle. Reports the pointer position; the host maps it to data:
212
+
213
+ ```html
214
+ <link rel="stylesheet" href="@ponchia/ui/css/crosshair.css" />
215
+ ```
216
+
217
+ Mark the plot `[data-bronto-crosshair]` (position: relative) and put a
218
+ `ui-crosshair` overlay inside it with `ui-crosshair__line ui-crosshair__line--x`
219
+ and/or `--y`, optional `ui-crosshair__badge`, and a `ui-readout` chip.
220
+ `initCrosshair()` (or `useCrosshair()`) sets `--crosshair-x/y` (px) on pointer
221
+ move, toggles `is-active`, and dispatches `bronto:crosshair:move`
222
+ ({ x, y, fx, fy } — px + 0..1 fractions) / `bronto:crosshair:leave`. It does NOT
223
+ find the nearest datum or map pixels→data (that needs your scales). `ui.crosshair({ muted })`.
224
+ Details: `docs/crosshair.md`.
225
+
226
+ Optional selection-state vocabulary — opt-in, cross-cutting, CSS only. The carve
227
+ from brush/lasso: Bronto does not do region selection/hit-testing; it ships only
228
+ the states an item can be in:
229
+
230
+ ```html
231
+ <link rel="stylesheet" href="@ponchia/ui/css/selection.css" />
232
+ ```
233
+
234
+ Apply `ui-sel` + `ui-sel--on` (selected), `ui-sel--off` (excluded), or
235
+ `ui-sel--maybe` (live-brush candidate) to any data element (chart mark, table
236
+ row, list item, map region) from YOUR selection/brush logic — Bronto only styles
237
+ them. `ui.sel({ state: 'on'|'off'|'maybe' })`. Details: `docs/selection.md`.
238
+
239
+ Optional source/citation/provenance trust layer — opt-in, CSS only, never in
240
+ the default bundle. The grammar for "where did this come from?" in generated
241
+ reports/AI output:
242
+
243
+ ```html
244
+ <link rel="stylesheet" href="@ponchia/ui/css/sources.css" />
245
+ ```
246
+
247
+ `ui-citation` (inline ref on a real `<a>`/`<button>`; `--chip` = named-source
248
+ pill), `ui-source-card` (+`__title`/`__origin`/`__time`/`__excerpt`/`__actions`),
249
+ `ui-source-list` (+`__item`), and `ui-provenance` (+`__item`). A cross-cutting
250
+ trust state sets the tone: `ui-src--verified`/`--reviewed`/`--generated`/
251
+ `--unverified`/`--stale`/`--conflict` — always paired with an author-written
252
+ label (never colour alone). Bronto styles + states; the host owns fetching,
253
+ numbering, and trust. `ui.citation({chip,state})` / `ui.source({state})` /
254
+ `ui.provenance({state})`. Details: `docs/sources.md`.
255
+
256
+ Optional lifecycle/system-state vocabulary — opt-in, CSS only, never in the
257
+ default bundle. The states apps live in (saving/saved/queued/offline/stale/
258
+ conflict/error/locked/reviewed/needs-review):
259
+
260
+ ```html
261
+ <link rel="stylesheet" href="@ponchia/ui/css/state.css" />
262
+ ```
263
+
264
+ `ui-state` (+`__label`/`__detail`) carries a tone dot + the canonical label;
265
+ the `ui-state--saving`/`--saved`/… modifier bakes the tone, `ui-state--busy`
266
+ pulses the indicator for
267
+ in-progress states (reduced-motion-safe). `ui-syncbar` is a page/document
268
+ save-status strip. The label IS the state (never colour alone); the host owns
269
+ the state machine, retry, and persistence — persistent state gets persistent UI,
270
+ not a toast. `ui.state({ state, busy })`. Details: `docs/state.md`.
271
+
272
+ Optional generated-content / AI-trust surfaces — opt-in, CSS only, never in the
273
+ default bundle. NOT a chat kit; the trust surfaces around generated content:
274
+
275
+ ```html
276
+ <link rel="stylesheet" href="@ponchia/ui/css/generated.css" />
277
+ ```
278
+
279
+ `ui-generated` (+`__label`) marks a machine-authored region; `ui-origin-label`
280
+ (+`--ai`) tags origin ("AI generated" / "Human reviewed"); `ui-reasoning`
281
+ (+`__body`) and `ui-tool-log` / `ui-tool-call` (+`__name`/`__status`/`__body`)
282
+ are quiet native-`<details>` collapsibles for reasoning + tool traces. Bronto
283
+ styles disclosure/origin/trace; the host owns model metadata, redaction, and
284
+ safety. No confidence/verdict widget (don't fabricate precision). `ui.originLabel({ ai })`.
285
+ Details: `docs/generated.md`.
286
+
287
+ Optional workbench primitives for tool UIs — opt-in, CSS only, never in the
288
+ default bundle:
289
+
290
+ ```html
291
+ <link rel="stylesheet" href="@ponchia/ui/css/workbench.css" />
292
+ ```
293
+
294
+ `ui-inspector` (+`__header`/`__body`) is a selected-object detail panel;
295
+ `ui-property` (+`__label`/`__value`) is a denser key/value row for it;
296
+ `ui-selectionbar` (+`__count`/`__actions`) is a raised action bar for the current
297
+ selection (pair with `ui-sel` states on the items). Layout/affordances only —
298
+ resizable split panes and drag handles are deferred. Class-only. Details:
299
+ `docs/workbench.md`.
300
+
301
+ Optional command palette — opt-in CSS shell (`css/command.css`) + `initCommand`
302
+ behavior. Filter + keyboard-navigate a DOM-authored command list:
303
+
304
+ ```html
305
+ <link rel="stylesheet" href="@ponchia/ui/css/command.css" />
306
+ ```
307
+
308
+ `ui-command` (+`__input`/`__list`/`__group`/`__item`/`__shortcut`/`__meta`/
309
+ `__empty`); `initCommand()` owns filtering, roving focus (Down/Up/Home/End),
310
+ Enter→`bronto:command:select` ({value,label}) and Escape→`bronto:command:close`.
311
+ The HOST owns the action registry + execution; NO global Cmd/Ctrl+K (open it
312
+ yourself, e.g. a <dialog> via initDialog). Pairs with `ui-shortcut`. Binding:
313
+ `useCommand()` (react/solid/qwik). Details: `docs/command.md`.
314
+
315
+ Optional static report layer for LLM-authored HTML reports — opt-in, never in
316
+ the default bundle. Pair it with the normal bundle, and add data-viz only when
317
+ the report contains charts. For standalone browser HTML, use real URLs; package
318
+ specifiers only work after a bundler rewrites them:
319
+
320
+ ```html
321
+ <link rel="stylesheet" href="./node_modules/@ponchia/ui/dist/bronto.css" />
322
+ <link rel="stylesheet" href="./node_modules/@ponchia/ui/dist/css/report.css" />
323
+ <link rel="stylesheet" href="./node_modules/@ponchia/ui/dist/css/dataviz.css" />
324
+ ```
325
+
326
+ In Vite/webpack/etc., import `@ponchia/ui`, `@ponchia/ui/css/report.css`, and
327
+ `@ponchia/ui/css/dataviz.css` instead.
328
+
329
+ Canonical report skeleton:
330
+
331
+ ```html
332
+ <main class="ui-report ui-report--numbered">
333
+ <header class="ui-report__cover">
334
+ <p class="ui-eyebrow">Report</p>
335
+ <h1 class="ui-report__title">System review</h1>
336
+ <p class="ui-report__subtitle">Static HTML, Chromium PDF-ready.</p>
337
+ <ol class="ui-report__meta">
338
+ <li>2026-06-01</li>
339
+ <li>Generated</li>
340
+ </ol>
341
+ </header>
342
+ <section class="ui-report__section">
343
+ <h2 class="ui-report__section-head">Executive summary</h2>
344
+ <div class="ui-report__summary">
345
+ <div class="ui-prose"><p>Use prose only for narrative body content.</p></div>
346
+ </div>
347
+ </section>
348
+ </main>
349
+ ```
350
+
351
+ Report rules for agents: static output by default; do not initialize behaviors
352
+ over untrusted generated content; sanitize arbitrary LLM/CMS/user HTML before
353
+ rendering; use semantic headings; give tables captions and header cells; give
354
+ charts captions, legends/direct labels, and fallback data; use
355
+ `ui-chart__plot`/`ui-chart__bar`/`ui-chart__fill` for simple static bar charts;
356
+ use `ui-report__section--unnumbered` for appendices in numbered reports; never
357
+ use raw chromatic inline colors. Full cookbook: `docs/reporting.md`.
358
+
92
359
  ## Authoritative offline references (shipped in this package)
93
360
 
94
361
  Read these from `node_modules/@ponchia/ui/` — no network needed:
@@ -101,9 +368,16 @@ Read these from `node_modules/@ponchia/ui/` — no network needed:
101
368
  the same source as the types and CI-drift-checked.
102
369
  - `docs/theming.md` — the token contract: which tokens to override, the
103
370
  light/dark model, and the rationed-accent rule.
371
+ - `docs/stability.md` — the public API matrix: stable vs internal surfaces,
372
+ root-import contract, behavior trust boundary, and glyph-mask CSP note.
104
373
  - `docs/usage.md` — the decision guide: badge vs chip vs dot, default
105
374
  density, prose-in-card, when to add a behavior. Read this before
106
375
  re-implementing UI the framework already has an opinion about.
376
+ - `docs/reporting.md` — static/PDF-first report cookbook for LLM-authored
377
+ reports, including report skeletons, chart rules, print utilities, and safety
378
+ boundaries.
379
+ - `docs/annotations.md` — SVG annotation recipes and helper guidance for
380
+ analytical figures.
107
381
  - `docs/contrast.md` — the published, CI-gated WCAG 2.1 contrast matrix
108
382
  for every contractual token pairing, per theme. Generated from the
109
383
  resolved palette; the build fails below the declared floor.
@@ -117,13 +391,15 @@ Read these from `node_modules/@ponchia/ui/` — no network needed:
117
391
  - `glyphs/glyphs.d.ts` — the `GlyphName` literal union plus `renderGlyph` /
118
392
  `glyphCells` signatures for the display glyphs. (The DOM form,
119
393
  `initDotGlyph`, is declared in `behaviors/index.d.ts`.)
394
+ - `annotations/index.d.ts` — no-dependency SVG path helpers for Bronto
395
+ annotation geometry.
120
396
  - `classes/vscode.css-custom-data.json` — editor autocomplete for tokens.
121
397
 
122
398
  ## Human-browsable references (not shipped in the npm tarball, by design)
123
399
 
124
400
  - Live kitchen-sink demo (every component, light/dark, RTL): https://ponchia.github.io/bronto-ui/
125
401
  - Architecture & the CSS-first decision: https://github.com/Ponchia/bronto-ui/blob/main/docs/architecture.md
126
- - Framework integration guides (Astro / SvelteKit / React-Solid / vanilla):
402
+ - Framework integration guides (Astro / SvelteKit / React-Solid-Qwik / vanilla):
127
403
  https://github.com/Ponchia/bronto-ui/tree/main/docs/getting-started
128
404
 
129
405
  ## Rules an agent should respect
@@ -131,6 +407,10 @@ Read these from `node_modules/@ponchia/ui/` — no network needed:
131
407
  - Never invent `ui-*` class names. Every valid class is a member of
132
408
  `cls` in `classes/index.d.ts`; if it is not there, it does not exist.
133
409
  - Prefer the `ui.*()` recipes over hand-concatenating modifier strings.
410
+ Report/layout classes added in 0.4.1 are the exception: use `cls.report*`,
411
+ `cls.chart*`, `cls.printOnly`, or literal documented class names because
412
+ there are no `ui.report()` / `ui.chart()` recipes yet. Annotations do have a
413
+ `ui.annotation()` recipe for variant/tone selection.
134
414
  - Override framework styles by writing your own rules outside
135
415
  `@layer bronto` — not with higher specificity or `!important`.
136
416
  - Color is tiered (ADR-0001): neutral canvas · one rationed accent · locked
@@ -138,4 +418,4 @@ Read these from `node_modules/@ponchia/ui/` — no network needed:
138
418
  tokens — re-point `--accent` (or a `data-bronto-skin` colorway), use status
139
419
  tokens for status, and `--chart-*` (from `css/dataviz.css`) for charts only.
140
420
  Don't add raw hues to components; `check:color-policy` enforces this.
141
- - Pre-1.0 SemVer: breaking changes ship in the *minor*. Pin `~0.x`.
421
+ - Pre-1.0 SemVer: breaking changes ship in the _minor_. Pin `~0.x`.