@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/README.md CHANGED
@@ -3,12 +3,12 @@
3
3
  [![npm](https://img.shields.io/npm/v/@ponchia/ui?logo=npm)](https://www.npmjs.com/package/@ponchia/ui)
4
4
  [![npm provenance](https://img.shields.io/badge/npm-provenance-blue?logo=npm)](https://www.npmjs.com/package/@ponchia/ui#provenance)
5
5
  [![runtime deps](https://img.shields.io/badge/runtime%20deps-0-brightgreen)](https://github.com/Ponchia/bronto-ui/blob/main/package.json)
6
- [![dist](https://img.shields.io/badge/dist-~73kB%20%2F%20~13kB%20gzip-informational)](https://github.com/Ponchia/bronto-ui/blob/main/scripts/check-dist.mjs)
6
+ [![dist](https://img.shields.io/badge/dist-~76kB%20%2F%20~13kB%20gzip-informational)](https://github.com/Ponchia/bronto-ui/blob/main/scripts/check-dist.mjs)
7
7
  [![CI](https://github.com/Ponchia/bronto-ui/actions/workflows/ci.yml/badge.svg)](https://github.com/Ponchia/bronto-ui/actions/workflows/ci.yml)
8
8
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Ponchia/bronto-ui/badge)](https://scorecard.dev/viewer/?uri=github.com/Ponchia/bronto-ui)
9
9
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/Ponchia/bronto-ui/blob/main/LICENSE)
10
10
 
11
- **A CSS-first, framework-agnostic UI framework built on token-driven restraint monochrome by default, one rationed accent, dot-matrix display type, hairline borders. Color is _tiered_: we don't decorate with it, we signal with it. The "Nothing"-inspired look is the default skin, not the architecture opt-in colorways (amber CRT · phosphor green · e-ink), a dot-matrix icon set, and a colourblind-safe data-viz palette prove the one knob is real. Zero runtime dependencies; re-brand the whole thing with one CSS variable.**
11
+ **A CSS-first design system for interfaces that explain themselves.** Works in plain HTML, every modern framework, and print/PDF there's no component runtime to adopt. The look is a deliberate stance: a neutral monochrome canvas with **one _rationed_ accent** (colour _signals_, it never decorates), dot-matrix display type and hairline borders, re-skinnable from a single `--accent` knob (opt-in amber-CRT · phosphor-green · e-ink colorways). And beyond the standard component set it ships an opt-in **analytical & report layer** — SVG annotations, legends, leader-line connectors, a guided-focus spotlight, text marks, a colourblind-safe data-viz palette and a static/PDF report grammar — for dashboards and LLM-authored reports. Zero runtime dependencies; re-brand the whole thing with one CSS variable.
12
12
 
13
13
  ### [Live demo →](https://ponchia.github.io/bronto-ui/)  ·  [Theme playground →](https://ponchia.github.io/bronto-ui/demo/theme-playground.html)
14
14
 
@@ -20,6 +20,8 @@ The demo is the kitchen sink — every component, light/dark, RTL, live theming.
20
20
 
21
21
  `@ponchia/ui` ships its design as **CSS**, not components. You drop in one stylesheet and style with semantic `ui-*` classes; an optional thin layer of typed class-name recipes and SSR-safe vanilla behaviors sits on top for the few things that genuinely need JS (theme persistence, dialogs, toasts, disclosure). The guiding principle is **color is rationed, structure carries meaning** — layout, type weight and the hairline do the work before a hue does, and the accent is a spotlight, not a paint bucket. Because everything lives in a single `@layer bronto`, your own un-layered CSS overrides the framework with no specificity fight and no `!important`.
22
22
 
23
+ It ships a complete, accessible **standard component set** — but that's not where it competes. Its differentiator is an opt-in **analytical & communication layer** for interfaces that make complex work legible: annotations, legends, connectors, marks, a guided-focus spotlight, lifecycle/system-state and source-provenance vocabularies, and a static/PDF report grammar. Each owns only its visual grammar and pure geometry — no chart engine, no state, no hit-testing. See **[docs/frontier-primitives.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/frontier-primitives.md)** for the thesis.
24
+
23
25
  ## Install
24
26
 
25
27
  ```bash
@@ -34,7 +36,7 @@ Or drop it in with no build step, straight from a CDN:
34
36
 
35
37
  ## Quick start
36
38
 
37
- **1. Load the CSS.** One flattened, minified bundle — the whole framework, one request (~73 kB raw / ~13 kB gzip):
39
+ **1. Load the CSS.** One flattened, minified bundle — the whole framework, one request (~76 kB raw / ~13 kB gzip):
38
40
 
39
41
  ```css
40
42
  @import '@ponchia/ui'; /* via a bundler */
@@ -79,7 +81,7 @@ toast('Saved', { tone: 'success' }); // body-anchored stack, no markup needed
79
81
 
80
82
  Behaviors cover theme persistence, disclosure, dropdown menus, native-`<dialog>` modals/drawers, tabs, combobox, form validation, table sort, carousel and toasts — wired by `data-bronto-*` attributes.
81
83
 
82
- **5. (Optional) display glyphs — a 43-glyph dot-matrix icon set:**
84
+ **5. (Optional) display glyphs — a 48-glyph dot-matrix icon set:**
83
85
 
84
86
  ```js
85
87
  import { renderGlyph } from '@ponchia/ui/glyphs';
@@ -89,7 +91,7 @@ el.innerHTML = renderGlyph('search', { label: 'Search' }); // role="img"
89
91
  // import { initDotGlyph } from '@ponchia/ui/behaviors'; initDotGlyph();
90
92
  ```
91
93
 
92
- Arrows, chevrons, check/close/plus/minus, search/menu/gear, info/warning/bell/lock, home/user/heart/star/spark rendered on the same `.ui-dotmatrix` primitive and re-skinned by the `--field-dot*` tokens (no SVG, no font). Default dot look is for **display** sizes; pass **`{ solid: true }`** to fuse the cells into a crisp pixel glyph that works as an **inline icon down to ~16px**.
94
+ Arrows, chevrons, check/close/plus/minus, search/menu/gear, info/warning/bell/lock, home/user/heart/star/spark and circle-family marks are rendered on the same `.ui-dotmatrix` primitive and re-skinned by the `--field-dot*` tokens. Default dot look is for **display** sizes; pass **`{ solid: true }`** to fuse the cells into a crisp pixel glyph that works as an **inline icon down to ~16px**. For dense lists/tables, `renderGlyph(name, { render: 'mask' })` emits one `.ui-icon` element backed by an internal CSS mask.
93
95
 
94
96
  ## What's in the box
95
97
 
@@ -100,8 +102,10 @@ Arrows, chevrons, check/close/plus/minus, search/menu/gear, info/warning/bell/lo
100
102
  - **Disclosure & nav** — tabs, accordion, segmented, breadcrumb, pagination, `ui-steps`, `ui-timeline`, `ui-pagehead`, `ui-kbd`.
101
103
  - **Shells** — an admin dashboard shell (`ui-app-*`) and a content/marketing site shell (`ui-site*`, `ui-container`).
102
104
  - **Prose** — `.ui-prose` styles raw, unclassed semantic HTML (Markdown / CMS / LLM output) with zero classes.
105
+ - **Analytical & communication primitives** _(opt-in)_ — `@ponchia/ui/css/analytical.css`: SVG **annotations** (subject/connector/note), standalone **legends**/data-keys, text/evidence **marks**, leader-line **connectors** (+ a pure `@ponchia/ui/connectors` geometry kernel), a guided-focus **spotlight**, a **crosshair**/readout, and a cross-cutting **selection** vocabulary. Each owns its visual grammar + pure geometry and refuses scales/state/hit-testing — figures that explain themselves, not a chart engine. Plus standalone **`source`/provenance** (trust) and **lifecycle `state`** leaves.
106
+ - **Reports** _(opt-in)_ — `@ponchia/ui/css/report.css`, a static/PDF-first report grammar for LLM-authored HTML: covers, sections, findings, evidence, figures, chart wrappers and print utilities.
103
107
  - **Motion & dots** — the dot-matrix motif kit: dot grid, status dots, dot loaders, the orbital spinner, matrix reveal — all reduced-motion aware.
104
- - **Glyphs** — `@ponchia/ui/glyphs`, a 43-glyph dot-matrix icon set on the `.ui-dotmatrix` primitive (display marks + crisp `solid` inline icons), no SVG/font.
108
+ - **Glyphs** — `@ponchia/ui/glyphs`, a 48-glyph dot-matrix icon set on the `.ui-dotmatrix` primitive (display marks + crisp `solid` inline icons + one-node `.ui-icon` mask rendering).
105
109
  - **Colorways** _(opt-in)_ — `data-bronto-skin="amber-crt | phosphor-green | e-ink"`: a root-level recolour of the one accent (OKLCH, per-theme, contrast-gated), never in the default bundle.
106
110
  - **Data-viz** _(opt-in)_ — a colourblind-safe chart palette (`--chart-*` + dot-matrix pattern fills, resolved hex in `charts.json`), gated under simulated protan/deutan/tritan vision. Charts only, never UI chrome.
107
111
 
@@ -116,7 +120,7 @@ Everything accent-colored derives from a single `--accent` variable via `color-m
116
120
  .promo { --accent: #16a34a; } /* …or just this section green */
117
121
  ```
118
122
 
119
- Buttons, focus rings, dot motifs, accent borders and soft fills all follow automatically. Light/dark is `data-theme="light"` / `"dark"` on `<html>` (defaults to `prefers-color-scheme`); `data-density` and `data-contrast` give density and contrast presets. A full re-skin (radius, display face, dot density, surfaces) is a handful more token overrides — the "Nothing" look is the **default skin, not the architecture**.
123
+ Buttons, focus rings, dot motifs, accent borders and soft fills all follow automatically. Light/dark is `data-theme="light"` / `"dark"` on `<html>` (defaults to `prefers-color-scheme`); `data-density` and `data-contrast` give density and contrast presets. A full re-skin (radius, display face, dot density, surfaces) is a handful more token overrides — the default monochrome look is **one skin, not the architecture**.
120
124
 
121
125
  **One system, many skins.** That the knob is real isn't a claim — it ships: drop in `@ponchia/ui/css/skins.css` and set `data-bronto-skin="amber-crt | phosphor-green | e-ink"` on `<html>` for a complete, contrast-gated recolour (the derived accent family, focus ring, dot-matrix and glyphs all follow). Colour is governed in **tiers** — neutral canvas · one accent · locked status · display expression · opt-in data-viz — so it always earns its place; the full constitution is **[ADR-0001](https://github.com/Ponchia/bronto-ui/blob/main/docs/adr/0001-color-system.md)**.
122
126
 
@@ -128,7 +132,7 @@ Not an afterthought — a gate. Every contractual token pairing has a declared W
128
132
 
129
133
  ## Works with anything
130
134
 
131
- The CSS is the framework, so it works with React, Svelte/SvelteKit, Astro, Vue, Solid or plain HTML — there's no component runtime to adopt. The optional `classes` and `behaviors` entrypoints pull in **no** UI framework and are SSR-safe. For React and Solid there are also **optional thin bindings** — `@ponchia/ui/react` and `@ponchia/ui/solid` wrap the behaviors as hooks (`useDialog`, `useToast`, …); `react`/`solid-js` are optional peer deps, so the core stays zero-dependency.
135
+ The CSS is the framework, so it works with React, Svelte/SvelteKit, Astro, Vue, Solid, Qwik or plain HTML — there's no component runtime to adopt. The optional `classes` and `behaviors` entrypoints pull in **no** UI framework and are SSR-safe. For React, Solid and Qwik there are also **optional thin bindings** — `@ponchia/ui/react`, `@ponchia/ui/solid` and `@ponchia/ui/qwik` wrap the behaviors as hooks (`useDialog`, `useToast`, …); `react`/`solid-js`/`@builder.io/qwik` are optional peer deps, so the core stays zero-dependency.
132
136
 
133
137
  Per-framework getting-started guides + runnable example apps live in the repo:
134
138
 
@@ -137,41 +141,47 @@ Per-framework getting-started guides + runnable example apps live in the repo:
137
141
  | Vanilla / Vite / plain HTML | [vanilla.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/getting-started/vanilla.md) | [`examples/vanilla-vite`](https://github.com/Ponchia/bronto-ui/tree/main/examples/vanilla-vite) |
138
142
  | Astro | [astro.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/getting-started/astro.md) | [`examples/astro`](https://github.com/Ponchia/bronto-ui/tree/main/examples/astro) |
139
143
  | SvelteKit | [sveltekit.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/getting-started/sveltekit.md) | [`examples/sveltekit`](https://github.com/Ponchia/bronto-ui/tree/main/examples/sveltekit) |
140
- | React / Solid (snippet) | [react-solid.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/getting-started/react-solid.md) | |
144
+ | React | [react-solid.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/getting-started/react-solid.md) | [`examples/react-vite`](https://github.com/Ponchia/bronto-ui/tree/main/examples/react-vite) |
145
+ | Solid | [react-solid.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/getting-started/react-solid.md) | [`examples/solid-vite`](https://github.com/Ponchia/bronto-ui/tree/main/examples/solid-vite) |
146
+ | Qwik | [react-solid.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/getting-started/react-solid.md) | [`examples/qwik-vite`](https://github.com/Ponchia/bronto-ui/tree/main/examples/qwik-vite) |
141
147
  | Tailwind / cascade-layer interop | [tailwind.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/interop/tailwind.md) | — |
142
148
 
143
149
  ## Extras
144
150
 
145
151
  - **Tokens as data** — `import tokens, { themeColor, cssVars } from '@ponchia/ui/tokens'` (plus `tokens.json`, W3C DTCG `tokens.dtcg.json`, and `tokens/resolved.json` — concrete hex per theme for canvas/SVG/MapLibre).
146
- - **Chart colours for dashboards** — `import charts from '@ponchia/ui/charts.json'` (resolved hex per theme; series 1 = your accent) and the opt-in `@ponchia/ui/css/dataviz.css`.
152
+ - **Chart colours for dashboards** — `import charts from '@ponchia/ui/charts.json' with { type: 'json' }` in Node ESM, or the same path through a bundler JSON import (resolved hex per theme; series 1 = your accent) plus the opt-in `@ponchia/ui/css/dataviz.css`.
153
+ - **Static reports for LLMs** — add `@ponchia/ui/css/report.css` for report structure and print utilities; pair with `@ponchia/ui/css/dataviz.css` only when the report contains charts. Full cookbook: `docs/reporting.md`.
154
+ - **Modern-platform motion** — overlays (modal/drawer/popover), toasts and the `<details>` accordion animate **in and out** with zero JS (`@starting-style` + `allow-discrete`, `::details-content` + `interpolate-size`). Progressive-enhancement extras: `.ui-scroll-progress` / `.ui-scroll-reveal` (scroll-driven, no JS) and `.ui-vt` for View Transitions. All degrade to a static end-state and respect `prefers-reduced-motion`. For smooth **cross-document** navigations, add the document-global one-liner to your own top-level (unlayered) CSS: `@view-transition { navigation: auto; }`.
147
155
  - **Editor IntelliSense** — point VS Code at the shipped custom-data file so every token autocompletes in `var(--…)`:
148
156
  ```json
149
157
  { "css.customData": ["node_modules/@ponchia/ui/classes/vscode.css-custom-data.json"] }
150
158
  ```
151
- - **For AI coding agents** — the package ships `llms.txt` at its root plus `docs/reference.md`, `docs/usage.md`, `docs/theming.md`, `docs/contrast.md`, the color constitution `docs/adr/0001-color-system.md` and the `CHANGELOG` inside the tarball, so an offline agent has the full API and rationale without guessing.
159
+ - **For AI coding agents** — the package ships `llms.txt` at its root plus `docs/reference.md`, `docs/usage.md`, `docs/reporting.md`, `docs/theming.md`, `docs/contrast.md`, `docs/stability.md`, the color constitution `docs/adr/0001-color-system.md` and the `CHANGELOG` inside the tarball, so an offline agent has the full API and rationale without guessing.
152
160
 
153
- > The package root is **CSS-only**: `@import '@ponchia/ui'` in CSS, never `import '@ponchia/ui'` in JS. The JS entrypoints are the explicit subpaths `/tokens`, `/classes`, `/behaviors`, `/glyphs`.
161
+ > The package root is **CSS-only**. Use `@import '@ponchia/ui'` in CSS, or `import '@ponchia/ui'` only as a CSS side-effect import in a CSS-aware bundler (Vite, Astro, SvelteKit, webpack). Do not import the package root from Node/runtime JS. JS entrypoints are explicit subpaths: `/tokens`, `/classes`, `/behaviors`, `/glyphs`, `/react`, `/solid`, `/qwik`, `/skins`, and `/charts`.
162
+ > JS subpaths are **ESM-only**. CommonJS consumers should use dynamic
163
+ > `import('@ponchia/ui/behaviors')`.
154
164
 
155
165
  ## Browser support
156
166
 
157
- Evergreen only. The framework relies on cascade layers, `:has()`, `color-mix()`, CSS logical properties and native `<dialog>`. Floor: **Chrome/Edge 111+, Safari 16.4+, Firefox 121+** (early 2023 onward). No build-time fallback ships; pin an older tag if you need below this.
167
+ Recent-evergreen, by design. The framework targets the modern web platform — cascade layers, `:has()`, `color-mix()`/`oklch()`, logical properties, native `<dialog>`, and the 2026 interaction primitives (`@starting-style`, `transition-behavior: allow-discrete`, `light-dark()`). Floor: **Chrome/Edge 125+, Safari 18+, Firefox 129+** (early–mid 2025). No fallback ships below this — pin an older tag if you need it. See [ADR-0002](https://github.com/Ponchia/bronto-ui/blob/main/docs/adr/0002-scope-and-2026-baseline.md) for the scope/greenfield rationale.
158
168
 
159
169
  ## Versioning
160
170
 
161
- Pre-1.0 and deliberately so. **Until `1.0.0`, breaking changes ship in the _minor_** (`0.x.0`); patches (`0.x.y`) are always non-breaking. Pin with the patch range — at `0.x`, `~0.4.0` (and equivalently `^0.4.0`) resolves to `>=0.4.0 <0.5.0`, giving you safe patches while holding back the next breaking minor. Every breaking change is called out under a **BREAKING** heading in the **[CHANGELOG](https://github.com/Ponchia/bronto-ui/blob/main/CHANGELOG.md)** with a migration note.
171
+ Pre-1.0 and deliberately so. **Until `1.0.0`, breaking changes ship in the _minor_** (`0.x.0`); patches (`0.x.y`) are always non-breaking. Pin with the patch range — at `0.x`, `~0.5.0` (and equivalently `^0.5.0`) resolves to `>=0.5.0 <0.6.0`, giving you safe patches while holding back the next breaking minor. Every breaking change is called out under a **BREAKING** heading in the **[CHANGELOG](https://github.com/Ponchia/bronto-ui/blob/main/CHANGELOG.md)** with a migration note.
162
172
 
163
- Contractual (changes are breaking): the `--accent` derivation and token **names**, the `.ui-*` class and recipe names, the `data-bronto-*` attributes, and each behavior's cleanup contract. Not contractual (may change any release): token **values** (visual tuning) and internal leaf-file / `@layer` boundaries.
173
+ Contractual (changes are breaking): token **names** and documented token roles, `.ui-*` class and recipe names, `data-bronto-*` attributes, exported behavior/glyph/binding function names and each behavior's cleanup contract. Not contractual (may change any release): exact token **values** and generated colour math outputs (visual tuning) unless a doc explicitly says the value is stable, plus internal leaf-file / `@layer` boundaries. See **[docs/stability.md](https://github.com/Ponchia/bronto-ui/blob/main/docs/stability.md)** for the full public-surface matrix.
164
174
 
165
175
  Release candidates publish to the `next` dist-tag, never to `latest` — opt in with `npm i @ponchia/ui@next` to try an upcoming version early. A plain `npm i @ponchia/ui` only ever resolves a stable release.
166
176
 
167
177
  ## Links
168
178
 
169
179
  - **[Live demo](https://ponchia.github.io/bronto-ui/)** · **[Theme playground](https://ponchia.github.io/bronto-ui/demo/theme-playground.html)**
170
- - **[Class reference](https://github.com/Ponchia/bronto-ui/blob/main/docs/reference.md)** · **[Usage guide](https://github.com/Ponchia/bronto-ui/blob/main/docs/usage.md)** · **[Theming](https://github.com/Ponchia/bronto-ui/blob/main/docs/theming.md)** · **[Contrast](https://github.com/Ponchia/bronto-ui/blob/main/docs/contrast.md)** · **[Color system (ADR-0001)](https://github.com/Ponchia/bronto-ui/blob/main/docs/adr/0001-color-system.md)**
180
+ - **[Class reference](https://github.com/Ponchia/bronto-ui/blob/main/docs/reference.md)** · **[Usage guide](https://github.com/Ponchia/bronto-ui/blob/main/docs/usage.md)** · **[Theming](https://github.com/Ponchia/bronto-ui/blob/main/docs/theming.md)** · **[Contrast](https://github.com/Ponchia/bronto-ui/blob/main/docs/contrast.md)** · **[Color system (ADR-0001)](https://github.com/Ponchia/bronto-ui/blob/main/docs/adr/0001-color-system.md)** · **[Scope & 2026 baseline (ADR-0002)](https://github.com/Ponchia/bronto-ui/blob/main/docs/adr/0002-scope-and-2026-baseline.md)**
171
181
  - **[CHANGELOG](https://github.com/Ponchia/bronto-ui/blob/main/CHANGELOG.md)** · **[Roadmap](https://github.com/Ponchia/bronto-ui/blob/main/ROADMAP.md)** · **[Contributing](https://github.com/Ponchia/bronto-ui/blob/main/CONTRIBUTING.md)**
172
182
 
173
183
  ## License
174
184
 
175
185
  [MIT](https://github.com/Ponchia/bronto-ui/blob/main/LICENSE) © Ponchia.
176
186
 
177
- The bundled **Doto** font (`fonts/*.ttf`) is © 2024 The Doto Project Authors and licensed separately under the [SIL Open Font License 1.1](https://github.com/Ponchia/bronto-ui/blob/main/fonts/OFL.txt) — see `fonts/OFL.txt`.
187
+ The bundled **Doto** font (`fonts/*.woff2`) is © 2024 The Doto Project Authors and licensed separately under the [SIL Open Font License 1.1](https://github.com/Ponchia/bronto-ui/blob/main/fonts/OFL.txt) — see `fonts/OFL.txt`.
@@ -0,0 +1,280 @@
1
+ /** @ponchia/ui — SVG annotation geometry helpers. */
2
+
3
+ export interface AnnotationPoint {
4
+ x: number;
5
+ y: number;
6
+ }
7
+
8
+ export interface AnnotationOffset {
9
+ dx: number;
10
+ dy: number;
11
+ }
12
+
13
+ export type AnnotationConnectorType = 'callout' | 'elbow' | 'curve';
14
+ export type AnnotationAlign = 'start' | 'middle' | 'end';
15
+ export type AnnotationValign = 'top' | 'middle' | 'bottom';
16
+ export type AxisOrientation = 'horizontal' | 'vertical';
17
+ export type TimelineDirection = 'up' | 'down' | 'left' | 'right';
18
+
19
+ export interface CircleSubject {
20
+ type: 'circle';
21
+ radius: number;
22
+ radiusPadding?: number;
23
+ }
24
+
25
+ export interface RectSubject {
26
+ type: 'rect';
27
+ width: number;
28
+ height: number;
29
+ x?: number;
30
+ y?: number;
31
+ padding?: number;
32
+ }
33
+
34
+ export type ConnectorSubject = CircleSubject | RectSubject;
35
+
36
+ export interface ConnectorOptions extends AnnotationOffset {
37
+ subject?: ConnectorSubject;
38
+ }
39
+
40
+ export interface CircleSubjectOptions {
41
+ radius: number;
42
+ }
43
+
44
+ export interface RectSubjectOptions {
45
+ width: number;
46
+ height: number;
47
+ x?: number;
48
+ y?: number;
49
+ padding?: number;
50
+ }
51
+
52
+ export interface ThresholdOptions {
53
+ x1?: number;
54
+ y1?: number;
55
+ x2: number;
56
+ y2: number;
57
+ }
58
+
59
+ export interface AxisThresholdOptions {
60
+ orientation?: AxisOrientation;
61
+ value?: number;
62
+ start?: number;
63
+ end: number;
64
+ }
65
+
66
+ export interface BracketSubjectOptions {
67
+ x1: number;
68
+ y1: number;
69
+ x2: number;
70
+ y2: number;
71
+ depth?: number;
72
+ }
73
+
74
+ export interface BandSubjectOptions {
75
+ x?: number;
76
+ y?: number;
77
+ width: number;
78
+ height: number;
79
+ padding?: number;
80
+ }
81
+
82
+ export interface SlopeSubjectOptions {
83
+ x1: number;
84
+ y1: number;
85
+ x2: number;
86
+ y2: number;
87
+ }
88
+
89
+ export interface ComparisonBraceOptions {
90
+ x1: number;
91
+ y1: number;
92
+ x2: number;
93
+ y2: number;
94
+ depth?: number;
95
+ }
96
+
97
+ export interface OutlierClusterOptions {
98
+ points: AnnotationPoint[];
99
+ radius?: number;
100
+ }
101
+
102
+ export interface TimelineEventOptions {
103
+ size?: number;
104
+ direction?: TimelineDirection;
105
+ }
106
+
107
+ export interface EvidenceMarkerOptions {
108
+ x?: number;
109
+ y?: number;
110
+ width?: number;
111
+ height?: number;
112
+ padding?: number;
113
+ }
114
+
115
+ export interface ConnectorEndDotOptions extends AnnotationPoint {
116
+ radius?: number;
117
+ }
118
+
119
+ export interface ConnectorEndArrowOptions {
120
+ x1?: number;
121
+ y1?: number;
122
+ x2: number;
123
+ y2: number;
124
+ size?: number;
125
+ }
126
+
127
+ export interface NoteTransformOptions {
128
+ dx?: number;
129
+ dy?: number;
130
+ x?: number;
131
+ y?: number;
132
+ align?: AnnotationAlign;
133
+ valign?: AnnotationValign;
134
+ width?: number;
135
+ height?: number;
136
+ }
137
+
138
+ export interface AnnotationBounds {
139
+ x?: number;
140
+ y?: number;
141
+ width: number;
142
+ height: number;
143
+ }
144
+
145
+ export interface NotePlacementOptions {
146
+ x?: number;
147
+ y?: number;
148
+ width: number;
149
+ height: number;
150
+ bounds: AnnotationBounds;
151
+ padding?: number;
152
+ gap?: number;
153
+ preferred?: 'right' | 'left' | 'top' | 'bottom';
154
+ }
155
+
156
+ export interface NotePlacement {
157
+ dx: number;
158
+ dy: number;
159
+ align: AnnotationAlign;
160
+ valign: AnnotationValign;
161
+ transform: string;
162
+ }
163
+
164
+ export type AnnotationPartsSubject =
165
+ | CircleSubject
166
+ | RectSubject
167
+ | ({ type: 'threshold' } & ThresholdOptions)
168
+ | ({ type: 'bracket' } & BracketSubjectOptions)
169
+ | ({ type: 'band' } & BandSubjectOptions)
170
+ | ({ type: 'slope' } & SlopeSubjectOptions)
171
+ | ({ type: 'compare' } & ComparisonBraceOptions)
172
+ | ({ type: 'cluster' } & OutlierClusterOptions)
173
+ | ({ type: 'axis' } & AxisThresholdOptions)
174
+ | ({ type: 'timeline' } & TimelineEventOptions)
175
+ | ({ type: 'evidence' } & EvidenceMarkerOptions);
176
+
177
+ export interface AnnotationPartsOptions {
178
+ type?: AnnotationConnectorType;
179
+ x?: number;
180
+ y?: number;
181
+ dx?: number;
182
+ dy?: number;
183
+ subject?: AnnotationPartsSubject;
184
+ }
185
+
186
+ export interface AnnotationParts {
187
+ transform: string;
188
+ subject: string;
189
+ connector: string;
190
+ note: string;
191
+ }
192
+
193
+ export declare function annotationTransform(point?: Partial<AnnotationPoint>): string;
194
+ export declare function noteTransform(options?: NoteTransformOptions): string;
195
+ export declare function notePlacement(options: NotePlacementOptions): NotePlacement;
196
+ export declare function circleSubjectPath(options: CircleSubjectOptions): string;
197
+ export declare function rectSubjectPath(options: RectSubjectOptions): string;
198
+ export declare function thresholdPath(options: ThresholdOptions): string;
199
+ export declare function axisThresholdPath(options: AxisThresholdOptions): string;
200
+ export declare function bracketSubjectPath(options: BracketSubjectOptions): string;
201
+ export declare function bandSubjectPath(options: BandSubjectOptions): string;
202
+ export declare function slopeSubjectPath(options: SlopeSubjectOptions): string;
203
+ export declare function comparisonBracePath(options: ComparisonBraceOptions): string;
204
+ export declare function outlierClusterPath(options: OutlierClusterOptions): string;
205
+ export declare function timelineEventPath(options?: TimelineEventOptions): string;
206
+ export declare function evidenceMarkerPath(options?: EvidenceMarkerOptions): string;
207
+ export declare function connectorEndDot(options: ConnectorEndDotOptions): string;
208
+ export declare function connectorEndArrow(options: ConnectorEndArrowOptions): string;
209
+ export declare function connectorLine(options: ConnectorOptions): string;
210
+ export declare function connectorElbow(options: ConnectorOptions): string;
211
+ export declare function connectorCurve(options: ConnectorOptions): string;
212
+ export declare function annotationParts(options?: AnnotationPartsOptions): AnnotationParts;
213
+
214
+ export interface DeclutterLabelItem {
215
+ /** Desired centre coordinate along the axis. */
216
+ pos: number;
217
+ /** The label's extent along the axis. */
218
+ size: number;
219
+ }
220
+ export interface DeclutterLabelsOptions {
221
+ /** Minimum gap kept between adjacent labels. Default 0. */
222
+ gap?: number;
223
+ /** Lower bound of the axis. Default -Infinity. */
224
+ min?: number;
225
+ /** Upper bound of the axis. Default Infinity. */
226
+ max?: number;
227
+ }
228
+ /**
229
+ * 1-D label declutter: nudge overlapping labels apart, order-preserving and
230
+ * deterministic. Returns the adjusted centre per input item (input order). Not
231
+ * a general 2-D collision solver.
232
+ */
233
+ export declare function declutterLabels(
234
+ items: DeclutterLabelItem[],
235
+ opts?: DeclutterLabelsOptions,
236
+ ): number[];
237
+
238
+ export interface DirectLabelItem {
239
+ /** The true data point the label refers to (figure coordinates). */
240
+ anchor: AnnotationPoint;
241
+ /** The label's extent along the layout axis. */
242
+ size: number;
243
+ /** Optional identifier, echoed back on the matching output (input order). */
244
+ key?: string | number;
245
+ }
246
+ export interface DirectLabelsOptions {
247
+ /** Axis the labels declutter along. 'y' = a vertical column. Default 'y'. */
248
+ axis?: 'x' | 'y';
249
+ /** Fixed coordinate on the other axis where the label column/row sits. Default 0. */
250
+ cross?: number;
251
+ /** Minimum gap kept between adjacent labels. Default 0. */
252
+ gap?: number;
253
+ /** Lower bound of the layout axis. Default -Infinity. */
254
+ min?: number;
255
+ /** Upper bound of the layout axis. Default Infinity. */
256
+ max?: number;
257
+ /** Leader-line shape. Default 'straight'. */
258
+ shape?: 'straight' | 'elbow' | 'curve';
259
+ }
260
+ export interface DirectLabel {
261
+ /** Placed label point — the leader's label-side end. */
262
+ x: number;
263
+ y: number;
264
+ /** The echoed input anchor. */
265
+ anchor: AnnotationPoint;
266
+ /** The echoed input key, if any. */
267
+ key?: string | number;
268
+ /** SVG path for the leader (anchor → label point); '' if they coincide. */
269
+ d: string;
270
+ }
271
+ /**
272
+ * Direct labeling: declutter labels along one axis and draw a leader from each
273
+ * anchor to its placed label (reusing the connector kernel). Deterministic and
274
+ * pure — no scales, no DOM, no 2-D placement. Output feeds a
275
+ * `<path class="ui-annotation__connector">` plus a label at `{x, y}`.
276
+ */
277
+ export declare function directLabels(
278
+ items: DirectLabelItem[],
279
+ opts?: DirectLabelsOptions,
280
+ ): DirectLabel[];