@ponchia/ui 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/usage.md CHANGED
@@ -146,6 +146,105 @@ carousel crops (`cover`); the lightbox shows the whole image (`contain`).
146
146
  This is deliberately *not* an auto-playing marketing slider (no timers, no
147
147
  infinite-clone track). It's a gallery: the user drives it.
148
148
 
149
+ ## Display glyphs: when (and when not)
150
+
151
+ `@ponchia/ui/glyphs` is a 43-glyph dot-matrix icon set — navigation
152
+ (`arrow-*`, `chevron-*`), actions (`check`, `close`, `plus`, `minus`,
153
+ `search`, `menu`, `gear`), status (`info`, `warning`, `bell`, `lock`) and
154
+ common marks (`home`, `user`, `heart`, `star`, `spark`) — rendered on the
155
+ `.ui-dotmatrix` primitive, so they re-skin with the same `--field-dot*`
156
+ tokens as every other dot surface (no SVG, no icon font).
157
+
158
+ **Two rendering modes — pick by size.** The dots need physical room to
159
+ read, so the default _dot_ look is for **display** sizes (~40px up: hero
160
+ marks, empty states, status bursts, section headers, large buttons). For
161
+ small/inline use pass **`solid: true`** (or `data-bronto-glyph-solid`):
162
+ that fuses the cells into a square, gapless pixel glyph that stays crisp and
163
+ legible down to **~16px** — so the same set doubles as real inline UI icons,
164
+ not just decoration. (Below the dot fragments into dot-soup; solid does not.)
165
+
166
+ `renderGlyph(name, { label })` returns an SSR-safe string: decorative
167
+ (`aria-hidden`) by default, or `role="img"` + `aria-label` when you pass a
168
+ `label` — which is how it conveys meaning to assistive tech. Prefer the
169
+ `data-bronto-glyph` placeholder + `initDotGlyph()` when the markup is
170
+ easier dropped than inlined. Size with `--dotmatrix-dot` (and a tight
171
+ `--dotmatrix-gap`) for an intrinsic dot, or let it stretch to its container.
172
+ It is still a pixel-grid aesthetic, not a hairline vector set — but it now
173
+ spans both inline-icon and display use from one source.
174
+
175
+ **Animation** is opt-in via `anim` (`renderGlyph(name, { anim: 'reveal' })`)
176
+ or `data-bronto-glyph-anim`: `reveal` powers the cells on in a scan (a
177
+ dot-matrix booting up), `pulse` makes the glyph breathe for a live/attention
178
+ state. It's **decorative only** — disabled under `prefers-reduced-motion`,
179
+ and the meaning still lives in the static frame + `label`, never in the
180
+ motion. Don't animate to convey information a reduced-motion user would miss.
181
+ Tune the scan speed with `--dotmatrix-reveal-step` (delay per cell, default
182
+ `3ms`). Use `pulse` sparingly — one attention target per view: it loops
183
+ indefinitely, and animation that runs in parallel with other content is the
184
+ consumer's WCAG 2.2.2 (Pause/Stop/Hide) responsibility (reduced-motion is not
185
+ a substitute).
186
+
187
+ A few sharp edges to know:
188
+
189
+ - **Inline icon recipe.** `renderGlyph` returns a `<span>`, so it's valid
190
+ inline and inside a `<button>`. For an inline UI icon, render `solid` and
191
+ size the dot, e.g. inside a button:
192
+ `` `<button class="ui-button">${renderGlyph('search', { solid: true, dot: '1.2px', label: 'Search' })}<span>Search</span></button>` `` —
193
+ the button's `display: inline-flex; gap` aligns icon + text. For icon-in-prose,
194
+ set `--dotmatrix-dot` to ~`0.08em` and `vertical-align: -0.15em` on the span.
195
+ - **`solid` wins.** `solid: true` implies glyph-only and forces
196
+ `--dotmatrix-gap: 0` / square cells, so a `grid: true` or `gap` passed
197
+ alongside it is ignored.
198
+ - **Directional glyphs are physical, not logical.** `arrow-left/right`,
199
+ `chevron-left/right` are fixed bitmaps; in an RTL context flip them yourself
200
+ (e.g. swap the name, or `transform: scaleX(-1)`), the framework won't.
201
+ - **Cost / icon-at-scale.** The default (cell) render is a 16×16 grid = 256
202
+ cells (DOM nodes), regardless of `solid`/`grid`; `anim: 'reveal'` adds a
203
+ per-cell `--i`. That's the dot-matrix display look — but for **many** icons
204
+ (one in every row of a long table) use **`renderGlyph(name, { render: 'mask' })`**:
205
+ it returns a **single** `.ui-icon` element masked by the bitmap (one node, not
206
+ 256), sizes to `size` / `--icon-size` (default `1em`) and inherits
207
+ `currentColor` — a normal inline icon. Pick `render: 'mask'` for icons,
208
+ the cell render for display marks/animation.
209
+
210
+ ## Colorways: when to reach for a skin
211
+
212
+ `@ponchia/ui/css/skins.css` adds `data-bronto-skin="amber-crt |
213
+ phosphor-green | e-ink"` — a **root-level** colorway (apply on `<html>`, like
214
+ `data-theme`) that re-points the one accent to a different single hue.
215
+
216
+ - **Use a skin** when you want a distinct, on-brand _look_ (a phosphor/CRT or
217
+ e-ink feel) for the whole page or app — it's the supported, contrast-gated
218
+ way to recolour without leaving the design system.
219
+ - **Use a raw `--accent` override** when you just need _your_ brand hue: it's
220
+ one declaration (see "Re-brand obligations" below) — but then contrast is
221
+ yours, whereas the shipped skins are pre-gated.
222
+ - **Don't** put `data-bronto-skin` on a subtree — it's root-level by design
223
+ (the derived accent family only recomputes at `:root`); a subtree skin
224
+ no-ops. For a one-section recolour, scope a raw `--accent` override instead.
225
+ - It's **opt-in**: a separate stylesheet, never in `dist/bronto.css`. No skin
226
+ imported → zero cost. Full detail in [theming.md](theming.md) → "Display
227
+ colorways".
228
+
229
+ ## Data-viz colours: charts, not chrome
230
+
231
+ `@ponchia/ui/css/dataviz.css` (opt-in) adds a Tier-4 chart palette for
232
+ dashboards: `--chart-1..8` (categorical), `--chart-seq-*` (sequential),
233
+ `--chart-div-*` (diverging), and `--chart-pattern-1..8` (dot-matrix fills).
234
+
235
+ - **Use it for charts only.** These are not UI tokens — a build gate fails if
236
+ `var(--chart-*)` appears in component CSS. Style buttons/badges with the
237
+ accent/status tiers, not chart colours.
238
+ - **Series 1 is the accent**, so your brand leads the palette; series 2–8 are a
239
+ colourblind-safe set (gated for distinctness under protan/deutan/tritan).
240
+ - **Always pair colour with pattern** (`--chart-pattern-N`) and/or a direct
241
+ label — never colour alone (WCAG 1.4.1):
242
+ `background: var(--chart-3); background-image: var(--chart-pattern-3); background-size: var(--chart-pattern-size);`
243
+ - **In JS** (Chart.js, canvas, SVG): import resolved hex from
244
+ `@ponchia/ui/charts.json` (`{ light, dark }`, series 1 = the resolved accent).
245
+ Cap a chart at ~8 series. Full detail in [theming.md](theming.md) →
246
+ "Data-viz palette".
247
+
149
248
  ## When to add a behavior
