@ponchia/ui 0.3.5 → 0.3.6

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/dist/css/dots.css CHANGED
@@ -1 +1 @@
1
- @layer bronto{.ui-dotgrid{--_dot: var(--dot-color,var(--field-dot));background-image: radial-gradient(var(--_dot) var(--dot-size),transparent var(--dot-size));background-position: 0 0;background-size: var(--dot-gap) var(--dot-gap)}.ui-dotgrid--accent{--dot-color: var(--field-dot-accent)}.ui-dotgrid--dense{--dot-gap: 8px}.ui-dotfield{--_dot: var(--dot-color,var(--field-dot));background-image: radial-gradient(var(--_dot) var(--dot-size),transparent var(--dot-size));background-size: var(--dot-gap) var(--dot-gap);inset: 0;mask-image: radial-gradient(ellipse at 50% 0%,#000 30%,transparent 78%);pointer-events: none;position: fixed;z-index: var(--z-base)}.ui-dotrule{background-image: radial-gradient(var(--line-strong) 1.4px,transparent 1.4px);background-position: 0 50%;background-size: 9px 9px;border: 0;block-size: 3px;margin: var(--space-md) 0;inline-size: 100%}.ui-dotmatrix{display: grid;gap: var(--dotmatrix-gap,0.5rem);grid-template-columns: repeat(var(--dotmatrix-cols,12),minmax(0,1fr))}.ui-dotmatrix__cell{aspect-ratio: 1;background: var(--field-dot);border-radius: 50%}.ui-dotmatrix__cell--hot{background: var(--field-dot-hot)}.ui-dotmatrix__cell--accent{background: var(--field-dot-accent)}.ui-dot{background: var(--text-dim);border-radius: 50%;display: inline-block;flex: 0 0 auto;block-size: 0.55rem;inline-size: 0.55rem}.ui-dot--accent{background: var(--accent)}.ui-dot--success{background: var(--success)}.ui-dot--warning{background: var(--warning)}.ui-dot--danger{background: var(--danger)}.ui-dot--info{background: var(--info)}.ui-dot--live{background: var(--success);box-shadow: 0 0 0 0 color-mix(in srgb,var(--success) 70%,transparent);position: relative}.ui-dot--live::after{animation: pulseRing 1.8s var(--ease-out) infinite;border: 1px solid var(--success);border-radius: 50%;content: '';inset: -3px;position: absolute}.ui-status{align-items: center;color: var(--text-soft);display: inline-flex;font-family: var(--mono);font-size: var(--text-xs);gap: 0.45rem;letter-spacing: var(--tracking-wide);text-transform: uppercase}.ui-dotloader{align-items: center;display: inline-flex;gap: 0.32rem}.ui-dotloader span{animation: pulseDot 1s var(--ease-standard) infinite;background: var(--accent);border-radius: 50%;block-size: 0.42rem;inline-size: 0.42rem}.ui-dotloader span:nth-child(2){animation-delay: 0.16s}.ui-dotloader span:nth-child(3){animation-delay: 0.32s}.ui-dotspinner{--ds-box: 1.6rem;--ds-dot: 0.26rem;animation: uiSpin 1s steps(8) infinite;display: inline-block;flex: 0 0 auto;block-size: var(--ds-box);position: relative;inline-size: var(--ds-box)}.ui-dotspinner i{background: var(--accent);border-radius: 50%;block-size: var(--ds-dot);inset: 0;margin: auto;position: absolute;inline-size: var(--ds-dot)}.ui-dotspinner i:nth-child(1){opacity: 1;transform: rotate(0deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(2){opacity: 0.85;transform: rotate(45deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(3){opacity: 0.7;transform: rotate(90deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(4){opacity: 0.56;transform: rotate(135deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(5){opacity: 0.44;transform: rotate(180deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(6){opacity: 0.34;transform: rotate(225deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(7){opacity: 0.25;transform: rotate(270deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(8){opacity: 0.16;transform: rotate(315deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner--sm{--ds-box: 1.05rem;--ds-dot: 0.18rem}.ui-dotspinner--lg{--ds-box: 2.6rem;--ds-dot: 0.42rem}.ui-dotbar{display: flex;gap: 3px}.ui-dotbar i{background: var(--field-dot);border-radius: var(--radius-sm);flex: 1;block-size: 0.5rem}.ui-dotbar i.is-on{background: var(--accent)}.ui-dotbar--indeterminate i{animation: pulseDot 1.1s var(--ease-standard) infinite}.ui-dotbar--indeterminate i:nth-child(2){animation-delay: 0.09s}.ui-dotbar--indeterminate i:nth-child(3){animation-delay: 0.18s}.ui-dotbar--indeterminate i:nth-child(4){animation-delay: 0.27s}.ui-dotbar--indeterminate i:nth-child(5){animation-delay: 0.36s}.ui-dotbar--indeterminate i:nth-child(6){animation-delay: 0.45s}.ui-dotbar--indeterminate i:nth-child(7){animation-delay: 0.54s}.ui-dotbar--indeterminate i:nth-child(n + 8){animation-delay: 0.63s}.ui-matrix{clip-path: inset(0 100% 0 0);transition: clip-path var(--duration-slow) var(--ease-out)}.ui-matrix.is-in{clip-path: inset(0 0 0 0)}@media (prefers-reduced-motion: reduce){.ui-dot--live::after,.ui-dotloader span,.ui-dotspinner,.ui-dotbar--indeterminate i{animation: none}.ui-dotspinner i{opacity: 0.6}.ui-matrix{clip-path: none}}}
1
+ @layer bronto{.ui-dotgrid{--_dot: var(--dot-color,var(--field-dot));background-image: radial-gradient(var(--_dot) var(--dot-size),transparent var(--dot-size));background-position: 0 0;background-size: var(--dot-gap) var(--dot-gap)}.ui-dotgrid--accent{--dot-color: var(--field-dot-accent)}.ui-dotgrid--dense{--dot-gap: 8px}.ui-dotfield{--_dot: var(--dot-color,var(--field-dot));background-image: radial-gradient(var(--_dot) var(--dot-size),transparent var(--dot-size));background-size: var(--dot-gap) var(--dot-gap);inset: 0;mask-image: radial-gradient(ellipse at 50% 0%,#000 30%,transparent 78%);pointer-events: none;position: fixed;z-index: var(--z-base)}.ui-dotrule{background-image: radial-gradient(var(--line-strong) 1.4px,transparent 1.4px);background-position: 0 50%;background-size: 9px 9px;border: 0;block-size: 3px;margin: var(--space-md) 0;inline-size: 100%}.ui-dotmatrix{display: grid;gap: var(--dotmatrix-gap,0.5rem);grid-template-columns: repeat(var(--dotmatrix-cols,12),var(--dotmatrix-dot,minmax(0,1fr)))}.ui-dotmatrix__cell{aspect-ratio: 1;background: var(--field-dot);border-radius: var(--dotmatrix-dot-radius,50%)}.ui-dotmatrix__cell--hot{background: var(--field-dot-hot)}.ui-dotmatrix__cell--accent{background: var(--field-dot-accent)}.ui-dotmatrix--reveal .ui-dotmatrix__cell{animation: dotmatrixCellOn var(--duration-base) var(--ease-out) both;animation-delay: calc(var(--i,0) * var(--dotmatrix-reveal-step,3ms))}@keyframes dotmatrixCellOn{from{opacity: 0;transform: scale(0.35)}}.ui-dotmatrix--pulse{animation: dotmatrixPulse 1.8s var(--ease-standard) infinite}@keyframes dotmatrixPulse{50%{opacity: 0.55;transform: scale(0.94)}}.ui-dot{background: var(--text-dim);border-radius: 50%;display: inline-block;flex: 0 0 auto;block-size: 0.55rem;inline-size: 0.55rem}.ui-dot--accent{background: var(--accent)}.ui-dot--success{background: var(--success)}.ui-dot--warning{background: var(--warning)}.ui-dot--danger{background: var(--danger)}.ui-dot--info{background: var(--info)}.ui-dot--live{background: var(--success);box-shadow: 0 0 0 0 color-mix(in srgb,var(--success) 70%,transparent);position: relative}.ui-dot--live::after{animation: pulseRing 1.8s var(--ease-out) infinite;border: 1px solid var(--success);border-radius: 50%;content: '';inset: -3px;position: absolute}.ui-status{align-items: center;color: var(--text-soft);display: inline-flex;font-family: var(--mono);font-size: var(--text-xs);gap: 0.45rem;letter-spacing: var(--tracking-wide);text-transform: uppercase}.ui-dotloader{align-items: center;display: inline-flex;gap: 0.32rem}.ui-dotloader span{animation: pulseDot 1s var(--ease-standard) infinite;background: var(--accent);border-radius: 50%;block-size: 0.42rem;inline-size: 0.42rem}.ui-dotloader span:nth-child(2){animation-delay: 0.16s}.ui-dotloader span:nth-child(3){animation-delay: 0.32s}.ui-dotspinner{--ds-box: 1.6rem;--ds-dot: 0.26rem;animation: uiSpin 1s steps(8) infinite;display: inline-block;flex: 0 0 auto;block-size: var(--ds-box);position: relative;inline-size: var(--ds-box)}.ui-dotspinner i{background: var(--accent);border-radius: 50%;block-size: var(--ds-dot);inset: 0;margin: auto;position: absolute;inline-size: var(--ds-dot)}.ui-dotspinner i:nth-child(1){opacity: 1;transform: rotate(0deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(2){opacity: 0.85;transform: rotate(45deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(3){opacity: 0.7;transform: rotate(90deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(4){opacity: 0.56;transform: rotate(135deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(5){opacity: 0.44;transform: rotate(180deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(6){opacity: 0.34;transform: rotate(225deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(7){opacity: 0.25;transform: rotate(270deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner i:nth-child(8){opacity: 0.16;transform: rotate(315deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2))}.ui-dotspinner--sm{--ds-box: 1.05rem;--ds-dot: 0.18rem}.ui-dotspinner--lg{--ds-box: 2.6rem;--ds-dot: 0.42rem}.ui-dotbar{display: flex;gap: 3px}.ui-dotbar i{background: var(--field-dot);border-radius: var(--radius-sm);flex: 1;block-size: 0.5rem}.ui-dotbar i.is-on{background: var(--accent)}.ui-dotbar--indeterminate i{animation: pulseDot 1.1s var(--ease-standard) infinite}.ui-dotbar--indeterminate i:nth-child(2){animation-delay: 0.09s}.ui-dotbar--indeterminate i:nth-child(3){animation-delay: 0.18s}.ui-dotbar--indeterminate i:nth-child(4){animation-delay: 0.27s}.ui-dotbar--indeterminate i:nth-child(5){animation-delay: 0.36s}.ui-dotbar--indeterminate i:nth-child(6){animation-delay: 0.45s}.ui-dotbar--indeterminate i:nth-child(7){animation-delay: 0.54s}.ui-dotbar--indeterminate i:nth-child(n + 8){animation-delay: 0.63s}.ui-matrix{clip-path: inset(0 100% 0 0);transition: clip-path var(--duration-slow) var(--ease-out)}.ui-matrix.is-in{clip-path: inset(0 0 0 0)}@media (prefers-reduced-motion: reduce){.ui-dot--live::after,.ui-dotloader span,.ui-dotspinner,.ui-dotbar--indeterminate i,.ui-dotmatrix--reveal .ui-dotmatrix__cell,.ui-dotmatrix--pulse{animation: none}.ui-dotspinner i{opacity: 0.6}.ui-matrix{clip-path: none}}}
package/docs/reference.md CHANGED
@@ -9,7 +9,7 @@ rendering of every class is the kitchen-sink demo:
9
9
  **<https://ponchia.github.io/bronto-ui/>**. Theming knobs and the token
10
10
  contract: [docs/theming.md](theming.md).
11
11
 
12
- - 260 classes across 109 component groups
12
+ - 262 classes across 109 component groups
13
13
  - Import the typed registry: `import { cls, ui, cx } from '@ponchia/ui/classes'`
14
14
  - Tokens as data: `import { cssVars, tokens, themeColor } from '@ponchia/ui/tokens'`
15
15
 
@@ -329,6 +329,8 @@ each one matches a real selector in the stylesheet.
329
329
  | `cls.dotmatrixCell` | `ui-dotmatrix__cell` | part |
330
330
  | `cls.dotmatrixCellAccent` | `ui-dotmatrix__cell--accent` | modifier |
331
331
  | `cls.dotmatrixCellHot` | `ui-dotmatrix__cell--hot` | modifier |
332
+ | `cls.dotmatrixPulse` | `ui-dotmatrix--pulse` | modifier |
333
+ | `cls.dotmatrixReveal` | `ui-dotmatrix--reveal` | modifier |
332
334
 
333
335
  ### `.ui-dotrule`
334
336
 
package/docs/usage.md CHANGED
@@ -146,6 +146,64 @@ 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.** Each glyph is a 16×16 grid = 256 cells (DOM nodes / spans),
202
+ regardless of mode; `anim: 'reveal'` adds a per-cell `--i` (≈doubles the
203
+ string size). Fine for display marks and the odd inline icon — but don't
204
+ render hundreds at once (e.g. one in every row of a long table) without
205
+ measuring.
206
+
149
207
  ## When to add a behavior
150
208
 
151
209
  The CSS is the framework; `@ponchia/ui/behaviors` is the *sanctioned*
@@ -0,0 +1,102 @@
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
+ | 'chevron-down'
14
+ | 'chevron-left'
15
+ | 'chevron-right'
16
+ | 'chevron-up'
17
+ | 'clock'
18
+ | 'close'
19
+ | 'download'
20
+ | 'edit'
21
+ | 'eye'
22
+ | 'eye-off'
23
+ | 'file'
24
+ | 'folder'
25
+ | 'gear'
26
+ | 'grid'
27
+ | 'heart'
28
+ | 'home'
29
+ | 'info'
30
+ | 'link'
31
+ | 'lock'
32
+ | 'mail'
33
+ | 'menu'
34
+ | 'minus'
35
+ | 'moon'
36
+ | 'more-horizontal'
37
+ | 'more-vertical'
38
+ | 'pause'
39
+ | 'play'
40
+ | 'plus'
41
+ | 'refresh'
42
+ | 'search'
43
+ | 'spark'
44
+ | 'star'
45
+ | 'sun'
46
+ | 'trash'
47
+ | 'upload'
48
+ | 'user'
49
+ | 'warning';
50
+
51
+ /** A glyph name, or any string — for dynamic dispatch (a CMS/config value). The
52
+ * known names still autocomplete; an unknown name hits the runtime fallback
53
+ * (`glyph`→`undefined`, `glyphCells`→`[]`, `renderGlyph`→`''`). */
54
+ export type GlyphNameInput = GlyphName | (string & {});
55
+
56
+ /** A glyph is 16 rows of 16 chars: `.` off, `#` lit, `*` accent. */
57
+ export type Glyph = readonly string[];
58
+
59
+ /** One rendered dot. `on:false` is an unlit panel dot; tone picks the lit color. */
60
+ export interface GlyphCell {
61
+ on: boolean;
62
+ tone?: 'hot' | 'accent';
63
+ }
64
+
65
+ export interface RenderGlyphOptions {
66
+ /** Show the unlit panel dots (default true). `false` → glyph-only look. */
67
+ grid?: boolean;
68
+ /** Render lit cells as square, gapless pixels (a filled silhouette) instead
69
+ * of separated dots — the legible mode at small/inline sizes (~16–24px).
70
+ * Implies glyph-only. Default false (the dot-matrix display look). */
71
+ solid?: boolean;
72
+ /** Opt into a decorative animation (disabled under `prefers-reduced-motion`;
73
+ * meaning stays in the static frame + `label`). `'reveal'` powers the cells
74
+ * on in a scan; `'pulse'` makes the glyph breathe. */
75
+ anim?: 'reveal' | 'pulse';
76
+ /** Expose as `role="img"` with this label; omit for decorative (aria-hidden). */
77
+ label?: string;
78
+ /** CSS length for one dot (sets `--dotmatrix-dot`; sanitized — a value that
79
+ * is not a plain length/calc expression is dropped). */
80
+ dot?: string;
81
+ /** CSS length for the gap between dots (sets `--dotmatrix-gap`; sanitized
82
+ * the same way as `dot`). */
83
+ gap?: string;
84
+ }
85
+
86
+ /** The grid edge length (rows = cols = 16). */
87
+ export declare const GLYPH_SIZE: 16;
88
+
89
+ /** The frozen name→bitmap registry. */
90
+ export declare const GLYPHS: Readonly<Record<GlyphName, Glyph>>;
91
+
92
+ /** Every glyph name, frozen and sorted. */
93
+ export declare const GLYPH_NAMES: readonly GlyphName[];
94
+
95
+ /** The raw bitmap rows for a glyph, or `undefined` if the name is unknown. */
96
+ export declare function glyph(name: GlyphNameInput): Glyph | undefined;
97
+
98
+ /** 256 cell descriptors (row-major), or `[]` if unknown. */
99
+ export declare function glyphCells(name: GlyphNameInput): GlyphCell[];
100
+
101
+ /** A full `.ui-dotmatrix` HTML string for a glyph (`''` if unknown). */
102
+ export declare function renderGlyph(name: GlyphNameInput, options?: RenderGlyphOptions): string;