@ponchia/ui 0.6.10 → 0.6.12

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 (149) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +38 -25
  3. package/annotations/index.d.ts +15 -15
  4. package/annotations/index.d.ts.map +1 -1
  5. package/annotations/index.js +52 -34
  6. package/behaviors/carousel.d.ts +7 -3
  7. package/behaviors/carousel.d.ts.map +1 -1
  8. package/behaviors/carousel.js +157 -27
  9. package/behaviors/combobox.d.ts +1 -1
  10. package/behaviors/combobox.d.ts.map +1 -1
  11. package/behaviors/combobox.js +46 -23
  12. package/behaviors/command.d.ts +1 -1
  13. package/behaviors/command.d.ts.map +1 -1
  14. package/behaviors/command.js +63 -23
  15. package/behaviors/connectors.d.ts.map +1 -1
  16. package/behaviors/connectors.js +126 -19
  17. package/behaviors/crosshair.d.ts.map +1 -1
  18. package/behaviors/crosshair.js +71 -8
  19. package/behaviors/dialog.d.ts.map +1 -1
  20. package/behaviors/dialog.js +20 -3
  21. package/behaviors/disclosure.d.ts.map +1 -1
  22. package/behaviors/disclosure.js +35 -6
  23. package/behaviors/dismissible.js +1 -1
  24. package/behaviors/forms.d.ts +23 -2
  25. package/behaviors/forms.d.ts.map +1 -1
  26. package/behaviors/forms.js +97 -9
  27. package/behaviors/glyph.d.ts.map +1 -1
  28. package/behaviors/glyph.js +56 -5
  29. package/behaviors/internal.d.ts.map +1 -1
  30. package/behaviors/internal.js +52 -5
  31. package/behaviors/menu.d.ts.map +1 -1
  32. package/behaviors/menu.js +2 -1
  33. package/behaviors/modal.d.ts +13 -13
  34. package/behaviors/modal.d.ts.map +1 -1
  35. package/behaviors/modal.js +283 -96
  36. package/behaviors/popover.d.ts.map +1 -1
  37. package/behaviors/popover.js +58 -8
  38. package/behaviors/sources.d.ts.map +1 -1
  39. package/behaviors/sources.js +24 -3
  40. package/behaviors/splitter.d.ts.map +1 -1
  41. package/behaviors/splitter.js +27 -6
  42. package/behaviors/table.d.ts.map +1 -1
  43. package/behaviors/table.js +44 -7
  44. package/behaviors/tabs.d.ts.map +1 -1
  45. package/behaviors/tabs.js +51 -14
  46. package/behaviors/theme.d.ts.map +1 -1
  47. package/behaviors/theme.js +64 -4
  48. package/behaviors/toast.d.ts +6 -1
  49. package/behaviors/toast.d.ts.map +1 -1
  50. package/behaviors/toast.js +48 -12
  51. package/classes/classes.json +24 -0
  52. package/classes/index.d.ts +3 -2
  53. package/classes/index.js +77 -39
  54. package/classes/vscode.css-custom-data.json +1 -1
  55. package/connectors/index.d.ts +4 -4
  56. package/connectors/index.d.ts.map +1 -1
  57. package/connectors/index.js +14 -12
  58. package/css/annotations.css +1 -0
  59. package/css/app.css +7 -0
  60. package/css/base.css +3 -0
  61. package/css/bullet.css +41 -7
  62. package/css/code.css +14 -0
  63. package/css/command.css +10 -0
  64. package/css/dataviz.css +27 -0
  65. package/css/diff.css +2 -0
  66. package/css/disclosure.css +8 -0
  67. package/css/dots.css +1 -1
  68. package/css/feedback.css +9 -0
  69. package/css/interval.css +20 -2
  70. package/css/legend.css +10 -9
  71. package/css/marks.css +1 -0
  72. package/css/motion.css +2 -0
  73. package/css/overlay.css +14 -2
  74. package/css/primitives.css +1 -1
  75. package/css/report.css +3 -0
  76. package/css/sources.css +4 -4
  77. package/css/spotlight.css +6 -0
  78. package/css/table.css +19 -0
  79. package/css/term.css +4 -1
  80. package/css/tokens.css +9 -14
  81. package/dist/bronto.css +1 -1
  82. package/dist/css/analytical.css +1 -1
  83. package/dist/css/app.css +1 -1
  84. package/dist/css/bullet.css +1 -1
  85. package/dist/css/code.css +1 -1
  86. package/dist/css/command.css +1 -1
  87. package/dist/css/dataviz.css +1 -1
  88. package/dist/css/diff.css +1 -1
  89. package/dist/css/disclosure.css +1 -1
  90. package/dist/css/dots.css +1 -1
  91. package/dist/css/feedback.css +1 -1
  92. package/dist/css/interval.css +1 -1
  93. package/dist/css/legend.css +1 -1
  94. package/dist/css/marks.css +1 -1
  95. package/dist/css/overlay.css +1 -1
  96. package/dist/css/primitives.css +1 -1
  97. package/dist/css/report-kit.css +1 -1
  98. package/dist/css/sources.css +1 -1
  99. package/dist/css/spotlight.css +1 -1
  100. package/dist/css/table.css +1 -1
  101. package/dist/css/term.css +1 -1
  102. package/dist/css/tokens.css +1 -1
  103. package/docs/architecture.md +6 -4
  104. package/docs/bullet.md +6 -1
  105. package/docs/clamp.md +5 -0
  106. package/docs/command.md +3 -2
  107. package/docs/contrast.md +3 -3
  108. package/docs/crosshair.md +6 -0
  109. package/docs/dots.md +10 -3
  110. package/docs/figure.md +7 -0
  111. package/docs/glyphs.md +14 -2
  112. package/docs/highlights.md +9 -0
  113. package/docs/interval.md +6 -0
  114. package/docs/mermaid.md +5 -3
  115. package/docs/package-contract.md +24 -1
  116. package/docs/reference.md +1 -1
  117. package/docs/reporting.md +8 -8
  118. package/docs/selection.md +9 -0
  119. package/docs/sources.md +5 -0
  120. package/docs/stability.md +31 -6
  121. package/docs/state.md +6 -0
  122. package/docs/textref.md +18 -13
  123. package/docs/theming.md +18 -8
  124. package/docs/toc.md +6 -0
  125. package/docs/tree.md +9 -2
  126. package/docs/usage.md +12 -2
  127. package/docs/vega.md +5 -3
  128. package/glyphs/glyphs.js +62 -8
  129. package/index.d.ts +1 -0
  130. package/llms.txt +18 -14
  131. package/package.json +98 -6
  132. package/qwik/index.d.ts +4 -3
  133. package/qwik/index.d.ts.map +1 -1
  134. package/qwik/index.js +7 -5
  135. package/react/index.d.ts +4 -3
  136. package/react/index.d.ts.map +1 -1
  137. package/react/index.js +3 -2
  138. package/solid/index.d.ts +7 -5
  139. package/solid/index.d.ts.map +1 -1
  140. package/solid/index.js +11 -7
  141. package/tokens/figma.variables.json +2 -2
  142. package/tokens/index.js +1 -1
  143. package/tokens/index.json +2 -2
  144. package/tokens/resolved.json +1 -1
  145. package/tokens/tokens.dtcg.json +1 -1
  146. package/tokens/vega.d.ts +1 -1
  147. package/tokens/vega.js +3 -2
  148. package/vue/index.d.ts.map +1 -1
  149. package/vue/index.js +37 -3
