@ponchia/ui 0.6.0 → 0.6.4

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 (162) hide show
  1. package/CHANGELOG.md +82 -4
  2. package/README.md +1 -1
  3. package/annotations/index.d.ts.map +1 -1
  4. package/annotations/index.js +36 -33
  5. package/behaviors/carousel.d.ts +28 -0
  6. package/behaviors/carousel.d.ts.map +1 -0
  7. package/behaviors/carousel.js +3 -0
  8. package/behaviors/combobox.d.ts +40 -0
  9. package/behaviors/combobox.d.ts.map +1 -0
  10. package/behaviors/combobox.js +71 -20
  11. package/behaviors/command.d.ts +41 -0
  12. package/behaviors/command.d.ts.map +1 -0
  13. package/behaviors/command.js +9 -0
  14. package/behaviors/connectors.d.ts +17 -0
  15. package/behaviors/connectors.d.ts.map +1 -0
  16. package/behaviors/connectors.js +3 -0
  17. package/behaviors/crosshair.d.ts +42 -0
  18. package/behaviors/crosshair.d.ts.map +1 -0
  19. package/behaviors/crosshair.js +19 -1
  20. package/behaviors/dialog.d.ts +20 -0
  21. package/behaviors/dialog.d.ts.map +1 -0
  22. package/behaviors/dialog.js +3 -0
  23. package/behaviors/disclosure.d.ts +10 -0
  24. package/behaviors/disclosure.d.ts.map +1 -0
  25. package/behaviors/disclosure.js +3 -0
  26. package/behaviors/dismissible.d.ts +10 -0
  27. package/behaviors/dismissible.d.ts.map +1 -0
  28. package/behaviors/dismissible.js +3 -0
  29. package/behaviors/forms.d.ts +27 -0
  30. package/behaviors/forms.d.ts.map +1 -0
  31. package/behaviors/forms.js +18 -5
  32. package/behaviors/glyph.d.ts +21 -0
  33. package/behaviors/glyph.d.ts.map +1 -0
  34. package/behaviors/glyph.js +82 -4
  35. package/behaviors/index.d.ts +31 -237
  36. package/behaviors/index.d.ts.map +1 -0
  37. package/behaviors/index.js +17 -0
  38. package/behaviors/inert.d.ts +20 -0
  39. package/behaviors/inert.d.ts.map +1 -0
  40. package/behaviors/inert.js +46 -0
  41. package/behaviors/internal.d.ts +25 -0
  42. package/behaviors/internal.d.ts.map +1 -0
  43. package/behaviors/internal.js +30 -1
  44. package/behaviors/legend.d.ts +35 -0
  45. package/behaviors/legend.d.ts.map +1 -0
  46. package/behaviors/legend.js +9 -0
  47. package/behaviors/menu.d.ts +16 -0
  48. package/behaviors/menu.d.ts.map +1 -0
  49. package/behaviors/menu.js +3 -0
  50. package/behaviors/modal.d.ts +41 -0
  51. package/behaviors/modal.d.ts.map +1 -0
  52. package/behaviors/modal.js +124 -0
  53. package/behaviors/popover.d.ts +28 -0
  54. package/behaviors/popover.d.ts.map +1 -0
  55. package/behaviors/popover.js +17 -17
  56. package/behaviors/spotlight.d.ts +17 -0
  57. package/behaviors/spotlight.d.ts.map +1 -0
  58. package/behaviors/spotlight.js +3 -0
  59. package/behaviors/table.d.ts +36 -0
  60. package/behaviors/table.d.ts.map +1 -0
  61. package/behaviors/table.js +48 -8
  62. package/behaviors/tabs.d.ts +20 -0
  63. package/behaviors/tabs.d.ts.map +1 -0
  64. package/behaviors/tabs.js +3 -0
  65. package/behaviors/theme.d.ts +54 -0
  66. package/behaviors/theme.d.ts.map +1 -0
  67. package/behaviors/theme.js +17 -0
  68. package/behaviors/toast.d.ts +49 -0
  69. package/behaviors/toast.d.ts.map +1 -0
  70. package/behaviors/toast.js +34 -2
  71. package/classes/classes.json +747 -15
  72. package/classes/index.d.ts +118 -3
  73. package/classes/index.js +264 -66
  74. package/connectors/index.d.ts +12 -0
  75. package/connectors/index.d.ts.map +1 -1
  76. package/connectors/index.js +23 -2
  77. package/css/app.css +26 -0
  78. package/css/bullet.css +108 -0
  79. package/css/code.css +98 -0
  80. package/css/content.css +15 -2
  81. package/css/crosshair.css +7 -7
  82. package/css/diff.css +153 -0
  83. package/css/disclosure.css +18 -4
  84. package/css/dots.css +246 -9
  85. package/css/feedback.css +39 -7
  86. package/css/forms.css +71 -3
  87. package/css/legend.css +5 -2
  88. package/css/motion.css +79 -14
  89. package/css/overlay.css +59 -2
  90. package/css/primitives.css +67 -8
  91. package/css/report.css +43 -4
  92. package/css/sidenote.css +67 -0
  93. package/css/skins.css +9 -0
  94. package/css/spark.css +76 -0
  95. package/css/table.css +16 -3
  96. package/css/term.css +110 -0
  97. package/css/textref.css +63 -0
  98. package/css/toc.css +91 -0
  99. package/css/tokens.css +14 -1
  100. package/css/tree.css +134 -0
  101. package/dist/bronto.css +1 -1
  102. package/dist/css/analytical.css +1 -1
  103. package/dist/css/app.css +1 -1
  104. package/dist/css/bullet.css +1 -0
  105. package/dist/css/code.css +1 -0
  106. package/dist/css/content.css +1 -1
  107. package/dist/css/crosshair.css +1 -1
  108. package/dist/css/diff.css +1 -0
  109. package/dist/css/disclosure.css +1 -1
  110. package/dist/css/dots.css +1 -1
  111. package/dist/css/feedback.css +1 -1
  112. package/dist/css/forms.css +1 -1
  113. package/dist/css/legend.css +1 -1
  114. package/dist/css/motion.css +1 -1
  115. package/dist/css/overlay.css +1 -1
  116. package/dist/css/primitives.css +1 -1
  117. package/dist/css/report.css +1 -1
  118. package/dist/css/sidenote.css +1 -0
  119. package/dist/css/skins.css +1 -1
  120. package/dist/css/spark.css +1 -0
  121. package/dist/css/table.css +1 -1
  122. package/dist/css/term.css +1 -0
  123. package/dist/css/textref.css +1 -0
  124. package/dist/css/toc.css +1 -0
  125. package/dist/css/tokens.css +1 -1
  126. package/dist/css/tree.css +1 -0
  127. package/docs/annotations.md +39 -0
  128. package/docs/architecture.md +2 -3
  129. package/docs/bullet.md +78 -0
  130. package/docs/code.md +76 -0
  131. package/docs/d2.md +4 -3
  132. package/docs/diff.md +146 -0
  133. package/docs/dots.md +146 -0
  134. package/docs/glyphs.md +114 -0
  135. package/docs/legends.md +8 -4
  136. package/docs/mermaid.md +21 -4
  137. package/docs/reference.md +168 -8
  138. package/docs/reporting.md +49 -17
  139. package/docs/sidenote.md +64 -0
  140. package/docs/spark.md +78 -0
  141. package/docs/stability.md +1 -0
  142. package/docs/term.md +81 -0
  143. package/docs/textref.md +78 -0
  144. package/docs/theming.md +44 -5
  145. package/docs/toc.md +83 -0
  146. package/docs/tree.md +74 -0
  147. package/docs/usage.md +264 -23
  148. package/docs/vega.md +22 -3
  149. package/glyphs/glyphs.d.ts +61 -0
  150. package/glyphs/glyphs.js +600 -31
  151. package/llms.txt +169 -15
  152. package/package.json +51 -7
  153. package/qwik/index.d.ts +4 -2
  154. package/qwik/index.d.ts.map +1 -1
  155. package/qwik/index.js +10 -0
  156. package/react/index.d.ts +4 -2
  157. package/react/index.d.ts.map +1 -1
  158. package/react/index.js +6 -0
  159. package/solid/index.d.ts +6 -2
  160. package/solid/index.d.ts.map +1 -1
  161. package/solid/index.js +6 -0
  162. package/tokens/skins.js +22 -9
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.
package/docs/d2.md CHANGED
@@ -43,9 +43,10 @@ For a build step or non-JS host, read `@ponchia/ui/d2.json` directly.
43
43
  > [Vega](./vega.md#from-a-cdn-no-bundler) and [Mermaid](./mermaid.md#theme-a-diagram).
44
44
  > This is rarely an issue for D2, whose native path is **build-time
45
45
  > pre-rendering** to a frozen SVG (no client runtime — see below), but if you do
46
- > theme D2 in the browser over `file://`, **inline** the slot map (paste the
47
- > object from `d2.json`) rather than importing it. Over `http(s)` the
48
- > `import`/`fetch` forms both work.
46
+ > theme D2 in the browser over `file://`, **inline** the slot map (generate the
47
+ > paste-ready literal with `npm run emit:theme d2 light` / `dark`, and guard it
48
+ > against token drift with `npm run emit:theme:check <file>`) rather than
49
+ > importing it. Over `http(s)` the `import`/`fetch` forms both work.
49
50
 
50
51
  ### Why resolved colours, not `var(--x)`
51
52
 
package/docs/diff.md ADDED
@@ -0,0 +1,146 @@
1
+ # Diff
2
+
3
+ `@ponchia/ui/css/diff.css` is an opt-in change-review grammar — the surface for
4
+ showing what **changed**: code review, changelogs, version history, config
5
+ diffs, and generated reports. Marks call out a sentence; diff calls out a line.
6
+ Its sibling is [code.css](code.md) (single-file fenced code); the two share one
7
+ change vocabulary — `--add` / `--remove` (`ui-diff__row--remove` ↔
8
+ `ui-code__line--remove`).
9
+
10
+ ```css
11
+ @import '@ponchia/ui';
12
+ @import '@ponchia/ui/css/diff.css';
13
+ ```
14
+
15
+ ```html
16
+ <!-- node_modules / CDN: source css/ → built dist/css/ -->
17
+ <link rel="stylesheet" href="./node_modules/@ponchia/ui/dist/css/diff.css" />
18
+ ```
19
+
20
+ ## Boundary — what Bronto owns vs. what you own
21
+
22
+ Bronto paints the gutter grammar. **You** pre-classify each row (`add` /
23
+ `remove` / `context`), compute the hunks, and align the two sides in split
24
+ view. Bronto never parses or diffs source — that needs your tokenizer and your
25
+ alignment, the same line that removed the local chart renderer in `0.6.0`. Feed
26
+ it rows that are already classified by your diff engine (`jsdiff`, `git`, a
27
+ language server, …).
28
+
29
+ ## Unified view — `.ui-diff`
30
+
31
+ Rows are direct children of `.ui-diff` (optionally grouped in a
32
+ `.ui-diff__hunk`). Columns are `[old-ln] [new-ln] [code]`. Mark each changed row
33
+ `--add` / `--remove`, and leave unchanged rows `--context`. Line numbers are
34
+ decorative — keep them `aria-hidden`; the `__code` cell carries the content.
35
+
36
+ ```html
37
+ <div class="ui-diff">
38
+ <div class="ui-diff__hunk">
39
+ <div class="ui-diff__head">@@ -12,6 +12,6 @@ .ui-diff</div>
40
+
41
+ <div class="ui-diff__row ui-diff__row--context">
42
+ <span class="ui-diff__ln" aria-hidden="true">12</span>
43
+ <span class="ui-diff__ln" aria-hidden="true">12</span>
44
+ <code class="ui-diff__code"> border: 1px solid var(--line);</code>
45
+ </div>
46
+
47
+ <div class="ui-diff__row ui-diff__row--remove">
48
+ <span class="ui-diff__ln" aria-hidden="true">13</span>
49
+ <span class="ui-diff__ln" aria-hidden="true"></span>
50
+ <code class="ui-diff__code"> font-size: var(--text-sm);</code>
51
+ </div>
52
+
53
+ <div class="ui-diff__row ui-diff__row--add">
54
+ <span class="ui-diff__ln" aria-hidden="true"></span>
55
+ <span class="ui-diff__ln" aria-hidden="true">13</span>
56
+ <code class="ui-diff__code"> font-size: var(--text-xs);</code>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ ```
61
+
62
+ A removed row leaves the **new** line-number cell empty; an added row leaves the
63
+ **old** one empty. The `+` / `−` gutter glyph is painted for you from the row
64
+ modifier.
65
+
66
+ ## Split view — `.ui-diff--split`
67
+
68
+ `.ui-diff--split` lays two `.ui-diff__pane` columns side by side (old | new),
69
+ each its own `[ln] [code]` grid. Put `--remove` / `--context` rows in the left
70
+ pane and `--add` / `--context` rows in the right. You align the panes by
71
+ emitting matching row counts (filler `--context` rows where one side is empty).
72
+
73
+ ```html
74
+ <div class="ui-diff ui-diff--split">
75
+ <div class="ui-diff__pane">
76
+ <div class="ui-diff__row ui-diff__row--context">
77
+ <span class="ui-diff__ln" aria-hidden="true">1</span>
78
+ <code class="ui-diff__code">retries: 3</code>
79
+ </div>
80
+ <div class="ui-diff__row ui-diff__row--remove">
81
+ <span class="ui-diff__ln" aria-hidden="true">2</span>
82
+ <code class="ui-diff__code">timeout: 30</code>
83
+ </div>
84
+ </div>
85
+ <div class="ui-diff__pane">
86
+ <div class="ui-diff__row ui-diff__row--context">
87
+ <span class="ui-diff__ln" aria-hidden="true">1</span>
88
+ <code class="ui-diff__code">retries: 3</code>
89
+ </div>
90
+ <div class="ui-diff__row ui-diff__row--add">
91
+ <span class="ui-diff__ln" aria-hidden="true">2</span>
92
+ <code class="ui-diff__code">timeout: 10</code>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ ```
97
+
98
+ For a hard before/after of two whole files, you can also drop two plain
99
+ `.ui-diff` blocks into a [`.ui-compare--2up`](./reporting.md) layout.
100
+
101
+ ## Class reference
102
+
103
+ | Class | Role |
104
+ | --- | --- |
105
+ | `.ui-diff` | The grid container (unified view). |
106
+ | `.ui-diff--split` | Two-pane (old \| new) layout modifier. |
107
+ | `.ui-diff__pane` | One column in split view; its own `[ln] [code]` grid. |
108
+ | `.ui-diff__hunk` | Optional `role="rowgroup"` wrapper for a hunk (layout-transparent). |
109
+ | `.ui-diff__head` | A hunk / file header row, spanning all columns. |
110
+ | `.ui-diff__row` | A line row (layout-transparent; the cells are the grid items). |
111
+ | `.ui-diff__row--add` | Added line — green tint + `+` gutter glyph. |
112
+ | `.ui-diff__row--remove` | Removed line — red tint + `−` gutter glyph. |
113
+ | `.ui-diff__row--context` | Unchanged line — no tint, blank gutter. |
114
+ | `.ui-diff__ln` | A line-number gutter cell (`tabular-nums`, `user-select:none`; keep it `aria-hidden`). |
115
+ | `.ui-diff__code` | A code cell. Long lines wrap (no horizontal scroll), so it prints cleanly. |
116
+
117
+ ## Recipes
118
+
119
+ ```js
120
+ import { ui } from '@ponchia/ui/classes';
121
+
122
+ ui.diff(); // "ui-diff"
123
+ ui.diff({ split: true }); // "ui-diff ui-diff--split"
124
+ ui.diffRow({ change: 'add' }); // "ui-diff__row ui-diff__row--add"
125
+ ui.diffRow({ change: 'remove' }); // "ui-diff__row ui-diff__row--remove"
126
+ ui.diffRow(); // "ui-diff__row" (use --context for an explicit unchanged row)
127
+ ```
128
+
129
+ ## Accessibility & robustness
130
+
131
+ - **Redundant channel (WCAG 1.4.1).** Add/remove is never colour-only: the
132
+ `+` / `−` gutter glyph is generated content, so it survives **forced colours**
133
+ and **print**, where the tone tint is dropped. In forced-colors mode the
134
+ changed code cell also gains an inline-start border.
135
+ - **Print.** Tints are forced through with `print-color-adjust: exact`, and long
136
+ lines wrap rather than clip — the diff survives the PDF pipeline.
137
+ - **Line numbers** are decorative: `aria-hidden` them and they stay out of a
138
+ copy selection (`user-select: none`), so copying the diff yields clean code.
139
+ - **Semantics** live in your markup. By default the surface is role-free: with
140
+ the line numbers `aria-hidden`, a screen reader reads the `__code` lines in
141
+ DOM order, which is a faithful reading of the diff. If you want grid/table
142
+ semantics, use a real `<table>` or add the full ARIA chain
143
+ (`role="table" > rowgroup > row > cell`) — a bare `role="row"` without
144
+ `role="cell"` children is an ARIA violation. If the change kind itself must be
145
+ announced, add an off-screen label per changed row; do not rely on the glyph
146
+ alone.
package/docs/dots.md ADDED
@@ -0,0 +1,146 @@
1
+ # Dot surfaces
2
+
3
+ The dot-matrix is the library's signature: one lit/dim/accent dot vocabulary
4
+ expressed across backgrounds, dividers, indicators, loaders, and a family of
5
+ data-bound reporting surfaces. Everything here lives in the core stylesheet
6
+ (`@ponchia/ui` / `@ponchia/ui/css`); the glyph icon set built on the same
7
+ primitive is documented in `docs/glyphs.md`.
8
+
9
+ All classes are in the typed registry (`@ponchia/ui/classes`) and the
10
+ language-neutral `classes.json`; those are authoritative.
11
+
12
+ ## Tokens
13
+
14
+ Density and expression are token-driven, so a surface re-skins without new CSS:
15
+
16
+ | Token | Role |
17
+ | --- | --- |
18
+ | `--field-dot` | the dim (unlit) dot |
19
+ | `--field-dot-hot` | a lit cell |
20
+ | `--field-dot-accent` | the accented lit cell |
21
+ | `--dot-gap` / `--dot-size` | `.ui-dotgrid` background density |
22
+ | `--dotmatrix-cols` / `--dotmatrix-gap` / `--dotmatrix-dot` | matrix density |
23
+ | `--dotmatrix-dot-radius` | `0` fuses dots into crisp pixels |
24
+
25
+ The Tier-3 **display-expression** knobs speak in brightness + time rather than
26
+ hue (a dot-matrix display has no decorative colour). They default to a no-op, so
27
+ the base render is unchanged; the opt-in colorways (`@ponchia/ui/css/skins.css`)
28
+ set them:
29
+
30
+ | Knob | Default | Effect |
31
+ | --- | --- | --- |
32
+ | `--dotmatrix-glow` | `0` (off) | phosphor bloom around lit cells |
33
+ | `--dotmatrix-pulse-min` | `0.55` | the floor the `--pulse` animation dips to |
34
+ | `--dotmatrix-reveal-step` | `3ms` | per-cell cadence of the `--reveal` scan |
35
+
36
+ ## Decorative surfaces
37
+
38
+ - `.ui-dotgrid` — a tiled dot-grid background (`--accent`, `--dense` modifiers).
39
+ - `.ui-dotfield` — a fixed full-bleed dot backdrop.
40
+ - `.ui-dotrule` — a dotted divider in place of a plain rule.
41
+ - `.ui-halftone` — render host content (an `<img>` or a box with its own
42
+ background) through a dot lattice, so a thumbnail takes on the dot look. A
43
+ **style filter**, not a data viz — the dots are a fixed lattice, not
44
+ value-modulated. Tune with `--halftone-dot` / `--halftone-gap`.
45
+
46
+ ## Indicators & loaders
47
+
48
+ - `.ui-dot` — a status dot with tones (`--accent/--success/--warning/--danger/--info`)
49
+ and a `--live` pulse ring; `.ui-status` is the dot + label row.
50
+ - `.ui-dotloader` — three blinking dots.
51
+ - `.ui-dotspinner` — the signature comet loader (`--sm` / `--lg`).
52
+ - `.ui-dotbar` — a segmented LED progress bar; light a segment with `is-on`,
53
+ or `--indeterminate` for the sweep.
54
+
55
+ ## Data-bound reporting surfaces
56
+
57
+ These map data onto the dot vocabulary. The boundary is the same as `ui-spark`
58
+ and `ui-meter`: **the host normalises the data** (lights `is-on`, sets
59
+ `data-level`, or writes `--v` 0..1) and the leaf only lays out + tones. None
60
+ compute a scale, bin, or threshold. Each is opaque to assistive tech, so the
61
+ container MUST carry a host-written `role="img"` + `aria-label` with the exact
62
+ value — rounding to whole cells is presentation-only, so keep the figure in the
63
+ label (WCAG 1.4.1).
64
+
65
+ ### `.ui-matrix` cell grid
66
+
67
+ `.ui-dotmatrix` is the raw data-bound grid (the one the glyphs render on): a grid
68
+ of `.ui-dotmatrix__cell` (with `--hot` / `--accent` tones) plus the `--reveal` /
69
+ `--pulse` animations. You map data → cell class.
70
+
71
+ ### `.ui-waffle` — unit / part-to-whole
72
+
73
+ An N×N field of dots ("73 of 100"). The host marks the lit cells with `is-on`.
74
+
75
+ ```html
76
+ <div class="ui-waffle" role="img" aria-label="73 of 100 quota met" style="--waffle-cols: 10">
77
+ <i class="is-on"></i><i class="is-on"></i><!-- … 73 lit, 27 dim … --><i></i>
78
+ </div>
79
+ ```
80
+
81
+ Knobs: `--waffle-cols` (default 10), `--waffle-gap`, `--waffle-size`.
82
+
83
+ ### `.ui-activity` — contribution / calendar heatmap
84
+
85
+ A GitHub-style density-over-time grid. Day cells flow down each weekday column
86
+ (`grid-auto-flow: column`); intensity is `data-level="0..4"`, a 5-step ramp the
87
+ host bins the data into.
88
+
89
+ ```html
90
+ <div class="ui-activity" role="img" aria-label="commits, last 12 weeks">
91
+ <i data-level="0"></i><i data-level="3"></i><i data-level="4"></i><!-- … -->
92
+ </div>
93
+ ```
94
+
95
+ Knobs: `--activity-rows` (default 7), `--activity-cell`, `--activity-gap`.
96
+
97
+ ### `.ui-level` — LED level / VU meter
98
+
99
+ A vertical column of discrete segments lit to a threshold (signal, load, VU). It
100
+ fills from the bottom; the host lights segments with `is-on`. `--warn` /
101
+ `--danger` re-point the lit colour for the whole meter when the host crosses a
102
+ threshold (the host owns the threshold).
103
+
104
+ ```html
105
+ <div class="ui-level ui-level--warn" role="img" aria-label="CPU 82%, high">
106
+ <i class="is-on"></i><i class="is-on"></i><i></i><!-- … --></div>
107
+ ```
108
+
109
+ Knobs: `--level-segments` count is up to your markup; `--level-height`,
110
+ `--level-size`, `--level-gap`.
111
+
112
+ ### `.ui-dotgauge` — radial dot gauge
113
+
114
+ A 0..1 reading (`--v`) drawn as a ring of dots filling along an arc.
115
+
116
+ ```html
117
+ <div class="ui-dotgauge" role="img" aria-label="Health 64%" style="--v: .64"></div>
118
+ ```
119
+
120
+ Knobs: `--v`, `--gauge-size`, `--gauge-sweep` (default 270deg), `--gauge-from`,
121
+ `--gauge-dot`.
122
+
123
+ ### `.ui-readout` — big dot-matrix numeric
124
+
125
+ The row wrapper produced by `renderReadout` (see `docs/glyphs.md`) — a Nothing-style
126
+ hero numeric composed from digit glyphs. Tune character spacing with
127
+ `--readout-gap`; spaces render as a `.ui-readout__spacer` of width `--readout-space`.
128
+
129
+ ### `.ui-spark--dots`
130
+
131
+ A modifier on the inline `ui-spark` dataword (`@ponchia/ui/css/spark.css`) that
132
+ renders each bar as a stack of dots instead of a solid bar — same `--v` contract.
133
+
134
+ ## Responsive density — `.ui-dotfit`
135
+
136
+ Wrap a dot surface in `.ui-dotfit` to make it respond to its **container** (the
137
+ card) rather than the viewport, via a container query — so the same component
138
+ reads well in a wide hero and a narrow tile without per-instance overrides.
139
+
140
+ ## Accessibility, forced colors & print
141
+
142
+ Lit/dim/accent encode meaning via background-color, which Windows High Contrast
143
+ Mode flattens — the dot surfaces opt out of forced-color remapping and pin lit
144
+ states to distinct system colours (the activity ramp collapses to present vs
145
+ absent). The data surfaces also set `print-color-adjust: exact` so their fills
146
+ survive printing. Animations honour `prefers-reduced-motion`.
package/docs/glyphs.md ADDED
@@ -0,0 +1,114 @@
1
+ # Display glyphs
2
+
3
+ `@ponchia/ui/glyphs` is a small, frozen bitmap icon set rendered on the same
4
+ `.ui-dotmatrix` dot primitive as every other dot surface (see `docs/dots.md`).
5
+ CSS-first to the core: a glyph is just a grid of dot cells — `.` off, `#` hot,
6
+ `*` accent — so it re-skins with the same `--field-dot*` tokens and the Tier-3
7
+ display knobs as the rest of the dot family. No icon font, no SVG sprite, no
8
+ runtime dependency. The module is side-effect-free and SSR-safe (`renderGlyph`
9
+ returns a string; nothing touches the DOM).
10
+
11
+ ```js
12
+ import { renderGlyph } from '@ponchia/ui/glyphs';
13
+ el.innerHTML = renderGlyph('check', { label: 'Done' });
14
+ ```
15
+
16
+ The authoritative API is the generated, CI-drift-checked `glyphs/glyphs.d.ts`.
17
+ Read it before guessing a name — `GlyphName` is a literal union, so a typo is a
18
+ type error.
19
+
20
+ ## Three render paths
21
+
22
+ A glyph can be drawn three ways; pick by size regime.
23
+
24
+ | Path | Call | DOM | When |
25
+ | --- | --- | --- | --- |
26
+ | Dot display | `renderGlyph(name)` | `GLYPH_SIZE²` cells (256) | the signature dot-matrix look at display sizes |
27
+ | Solid pixel | `renderGlyph(name, { solid: true })` | 256 cells, fused | legible crisp icon at small/inline sizes (~16–24px) |
28
+ | Mask icon | `renderGlyph(name, { render: 'mask' })` | **one** `.ui-icon` node | icon-at-scale (e.g. one per table row): inherits `currentColor`, scales with the text |
29
+
30
+ The mask path is the lightest — one node instead of 256 — and the right default
31
+ for an icon repeated many times. It is single-tone: an accent `*` cell renders
32
+ the same as a hot `#` cell (both become opaque mask regions).
33
+
34
+ > Pixel-crisp sizes: the bitmap is a 16-unit grid, so the dot/solid paths look
35
+ > sharpest at integer multiples (16/32/48/64px). The mask path scales smoothly
36
+ > with the text; arbitrary `em` sizes soften the edges slightly, which is fine
37
+ > for inline use.
38
+
39
+ ### `renderGlyph(name, options)`
40
+
41
+ | Option | Type | Default | Effect |
42
+ | --- | --- | --- | --- |
43
+ | `grid` | boolean | `true` | show the unlit panel dots; `false` → glyph-only |
44
+ | `solid` | boolean | `false` | square, gapless pixels (implies glyph-only) |
45
+ | `anim` | `'reveal' \| 'pulse'` | — | decorative animation (reduced-motion-safe) |
46
+ | `label` | string | — | expose as `role="img"` with this name; omit → decorative (`aria-hidden`) |
47
+ | `dot` | CSS length | `0.08em` | one dot size (`--dotmatrix-dot`; sanitized) |
48
+ | `gap` | CSS length | — | gap between dots (`--dotmatrix-gap`; sanitized) |
49
+ | `render` | `'mask'` | — | the one-node `.ui-icon` path |
50
+ | `size` | CSS length | `1em` | with `render: 'mask'`, the icon size (`--icon-size`) |
51
+
52
+ ## Big numeric readout — `renderReadout`
53
+
54
+ Compose digits and punctuation into a row of dot-matrix glyphs — the
55
+ Nothing-style hero numeric for a KPI, clock, countdown, or percentage.
56
+
57
+ ```js
58
+ import { renderReadout } from '@ponchia/ui/glyphs';
59
+ el.innerHTML = renderReadout('12:48', { label: '12:48 remaining' });
60
+ el.innerHTML = renderReadout('73%', { label: '73 percent of quota', render: 'mask' });
61
+ ```
62
+
63
+ Recognised characters: `0-9`, `:`, `,`, `.`, `%`, `-`, `+`, and space (a blank
64
+ advance). Anything else is skipped. Every per-glyph option (`solid`, `render`,
65
+ `dot`, `gap`, `anim`) passes through to each character; `gap` sets the spacing
66
+ between characters. The digits are decorative — the readout's **value** is its
67
+ accessible name, so pass a `label` (it defaults to the raw text). Output wraps
68
+ in `.ui-readout` (see `docs/dots.md`).
69
+
70
+ ## Finding a glyph — `findGlyphs` / `GLYPH_TAGS`
71
+
72
+ Names follow the "depict, don't name the purpose" convention (`trash`, not
73
+ `delete`). `findGlyphs(query)` resolves an intent word to real names by matching
74
+ the name OR a curated search alias (`GLYPH_TAGS`), case-insensitively:
75
+
76
+ ```js
77
+ import { findGlyphs } from '@ponchia/ui/glyphs';
78
+ findGlyphs('delete'); // → ['trash']
79
+ findGlyphs('chart'); // → ['bar-chart']
80
+ findGlyphs(''); // → every name
81
+ ```
82
+
83
+ ## DOM placeholders — `initDotGlyph`
84
+
85
+ When you would rather drop a placeholder than inline markup, the optional
86
+ `initDotGlyph` behavior (`@ponchia/ui/behaviors`) expands
87
+ `[data-bronto-glyph="name"]` in place. It is idempotent and returns a cleanup.
88
+
89
+ | Attribute | Value | Effect |
90
+ | --- | --- | --- |
91
+ | `data-bronto-glyph` | glyph name | expand into a `.ui-dotmatrix` grid |
92
+ | `data-bronto-glyph-label` | text | expose as `role="img"`; omit → decorative |
93
+ | `data-bronto-glyph-solid` | — | square, gapless pixel glyph |
94
+ | `data-bronto-glyph-anim` | `reveal \| pulse` | decorative animation |
95
+ | `data-bronto-glyph-render` | `mask` | the one-node `.ui-icon` path (not 256 cells) |
96
+ | `data-bronto-glyph-size` | CSS length | with `render="mask"`, sets `--icon-size` |
97
+
98
+ An unknown name is left untouched.
99
+
100
+ ## Accessibility
101
+
102
+ - A glyph next to a text label is redundant → keep it decorative (`aria-hidden`,
103
+ the default).
104
+ - An icon-only control labels the **control** (`aria-label` on the `<button>`),
105
+ not the glyph.
106
+ - A standalone meaningful glyph passes `label` → `role="img"` + `aria-label`.
107
+ - A cell-mode glyph is a sea of 256 nodes; the wrapper carries the single
108
+ `role="img"`/`aria-hidden`, so assistive tech never walks the cells.
109
+
110
+ ## Two-tone glyphs
111
+
112
+ The accent (`*`) tone lifts one feature of a glyph onto `--field-dot-accent`.
113
+ The curated two-tone set is `spark`, `warning`, and `info`; every other glyph is
114
+ monotone. Two-tone only shows in the cell render — the mask path is single-tone.
package/docs/legends.md CHANGED
@@ -48,7 +48,7 @@ Recommended structure: wrap the figure and its key in a `<figure>` with a
48
48
  <li class="ui-legend__item">
49
49
  <span
50
50
  class="ui-legend__swatch"
51
- style="--chart-color: var(--chart-1); --chart-pattern: var(--chart-pattern-1)"
51
+ style="--chart-color: var(--chart-3); --chart-pattern: var(--chart-pattern-3)"
52
52
  aria-hidden="true"
53
53
  ></span>
54
54
  <span class="ui-legend__label">Research</span>
@@ -90,9 +90,13 @@ chart mark uses, or with a `--N` index helper for the categorical palette.
90
90
  `ui-legend__swatch--circle` and `ui-legend__swatch--line` change the chip shape
91
91
  (dot series, line series).
92
92
 
93
- A `ui-legend__symbol` chip is an `.ui-icon` mask — it needs a `--icon-mask`
94
- (e.g. `style="--icon-mask: var(--glyph-dot)"`) or it paints a solid square, like
95
- any [icon](./reference.md). And an **interactive** legend entry must be a real
93
+ A `ui-legend__symbol` chip is an `.ui-icon` mask — it needs a `--icon-mask` or it
94
+ paints a solid square, like any [icon](./reference.md). There is **no `--glyph-*`
95
+ token**: build the mask value with
96
+ [`renderGlyph(name, { render: 'mask' })`](./reference.md) from `@ponchia/ui/glyphs`,
97
+ e.g. `el.style.setProperty('--icon-mask', renderGlyph('circle', { render: 'mask' }).match(/--icon-mask:([^"]+)/)[1])`,
98
+ or render the whole chip with `renderGlyph('circle', { render: 'mask', label })` and
99
+ add the `ui-legend__symbol` class to it. And an **interactive** legend entry must be a real
96
100
  `<button>` (as in the example below) — a non-button `ui-legend__item` carrying
97
101
  `data-series` is not keyboard-reachable.
98
102
 
package/docs/mermaid.md CHANGED
@@ -38,14 +38,31 @@ or read the raw maps (`import { mermaid } from '@ponchia/ui/mermaid'` →
38
38
  `{ light, dark }`). For a build step or non-JS host, read
39
39
  `@ponchia/ui/mermaid.json` directly.
40
40
 
41
+ **Over `http(s)`, import the helper from a CDN as an ES module** — no bundler.
42
+ `tokens/mermaid.js` (the `@ponchia/ui/mermaid` entry) has **zero dependencies**,
43
+ so it loads directly as a browser ES module; you get `brontoMermaidTheme(theme)`
44
+ itself, not a frozen object to keep in sync. Pin the package version; this needs
45
+ a real origin (not `file://` — inline or pre-render to SVG there, per the note
46
+ below):
47
+
48
+ ```html
49
+ <script type="module">
50
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
51
+ import { brontoMermaidTheme } from 'https://cdn.jsdelivr.net/npm/@ponchia/ui@VERSION/tokens/mermaid.js';
52
+ mermaid.initialize(brontoMermaidTheme('light'));
53
+ await mermaid.run();
54
+ </script>
55
+ ```
56
+
41
57
  > **file:// portability.** A report opened straight from disk (`file://`) cannot
42
58
  > `import` the `@ponchia/ui/mermaid` module **nor** `fetch('…/mermaid.json')` —
43
59
  > the browser blocks both across the `null`/file origin (CORS), exactly as with
44
60
  > [Vega](./vega.md#from-a-cdn-no-bundler). So for a double-clickable or PDF-bound
45
- > report either **inline the theme variables** (paste the object for your theme
46
- > from `mermaid.json` into the init directive) or, better, **pre-render to a
47
- > frozen SVG** with the Mermaid CLI (`mmdc`, below) so there is no runtime at
48
- > all. Over an `http(s)` origin the `import`/`fetch` forms both work.
61
+ > report either **inline the theme variables** (generate the paste-ready literal
62
+ > with `npm run emit:theme mermaid light` / `dark` guard it from token drift
63
+ > with `npm run emit:theme:check <file>`) or, better, **pre-render to a frozen
64
+ > SVG** with the Mermaid CLI (`mmdc`, below) so there is no runtime at all. Over
65
+ > an `http(s)` origin the `import`/`fetch` forms both work.
49
66
 
50
67
  The result is monochrome surfaces and lines with the rationed accent reserved
51
68
  for notes — a diagram that looks like the rest of a bronto surface and