150
249
 
151
250
  The CSS is the framework; `@ponchia/ui/behaviors` is the *sanctioned*
@@ -0,0 +1,115 @@
1
+ /** @ponchia/ui — GENERATED from glyphs/glyphs.js by scripts/gen-glyphs.mjs.
2
+ * Do not edit by hand; run `npm run glyphs:build`. Drift-checked in CI. */
3
+
4
+ /** Every display-glyph name @ponchia/ui ships (literal union). Use this as a
5
+ * type annotation to reject typos (`const n: GlyphName = 'arow'` is an error). */
6
+ export type GlyphName =
7
+ | 'arrow-down'
8
+ | 'arrow-left'
9
+ | 'arrow-right'
10
+ | 'arrow-up'
11
+ | 'bell'
12
+ | 'check'
13
+ | 'check-circle'
14
+ | 'chevron-down'
15
+ | 'chevron-left'
16
+ | 'chevron-right'
17
+ | 'chevron-up'
18
+ | 'circle'
19
+ | 'clock'
20
+ | 'close'
21
+ | 'download'
22
+ | 'edit'
23
+ | 'eye'
24
+ | 'eye-off'
25
+ | 'file'
26
+ | 'folder'
27
+ | 'gear'
28
+ | 'grid'
29
+ | 'heart'
30
+ | 'home'
31
+ | 'info'
32
+ | 'link'
33
+ | 'lock'
34
+ | 'mail'
35
+ | 'menu'
36
+ | 'minus'
37
+ | 'minus-circle'
38
+ | 'moon'
39
+ | 'more-horizontal'
40
+ | 'more-vertical'
41
+ | 'pause'
42
+ | 'play'
43
+ | 'plus'
44
+ | 'plus-circle'
45
+ | 'refresh'
46
+ | 'search'
47
+ | 'spark'
48
+ | 'star'
49
+ | 'sun'
50
+ | 'trash'
51
+ | 'upload'
52
+ | 'user'
53
+ | 'warning'
54
+ | 'x-circle';
55
+
56
+ /** A glyph name, or any string — for dynamic dispatch (a CMS/config value). The
57
+ * known names still autocomplete; an unknown name hits the runtime fallback
58
+ * (`glyph`→`undefined`, `glyphCells`→`[]`, `renderGlyph`→`''`). */
59
+ export type GlyphNameInput = GlyphName | (string & {});
60
+
61
+ /** A glyph is 16 rows of 16 chars: `.` off, `#` lit, `*` accent. */
62
+ export type Glyph = readonly string[];
63
+
64
+ /** One rendered dot. `on:false` is an unlit panel dot; tone picks the lit color. */
65
+ export interface GlyphCell {
66
+ on: boolean;
67
+ tone?: 'hot' | 'accent';
68
+ }
69
+
70
+ export interface RenderGlyphOptions {
71
+ /** Show the unlit panel dots (default true). `false` → glyph-only look. */
72
+ grid?: boolean;
73
+ /** Render lit cells as square, gapless pixels (a filled silhouette) instead
74
+ * of separated dots — the legible mode at small/inline sizes (~16–24px).
75
+ * Implies glyph-only. Default false (the dot-matrix display look). */
76
+ solid?: boolean;
77
+ /** Opt into a decorative animation (disabled under `prefers-reduced-motion`;
78
+ * meaning stays in the static frame + `label`). `'reveal'` powers the cells
79
+ * on in a scan; `'pulse'` makes the glyph breathe. */
80
+ anim?: 'reveal' | 'pulse';
81
+ /** Expose as `role="img"` with this label; omit for decorative (aria-hidden). */
82
+ label?: string;
83
+ /** CSS length for one dot (sets `--dotmatrix-dot`; sanitized — a value that
84
+ * is not a plain length/calc expression is dropped). */
85
+ dot?: string;
86
+ /** CSS length for the gap between dots (sets `--dotmatrix-gap`; sanitized
87
+ * the same way as `dot`). */
88
+ gap?: string;
89
+ /** `'mask'` returns a single `.ui-icon` element masked by the glyph bitmap
90
+ * (one DOM node, not GLYPH_SIZE²) — the icon-at-scale path. Inherits
91
+ * `currentColor`; the cell-mode options above don't apply. Needs the
92
+ * `.ui-icon` rule from `@ponchia/ui/css`. */
93
+ render?: 'mask';
94
+ /** With `render: 'mask'`, the icon size (sets `--icon-size`; defaults to
95
+ * `1em`; sanitized to a length/calc allowlist). */
96
+ size?: string;
97
+ }
98
+
99
+ /** The grid edge length (rows = cols = 16). */
100
+ export declare const GLYPH_SIZE: 16;
101
+
102
+ /** The frozen name→bitmap registry. */
103
+ export declare const GLYPHS: Readonly<Record<GlyphName, Glyph>>;
104
+
105
+ /** Every glyph name, frozen and sorted. */
106
+ export declare const GLYPH_NAMES: readonly GlyphName[];
107
+
108
+ /** The raw bitmap rows for a glyph, or `undefined` if the name is unknown. */
109
+ export declare function glyph(name: GlyphNameInput): Glyph | undefined;
110
+
111
+ /** 256 cell descriptors (row-major), or `[]` if unknown. */
112
+ export declare function glyphCells(name: GlyphNameInput): GlyphCell[];
113
+
114
+ /** A full `.ui-dotmatrix` HTML string for a glyph (`''` if unknown). */
115
+ export declare function renderGlyph(name: GlyphNameInput, options?: RenderGlyphOptions): string;