package/docs/mermaid.md CHANGED
@@ -71,8 +71,10 @@ below):
71
71
  > an `http(s)` origin the `import`/`fetch` forms both work.
72
72
 
73
73
  The result is monochrome surfaces and lines with the rationed accent reserved
74
- for notes — a diagram that looks like the rest of a bronto surface and
75
- **re-skins for free** when you change `--accent`, exactly like the chart palette.
74
+ for notes — a diagram that looks like the rest of a bronto surface. The accent is
75
+ **resolved into the theme map** you pass to `initialize`, not read live from
76
+ `--accent`: to change it you re-`initialize` with the new palette and re-render;
77
+ an already-drawn diagram does not live-reskin (see below).
76
78
 
77
79
  ### Why resolved colours, not `var(--x)`
78
80
 
@@ -90,7 +92,7 @@ switch, re-`initialize` with the other palette and re-render.
90
92
  - **Chart-like** diagrams carry a categorical series palette — **pie**
91
93
  (`pie1`…`pie12`), **git** (`git0`…`git7`), and **user-journey**
92
94
  (`fillType0`…`fillType7`) are wired to the CVD-safe
93
- [charts palette](./legends.md) (series 1 = the live accent).
95
+ [charts palette](./legends.md) (series 1 = the resolved accent).
94
96
  - **Structural** diagrams — flowchart, sequence, class, ER, state — use the
95
97
  monochrome node/edge/cluster grammar and spend the accent only on notes.
96
98
  - **Not themed: `gantt` and `timeline`.** Their colours come from
@@ -25,7 +25,7 @@ semantic versioning contract for the surfaces listed here.
25
25
 
26
26
  | Export | Target | Group | Stability | Contract |
27
27
  | --- | --- | --- | --- | --- |
28
- | `.` | style: `./dist/bronto.css`<br>default: `./dist/bronto.css` | CSS root bundle | Stable | CSS-only package root. Supported as a CSS side-effect import in CSS-aware bundlers; not a Node/runtime JS entrypoint. |
28
+ | `.` | types: `./index.d.ts`<br>style: `./dist/bronto.css`<br>default: `./dist/bronto.css` | CSS root bundle | Stable | CSS-only package root. Supported as a CSS side-effect import in CSS-aware bundlers; not a Node/runtime JS entrypoint. |
29
29
  | `./dist/bronto.css` | `./dist/bronto.css` | Flattened CSS bundle | Stable path | The prebuilt default stylesheet. Generated from css/core.css and byte-checked by check:dist. |
30
30
  | `./tailwind` | `./tailwind.css` | Tailwind CSS bridge | Stable additive | CSS-only Tailwind v4 theme/variant bridge. It maps Bronto tokens into Tailwind namespaces; it does not import component CSS. |
31
31
  | `./tailwind.css` | `./tailwind.css` | Tailwind CSS bridge | Stable additive | CSS-only Tailwind v4 theme/variant bridge. It maps Bronto tokens into Tailwind namespaces; it does not import component CSS. |
@@ -180,6 +180,28 @@ semantic versioning contract for the surfaces listed here.
180
180
  | `./classes` | types: `./classes/index.d.ts`<br>default: `./classes/index.js` | Class recipes JS | Stable | ESM class registry, recipes, attrs helpers, and cx joiner. The emitted class vocabulary is public. |
181
181
  | `./classes.json` | `./classes/classes.json` | Machine-readable data | Stable additive | JSON package data for non-JS/tooling consumers. Shape is public unless the paired doc marks a field internal. |
182
182
  | `./behaviors` | types: `./behaviors/index.d.ts`<br>default: `./behaviors/index.js` | Vanilla behavior JS | Stable | ESM, SSR-safe, cleanup-returning behavior initializers. Behavior internals are not public. |
