@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.
- package/CHANGELOG.md +386 -4
- package/MIGRATIONS.json +14 -0
- package/README.md +29 -6
- package/annotations/index.d.ts +398 -276
- package/annotations/index.d.ts.map +1 -0
- package/annotations/index.js +350 -77
- package/behaviors/carousel.d.ts +28 -0
- package/behaviors/carousel.d.ts.map +1 -0
- package/behaviors/carousel.js +20 -16
- package/behaviors/combobox.d.ts +40 -0
- package/behaviors/combobox.d.ts.map +1 -0
- package/behaviors/combobox.js +111 -29
- package/behaviors/command.d.ts +41 -0
- package/behaviors/command.d.ts.map +1 -0
- package/behaviors/command.js +27 -15
- package/behaviors/connectors.d.ts +17 -0
- package/behaviors/connectors.d.ts.map +1 -0
- package/behaviors/connectors.js +7 -5
- package/behaviors/crosshair.d.ts +42 -0
- package/behaviors/crosshair.d.ts.map +1 -0
- package/behaviors/crosshair.js +23 -6
- package/behaviors/dialog.d.ts +20 -0
- package/behaviors/dialog.d.ts.map +1 -0
- package/behaviors/dialog.js +6 -2
- package/behaviors/disclosure.d.ts +10 -0
- package/behaviors/disclosure.d.ts.map +1 -0
- package/behaviors/disclosure.js +6 -2
- package/behaviors/dismissible.d.ts +10 -0
- package/behaviors/dismissible.d.ts.map +1 -0
- package/behaviors/dismissible.js +6 -2
- package/behaviors/forms.d.ts +27 -0
- package/behaviors/forms.d.ts.map +1 -0
- package/behaviors/forms.js +54 -13
- package/behaviors/glyph.d.ts +14 -0
- package/behaviors/glyph.d.ts.map +1 -0
- package/behaviors/glyph.js +28 -5
- package/behaviors/index.d.ts +31 -237
- package/behaviors/index.d.ts.map +1 -0
- package/behaviors/index.js +17 -0
- package/behaviors/inert.d.ts +20 -0
- package/behaviors/inert.d.ts.map +1 -0
- package/behaviors/inert.js +46 -0
- package/behaviors/internal.d.ts +25 -0
- package/behaviors/internal.d.ts.map +1 -0
- package/behaviors/internal.js +77 -1
- package/behaviors/legend.d.ts +35 -0
- package/behaviors/legend.d.ts.map +1 -0
- package/behaviors/legend.js +32 -2
- package/behaviors/menu.d.ts +16 -0
- package/behaviors/menu.d.ts.map +1 -0
- package/behaviors/menu.js +6 -2
- package/behaviors/modal.d.ts +41 -0
- package/behaviors/modal.d.ts.map +1 -0
- package/behaviors/modal.js +124 -0
- package/behaviors/popover.d.ts +28 -0
- package/behaviors/popover.d.ts.map +1 -0
- package/behaviors/popover.js +78 -7
- package/behaviors/spotlight.d.ts +17 -0
- package/behaviors/spotlight.d.ts.map +1 -0
- package/behaviors/spotlight.js +7 -5
- package/behaviors/table.d.ts +36 -0
- package/behaviors/table.d.ts.map +1 -0
- package/behaviors/table.js +84 -17
- package/behaviors/tabs.d.ts +20 -0
- package/behaviors/tabs.d.ts.map +1 -0
- package/behaviors/tabs.js +17 -14
- package/behaviors/theme.d.ts +54 -0
- package/behaviors/theme.d.ts.map +1 -0
- package/behaviors/theme.js +22 -3
- package/behaviors/toast.d.ts +49 -0
- package/behaviors/toast.d.ts.map +1 -0
- package/behaviors/toast.js +47 -3
- package/classes/classes.json +2527 -0
- package/classes/index.d.ts +134 -15
- package/classes/index.js +280 -80
- package/classes/vscode.css-custom-data.json +12 -0
- package/connectors/index.d.ts +201 -69
- package/connectors/index.d.ts.map +1 -0
- package/connectors/index.js +142 -25
- package/css/app.css +69 -13
- package/css/base.css +15 -10
- package/css/bullet.css +108 -0
- package/css/code.css +98 -0
- package/css/connectors.css +17 -0
- package/css/content.css +22 -3
- package/css/crosshair.css +7 -7
- package/css/dataviz.css +5 -1
- package/css/diff.css +153 -0
- package/css/disclosure.css +53 -7
- package/css/dots.css +94 -7
- package/css/feedback.css +97 -7
- package/css/forms.css +113 -4
- package/css/legend.css +16 -9
- package/css/marks.css +38 -8
- package/css/motion.css +98 -53
- package/css/navigation.css +7 -0
- package/css/overlay.css +90 -3
- package/css/primitives.css +158 -13
- package/css/report.css +73 -56
- package/css/sidenote.css +67 -0
- package/css/site.css +16 -2
- package/css/sources.css +43 -1
- package/css/spark.css +62 -0
- package/css/spotlight.css +1 -1
- package/css/table.css +9 -2
- package/css/term.css +110 -0
- package/css/textref.css +63 -0
- package/css/toc.css +91 -0
- package/css/tokens.css +49 -1
- package/css/tree.css +134 -0
- package/css/workbench.css +1 -1
- package/dist/bronto.css +1 -1
- package/dist/css/analytical.css +1 -1
- package/dist/css/app.css +1 -1
- package/dist/css/base.css +1 -1
- package/dist/css/bullet.css +1 -0
- package/dist/css/code.css +1 -0
- package/dist/css/connectors.css +1 -1
- package/dist/css/content.css +1 -1
- package/dist/css/crosshair.css +1 -1
- package/dist/css/diff.css +1 -0
- package/dist/css/disclosure.css +1 -1
- package/dist/css/dots.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/forms.css +1 -1
- package/dist/css/legend.css +1 -1
- package/dist/css/marks.css +1 -1
- package/dist/css/motion.css +1 -1
- package/dist/css/navigation.css +1 -1
- package/dist/css/overlay.css +1 -1
- package/dist/css/primitives.css +1 -1
- package/dist/css/report.css +1 -1
- package/dist/css/sidenote.css +1 -0
- package/dist/css/site.css +1 -1
- package/dist/css/sources.css +1 -1
- package/dist/css/spark.css +1 -0
- package/dist/css/spotlight.css +1 -1
- package/dist/css/table.css +1 -1
- package/dist/css/term.css +1 -0
- package/dist/css/textref.css +1 -0
- package/dist/css/toc.css +1 -0
- package/dist/css/tokens.css +1 -1
- package/dist/css/tree.css +1 -0
- package/dist/css/workbench.css +1 -1
- package/docs/adr/0003-theme-model.md +1 -1
- package/docs/annotations.md +133 -14
- package/docs/architecture.md +49 -6
- package/docs/bullet.md +78 -0
- package/docs/code.md +76 -0
- package/docs/contrast.md +116 -92
- package/docs/d2.md +196 -0
- package/docs/diff.md +146 -0
- package/docs/legends.md +23 -3
- package/docs/marks.md +9 -2
- package/docs/mermaid.md +169 -0
- package/docs/reference.md +201 -26
- package/docs/reporting.md +416 -57
- package/docs/sidenote.md +64 -0
- package/docs/sources.md +27 -0
- package/docs/spark.md +78 -0
- package/docs/stability.md +10 -2
- package/docs/term.md +81 -0
- package/docs/textref.md +78 -0
- package/docs/theming.md +44 -5
- package/docs/toc.md +83 -0
- package/docs/tree.md +74 -0
- package/docs/usage.md +354 -16
- package/docs/vega.md +244 -0
- package/docs/workbench.md +7 -1
- package/glyphs/glyphs.js +13 -5
- package/llms.txt +285 -14
- package/package.json +95 -17
- package/qwik/index.d.ts +44 -59
- package/qwik/index.d.ts.map +1 -0
- package/qwik/index.js +65 -3
- package/react/index.d.ts +41 -61
- package/react/index.d.ts.map +1 -0
- package/react/index.js +63 -3
- package/solid/index.d.ts +68 -61
- package/solid/index.d.ts.map +1 -0
- package/solid/index.js +66 -3
- package/tokens/d2.d.ts +38 -0
- package/tokens/d2.js +71 -0
- package/tokens/d2.json +43 -0
- package/tokens/index.d.ts +5 -5
- package/tokens/index.js +15 -1
- package/tokens/index.json +9 -0
- package/tokens/mermaid.d.ts +23 -0
- package/tokens/mermaid.js +181 -0
- package/tokens/mermaid.json +163 -0
- package/tokens/resolved.json +45 -1
- package/tokens/skins.js +3 -2
- package/tokens/tokens.dtcg.json +26 -0
- package/tokens/vega.d.ts +34 -0
- package/tokens/vega.js +155 -0
- package/tokens/vega.json +179 -0
package/docs/annotations.md
CHANGED
|
@@ -77,7 +77,15 @@ offset, matching d3-annotation's mental model.
|
|
|
77
77
|
|
|
78
78
|
The visible note text should also be represented in the figure caption,
|
|
79
79
|
`<desc>`, fallback table, or surrounding prose when the figure is complex.
|
|
80
|
-
|
|
80
|
+
|
|
81
|
+
Match the accessibility treatment to the annotation's job, in both directions:
|
|
82
|
+
a **data** annotation (a peak, a threshold, a watched region — it says something
|
|
83
|
+
the reader needs) must stay readable, so represent its text as above and do
|
|
84
|
+
**not** `aria-hidden` it; a purely **decorative** mark (a cover flourish, a
|
|
85
|
+
margin doodle that carries no data) should be `aria-hidden="true"` and
|
|
86
|
+
`focusable="false"` on the whole SVG so a screen reader skips the decoration.
|
|
87
|
+
The one thing to avoid is the middle: a meaningful callout hidden from assistive
|
|
88
|
+
tech, or decoration announced as if it were data.
|
|
81
89
|
|
|
82
90
|
## Variants and motion
|
|
83
91
|
|
|
@@ -132,7 +140,7 @@ selections, DOM nodes, or frameworks.
|
|
|
132
140
|
| --- | --- |
|
|
133
141
|
| `annotationTransform({ x, y })` | Group transform for the subject anchor. |
|
|
134
142
|
| `noteTransform({ dx, dy, align, valign, width, height })` | Note transform from the subject anchor, with optional alignment. |
|
|
135
|
-
| `notePlacement({ x, y, width, height, bounds, preferred })` | Bounded note offset, alignment and transform for one annotation. |
|
|
143
|
+
| `notePlacement({ x, y, width, height, bounds, preferred, inset })` | Bounded note offset, alignment and transform for one annotation. `inset` reserves an extra margin (e.g. the title stroke-halo, ~3) so a placement that "fits" doesn't clip. |
|
|
136
144
|
| `declutterLabels(items, { gap, min, max })` | Adjusted centres for `items` (`[{ pos, size }]`) so labels don't overlap along one axis (order-preserving). |
|
|
137
145
|
| `directLabels(items, { axis, cross, gap, min, max, shape })` | Decluttered label points **and** a leader path per item: `[{ x, y, anchor, key, d }]`. |
|
|
138
146
|
| `circleSubjectPath({ radius })` | Circle subject path. |
|
|
@@ -147,10 +155,10 @@ selections, DOM nodes, or frameworks.
|
|
|
147
155
|
| `timelineEventPath({ size, direction })` | Event pin marker path. |
|
|
148
156
|
| `evidenceMarkerPath({ x, y, width, height, padding })` | Centered square/rect evidence marker path. |
|
|
149
157
|
| `connectorLine({ dx, dy, subject })` | Straight connector, trimmed against circle/rect subjects. |
|
|
150
|
-
| `connectorElbow({ dx, dy, subject })` |
|
|
158
|
+
| `connectorElbow({ dx, dy, subject, mid })` | Right-angle dogleg connector (H/V/H); `mid` (0..1, default 0.5) sets the turn position along the dominant axis. |
|
|
151
159
|
| `connectorCurve({ dx, dy, subject })` | Deterministic cubic connector. |
|
|
152
160
|
| `connectorEndDot({ x, y, radius })` | Dot marker path. |
|
|
153
|
-
| `connectorEndArrow({ x1, y1, x2, y2, size })` | Arrow marker path. |
|
|
161
|
+
| `connectorEndArrow({ x1, y1, x2, y2, size, spread })` | Arrow marker path. `x1,y1`→`x2,y2` sets the direction (the head sits at `x2,y2`); `spread` is the half-angle (default 0.32 ≈ a crisp 37° head). |
|
|
154
162
|
| `annotationParts(options)` | Convenience object with `transform`, `subject`, `connector`, and `note`. |
|
|
155
163
|
|
|
156
164
|
`declutterLabels` is a deliberately small, deterministic **1-D** declutter for
|
|
@@ -193,6 +201,119 @@ back to another side or a clamped note transform. It is not a collision solver
|
|
|
193
201
|
for a whole chart. For dense annotation sets, pre-compute positions or author a
|
|
194
202
|
mobile-specific SVG.
|
|
195
203
|
|
|
204
|
+
### Using the helpers in a static, no-JS report
|
|
205
|
+
|
|
206
|
+
The [report layer](./reporting.md) is static and ships no behavior JS, but these
|
|
207
|
+
helpers are JS — so in a hand- or LLM-authored report you can't call them at
|
|
208
|
+
render time. Bridge the gap by running them **once, at author/build time**, and
|
|
209
|
+
pasting the returned strings straight into the SVG. The output is deterministic
|
|
210
|
+
(path numbers are rounded to three decimals), so the strings are stable and
|
|
211
|
+
diff-friendly.
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
// author-time only — copy the logged strings into the static HTML
|
|
215
|
+
import { circleSubjectPath, connectorLine } from '@ponchia/ui/annotations';
|
|
216
|
+
|
|
217
|
+
circleSubjectPath({ radius: 15 });
|
|
218
|
+
// "M0,-15A15,15 0 1 1 0,15A15,15 0 1 1 0,-15Z"
|
|
219
|
+
connectorLine({ dx: 78, dy: -38, subject: { type: 'circle', radius: 15, radiusPadding: 0 } });
|
|
220
|
+
// "M13.485,-6.57L78,-38"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
The static markup then carries only the resolved strings — no runtime, no
|
|
224
|
+
import:
|
|
225
|
+
|
|
226
|
+
```html
|
|
227
|
+
<g class="ui-annotation ui-annotation--circle ui-annotation--accent" transform="translate(34, 58)">
|
|
228
|
+
<path class="ui-annotation__subject" d="M0,-15A15,15 0 1 1 0,15A15,15 0 1 1 0,-15Z" />
|
|
229
|
+
<path class="ui-annotation__connector" d="M13.485,-6.57L78,-38" />
|
|
230
|
+
<g class="ui-annotation__note" transform="translate(78, -38)">…</g>
|
|
231
|
+
</g>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The same author-time-copy idea covers foreign-renderer theme literals: a
|
|
235
|
+
`file://` report can't `import` the Vega/Mermaid/D2 theme helpers either, so
|
|
236
|
+
`npm run emit:theme <vega|mermaid|d2> <light|dark>` prints the resolved object to
|
|
237
|
+
paste inline, and `npm run emit:theme:check <file>` re-checks a pasted block
|
|
238
|
+
against current tokens. See [vega.md](./vega.md#from-a-cdn-no-bundler).
|
|
239
|
+
|
|
240
|
+
## Using annotations off-chart
|
|
241
|
+
|
|
242
|
+
Annotations are not only for charts. Two report uses worth calling out:
|
|
243
|
+
|
|
244
|
+
- **A decorative margin mark.** A small `ui-annotation` group — a circled point
|
|
245
|
+
with a short note — adds a hand-annotated feel to a report cover or section
|
|
246
|
+
opener. It carries no data, so mark the whole SVG `aria-hidden="true"` and
|
|
247
|
+
`focusable="false"`: a screen reader should not read decoration.
|
|
248
|
+
|
|
249
|
+
```html
|
|
250
|
+
<svg width="440" height="92" viewBox="0 0 440 92" aria-hidden="true" focusable="false">
|
|
251
|
+
<g class="ui-annotation ui-annotation--circle ui-annotation--accent" transform="translate(34, 58)">
|
|
252
|
+
<path class="ui-annotation__subject" d="M0,-15A15,15 0 1 1 0,15A15,15 0 1 1 0,-15Z" />
|
|
253
|
+
<circle r="3.5" fill="var(--accent)" />
|
|
254
|
+
<path class="ui-annotation__connector" d="M13.485,-6.57L78,-38" />
|
|
255
|
+
<g class="ui-annotation__note" transform="translate(78, -38)">
|
|
256
|
+
<path class="ui-annotation__note-line" d="M0,0H188" />
|
|
257
|
+
<text class="ui-annotation__title" y="-8">You are here</text>
|
|
258
|
+
<text class="ui-annotation__label" y="12">a short, terse label</text>
|
|
259
|
+
</g>
|
|
260
|
+
</g>
|
|
261
|
+
</svg>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
- **A data note that sits off the plot area, statically (no JS).** When the
|
|
265
|
+
callout carries a *finding* — a threshold, a labelled event — rather than
|
|
266
|
+
decoration, it must **not** be `aria-hidden`; instead mirror its text in the
|
|
267
|
+
figure's `<desc>` and the fallback table so the data reaches every reader. The
|
|
268
|
+
JS `notePlacement()` helper computes this offset for you, but for a frozen
|
|
269
|
+
`file://`/print-bound report you place it by hand: reserve a band at the top of
|
|
270
|
+
the `viewBox` for the note (don't draw bars into it), keep the subject (the
|
|
271
|
+
rule/point) on the plot, and translate the `__note` group **up, above the
|
|
272
|
+
plot**, so the connector points off the plotting area. Author the `viewBox` near
|
|
273
|
+
the rendered pixel size to keep text near 1× (see the user-unit trap below):
|
|
274
|
+
|
|
275
|
+
```html
|
|
276
|
+
<svg viewBox="0 0 360 180" role="img" aria-labelledby="fig-t fig-d" style="max-inline-size: 540px; width: 100%">
|
|
277
|
+
<title id="fig-t">Write success rate over the incident day</title>
|
|
278
|
+
<!-- The note text is repeated here so it is not JS- or sight-only. -->
|
|
279
|
+
<desc id="fig-d">…dashed line marks the 99.9% SLO floor; the off-plot callout reads "SLO floor 99.9% — below floor 41 min".</desc>
|
|
280
|
+
|
|
281
|
+
<line x1="36" y1="150" x2="336" y2="150" stroke="var(--line)" /><!-- axis -->
|
|
282
|
+
<!-- bars drawn only below y≈40, leaving the top band free for the note -->
|
|
283
|
+
|
|
284
|
+
<!-- subject rule ON the plot; note translated UP, off the plotting area -->
|
|
285
|
+
<g class="ui-annotation ui-annotation--threshold ui-annotation--danger" transform="translate(0, 44)">
|
|
286
|
+
<path class="ui-annotation__subject" d="M36,0L336,0" stroke-dasharray="4 4" />
|
|
287
|
+
<path class="ui-annotation__connector" d="M180,0L150,-30" />
|
|
288
|
+
<g class="ui-annotation__note" transform="translate(40, -34)">
|
|
289
|
+
<path class="ui-annotation__note-line" d="M0,0H150" />
|
|
290
|
+
<text class="ui-annotation__title" y="-8">SLO floor 99.9%</text>
|
|
291
|
+
<text class="ui-annotation__label" y="12">Below floor 41 min</text>
|
|
292
|
+
</g>
|
|
293
|
+
</g>
|
|
294
|
+
</svg>
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
- **Bracketing a passage of prose belongs to marks, not here.** To bracket a
|
|
298
|
+
sentence or paragraph in running text, use `.ui-bracket-note` from the
|
|
299
|
+
[marks layer](./marks.md) — it is the prose analogue of
|
|
300
|
+
`ui-annotation--bracket`. SVG annotations are for SVG figures.
|
|
301
|
+
|
|
302
|
+
## Sizing: the user-unit trap
|
|
303
|
+
|
|
304
|
+
Annotation text (`__title`, `__label`) is sized in **SVG user units**, so it
|
|
305
|
+
scales with the figure. A 360-unit-wide chart stretched across a full report
|
|
306
|
+
column is scaled roughly 2.5–3×, and the callout text scales with it — long
|
|
307
|
+
notes turn huge and overflow the `viewBox` (SVG text is clipped, not wrapped).
|
|
308
|
+
Two rules keep callouts readable:
|
|
309
|
+
|
|
310
|
+
- **Keep note text terse** — a title and a few words, like the recipe examples
|
|
311
|
+
(`Peak`, `Limit`, `80 kB cap`). Push the full sentence into the figure caption,
|
|
312
|
+
the `<desc>`, or the fallback table.
|
|
313
|
+
- **Constrain the figure width** so the user-unit → pixel scale stays near
|
|
314
|
+
1–1.5×: set a `max-inline-size` on the SVG instead of letting it stretch to the
|
|
315
|
+
whole column, or author the `viewBox` at roughly the rendered pixel size.
|
|
316
|
+
|
|
196
317
|
## Density and responsive rules
|
|
197
318
|
|
|
198
319
|
Annotations are strongest when they explain the few things a reader would miss.
|
|
@@ -294,8 +415,8 @@ stable references, threshold annotations for limits, circle/rect subjects for
|
|
|
294
415
|
specific data, and badge markers for compact index points.
|
|
295
416
|
|
|
296
417
|
```html
|
|
297
|
-
<figure class="ui-report__figure ui-
|
|
298
|
-
<figcaption id="annotated-chart" class="ui-
|
|
418
|
+
<figure class="ui-report__figure ui-print-exact" role="group" aria-labelledby="annotated-chart">
|
|
419
|
+
<figcaption id="annotated-chart" class="ui-report__caption">
|
|
299
420
|
Fig 2 - Weekly throughput, annotated at the peak
|
|
300
421
|
</figcaption>
|
|
301
422
|
<svg viewBox="0 0 360 160" role="img" aria-labelledby="throughput-title throughput-desc">
|
|
@@ -328,14 +449,12 @@ specific data, and badge markers for compact index points.
|
|
|
328
449
|
</g>
|
|
329
450
|
</g>
|
|
330
451
|
</svg>
|
|
331
|
-
<div class="ui-
|
|
332
|
-
<
|
|
333
|
-
<
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
</table>
|
|
338
|
-
</div>
|
|
452
|
+
<div class="ui-table-wrap">
|
|
453
|
+
<table class="ui-table ui-table--dense">
|
|
454
|
+
<caption>Annotated chart source data</caption>
|
|
455
|
+
<thead><tr><th>Week</th><th class="is-num">Hours</th></tr></thead>
|
|
456
|
+
<tbody><tr><td>Week 4</td><td class="is-num">18</td></tr></tbody>
|
|
457
|
+
</table>
|
|
339
458
|
</div>
|
|
340
459
|
</figure>
|
|
341
460
|
```
|
package/docs/architecture.md
CHANGED
|
@@ -61,7 +61,7 @@ on top of the CSS, none of which require a framework commitment**:
|
|
|
61
61
|
not in three places (the two CSS dark blocks are now identical by
|
|
62
62
|
construction), resolving the duplication ADR-0003 flagged. The CSS-only
|
|
63
63
|
presets (density / contrast / OLED) stay hand-authored below a marker and are
|
|
64
|
-
preserved across regeneration. `scripts/check-
|
|
64
|
+
preserved across regeneration. `scripts/check-fresh.mjs` fails CI if
|
|
65
65
|
`css/tokens.css` drifts from the model.
|
|
66
66
|
- **classes/** — `cls` is the flat registry; recipes only emit from it;
|
|
67
67
|
`scripts/check-classes.mjs` enforces a bidirectional match with the
|
|
@@ -79,6 +79,51 @@ on top of the CSS, none of which require a framework commitment**:
|
|
|
79
79
|
- **react/** / **solid/** / **qwik/** — optional lifecycle adapters over `behaviors/`.
|
|
80
80
|
They do not define markup, own state, or fork behavior logic; they only run
|
|
81
81
|
the vanilla initializers on mount and cleanup on unmount/dispose.
|
|
82
|
+
- **`css/analytical.css` — the analytical roll-up.** This convenience file
|
|
83
|
+
`@import`s exactly **seven** analytical-figure leaves: `annotations`,
|
|
84
|
+
`legend`, `marks`, `connectors`, `spotlight`, `crosshair`, and `selection`.
|
|
85
|
+
The adjacent opt-in leaves — `sources`, `state`, `generated`, `workbench`,
|
|
86
|
+
and `command` — are report/tooling/trust surfaces that are intentionally
|
|
87
|
+
**not** part of the analytical roll-up and must be imported individually.
|
|
88
|
+
Importing `analytical.css` does not pull in any of those five.
|
|
89
|
+
- **Root export (`.`) is CSS-only.** `exports["."]` resolves to the CSS
|
|
90
|
+
bundle (`dist/bronto.css`). It is a CSS side-effect import for CSS-aware
|
|
91
|
+
bundlers (`@import '@ponchia/ui'` in CSS, or a side-effect
|
|
92
|
+
`import '@ponchia/ui'` in Vite/Astro/SvelteKit). There is no runtime JS at
|
|
93
|
+
the package root — Node/runtime JS imports of `.` are not supported. All JS
|
|
94
|
+
entrypoints are explicit subpaths (`/behaviors`, `/classes`, `/tokens`,
|
|
95
|
+
`/glyphs`, `/react`, `/solid`, `/qwik`, `/skins`, `/charts`). This is a
|
|
96
|
+
permanent, intentional contract.
|
|
97
|
+
|
|
98
|
+
## Repository layout
|
|
99
|
+
|
|
100
|
+
The repo root mixes five kinds of directory that look alike but follow very
|
|
101
|
+
different rules. Two distinctions matter most: several are **path-frozen
|
|
102
|
+
published subpaths** — the directory name _is_ the public import specifier
|
|
103
|
+
(`@ponchia/ui/react` resolves to `./react/`), so they cannot be moved or
|
|
104
|
+
renamed — and several are **generated** and must never be hand-edited (a
|
|
105
|
+
generator overwrites them and a drift gate fails CI).
|
|
106
|
+
|
|
107
|
+
| Path | Kind | Edit here? | Notes |
|
|
108
|
+
| --- | --- | --- | --- |
|
|
109
|
+
| `css/` | source | yes | The framework. Hand-authored `@layer bronto` CSS. (`css/tokens.css` palette blocks and `css/generated.css` are generated — see below.) |
|
|
110
|
+
| `tokens/index.js` | source | yes | The single source of truth for token **values** (`cssVars`). |
|
|
111
|
+
| `classes/index.js`, `behaviors/`, `annotations/`, `connectors/`, `react/`, `solid/`, `qwik/`, `glyphs/`, `shiki/` | source · published-subpath (path-frozen) | yes — but **do not move** | Authored ESM shipped as-is; the dir name is the public import path. The `.d.ts` beside them are generated/drift-checked: `connectors`/`annotations`/`react`/`solid`/`qwik`/`behaviors` are emitted from JSDoc by `tsc` (`npm run dts:emit`), `classes`/`tokens`/`glyphs` from the runtime. No leaf `.d.ts` is hand-maintained. |
|
|
112
|
+
| `dist/` | generated | no | Build of `css/` (`npm run dist:build`); byte-checked by `check:dist`. |
|
|
113
|
+
| `tokens/index.json`, `tokens/resolved.json`, `tokens/tokens.dtcg.json`, `tokens/charts.json`, `classes/index.d.ts`, `tokens/index.d.ts`, `tokens/{skins,charts}.d.ts`, `glyphs/glyphs.d.ts`, `classes/vscode.css-custom-data.json`, `docs/reference.md` | generated | no | Committed build artifacts; regenerate with `npm run prepack`, never hand-edit. Drift-checked in `npm run check`. |
|
|
114
|
+
| `fonts/` | vendored | — | The Doto webfont (woff2) + its OFL license. |
|
|
115
|
+
| `scripts/` | tooling | yes | `gen-*` regenerate artifacts, `check-*` are the drift/contract gates wired into `npm run check`, plus `build-dist`, `serve`, `size-report`. |
|
|
116
|
+
| `docs/` | source (mostly) | yes | Hand-authored docs + ADRs; the curated subset in `package.json` `files` ships in the tarball. `docs/reference.md` is generated. |
|
|
117
|
+
| `demo/`, `test/`, `examples/` | fixtures | yes | The self-driving demo/showcase, the unit + Playwright e2e suite, and consumer example apps built against the packed tarball. |
|
|
118
|
+
| `.github/`, `*.config.mjs`, `.prettierrc`, `.stylelintrc.json`, `tsconfig.json`, `.editorconfig` | config | yes | CI workflows and tool config. |
|
|
119
|
+
| `package.json`, `llms.txt`, `CHANGELOG.md`, `MIGRATIONS.json`, `README.md`, `CONTRIBUTING.md`, `ROADMAP.md`, `LICENSE` | meta | yes | Manifest, the agent entrypoint, the curated changelog, the rename map, and project docs. |
|
|
120
|
+
|
|
121
|
+
The **path-frozen** dirs are the cost of zero-build, path-stable publishing:
|
|
122
|
+
`files` map 1:1 to published paths and the consumer's own bundler tree-shakes
|
|
123
|
+
the ESM, so there is no `src/` indirection (and no JS bundler — see the
|
|
124
|
+
distribution decision below). **Generated** files are regenerated from their
|
|
125
|
+
source and policed by a drift gate — edit the source, run the generator, commit
|
|
126
|
+
the result.
|
|
82
127
|
|
|
83
128
|
## Drift control
|
|
84
129
|
|
|
@@ -89,12 +134,10 @@ gating" below), so a version that fails any invariant never reaches npm.
|
|
|
89
134
|
| Invariant | Enforced by |
|
|
90
135
|
| ----------------------------------------------- | ------------------- |
|
|
91
136
|
| exports / import graph / `files` consistent | `check-exports.mjs` |
|
|
92
|
-
| `tokens.css`
|
|
137
|
+
| pure generated mirrors fresh — `tokens.css`/`index.json`, `dtcg.json`, `resolved.json`, `classes`/`tokens` `.d.ts`, `reference.md`, vscode data — each byte-equal to its generator (registry: `scripts/lib/artifacts.mjs`) | `check-fresh.mjs` |
|
|
93
138
|
| `classes` `cls` ⇄ `.ui-*` selectors | `check-classes.mjs` |
|
|
94
|
-
| `
|
|
95
|
-
| `annotations`/`connectors` hand-written `.d.ts` ⇄ exports | `check-helpers-dts.mjs` |
|
|
139
|
+
| `connectors`/`annotations`/`react`/`solid`/`qwik`/`behaviors` `.d.ts` (+ maps) == fresh `tsc` emit of their JSDoc | `check-dts-emit.mjs` |
|
|
96
140
|
| legend swatch colours ⊆ `charts.js` · opt-in | `check-legend.mjs` |
|
|
97
|
-
| `tokens.dtcg.json` ⇄ token model | `check-dtcg.mjs` |
|
|
98
141
|
| color tokens tiered · no raw chromatic color in components | `check-color-policy.mjs` |
|
|
99
142
|
| `css/skins.css` ⇄ `tokens/skins.js` · colorways opt-in | `check-skins.mjs` |
|
|
100
143
|
| every shipped colorway accent meets its WCAG floor | `check-contrast.mjs` |
|
|
@@ -116,7 +159,7 @@ payload contract, raised only intentionally with a CHANGELOG note.
|
|
|
116
159
|
`test/types.test-d.ts`, whose `@ts-expect-error`s would fail to compile
|
|
117
160
|
if the generated literal `cls`/token types stopped rejecting typos —
|
|
118
161
|
so the *value* of the generated `.d.ts` is itself gated, not just their
|
|
119
|
-
freshness (`check-
|
|
162
|
+
freshness (`check-fresh`).
|
|
120
163
|
|
|
121
164
|
## Release gating
|
|
122
165
|
|
package/docs/bullet.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Bullet
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/bullet.css` is an opt-in **bullet graph** — Stephen Few's
|
|
4
|
+
compact "is this measure inside its qualitative budget, and how does it compare
|
|
5
|
+
to target?" figure. A thin measure bar sits over 2–3 grayscale qualitative range
|
|
6
|
+
bands, with a perpendicular target tick. It is the canonical SLO / error-budget
|
|
7
|
+
figure that `ui-meter` (a single label|bar|value reading) structurally cannot
|
|
8
|
+
encode — a bullet carries the bands and the target too. Few designed it grayscale
|
|
9
|
+
on purpose, which is exactly the Nothing palette.
|
|
10
|
+
|
|
11
|
+
```css
|
|
12
|
+
@import '@ponchia/ui';
|
|
13
|
+
@import '@ponchia/ui/css/bullet.css';
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## How it behaves
|
|
17
|
+
|
|
18
|
+
- **Bands** are a grayscale gradient with hard stops at `--band-lo` and
|
|
19
|
+
`--band-hi` — the qualitative ranges (e.g. poor / ok / good).
|
|
20
|
+
- **The measure** is a thin dark bar whose length is the normalised reading `--v`.
|
|
21
|
+
- **The target** is a full-height tick at `--t`; shape (not colour) distinguishes
|
|
22
|
+
it from the measure, per Few's grayscale rule.
|
|
23
|
+
|
|
24
|
+
## Wiring — the host normalises every value to 0..1
|
|
25
|
+
|
|
26
|
+
Identical contract to `ui-spark`: Bronto paints geometry, the host does the
|
|
27
|
+
arithmetic. Set the normalised values as custom properties; the figure refuses
|
|
28
|
+
raw values and scale computation.
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<figure>
|
|
32
|
+
<div
|
|
33
|
+
class="ui-bullet"
|
|
34
|
+
style="--band-lo: 0.6; --band-hi: 0.85"
|
|
35
|
+
role="img"
|
|
36
|
+
aria-label="uptime 99.62%, below the 99.9% target, in the warning band"
|
|
37
|
+
>
|
|
38
|
+
<div class="ui-bullet__measure ui-bullet__measure--neg" style="--v: 0.62"></div>
|
|
39
|
+
<div class="ui-bullet__target" style="--t: 0.9"></div>
|
|
40
|
+
</div>
|
|
41
|
+
<figcaption class="ui-bullet__label"><span>Uptime</span><span>99.62%</span></figcaption>
|
|
42
|
+
</figure>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The `.ui-bullet__label` row is optional — use it, a `<figcaption>`, or your own
|
|
46
|
+
caption. Either way the **reading lives in text**, never in colour alone.
|
|
47
|
+
|
|
48
|
+
## Class reference
|
|
49
|
+
|
|
50
|
+
| Class | Role |
|
|
51
|
+
| ---------------------------- | ------------------------------------------------------------ |
|
|
52
|
+
| `.ui-bullet` | The track + qualitative range bands (`--b1` / `--b2`). |
|
|
53
|
+
| `.ui-bullet__measure` | The measure bar; length is `--v` (0..1). |
|
|
54
|
+
| `.ui-bullet__measure--accent`| Paint the measure in the rationed accent (emphasis). |
|
|
55
|
+
| `.ui-bullet__measure--pos` | Paint the measure in the success tone. |
|
|
56
|
+
| `.ui-bullet__measure--neg` | Paint the measure in the danger tone. |
|
|
57
|
+
| `.ui-bullet__target` | The full-height target tick at `--t` (0..1). |
|
|
58
|
+
| `.ui-bullet__label` | Optional caption row (label + reading, in mono). |
|
|
59
|
+
|
|
60
|
+
| Custom property | On | Meaning |
|
|
61
|
+
| --------------- | --------------------- | -------------------------------------------------------- |
|
|
62
|
+
| `--v` | `.ui-bullet__measure` | **Required.** Normalised measure (0..1). |
|
|
63
|
+
| `--t` | `.ui-bullet__target` | Normalised target position (0..1); omit to drop the tick.|
|
|
64
|
+
| `--band-lo` | `.ui-bullet` | Lower band boundary (0..1, default `0.5`). |
|
|
65
|
+
| `--band-hi` | `.ui-bullet` | Upper band boundary (0..1, default `0.8`, ≥ `--band-lo`).|
|
|
66
|
+
|
|
67
|
+
## Accessibility & robustness
|
|
68
|
+
|
|
69
|
+
- A bare bullet is **opaque to assistive tech**, so `.ui-bullet` MUST carry a
|
|
70
|
+
host-written `role="img"` + `aria-label` that states the reading, the target,
|
|
71
|
+
and the band — colour is never the only channel (WCAG 1.4.1).
|
|
72
|
+
- Under `forced-colors` the band gradient flattens, so the track gains a border
|
|
73
|
+
and the marks repaint in the system text colour; the band meaning is carried by
|
|
74
|
+
the required label.
|
|
75
|
+
- Bands + marks are `print-color-adjust: exact`, so the figure survives the print
|
|
76
|
+
economy.
|
|
77
|
+
- Relationship to `ui-meter` (report.css): a meter is a labelled progress reading;
|
|
78
|
+
reach for a bullet when you also need a target and qualitative bands.
|
package/docs/code.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Code
|
|
2
|
+
|
|
3
|
+
`@ponchia/ui/css/code.css` is an opt-in chrome for **fenced code** — the surface
|
|
4
|
+
for code-as-evidence in changelogs, version history, config snippets, and
|
|
5
|
+
generated reports. It paints the frame, an optional line-number gutter, and
|
|
6
|
+
add / remove / highlight line states. Its sibling is [diff.css](diff.md)
|
|
7
|
+
(multi-column line/row diffs); the two share one change vocabulary —
|
|
8
|
+
`--add` / `--remove` (`ui-code__line--remove` ↔ `ui-diff__row--remove`).
|
|
9
|
+
|
|
10
|
+
```css
|
|
11
|
+
@import '@ponchia/ui';
|
|
12
|
+
@import '@ponchia/ui/css/code.css';
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Boundary — it never parses
|
|
16
|
+
|
|
17
|
+
Bronto owns the chrome; it does **not** tokenize or highlight syntax. The host
|
|
18
|
+
supplies the coloured token spans — hand-written, or from a real highlighter.
|
|
19
|
+
The shipped [`shiki/nothing.json`](../shiki/nothing.json) theme makes Shiki emit
|
|
20
|
+
spans with bronto token colours, so Shiki output drops straight in. A bronto
|
|
21
|
+
class that tried to parse source would cross the same line that removed the
|
|
22
|
+
chart renderer in 0.6.0.
|
|
23
|
+
|
|
24
|
+
## Markup
|
|
25
|
+
|
|
26
|
+
```html
|
|
27
|
+
<figure class="ui-code ui-code--numbered">
|
|
28
|
+
<figcaption class="ui-code__head">theme.css</figcaption>
|
|
29
|
+
<pre class="ui-code__body"><code><span class="ui-code__line">.ui-diff {</span>
|
|
30
|
+
<span class="ui-code__line ui-code__line--remove"> font-size: var(--text-sm);</span>
|
|
31
|
+
<span class="ui-code__line ui-code__line--add"> font-size: var(--text-xs);</span>
|
|
32
|
+
<span class="ui-code__line ui-code__line--hl"> line-height: 1.6;</span>
|
|
33
|
+
<span class="ui-code__line">}</span></code></pre>
|
|
34
|
+
</figure>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
- `ui-code--numbered` turns on the gutter; it numbers each `.ui-code__line` with
|
|
38
|
+
a CSS counter (no host bookkeeping).
|
|
39
|
+
- Each token span the host emits lives **inside** `.ui-code__line`; Bronto never
|
|
40
|
+
touches it.
|
|
41
|
+
- Plain text inside `.ui-code__body` (no `.ui-code__line` wrappers) renders as an
|
|
42
|
+
unnumbered code block — the line grammar is opt-in.
|
|
43
|
+
|
|
44
|
+
## Class reference
|
|
45
|
+
|
|
46
|
+
| Class | Role |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `.ui-code` | The `<figure>` frame. |
|
|
49
|
+
| `.ui-code--numbered` | Show the line-number gutter. |
|
|
50
|
+
| `.ui-code__head` | A filename / language bar (use on `<figcaption>`). |
|
|
51
|
+
| `.ui-code__body` | The `<pre>` scroll/wrap region. |
|
|
52
|
+
| `.ui-code__line` | One line — the unit numbered + state-tinted. |
|
|
53
|
+
| `.ui-code__line--add` | Added line (green wash). |
|
|
54
|
+
| `.ui-code__line--remove` | Removed line (red wash). |
|
|
55
|
+
| `.ui-code__line--hl` | Neutral highlight / call-out (accent wash, not a change). |
|
|
56
|
+
|
|
57
|
+
## Recipes
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
import { ui } from '@ponchia/ui/classes';
|
|
61
|
+
|
|
62
|
+
ui.code({ numbered: true }); // "ui-code ui-code--numbered"
|
|
63
|
+
ui.codeLine({ change: 'add' }); // "ui-code__line ui-code__line--add"
|
|
64
|
+
ui.codeLine({ change: 'hl' }); // "ui-code__line ui-code__line--hl"
|
|
65
|
+
ui.codeLine(); // "ui-code__line"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Accessibility & robustness
|
|
69
|
+
|
|
70
|
+
- **Line states never rely on colour.** In forced-colors mode each
|
|
71
|
+
add/remove/hl line gains an inline-start border, and the tints are forced through
|
|
72
|
+
`print-color-adjust: exact`.
|
|
73
|
+
- **Long lines wrap** rather than scroll, so the block prints cleanly.
|
|
74
|
+
- **Line numbers** are generated content (`user-select: none`), so copying the
|
|
75
|
+
block yields clean code with no line numbers.
|
|
76
|
+
- Keep real text in the `<code>`; don't encode meaning only in a tint.
|