@ponchia/ui 0.5.0 → 0.6.3

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 (196) hide show
  1. package/CHANGELOG.md +386 -4
  2. package/MIGRATIONS.json +14 -0
  3. package/README.md +29 -6
  4. package/annotations/index.d.ts +398 -276
  5. package/annotations/index.d.ts.map +1 -0
  6. package/annotations/index.js +350 -77
  7. package/behaviors/carousel.d.ts +28 -0
  8. package/behaviors/carousel.d.ts.map +1 -0
  9. package/behaviors/carousel.js +20 -16
  10. package/behaviors/combobox.d.ts +40 -0
  11. package/behaviors/combobox.d.ts.map +1 -0
  12. package/behaviors/combobox.js +111 -29
  13. package/behaviors/command.d.ts +41 -0
  14. package/behaviors/command.d.ts.map +1 -0
  15. package/behaviors/command.js +27 -15
  16. package/behaviors/connectors.d.ts +17 -0
  17. package/behaviors/connectors.d.ts.map +1 -0
  18. package/behaviors/connectors.js +7 -5
  19. package/behaviors/crosshair.d.ts +42 -0
  20. package/behaviors/crosshair.d.ts.map +1 -0
  21. package/behaviors/crosshair.js +23 -6
  22. package/behaviors/dialog.d.ts +20 -0
  23. package/behaviors/dialog.d.ts.map +1 -0
  24. package/behaviors/dialog.js +6 -2
  25. package/behaviors/disclosure.d.ts +10 -0
  26. package/behaviors/disclosure.d.ts.map +1 -0
  27. package/behaviors/disclosure.js +6 -2
  28. package/behaviors/dismissible.d.ts +10 -0
  29. package/behaviors/dismissible.d.ts.map +1 -0
  30. package/behaviors/dismissible.js +6 -2
  31. package/behaviors/forms.d.ts +27 -0
  32. package/behaviors/forms.d.ts.map +1 -0
  33. package/behaviors/forms.js +54 -13
  34. package/behaviors/glyph.d.ts +14 -0
  35. package/behaviors/glyph.d.ts.map +1 -0
  36. package/behaviors/glyph.js +28 -5
  37. package/behaviors/index.d.ts +31 -237
  38. package/behaviors/index.d.ts.map +1 -0
  39. package/behaviors/index.js +17 -0
  40. package/behaviors/inert.d.ts +20 -0
  41. package/behaviors/inert.d.ts.map +1 -0
  42. package/behaviors/inert.js +46 -0
  43. package/behaviors/internal.d.ts +25 -0
  44. package/behaviors/internal.d.ts.map +1 -0
  45. package/behaviors/internal.js +77 -1
  46. package/behaviors/legend.d.ts +35 -0
  47. package/behaviors/legend.d.ts.map +1 -0
  48. package/behaviors/legend.js +32 -2
  49. package/behaviors/menu.d.ts +16 -0
  50. package/behaviors/menu.d.ts.map +1 -0
  51. package/behaviors/menu.js +6 -2
  52. package/behaviors/modal.d.ts +41 -0
  53. package/behaviors/modal.d.ts.map +1 -0
  54. package/behaviors/modal.js +124 -0
  55. package/behaviors/popover.d.ts +28 -0
  56. package/behaviors/popover.d.ts.map +1 -0
  57. package/behaviors/popover.js +78 -7
  58. package/behaviors/spotlight.d.ts +17 -0
  59. package/behaviors/spotlight.d.ts.map +1 -0
  60. package/behaviors/spotlight.js +7 -5
  61. package/behaviors/table.d.ts +36 -0
  62. package/behaviors/table.d.ts.map +1 -0
  63. package/behaviors/table.js +84 -17
  64. package/behaviors/tabs.d.ts +20 -0
  65. package/behaviors/tabs.d.ts.map +1 -0
  66. package/behaviors/tabs.js +17 -14
  67. package/behaviors/theme.d.ts +54 -0
  68. package/behaviors/theme.d.ts.map +1 -0
  69. package/behaviors/theme.js +22 -3
  70. package/behaviors/toast.d.ts +49 -0
  71. package/behaviors/toast.d.ts.map +1 -0
  72. package/behaviors/toast.js +47 -3
  73. package/classes/classes.json +2527 -0
  74. package/classes/index.d.ts +134 -15
  75. package/classes/index.js +280 -80
  76. package/classes/vscode.css-custom-data.json +12 -0
  77. package/connectors/index.d.ts +201 -69
  78. package/connectors/index.d.ts.map +1 -0
  79. package/connectors/index.js +142 -25
  80. package/css/app.css +69 -13
  81. package/css/base.css +15 -10
  82. package/css/bullet.css +108 -0
  83. package/css/code.css +98 -0
  84. package/css/connectors.css +17 -0
  85. package/css/content.css +22 -3
  86. package/css/crosshair.css +7 -7
  87. package/css/dataviz.css +5 -1
  88. package/css/diff.css +153 -0
  89. package/css/disclosure.css +53 -7
  90. package/css/dots.css +94 -7
  91. package/css/feedback.css +97 -7
  92. package/css/forms.css +113 -4
  93. package/css/legend.css +16 -9
  94. package/css/marks.css +38 -8
  95. package/css/motion.css +98 -53
  96. package/css/navigation.css +7 -0
  97. package/css/overlay.css +90 -3
  98. package/css/primitives.css +158 -13
  99. package/css/report.css +73 -56
  100. package/css/sidenote.css +67 -0
  101. package/css/site.css +16 -2
  102. package/css/sources.css +43 -1
  103. package/css/spark.css +62 -0
  104. package/css/spotlight.css +1 -1
  105. package/css/table.css +9 -2
  106. package/css/term.css +110 -0
  107. package/css/textref.css +63 -0
  108. package/css/toc.css +91 -0
  109. package/css/tokens.css +49 -1
  110. package/css/tree.css +134 -0
  111. package/css/workbench.css +1 -1
  112. package/dist/bronto.css +1 -1
  113. package/dist/css/analytical.css +1 -1
  114. package/dist/css/app.css +1 -1
  115. package/dist/css/base.css +1 -1
  116. package/dist/css/bullet.css +1 -0
  117. package/dist/css/code.css +1 -0
  118. package/dist/css/connectors.css +1 -1
  119. package/dist/css/content.css +1 -1
  120. package/dist/css/crosshair.css +1 -1
  121. package/dist/css/diff.css +1 -0
  122. package/dist/css/disclosure.css +1 -1
  123. package/dist/css/dots.css +1 -1
  124. package/dist/css/feedback.css +1 -1
  125. package/dist/css/forms.css +1 -1
  126. package/dist/css/legend.css +1 -1
  127. package/dist/css/marks.css +1 -1
  128. package/dist/css/motion.css +1 -1
  129. package/dist/css/navigation.css +1 -1
  130. package/dist/css/overlay.css +1 -1
  131. package/dist/css/primitives.css +1 -1
  132. package/dist/css/report.css +1 -1
  133. package/dist/css/sidenote.css +1 -0
  134. package/dist/css/site.css +1 -1
  135. package/dist/css/sources.css +1 -1
  136. package/dist/css/spark.css +1 -0
  137. package/dist/css/spotlight.css +1 -1
  138. package/dist/css/table.css +1 -1
  139. package/dist/css/term.css +1 -0
  140. package/dist/css/textref.css +1 -0
  141. package/dist/css/toc.css +1 -0
  142. package/dist/css/tokens.css +1 -1
  143. package/dist/css/tree.css +1 -0
  144. package/dist/css/workbench.css +1 -1
  145. package/docs/adr/0003-theme-model.md +1 -1
  146. package/docs/annotations.md +133 -14
  147. package/docs/architecture.md +49 -6
  148. package/docs/bullet.md +78 -0
  149. package/docs/code.md +76 -0
  150. package/docs/contrast.md +116 -92
  151. package/docs/d2.md +196 -0
  152. package/docs/diff.md +146 -0
  153. package/docs/legends.md +23 -3
  154. package/docs/marks.md +9 -2
  155. package/docs/mermaid.md +169 -0
  156. package/docs/reference.md +201 -26
  157. package/docs/reporting.md +416 -57
  158. package/docs/sidenote.md +64 -0
  159. package/docs/sources.md +27 -0
  160. package/docs/spark.md +78 -0
  161. package/docs/stability.md +10 -2
  162. package/docs/term.md +81 -0
  163. package/docs/textref.md +78 -0
  164. package/docs/theming.md +44 -5
  165. package/docs/toc.md +83 -0
  166. package/docs/tree.md +74 -0
  167. package/docs/usage.md +354 -16
  168. package/docs/vega.md +244 -0
  169. package/docs/workbench.md +7 -1
  170. package/glyphs/glyphs.js +13 -5
  171. package/llms.txt +285 -14
  172. package/package.json +95 -17
  173. package/qwik/index.d.ts +44 -59
  174. package/qwik/index.d.ts.map +1 -0
  175. package/qwik/index.js +65 -3
  176. package/react/index.d.ts +41 -61
  177. package/react/index.d.ts.map +1 -0
  178. package/react/index.js +63 -3
  179. package/solid/index.d.ts +68 -61
  180. package/solid/index.d.ts.map +1 -0
  181. package/solid/index.js +66 -3
  182. package/tokens/d2.d.ts +38 -0
  183. package/tokens/d2.js +71 -0
  184. package/tokens/d2.json +43 -0
  185. package/tokens/index.d.ts +5 -5
  186. package/tokens/index.js +15 -1
  187. package/tokens/index.json +9 -0
  188. package/tokens/mermaid.d.ts +23 -0
  189. package/tokens/mermaid.js +181 -0
  190. package/tokens/mermaid.json +163 -0
  191. package/tokens/resolved.json +45 -1
  192. package/tokens/skins.js +3 -2
  193. package/tokens/tokens.dtcg.json +26 -0
  194. package/tokens/vega.d.ts +34 -0
  195. package/tokens/vega.js +155 -0
  196. package/tokens/vega.json +179 -0