183
+ | `./behaviors/carousel` | types: `./behaviors/carousel.d.ts`<br>default: `./behaviors/carousel.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
184
+ | `./behaviors/combobox` | types: `./behaviors/combobox.d.ts`<br>default: `./behaviors/combobox.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
185
+ | `./behaviors/command` | types: `./behaviors/command.d.ts`<br>default: `./behaviors/command.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
186
+ | `./behaviors/connectors` | types: `./behaviors/connectors.d.ts`<br>default: `./behaviors/connectors.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
187
+ | `./behaviors/crosshair` | types: `./behaviors/crosshair.d.ts`<br>default: `./behaviors/crosshair.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
188
+ | `./behaviors/dialog` | types: `./behaviors/dialog.d.ts`<br>default: `./behaviors/dialog.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
189
+ | `./behaviors/disclosure` | types: `./behaviors/disclosure.d.ts`<br>default: `./behaviors/disclosure.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
190
+ | `./behaviors/dismissible` | types: `./behaviors/dismissible.d.ts`<br>default: `./behaviors/dismissible.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
191
+ | `./behaviors/forms` | types: `./behaviors/forms.d.ts`<br>default: `./behaviors/forms.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
192
+ | `./behaviors/glyph` | types: `./behaviors/glyph.d.ts`<br>default: `./behaviors/glyph.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
193
+ | `./behaviors/inert` | types: `./behaviors/inert.d.ts`<br>default: `./behaviors/inert.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
194
+ | `./behaviors/legend` | types: `./behaviors/legend.d.ts`<br>default: `./behaviors/legend.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
195
+ | `./behaviors/menu` | types: `./behaviors/menu.d.ts`<br>default: `./behaviors/menu.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
196
+ | `./behaviors/modal` | types: `./behaviors/modal.d.ts`<br>default: `./behaviors/modal.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
197
+ | `./behaviors/popover` | types: `./behaviors/popover.d.ts`<br>default: `./behaviors/popover.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
198
+ | `./behaviors/sources` | types: `./behaviors/sources.d.ts`<br>default: `./behaviors/sources.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
199
+ | `./behaviors/splitter` | types: `./behaviors/splitter.d.ts`<br>default: `./behaviors/splitter.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
200
+ | `./behaviors/spotlight` | types: `./behaviors/spotlight.d.ts`<br>default: `./behaviors/spotlight.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
201
+ | `./behaviors/table` | types: `./behaviors/table.d.ts`<br>default: `./behaviors/table.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
202
+ | `./behaviors/tabs` | types: `./behaviors/tabs.d.ts`<br>default: `./behaviors/tabs.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
203
+ | `./behaviors/theme` | types: `./behaviors/theme.d.ts`<br>default: `./behaviors/theme.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
204
+ | `./behaviors/toast` | types: `./behaviors/toast.d.ts`<br>default: `./behaviors/toast.js` | Package subpath | Public | Exported package subpath. See docs/stability.md for semantic stability. |
183
205
  | `./glyphs` | types: `./glyphs/glyphs.d.ts`<br>default: `./glyphs/glyphs.js` | Geometry/render helper JS | Stable additive | ESM helper surface. Function names, options, and data shapes are public; rendering heuristics may tune. |
184
206
  | `./annotations` | types: `./annotations/index.d.ts`<br>default: `./annotations/index.js` | Geometry/render helper JS | Stable additive | ESM helper surface. Function names, options, and data shapes are public; rendering heuristics may tune. |
185
207
  | `./connectors` | types: `./connectors/index.d.ts`<br>default: `./connectors/index.js` | Geometry/render helper JS | Stable additive | ESM helper surface. Function names, options, and data shapes are public; rendering heuristics may tune. |
@@ -210,6 +232,7 @@ always includes `package.json`, `README.md`, `LICENSE`, and
210
232
  | `css` | Source CSS directory | Public source leaves. Mostly hand-authored; generated exceptions are called out in the provenance table. |
211
233
  | `dist` | Generated CSS directory | Prebuilt layered bundle and leaves. Never hand-edit. |
212
234
  | `tailwind.css` | Tailwind CSS bridge | CSS-only Tailwind v4 theme/variant bridge; hand-authored and not part of the default Bronto bundle. |
235
+ | `index.d.ts` | Package file | Included in the npm files allowlist. |
213
236
  | `fonts` | Vendored assets | Doto woff2 files plus OFL license. |
214
237
  | `tokens` | Mixed source/generated data | Token source plus generated JSON, declarations, and renderer theme data. |
215
238
  | `classes` | Mixed source/generated data | Class recipe source plus generated JSON/declarations/custom-data. |
package/docs/reference.md CHANGED
@@ -1678,7 +1678,7 @@ Exact mirror of the `:root` blocks in `css/tokens.css`
1678
1678
  | `--display` | `var(--dot-font)` |
1679
1679
  | `--display-weight` | `700` |
1680
1680
  | `--display-weight-strong` | `800` |
1681
- | `--text-2xs` | `0.68rem` |
1681
+ | `--text-2xs` | `0.72rem` |
1682
1682
  | `--text-xs` | `0.76rem` |
1683
1683
  | `--text-sm` | `0.86rem` |
1684
1684
  | `--text-base` | `0.95rem` |
package/docs/reporting.md CHANGED
@@ -54,18 +54,18 @@ No install? Link the same files from a CDN. Pin the version — pre-1.0, breakin
54
54
  changes ship in the minor (see [stability.md](./stability.md)):
55
55
 
56
56
  ```html
57
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.10/dist/bronto.css" />
58
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.10/dist/css/report-kit.css" />
57
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.12/dist/bronto.css" />
58
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.12/dist/css/report-kit.css" />
59
59
  ```
60
60
 
61
61
  Leaf-by-leaf CDN imports use the same `dist/css/` paths:
62
62
 
63
63
  ```html
64
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.10/dist/bronto.css" />
65
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.10/dist/css/report.css" />
66
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.10/dist/css/dataviz.css" />
67
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.10/dist/css/annotations.css" />
68
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.10/dist/css/legend.css" />
64
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.12/dist/bronto.css" />
65
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.12/dist/css/report.css" />
66
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.12/dist/css/dataviz.css" />
67
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.12/dist/css/annotations.css" />
68
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.12/dist/css/legend.css" />
69
69
  ```
70
70
 
71
71
  The CDN serves the package's own `fonts/` next to the CSS, so font URLs resolve
@@ -879,7 +879,7 @@ or validation runtime.
879
879
 
880
880
  ```json
881
881
  {
882
- "$schema": "https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.10/schemas/report-claims.v1.schema.json",
882
+ "$schema": "https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.12/schemas/report-claims.v1.schema.json",
883
883
  "schemaVersion": "bronto-report-claims.v1",
884
884
  "report": { "title": "Decision readiness", "type": "decision" },
885
885
  "claims": [
package/docs/selection.md CHANGED
@@ -34,7 +34,16 @@ logic — Bronto only styles them. `--off` uses `opacity` (which survives
34
34
  forced-colors), and `--on` falls back to the system `Highlight` color in
35
35
  forced-colors so the selection stays visible.
36
36
 
37
+ Pair the visual classes with host-owned selection semantics, such as
38
+ `aria-selected`, native checked state, or a visible selected-count label.
39
+
37
40
  > **Why no brush behavior?** A rectangular brush or lasso must translate a
38
41
  > dragged region into a data domain (or test points against a polygon), which
39
42
  > requires owning the chart's scales/geometry — across Bronto's line. Bring your
40
43
  > own selection logic; use these classes to render the result.
44
+
45
+ ## Related
46
+
47
+ `selection.css` is part of
48
+ [`analytical.css`](./reporting.md#the-analytical-toolbox-in-a-report). Use
49
+ [reference.md](./reference.md) for the generated class catalog.
package/docs/sources.md CHANGED
@@ -167,3 +167,8 @@ ui.provenance({ state: 'reviewed' }); // "ui-provenance ui-src--reviewed"
167
167
  `CanvasText`, so the label remains the channel.
168
168
  - Tone dots and the card's tone border carry `print-color-adjust: exact`, so
169
169
  they survive printing; expand citation URLs in print where it helps the reader.
170
+
171
+ ## Related
172
+
173
+ - [Usage](usage.md#static-reports) shows where citations fit in reports.
174
+ - [Reference](reference.md) lists the generated source and citation classes.
package/docs/stability.md CHANGED
@@ -1,4 +1,4 @@
1
- # Public API Stability
1
+ # Public API stability
2
2
 
3
3
  `@ponchia/ui` is pre-1.0. Breaking changes ship in the minor (`0.x.0`), and
4
4
  patches are non-breaking. In practical terms: **PATCH releases (`0.6.x`) are
@@ -11,7 +11,7 @@ For the exhaustive package-manifest inventory — every `exports` key, every
11
11
  shipped `files` entry, and the generated artifact provenance map — see
12
12
  [package-contract.md](./package-contract.md).
13
13
 
14
- ## Path To 1.0
14
+ ## Path to 1.0
15
15
 
16
16
  `1.0.0` is a stability declaration, not a catalog milestone. The package is
17
17
  ready for 1.0 when the existing public contract is boring to upgrade:
@@ -36,7 +36,7 @@ ready for 1.0 when the existing public contract is boring to upgrade:
36
36
  migration entry when machine-actionable, and the deprecate-one-minor policy
37
37
  has been followed or explicitly exempted for provably-unreferenced surface.
38
38
 
39
- ### 1.0 Readiness Ledger
39
+ ### 1.0 readiness ledger
40
40
 
41
41
  This ledger is the release-candidate checklist. A row is ready only when the
42
42
  evidence column is green for the candidate commit; prose approval alone is not
@@ -51,6 +51,31 @@ enough.
51
51
  | Bundle budget has headroom | `check:dist`, `check:public-metadata`, `check:pack`, and the README size badge keep default bundle and tarball claims visible. | Budget increases are intentional, reviewed, and named in `CHANGELOG.md`; accidental growth fails before release. |
52
52
  | Deprecation history is clean | `check:migrations`, `check:release`, `check:versions`, `MIGRATIONS.json`, and this deprecation policy tie breaking changes to changelog and migration evidence. | No removal ships without either a deprecate-one-minor trail or an explicit BREAKING note for provably-unreferenced surface. |
53
53
 
54
+ ### Adoption evidence for 1.0
55
+
56
+ This snapshot records product evidence as of 2026-07-10. Use three evidence
57
+ classes:
58
+
59
+ - **Downstream-proven:** A non-example app, site, report generator, or tool
60
+ imports the published surface and passes its own build or checks.
61
+ - **Package-proven:** Packed examples, unit tests, browser tests, and type tests
62
+ prove compatibility, but no inspected non-example consumer imports the
63
+ surface.
64
+ - **Speculative:** Neither downstream use nor a package-level executable proof
65
+ justifies freezing the surface into 1.0.
66
+
67
+ Package-proven is necessary but does not establish demand. During the catalog
68
+ freeze, do not expand package-only surfaces. Recheck this table against real
69
+ consumer upgrades before the 1.0 release candidate.
70
+
71
+ | Surface family | Current evidence | 1.0 disposition |
72
+ | --- | --- | --- |
73
+ | Core CSS, class recipes, vanilla behaviors, tokens, and Tailwind bridge | Downstream-proven across five inspected non-example app, site, and service consumers. | Stabilize names and behavior contracts. Use consumer upgrades as the release-candidate proof. |
74
+ | Report, provenance, analytical CSS, annotations, glyphs, skins, workbench CSS, chart data, and Vega theme | Downstream-proven across four inspected report, site, dashboard, and tool consumers. | Keep opt-in. Stabilize the consumed paths; do not broaden the catalog during the freeze. |
75
+ | Controlled non-`<dialog>` modal | Package-proven by stack, portal, late-node, focus, and cleanup regressions. No inspected non-example consumer initializes `initModal`. | Keep compatible through 0.6.x, but require a real consumer before declaring this alternative to native `<dialog>` stable in 1.0. |
76
+ | React, Solid, Qwik, Svelte, and Vue lifecycle adapters | Package-proven by packed examples, types, and lifecycle tests. No inspected non-example consumer imports an adapter entrypoint. | Pre-1.0 candidate only. Freeze the adapter set and require downstream adoption before declaring all five stable in 1.0. |
77
+ | Mermaid, D2, Shiki, Figma Variables, and the report-claims schema | Package-proven by generated-data, render, schema, and drift checks. No inspected non-example consumer currently supplies downstream proof for every path. | Keep compatible through 0.6.x, but decide each 1.0 contract from adoption evidence rather than generator coverage alone. |
78
+
54
79
  After 1.0, breaking changes move to majors. Until then, the table below is the
55
80
  current public-surface matrix and the release policy above still applies.
56
81
 
@@ -104,13 +129,13 @@ current public-surface matrix and the release policy above still applies.
104
129
  | Terms / glossary (`css/term.css`, `.ui-term`, `.ui-glossary`) | Stable additive | Term and glossary class names plus native-popover definition hooks are public. Opt-in, not in the default bundle. The host owns glossary content and terminology policy. |
105
130
  | Contents rail (`css/toc.css`, `.ui-toc*`) | Stable additive | TOC rail class/part names and current-section state classes are public. Opt-in, not in the default bundle. The host owns section observation and active-state updates. |
106
131
  | Tree outlines (`css/tree.css`, `.ui-tree*`) | Stable additive | Tree outline class names, depth styling, and native `<details>` composition are public. Opt-in, not in the default bundle. The host owns tree data, lazy loading, and selection state. |
107
- | Controlled-modal focus trap (`initModal`, `useModal`, `data-bronto-modal`) | Stable additive | For the `.ui-modal.is-open` (non-`<dialog>`) path: the `data-bronto-modal` opt-in marker, the `inert`-based focus trap + focus-return, and the cancelable `bronto:modal:close` (`detail: { reason }`) event are public. The consumer still owns the `is-open` class; the behavior never changes visibility. The native `<dialog>` path (`initDialog`) is the default and gets the trap for free. |
132
+ | Controlled-modal focus trap (`initModal`, `useModal`, `data-bronto-modal`) | Stable additive | For the `.ui-modal.is-open` (non-`<dialog>`) path: the `data-bronto-modal` opt-in marker, stack-aware `inert` ownership, focus-return, late-background trapping, owned portaled-popover admission, and the cancelable `bronto:modal:close` (`detail: { reason }`) event are public. The consumer still owns the `is-open` class; closing a parent suspends still-open controlled descendants until the parent reopens. The native `<dialog>` path (`initDialog`) remains the default. |
108
133
  | Keyboard-shortcut hint (`.ui-shortcut`, `.ui-shortcut__sep`) | Stable additive | Class names for the chord/sequence hint over `.ui-kbd` are public. Ships in the core layer (class-only, no recipe). |
109
134
  | Agent and migration data (`llms.txt`, `MIGRATIONS.json`) | Stable additive | `llms.txt` stays shipped as the offline agent entrypoint. `MIGRATIONS.json` stays a machine-readable migration map for breaking renames/removals. New migration entries are additive; removal of a migration record requires the same breaking-change discipline as the surface it describes. |
110
135
  | Generated docs shipped in npm | Stable paths | Exported docs paths stay shipped and resolvable within a compatible minor. Markdown/text assets are for reading unless your runtime has a loader. Generated content may change with the source contract. |
111
136
  | Demo, examples, tests, scripts | Internal | Useful for learning and verification, but not shipped runtime API unless a path is explicitly exported in `package.json`. |
112
137
 
113
- ## Deprecation Policy
138
+ ## Deprecation policy
114
139
 
115
140
  Public surface (`.ui-*` classes, `data-bronto-*` attributes, `cls`/token keys,
116
141
  behavior signatures, and exported schema values) is removed on a
@@ -126,7 +151,7 @@ A token/class/attribute that is provably referenced by no shipped CSS,
126
151
  component, behavior, or doc may skip that window and be removed with a BREAKING
127
152
  entry plus migration note; there is no working call-site to keep alive.
128
153
 
129
- ## Trust Boundary
154
+ ## Trust boundary
130
155
 
131
156
  Behaviors assume trusted application markup. If a delegated root includes
132
157
  untrusted CMS or user HTML, sanitize it first or do not initialize behaviors on
package/docs/state.md CHANGED
@@ -140,3 +140,9 @@ CSS only — there is no JS yet. Auto-updating elapsed time ("2m ago"), live
140
140
  progress text, polling, cancellation, retry, and conflict-resolution affordances
141
141
  are the host's job. A small optional behavior may come later if a real consumer
142
142
  needs it.
143
+
144
+ ## Related
145
+
146
+ Use the [reporting toolbox](./reporting.md#the-analytical-toolbox-in-a-report)
147
+ to place `state.css` in report surfaces, and use
148
+ [reference.md](./reference.md) for the generated class catalog.
package/docs/textref.md CHANGED
@@ -3,10 +3,11 @@
3
3
  `@ponchia/ui/css/textref.css` is an opt-in **deep-link-to-the-cited-sentence**
4
4
  provenance primitive. A citation whose `href` is a URL [Text
5
5
  Fragment](https://developer.mozilla.org/en-US/docs/Web/Text_fragments)
6
- (`#…:~:text=`): the browser scrolls to the exact quoted text and highlights it,
7
- and Bronto owns the on-brand `::target-text` paint. It is the inline counterpart
8
- to the static `ui-src` / `ui-citation` trust layer (`sources.css`), which can
9
- label a source but cannot point *inside* it.
6
+ (`#section:~:text=`): supporting browsers scroll to the exact quoted text and
7
+ highlight it, while the leading `#section` gives older engines a stable landing.
8
+ Bronto owns the on-brand `::target-text` paint. It is the inline counterpart to
9
+ the static `ui-src` / `ui-citation` trust layer (`sources.css`), which can label
10
+ a source but cannot point *inside* it.
10
11
 
11
12
  ```css
12
13
  @import '@ponchia/ui';
@@ -15,12 +16,13 @@ label a source but cannot point *inside* it.
15
16
 
16
17
  ## How it behaves
17
18
 
18
- - The link navigates to the document and the browser scrolls to + highlights the
19
- first match of the quoted text — no script, no anchors to pre-place.
19
+ - In Chrome/Edge 80+, Safari 16.1+, and Firefox 131+, the browser scrolls to
20
+ and highlights the first match of the quoted text — no script.
20
21
  - Bronto repaints that browser highlight (`::target-text`) in the rationed
21
22
  accent wash so it matches the rest of the trust layer.
22
- - On engines without Text Fragments the link still navigates to the page (or its
23
- `#section` anchor); the highlight is purely additive, so nothing breaks.
23
+ - On engines without Text Fragments, the `:~:text=` directive is ignored. Use a
24
+ real `#section:~:text=...` URL when you own the target page; a bare `#section`
25
+ can land only at the section, not at the quoted sentence.
24
26
 
25
27
  ## Wiring — the host builds the fragment URL
26
28
 
@@ -30,11 +32,12 @@ runtime for it. Build the `href` with a three-line pure helper and drop it on th
30
32
 
31
33
  ```js
32
34
  // Encode a quote as a URL Text Fragment directive.
33
- // encodeTextFragment('p95 latency fell 38%') -> '#:~:text=p95%20latency%20fell%2038%25'
34
- export function encodeTextFragment(quote, { prefix } = {}) {
35
+ // encodeTextFragment('p95 latency fell 38%', { section: 'incident-summary' })
36
+ // -> '#incident-summary:~:text=p95%20latency%20fell%2038%25'
37
+ export function encodeTextFragment(quote, { prefix, section } = {}) {
35
38
  const enc = (s) => encodeURIComponent(s).replace(/-/g, '%2D');
36
39
  const text = prefix ? `${enc(prefix)}-,${enc(quote)}` : enc(quote);
37
- return `#:~:text=${text}`;
40
+ return `${section ? `#${encodeURIComponent(section)}` : '#'}:~:text=${text}`;
38
41
  }
39
42
  ```
40
43
 
@@ -43,7 +46,7 @@ export function encodeTextFragment(quote, { prefix } = {}) {
43
46
  The migration cut p95 latency by 38%
44
47
  <a
45
48
  class="ui-textref"
46
- href="https://example.com/incident-review#:~:text=p95%20latency%20fell%2038%25"
49
+ href="https://example.com/incident-review#incident-summary:~:text=p95%20latency%20fell%2038%25"
47
50
  >jump to the source</a
48
51
  >.
49
52
  </p>
@@ -56,12 +59,14 @@ Notes for an autonomous author:
56
59
  - Keep the quote short and verbatim; a fuzzy or paraphrased quote won't match.
57
60
  - Use the optional `prefix` (a `prefix-,` directive) to disambiguate a quote that
58
61
  appears more than once on the target page.
62
+ - Firefox support starts at 131. The project floor includes Firefox 129, so make
63
+ the leading section fallback meaningful when exact quote landing matters.
59
64
 
60
65
  ## Class reference
61
66
 
62
67
  | Class | Role |
63
68
  | ------------- | ------------------------------------------------------------------- |
64
- | `.ui-textref` | A citation link whose `href` is a `#:~:text=` fragment; dotted underline + quote-jump cue. |
69
+ | `.ui-textref` | A citation link whose `href` is a `#section:~:text=` fragment; dotted underline + quote-jump cue. |
65
70
 
66
71
  | Custom property | On | Meaning |
67
72
  | ---------------------- | ------------- | ------------------------------------------------------------------------- |
package/docs/theming.md CHANGED
@@ -26,8 +26,15 @@ So a full re-brand is one declaration **at `:root` (or a theme root)**:
26
26
  :root[data-theme='dark'] { --accent: #6ea8ff; } /* per-theme tuning */
27
27
  ```
28
28
 
29
- Everything — buttons, focus rings, dot motifs, accent borders, soft
30
- fills — follows automatically, in both light and dark.
29
+ Core DOM accent surfaces — buttons, focus rings, dot motifs, accent borders,
30
+ soft fills — follow automatically, in both light and dark.
31
+
32
+ That scope matters. `--accent` is the core action/emphasis hue, not the whole
33
+ color system. Status colors (`--success`, `--warning`, `--danger`, `--info`),
34
+ neutral surface tiers, display-expression tokens, and the opt-in data-viz
35
+ palette stay separate governed tiers. Mermaid, D2, and Vega bridges emit
36
+ resolved renderer theme data; a rendered SVG or canvas does not live-reskin when
37
+ you change CSS `--accent` later.
31
38
 
32
39
  > **Re-branding a subtree (not `:root`) is only a *partial* re-brand.**
33
40
  > The derived family (`--accent-soft`, `--accent-strong`, `--accent-text`,
@@ -53,6 +60,8 @@ fills — follows automatically, in both light and dark.
53
60
  > When in doubt, re-brand at `:root`/`[data-theme]` — that path re-derives
54
61
  > the whole family for you.
55
62
 
63
+ ### Re-brand obligations
64
+
56
65
  > **Two contrast obligations when you change `--accent`:**
57
66
  >
58
67
  > 1. **Buttons** — pick an `--accent` with ≥ 4.5:1 against `--button-text`
@@ -106,7 +115,7 @@ fills — follows automatically, in both light and dark.
106
115
  set `accent-color` yourself on them — this is the one accent surface
107
116
  the framework can't tune for you.
108
117
 
109
- ## Beyond accent: a full re-skin (the knob really works)
118
+ ## Beyond accent: full re-skins
110
119
 
111
120
  The "Nothing" look is the **default skin, not the architecture**. It is
112
121
  a handful of token declarations deep — no selector hardcodes the
@@ -142,11 +151,12 @@ no fork:
142
151
  ```
143
152
 
144
153
  That changes buttons, cards, inputs, badges, focus rings, the display
145
- type and the dot motif together, because every component consumes those
146
- tokens — not because there is a per-component theme file. This is the
147
- difference between a *system* and a *skin*: the skin is swappable, the
148
- system (rationed colour, density, classless prose, minimal JS) is what
149
- you are actually adopting.
154
+ type and the dot motif together, because those surfaces consume the token
155
+ contract — not because there is a per-component theme file. It does not
156
+ rewrite status semantics or chart palettes; those are governed by their own
157
+ tiers. This is the difference between a *system* and a *skin*: the skin is
158
+ swappable, the system (rationed colour, density, classless prose, minimal JS)
159
+ is what you are actually adopting.
150
160
 
151
161
  Caveat, restated: a custom `--accent` is **your** contrast obligation —
152
162
  the shipped palettes are CI-gated ([contrast.md](contrast.md)), a re-skin
package/docs/toc.md CHANGED
@@ -81,3 +81,9 @@ for (const id of links.keys()) {
81
81
  navigation landmark.
82
82
  - Without the optional observer the rail is a normal anchored list — every link
83
83
  still jumps to its section.
84
+
85
+ ## Related
86
+
87
+ Use the [reporting toolbox](./reporting.md#the-analytical-toolbox-in-a-report)
88
+ to place `toc.css` in report surfaces, and use
89
+ [reference.md](./reference.md) for the generated class catalog.
package/docs/tree.md CHANGED
@@ -38,8 +38,9 @@ open/close animation come from the platform. This leaf is the hierarchy *layer*
38
38
  ```
39
39
 
40
40
  Add `name="…"` to sibling `<details class="ui-tree__branch">` to make them an
41
- exclusive-accordion group (only one open at a time) — a platform behaviour, no
42
- script.
41
+ exclusive-accordion group (only one open at a time) — a platform behaviour in
42
+ Chrome 120+, Safari 17.2+, and Firefox 130+. If Firefox 129 must enforce
43
+ exclusivity, add a host-owned JS fallback or leave the branches independent.
43
44
 
44
45
  ## A11y honesty — disclosure group, not an ARIA tree
45
46
 
@@ -72,3 +73,9 @@ honest as-is.
72
73
  open-branch accent cue is re-asserted with the system highlight.
73
74
  - The chevron spin and the auto-height animation both respect
74
75
  `prefers-reduced-motion`.
76
+
77
+ ## Related
78
+
79
+ Use the [reporting toolbox](./reporting.md#the-analytical-toolbox-in-a-report)
80
+ to place `tree.css` in report surfaces, and use
81
+ [reference.md](./reference.md) for the generated class catalog.
package/docs/usage.md CHANGED
@@ -377,6 +377,16 @@ also gives the overlay `role="dialog"` + `aria-modal="true"` and dev-warns if it
377
377
  has no accessible name (add `aria-label`/`aria-labelledby`). A drawer is a modal
378
378
  that enters from an edge — same rule.
379
379
 
380
+ Controlled modals share one document-level stack. Opening a sibling portal
381
+ modal makes the previous modal inert and keeps only the new top modal
382
+ interactive; closing it restores focus into the previous modal. An
383
+ `initPopover()` trigger inside the top modal may target a panel portaled
384
+ elsewhere in the document: while that panel is open, it joins the live modal
385
+ tree and owns Escape without releasing unrelated background content. Background
386
+ nodes added after the modal opens are trapped too. If a controlled parent modal
387
+ closes while a descendant still carries `is-open`, the descendant is suspended
388
+ with the parent and resumes if the parent reopens.
389
+
380
390
  **Scroll-lock is not automatic on either path.** Neither the native `<dialog>`
381
391
  nor the `is-open` path freezes the background — the page behind an open modal can
382
392
  still scroll. If that matters, toggle a lock yourself while the modal is open
@@ -404,7 +414,7 @@ infinite-clone track). It's a gallery: the user drives it.
404
414
 
405
415
  ## Display glyphs: when (and when not)
406
416
 
407
- `@ponchia/ui/glyphs` is a 48-glyph dot-matrix icon set — navigation
417
+ `@ponchia/ui/glyphs` is a 71-glyph dot-matrix icon set — navigation
408
418
  (`arrow-*`, `chevron-*`), actions (`check`, `close`, `plus`, `minus`,
409
419
  `search`, `menu`, `gear`), status (`info`, `warning`, `bell`, `lock`) and
410
420
  common marks (`home`, `user`, `heart`, `star`, `spark`, circle-family marks) — rendered on the
@@ -624,7 +634,6 @@ HTML, print/PDF, and before any hydration:
624
634
  | Accordion | native `<details>` / `<summary>` |
625
635
  | Segmented control (`ui-segmented`) | `:has(input:checked)` over a radio group |
626
636
  | Scroll-reveal (`ui-scroll-reveal`) | scroll-driven animation, zero JS |
627
- | Modal via native `<dialog>` | the element brings focus-trap + Escape; `initDialog` only adds open-triggers + focus-return |
628
637
 
629
638
  **Behavior-required — a CSS skin that needs its `init*` to be interactive.**
630
639
  These are JS widgets wearing the Bronto look; without the behavior they are inert
@@ -640,6 +649,7 @@ These are JS widgets wearing the Bronto look; without the behavior they are iner
640
649
  | Table sort/select (`[data-bronto-sortable]`) | `initTableSort` | a static table (still readable) |
641
650
  | Popover (`ui-popover`) | `initPopover` | no placement/ARIA — prefer the native `popover` attribute |
642
651
  | Carousel (`ui-carousel`) | `initCarousel` | a native scroll-snap track (usable, no controls) |
652
+ | Native dialog/lightbox (`<dialog>`, `ui-lightbox`) | `initDialog` | closed markup stays closed; `data-bronto-open`/close buttons do nothing. Do not use `open` as a modal fallback: it is non-modal and has no trigger/focus-return path |
643
653
  | Controlled modal (`ui-modal.is-open`) | `initModal` | open skin only — no inert trap, focus-return, or Escape close signal |
644
654
  | Menu (`data-bronto-menu`) | `initMenu` | a button next to a list with no open/close, outside-click, or Escape |
645
655
  | Dismissible alert/callout (`data-bronto-dismissible`) | `dismissible` | the close affordance is just a button; nothing is removed |
package/docs/vega.md CHANGED
@@ -143,8 +143,10 @@ series needs the redundant second channel, drive the mark's fill from the
143
143
 
144
144
  ### Spending the accent
145
145
 
146
- Series 1 of `range.category` **is** the live accent, so a single-series chart and
147
- the first category re-skin for free with `--accent`. To emphasise one mark in a
146
+ Series 1 of `range.category` resolves to the accent (per theme), so a single-series
147
+ chart and the first category carry the accent automatically baked into the
148
+ generated `config`, so regenerate the config and re-render to change it (Vega output
149
+ does not live-reskin from `--accent`). To emphasise one mark in a
148
150
  multi-series chart, paint just that mark with the accent and leave the rest
149
151
  neutral — the same "reserve the accent for the one thing a reader must not miss"
150
152
  rule the rest of the system follows. Two small helpers hand you the exact
@@ -169,7 +171,7 @@ const spec = {
169
171
  };
170
172
  ```
171
173
 
172
- `brontoVegaAccent(theme)` is `range.category[0]` (the live accent) and
174
+ `brontoVegaAccent(theme)` is `range.category[0]` (the resolved accent) and
173
175
  `brontoVegaNeutral(theme)` is the last category (the quiet neutral); re-read both
174
176
  when the theme toggles. Prefer them over digging the hex out of
175
177
  `tokens/resolved.json` — they are guaranteed to match the palette the config
package/glyphs/glyphs.js CHANGED
@@ -1396,14 +1396,18 @@ export function findGlyphs(query) {
1396
1396
 
1397
1397
  const TONE = { '#': 'hot', '*': 'accent' };
1398
1398
 
1399
+ function glyphRows(name) {
1400
+ return Object.hasOwn(GLYPHS, name) ? GLYPHS[name] : undefined;
1401
+ }
1402
+
1399
1403
  /** The raw bitmap rows for a glyph, or `undefined` if the name is unknown. */
1400
1404
  export function glyph(name) {
1401
- return GLYPHS[name];
1405
+ return glyphRows(name);
1402
1406
  }
1403
1407
 
1404
1408
  /** GLYPH_SIZE² cell descriptors (row-major), or `[]` if the name is unknown. */
1405
1409
  export function glyphCells(name) {
1406
- const rows = GLYPHS[name];
1410
+ const rows = glyphRows(name);
1407
1411
  if (!rows) return [];
1408
1412
  const cells = [];
1409
1413
  for (const row of rows) {
@@ -1425,7 +1429,7 @@ export function glyphCells(name) {
1425
1429
  * @returns {string}
1426
1430
  */
1427
1431
  export function glyphMask(name) {
1428
- const rows = GLYPHS[name];
1432
+ const rows = glyphRows(name);
1429
1433
  return rows ? maskUrl(rows) : '';
1430
1434
  }
1431
1435
 
@@ -1446,13 +1450,63 @@ function esc(s) {
1446
1450
  .replace(/"/g, '&quot;');
1447
1451
  }
1448
1452
 
1453
+ const CSS_LENGTH_UNITS =
1454
+ '(?:px|r?em|ch|ex|cap|ic|lh|rlh|vw|vh|vi|vb|vmin|vmax|' +
1455
+ 'svw|svh|svi|svb|svmin|svmax|lvw|lvh|lvi|lvb|lvmin|lvmax|' +
1456
+ 'dvw|dvh|dvi|dvb|dvmin|dvmax|cm|mm|q|in|pc|pt|%)';
1457
+ const CSS_NUMBER = '[-+]?(?:\\d*\\.\\d+|\\d+)';
1458
+ const CSS_LENGTH_RE = new RegExp(`^(?:0|${CSS_NUMBER}${CSS_LENGTH_UNITS})$`, 'i');
1459
+ const CSS_CALC_VALUE_RE = new RegExp(`^${CSS_NUMBER}(?:${CSS_LENGTH_UNITS})?`, 'i');
1460
+
1461
+ function isCalcLength(expr) {
1462
+ let rest = expr.trim();
1463
+ let depth = 0;
1464
+ let sawLength = false;
1465
+ let expectValue = true;
1466
+
1467
+ while (rest) {
1468
+ if (expectValue) {
1469
+ if (rest[0] === '(') {
1470
+ depth += 1;
1471
+ rest = rest.slice(1).trim();
1472
+ continue;
1473
+ }
1474
+ const token = rest.match(CSS_CALC_VALUE_RE)?.[0];
1475
+ if (!token) return false;
1476
+ if (CSS_LENGTH_RE.test(token)) sawLength = true;
1477
+ rest = rest.slice(token.length).trim();
1478
+ expectValue = false;
1479
+ continue;
1480
+ }
1481
+
1482
+ if (rest[0] === ')') {
1483
+ depth -= 1;
1484
+ if (depth < 0) return false;
1485
+ rest = rest.slice(1).trim();
1486
+ continue;
1487
+ }
1488
+
1489
+ const op = rest.match(/^[+\-*/]/)?.[0];
1490
+ if (!op) return false;
1491
+ rest = rest.slice(op.length).trim();
1492
+ expectValue = true;
1493
+ }
1494
+
1495
+ if (!sawLength) return false;
1496
+ if (expectValue) return false;
1497
+ return depth === 0;
1498
+ }
1499
+
1449
1500
  // `dot`, `gap`, and `size` land in an inline-CSS context (`style="…"`), where
1450
1501
  // HTML-escaping a `"` stops attribute breakout but a `;` would still open a
1451
- // second CSS declaration (overlay/clickjacking, selector exfil). So restrict
1452
- // them to length/calc syntax — digits, units, %, whitespace and `()+-*/.,` for
1453
- // calc()/clamp()/var() — and drop anything else rather than emit it.
1502
+ // second CSS declaration. Accept only concrete CSS lengths or calc() expressions.
1454
1503
  function cssLen(v) {
1455
- return /^[\w.%+\-*/()\s,]+$/.test(v) ? v : '';
1504
+ const value = String(v ?? '').trim();
1505
+ if (CSS_LENGTH_RE.test(value)) return value;
1506
+ if (value.toLowerCase().startsWith('calc(') && value.endsWith(')')) {
1507
+ return isCalcLength(value.slice(5, -1)) ? value : '';
1508
+ }
1509
+ return '';
1456
1510
  }
1457
1511
 
1458
1512
  /**
@@ -1514,7 +1568,7 @@ function maskUrl(rows) {
1514
1568
  * Needs `@ponchia/ui/css` (the `.ui-icon` rule).
1515
1569
  */
1516
1570
  export function renderGlyph(name, options = {}) {
1517
- const rows = GLYPHS[name];
1571
+ const rows = glyphRows(name);
1518
1572
  if (!rows) return '';
1519
1573
  const { grid = true, solid = false, anim, label, dot, gap, render, size } = options;
1520
1574
 
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};