package/docs/vega.md ADDED
@@ -0,0 +1,244 @@
1
+ # Vega-Lite
2
+
3
+ [Vega-Lite](https://vega.github.io/vega-lite/) is a **declarative JSON grammar
4
+ of graphics** — you describe a chart as data and it compiles (through
5
+ [Vega](https://vega.github.io/vega/)) to **SVG or canvas**. Like the
6
+ [Mermaid](./mermaid.md) and [D2](./d2.md) integrations, `@ponchia/ui` doesn't
7
+ render charts — it **themes** them from your tokens. Two things ship:
8
+
9
+ - `@ponchia/ui/vega` — `brontoVegaConfig(theme)`, the on-brand Vega-Lite
10
+ [`config`](https://vega.github.io/vega-lite/docs/config.html) object.
11
+ - `@ponchia/ui/vega.json` — the resolved per-theme config, for any consumer.
12
+
13
+ This is the idiomatic Vega theme shape — a `config`, the same kind the
14
+ [`vega-themes`](https://github.com/vega/vega-themes) package ships. Vega stays
15
+ the consumer's renderer; this is config only, and **Vega is not a dependency**
16
+ of bronto (the dev-only render-probe aside).
17
+
18
+ > Why Vega-Lite and not a bronto chart component? A chart needs **scales**
19
+ > (data → pixels) and **data binding** — the two things the analytical layer
20
+ > [refuses to own](./architecture.md). A spec is also something an
21
+ > LLM-from-another-system can emit as data, the same way it emits Mermaid/D2.
22
+ > So bronto themes a real charting grammar instead of shipping a fragile one.
23
+
24
+ ## Theme a chart
25
+
26
+ `brontoVegaConfig(theme)` returns a `config` object. Spread it into a spec, or
27
+ hand it to [vega-embed](https://github.com/vega/vega-embed):
28
+
29
+ ```js
30
+ import vegaEmbed from 'vega-embed';
31
+ import { brontoVegaConfig } from '@ponchia/ui/vega';
32
+
33
+ const theme = document.documentElement.dataset.theme === 'dark' ? 'dark' : 'light';
34
+
35
+ vegaEmbed('#chart', {
36
+ data: { values: [
37
+ { quarter: 'Q1', value: 42 },
38
+ { quarter: 'Q2', value: 58 },
39
+ { quarter: 'Q3', value: 50 },
40
+ ] },
41
+ mark: 'bar',
42
+ encoding: {
43
+ x: { field: 'quarter', type: 'nominal' },
44
+ y: { field: 'value', type: 'quantitative' },
45
+ },
46
+ }, { config: brontoVegaConfig(theme), renderer: 'svg', actions: false });
47
+ ```
48
+
49
+ Pass **`renderer: 'svg'`** (not vega-embed's `canvas` default): an SVG chart is
50
+ inspectable, themeable, survives the print/PDF pipeline, and is what the
51
+ [annotation layer](#annotate-a-chart) composes onto — a canvas chart prints as a
52
+ raster and carries no text alternative.
53
+
54
+ ### From a CDN, no bundler
55
+
56
+ Load Vega + Vega-Lite + vega-embed from **pinned `/build/*.min.js` UMD files**,
57
+ then pass the config. Pin exact versions and use the `/build/` path — a bare
58
+ `cdn.jsdelivr.net/npm/vega@6` redirect resolves to a module bundle that does
59
+ **not** register the global `window.vega`, so vega-embed throws and nothing
60
+ renders. Keep the three majors aligned: **Vega-Lite 6 targets Vega 6** (and
61
+ vega-embed 7), so don't mix a Vega-Lite 6 with a Vega 5 runtime:
62
+
63
+ ```html
64
+ <script src="https://cdn.jsdelivr.net/npm/vega@6.2.0/build/vega.min.js"></script>
65
+ <script src="https://cdn.jsdelivr.net/npm/vega-lite@6.4.3/build/vega-lite.min.js"></script>
66
+ <script src="https://cdn.jsdelivr.net/npm/vega-embed@7.1.0/build/vega-embed.min.js"></script>
67
+ <script>
68
+ // INLINE the config — generate the paste-ready literal with
69
+ // `npm run emit:theme vega light` (mirrors the annotations author-time-copy
70
+ // pattern). This is the only path that also works from a file:// report.
71
+ const brontoLight = {
72
+ /* …paste tokens/vega.json → light here… */
73
+ };
74
+ vegaEmbed('#chart', spec, { config: brontoLight, renderer: 'svg', actions: false });
75
+ </script>
76
+ ```
77
+
78
+ > **file:// portability.** A report opened straight from disk (`file://`) cannot
79
+ > `import` the `@ponchia/ui/vega` module **nor** `fetch('…/vega.json')` — the
80
+ > browser blocks both across the `null`/file origin (CORS). So for a
81
+ > double-clickable or PDF-bound report, **inline the resolved config object**
82
+ > (as above) rather than fetching it. Generate the paste-ready, sentinel-tagged
83
+ > literal with `npm run emit:theme vega light` (or `dark`); re-running
84
+ > `npm run emit:theme:check <file>` re-derives every tagged block and fails if a
85
+ > token change has left an inlined copy stale. Over an `http(s)` origin (a dev server, a
86
+ > static host, a bundler), the `import { brontoVegaConfig }` form and a
87
+ > `fetch('https://cdn.jsdelivr.net/npm/@ponchia/ui@VERSION/tokens/vega.json')`
88
+ > both work — pin the package version in the URL, since the unversioned latest
89
+ > may predate this target.
90
+
91
+ **Over `http(s)`, skip the inline copy — import the helper as an ES module.**
92
+ `tokens/vega.js` (the `@ponchia/ui/vega` entry) has **zero dependencies**, so it
93
+ loads straight from a CDN as a browser ES module with no bundler and no
94
+ import-map. You get `brontoVegaConfig(theme)` itself (live theme switching), not
95
+ a frozen object to keep in sync. Pin the package version; this needs a real
96
+ origin (it does **not** work from `file://` — use the inline form above there):
97
+
98
+ ```html
99
+ <script type="module">
100
+ import { brontoVegaConfig } from 'https://cdn.jsdelivr.net/npm/@ponchia/ui@VERSION/tokens/vega.js';
101
+ // vegaEmbed loaded from its UMD bundle above (window.vegaEmbed), or import it as ESM too.
102
+ vegaEmbed('#chart', spec, { config: brontoVegaConfig('light'), renderer: 'svg', actions: false });
103
+ </script>
104
+ ```
105
+
106
+ For a build step or non-JS host, read `@ponchia/ui/vega.json` directly
107
+ (`{ light, dark }`, each a ready Vega-Lite `config`).
108
+
109
+ ### Why resolved colours, not `var(--x)`
110
+
111
+ Vega-Lite compiles a spec to a Vega scene that renders to **SVG or canvas** —
112
+ colours are **baked into the output** and parsed by `d3-color`, which understands
113
+ real hex/rgb but **not** `var()` (nor `oklch()`). So the config ships **resolved
114
+ hex per theme**, projected from the same token source as
115
+ [`tokens/resolved.json`](./architecture.md) / [`charts.json`](./theming.md).
116
+ Re-call `brontoVegaConfig()` when the theme toggles and re-embed.
117
+
118
+ ### What the slots paint
119
+
120
+ The config keeps a chart **monochrome by default** — the rationed accent is the
121
+ one chromatic default (series 1 / the lone mark), never the chrome:
122
+
123
+ | Slot | Paint | bronto token |
124
+ | --- | --- | --- |
125
+ | `background` | Chart canvas | `--bg` |
126
+ | `view.stroke` | Plot frame | `--line` |
127
+ | `mark.color` | Default / single-series mark | `--accent` |
128
+ | `rule.color` | Reference rules, annotations | `--line-strong` |
129
+ | `axis.domainColor` · `tickColor` | Axis line · ticks | `--line-strong` |
130
+ | `axis.gridColor` | Gridlines | `--line` |
131
+ | `axis.labelColor` · `titleColor` | Tick labels · axis title | `--text-soft` · `--text` |
132
+ | `text.color` | Free `text`/`label` marks | `--text` |
133
+ | `legend.*` · `header.*` · `title.*` | Legend, facet headers, title | `--text-soft` / `--text` / `--text-dim` |
134
+ | `*.font` / `*Font` | All text | `--sans` |
135
+ | `range.category` | 8-series categorical palette | `charts.json` categorical (series 1 = accent) |
136
+ | `range.ordinal` · `ramp` · `heatmap` | Single-hue sequential ramp | `charts.json` sequential |
137
+ | `range.diverging` | − … neutral … + ramp | `charts.json` diverging |
138
+
139
+ The palette is the same CVD-safe, pattern-paired set documented in
140
+ [theming](./theming.md#data-viz) — colour is never the sole channel. When a
141
+ series needs the redundant second channel, drive the mark's fill from the
142
+ `--chart-pattern-*` tokens or pair a [legend](./legends.md) swatch.
143
+
144
+ ### Spending the accent
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
148
+ multi-series chart, paint just that mark with the accent and leave the rest
149
+ neutral — the same "reserve the accent for the one thing a reader must not miss"
150
+ rule the rest of the system follows. Two small helpers hand you the exact
151
+ per-theme hexes so you never hard-code a palette array index:
152
+
153
+ ```js
154
+ import { brontoVegaAccent, brontoVegaNeutral } from '@ponchia/ui/vega';
155
+
156
+ // e.g. a bar chart where only the 'Alert' category is loud:
157
+ const spec = {
158
+ /* …data… */
159
+ mark: 'bar',
160
+ encoding: {
161
+ x: { field: 'name', type: 'nominal' },
162
+ y: { field: 'value', type: 'quantitative' },
163
+ color: {
164
+ condition: { test: "datum.name === 'Alert'", value: brontoVegaAccent(theme) },
165
+ value: brontoVegaNeutral(theme),
166
+ },
167
+ legend: null,
168
+ },
169
+ };
170
+ ```
171
+
172
+ `brontoVegaAccent(theme)` is `range.category[0]` (the live accent) and
173
+ `brontoVegaNeutral(theme)` is the last category (the quiet neutral); re-read both
174
+ when the theme toggles. Prefer them over digging the hex out of
175
+ `tokens/resolved.json` — they are guaranteed to match the palette the config
176
+ already ships. In token terms the accent is `--chart-1` and the neutral is
177
+ `--chart-8`, so a [legend](./legends.md#swatch-colour) for an accent-rationed
178
+ chart keys those two series with `ui-legend__swatch--1` and
179
+ `ui-legend__swatch--8` — the swatches mirror the marks exactly.
180
+
181
+ ### Selecting the themed ramp in a spec
182
+
183
+ The config registers the ramps as **named ranges**, so a quantitative encoding
184
+ opts in with `scale: { range: 'heatmap' }` (or `'ramp'` / `'diverging'`) — the
185
+ range **name**, not a colour scheme:
186
+
187
+ ```js
188
+ {
189
+ mark: 'rect',
190
+ encoding: {
191
+ x: { field: 'x', type: 'nominal' },
192
+ y: { field: 'y', type: 'nominal' },
193
+ color: { field: 'v', type: 'quantitative', scale: { range: 'heatmap' } },
194
+ },
195
+ }
196
+ ```
197
+
198
+ > Use `scale: { range: 'heatmap' }`, **not** `scale: { scheme: 'heatmap' }`.
199
+ > `scheme:` looks up a registered Vega/d3 scheme by name and **throws** for
200
+ > `'heatmap'` (no such scheme) — the ramp is a custom `range` the bronto config
201
+ > defines, addressed by range name. A `quantitative` colour encoding already
202
+ > defaults to `range.heatmap`; name it explicitly only when a chart has several
203
+ > quantitative scales and you want a specific one (`'diverging'` for a signed
204
+ > domain around a neutral centre).
205
+
206
+ ### Sequential & diverging ramps invert by theme
207
+
208
+ `range.heatmap` / `ramp` / `ordinal` is a single-hue ramp that runs **pale → deep
209
+ as the value rises in light theme, and deep → pale in dark theme** (the bg flips,
210
+ so the ramp flips to stay legible against it). Two consequences:
211
+
212
+ - **Don't hard-code ink on a heatmap cell.** A fixed black (or white) label is
213
+ readable at one end of the ramp and invisible at the other — and the readable
214
+ end swaps between themes. Either omit per-cell labels and rely on the fallback
215
+ `ui-table`, or compute the label colour from the cell's luminance at render
216
+ time. bronto themes the ramp; it can't know your data domain, so it does not
217
+ ship a cell-ink helper.
218
+ - **A CSS gradient key won't pixel-match the Vega ramp.** A native
219
+ [`ui-legend--gradient`](./legends.md) track is interpolated in OKLCH; Vega
220
+ interpolates its `range.*` ramp in d3's RGB space. They share endpoints but
221
+ drift in the mid-tones, so a continuous gradient key placed beside a Vega
222
+ heatmap will not match its mid cells exactly. Use a **stepped** legend (one
223
+ swatch per band, each from the same `charts.json` ramp stop) when the key sits
224
+ next to the chart.
225
+
226
+ ## Annotate a chart
227
+
228
+ Vega renders to SVG, so the [annotation layer](./annotations.md) composes onto it
229
+ exactly as in the [Mermaid recipe](./mermaid.md#annotate-a-diagram): render to a
230
+ frozen SVG (vega-embed's `view.toSVG()`, or the Vega CLI), read the target mark's
231
+ box, and paste a `<g class="ui-annotation">` computed with
232
+ `@ponchia/ui/annotations`. The same caveat applies — Vega's internal SVG (element
233
+ ids, the `role`/`aria` structure, the scene transform) is **not a public
234
+ contract**, so pin your Vega version and key off the data, not generated ids.
235
+
236
+ ## Scope
237
+
238
+ bronto owns the theme config — gated structurally by `check:vega` (every colour
239
+ slot resolves, both themes, no `var()` leaks, every `range.*` ramp populated),
240
+ and separately a dev-only render-probe (`npm test`, via the `vega`/`vega-lite`
241
+ dev deps) asserts the colours actually land on a rendered chart — and the
242
+ annotation geometry. It does not own Vega's grammar, its rendering, or its internal SVG —
243
+ those stay Vega's, and the chart is a documented composition, not a shipped
244
+ runtime binding.
package/docs/workbench.md CHANGED
@@ -22,7 +22,7 @@ a `__body` of property rows.
22
22
 
23
23
  ```html
24
24
  <aside class="ui-inspector">
25
- <div class="ui-inspector__header">
25
+ <div class="ui-inspector__head">
26
26
  <h2 class="ui-eyebrow">Rectangle</h2>
27
27
  <button class="ui-button ui-button--subtle ui-button--sm" type="button">Reset</button>
28
28
  </div>
@@ -50,6 +50,12 @@ A label/value pair, denser than `ui-key-value` and tuned for an inspector. The
50
50
 
51
51
  ## Selection bar — `.ui-selectionbar`
52
52
 
53
+ > **Name note:** `.ui-selectionbar` is the workbench bulk-action bar (this
54
+ > section). It is unrelated to the `.ui-sel--on` / `.ui-sel--off` /
55
+ > `.ui-sel--maybe` selection-emphasis state classes in
56
+ > [`css/selection.css`](./selection.md), which style host-managed selection
57
+ > state on individual items.
58
+
53
59
  A raised bar of actions on the current selection: a `__count` on one side,
54
60
  `__actions` on the other. The host owns what is selected and what the actions do.
55
61
 
package/glyphs/glyphs.js CHANGED
@@ -23,7 +23,7 @@ export const GLYPH_SIZE = 16;
23
23
 
24
24
  // Raw bitmaps. Each is GLYPH_SIZE rows of GLYPH_SIZE chars over [.#*]:
25
25
  // `.` off · `#` hot · `*` accent. Only `spark` uses accent dots — it is the
26
- // canonical two-tone demo; the gate in check-glyphs.mjs enforces the shape.
26
+ // canonical two-tone demo; test/glyphs.test.mjs asserts the spark-only-* rule.
27
27
  const RAW = {
28
28
  circle: [
29
29
  '................',
@@ -941,9 +941,9 @@ function esc(s) {
941
941
  .replace(/"/g, '&quot;');
942
942
  }
943
943
 
944
- // `dot`/`gap` land in an inline-CSS context (`style="--dotmatrix-dot:VALUE"`),
945
- // where HTML-escaping a `"` stops attribute breakout but a `;` would still open
946
- // a second CSS declaration (overlay/clickjacking, selector exfil). So restrict
944
+ // `dot`, `gap`, and `size` land in an inline-CSS context (`style=""`), where
945
+ // HTML-escaping a `"` stops attribute breakout but a `;` would still open a
946
+ // second CSS declaration (overlay/clickjacking, selector exfil). So restrict
947
947
  // them to length/calc syntax — digits, units, %, whitespace and `()+-*/.,` for
948
948
  // calc()/clamp()/var() — and drop anything else rather than emit it.
949
949
  function cssLen(v) {
@@ -1004,6 +1004,8 @@ function maskUrl(rows) {
1004
1004
  * bitmap (one DOM node, not GLYPH_SIZE²) — the icon-at-scale path: it sizes to
1005
1005
  * `size` (or `--icon-size` / `1em`) and inherits `currentColor`. The
1006
1006
  * cell-mode options (grid/solid/anim/dot/gap) don't apply; `label` does.
1007
+ * Mask mode is single-tone: accent `*` cells (used by `spark`) render
1008
+ * identically to hot `#` cells — both become opaque mask regions.
1007
1009
  * Needs `@ponchia/ui/css` (the `.ui-icon` rule).
1008
1010
  */
1009
1011
  export function renderGlyph(name, options = {}) {
@@ -1025,7 +1027,13 @@ export function renderGlyph(name, options = {}) {
1025
1027
  const style = [`--dotmatrix-cols:${GLYPH_SIZE}`];
1026
1028
  const dotLen = dot && cssLen(dot);
1027
1029
  const gapLen = gap && cssLen(gap);
1028
- if (dotLen) style.push(`--dotmatrix-dot:${dotLen}`);
1030
+ // Default the dot track to an intrinsic icon scale when the author set no
1031
+ // (valid) `--dotmatrix-dot`. Without it the CSS grid falls back to
1032
+ // `minmax(0, 1fr)` and the 16×16 matrix balloons to fill its container
1033
+ // (full-bleed, ~1250px) — the string API would then render an icon-intent call
1034
+ // very differently from the DOM `initDotGlyph` path, which already defaults to
1035
+ // 0.08em. Mirror that default here so both paths render the same icon. (C7.)
1036
+ style.push(`--dotmatrix-dot:${dotLen || '0.08em'}`);
1029
1037
  // Solid mode fuses the dots into a crisp pixel glyph: square cells, no gap.
1030
1038
  if (solid) style.push('--dotmatrix-dot-radius:0', '--dotmatrix-gap:0');
1031
1039
  else if (gapLen) style.push(`--dotmatrix-gap:${gapLen}`);