@ponchia/ui 0.4.1 → 0.6.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.
Files changed (153) hide show
  1. package/CHANGELOG.md +552 -8
  2. package/MIGRATIONS.json +106 -0
  3. package/README.md +34 -8
  4. package/annotations/index.d.ts +402 -0
  5. package/annotations/index.d.ts.map +1 -0
  6. package/annotations/index.js +792 -0
  7. package/behaviors/carousel.js +198 -0
  8. package/behaviors/combobox.js +226 -0
  9. package/behaviors/command.js +190 -0
  10. package/behaviors/connectors.js +95 -0
  11. package/behaviors/crosshair.js +57 -0
  12. package/behaviors/dialog.js +74 -0
  13. package/behaviors/disclosure.js +26 -0
  14. package/behaviors/dismissible.js +25 -0
  15. package/behaviors/forms.js +186 -0
  16. package/behaviors/glyph.js +108 -0
  17. package/behaviors/index.d.ts +79 -0
  18. package/behaviors/index.js +18 -1409
  19. package/behaviors/internal.js +97 -0
  20. package/behaviors/legend.js +67 -0
  21. package/behaviors/menu.js +47 -0
  22. package/behaviors/popover.js +179 -0
  23. package/behaviors/spotlight.js +52 -0
  24. package/behaviors/table.js +136 -0
  25. package/behaviors/tabs.js +103 -0
  26. package/behaviors/theme.js +84 -0
  27. package/behaviors/toast.js +164 -0
  28. package/classes/classes.json +1857 -0
  29. package/classes/index.d.ts +306 -13
  30. package/classes/index.js +339 -12
  31. package/classes/vscode.css-custom-data.json +12 -0
  32. package/connectors/index.d.ts +191 -0
  33. package/connectors/index.d.ts.map +1 -0
  34. package/connectors/index.js +275 -0
  35. package/css/analytical.css +21 -0
  36. package/css/annotations.css +292 -0
  37. package/css/app.css +43 -13
  38. package/css/base.css +15 -10
  39. package/css/command.css +97 -0
  40. package/css/connectors.css +110 -0
  41. package/css/content.css +7 -1
  42. package/css/crosshair.css +100 -0
  43. package/css/dataviz.css +5 -1
  44. package/css/disclosure.css +38 -6
  45. package/css/dots.css +57 -0
  46. package/css/feedback.css +111 -2
  47. package/css/fonts.css +11 -7
  48. package/css/forms.css +42 -1
  49. package/css/generated.css +117 -0
  50. package/css/legend.css +272 -0
  51. package/css/marks.css +174 -0
  52. package/css/motion.css +24 -44
  53. package/css/navigation.css +7 -0
  54. package/css/overlay.css +31 -1
  55. package/css/primitives.css +109 -5
  56. package/css/report.css +39 -81
  57. package/css/selection.css +46 -0
  58. package/css/site.css +16 -2
  59. package/css/sources.css +221 -0
  60. package/css/spotlight.css +104 -0
  61. package/css/state.css +121 -0
  62. package/css/tokens.css +60 -37
  63. package/css/workbench.css +83 -0
  64. package/dist/bronto.css +1 -1
  65. package/dist/css/analytical.css +1 -0
  66. package/dist/css/annotations.css +1 -0
  67. package/dist/css/app.css +1 -1
  68. package/dist/css/base.css +1 -1
  69. package/dist/css/command.css +1 -0
  70. package/dist/css/connectors.css +1 -0
  71. package/dist/css/content.css +1 -1
  72. package/dist/css/crosshair.css +1 -0
  73. package/dist/css/disclosure.css +1 -1
  74. package/dist/css/dots.css +1 -1
  75. package/dist/css/feedback.css +1 -1
  76. package/dist/css/fonts.css +1 -1
  77. package/dist/css/forms.css +1 -1
  78. package/dist/css/generated.css +1 -0
  79. package/dist/css/legend.css +1 -0
  80. package/dist/css/marks.css +1 -0
  81. package/dist/css/motion.css +1 -1
  82. package/dist/css/navigation.css +1 -1
  83. package/dist/css/overlay.css +1 -1
  84. package/dist/css/primitives.css +1 -1
  85. package/dist/css/report.css +1 -1
  86. package/dist/css/selection.css +1 -0
  87. package/dist/css/site.css +1 -1
  88. package/dist/css/sources.css +1 -0
  89. package/dist/css/spotlight.css +1 -0
  90. package/dist/css/state.css +1 -0
  91. package/dist/css/tokens.css +1 -1
  92. package/dist/css/workbench.css +1 -0
  93. package/docs/adr/0003-theme-model.md +7 -4
  94. package/docs/annotations.md +425 -0
  95. package/docs/architecture.md +246 -0
  96. package/docs/command.md +95 -0
  97. package/docs/connectors.md +91 -0
  98. package/docs/contrast.md +116 -92
  99. package/docs/crosshair.md +63 -0
  100. package/docs/d2.md +195 -0
  101. package/docs/generated.md +91 -0
  102. package/docs/legends.md +184 -0
  103. package/docs/marks.md +93 -0
  104. package/docs/mermaid.md +152 -0
  105. package/docs/reference.md +385 -23
  106. package/docs/reporting.md +436 -63
  107. package/docs/selection.md +40 -0
  108. package/docs/sources.md +137 -0
  109. package/docs/spotlight.md +78 -0
  110. package/docs/stability.md +24 -2
  111. package/docs/state.md +85 -0
  112. package/docs/usage.md +123 -4
  113. package/docs/vega.md +225 -0
  114. package/docs/workbench.md +78 -0
  115. package/fonts/doto-400.woff2 +0 -0
  116. package/fonts/doto-500.woff2 +0 -0
  117. package/fonts/doto-600.woff2 +0 -0
  118. package/fonts/doto-700.woff2 +0 -0
  119. package/fonts/doto-800.woff2 +0 -0
  120. package/fonts/doto-900.woff2 +0 -0
  121. package/glyphs/glyphs.js +6 -4
  122. package/llms.txt +362 -14
  123. package/package.json +115 -12
  124. package/qwik/index.d.ts +42 -54
  125. package/qwik/index.d.ts.map +1 -0
  126. package/qwik/index.js +75 -3
  127. package/react/index.d.ts +39 -56
  128. package/react/index.d.ts.map +1 -0
  129. package/react/index.js +67 -3
  130. package/solid/index.d.ts +64 -56
  131. package/solid/index.d.ts.map +1 -0
  132. package/solid/index.js +70 -3
  133. package/tokens/d2.d.ts +38 -0
  134. package/tokens/d2.js +71 -0
  135. package/tokens/d2.json +43 -0
  136. package/tokens/index.d.ts +5 -5
  137. package/tokens/index.js +23 -5
  138. package/tokens/index.json +9 -0
  139. package/tokens/mermaid.d.ts +23 -0
  140. package/tokens/mermaid.js +181 -0
  141. package/tokens/mermaid.json +163 -0
  142. package/tokens/resolved.json +45 -1
  143. package/tokens/skins.js +3 -2
  144. package/tokens/tokens.dtcg.json +26 -0
  145. package/tokens/vega.d.ts +34 -0
  146. package/tokens/vega.js +155 -0
  147. package/tokens/vega.json +179 -0
  148. package/fonts/doto-400.ttf +0 -0
  149. package/fonts/doto-500.ttf +0 -0
  150. package/fonts/doto-600.ttf +0 -0
  151. package/fonts/doto-700.ttf +0 -0
  152. package/fonts/doto-800.ttf +0 -0
  153. package/fonts/doto-900.ttf +0 -0
@@ -0,0 +1,95 @@
1
+ # Command palette
2
+
3
+ `@ponchia/ui/css/command.css` + `initCommand` are an opt-in **command palette**:
4
+ a filter input over a grouped listbox of commands with shortcut hints. Command
5
+ palettes turn a product from a page collection into a tool. Existing libraries
6
+ (cmdk, kbar) are good — Bronto owns the *design-system contract* (the shell,
7
+ shortcuts, groups, meta) and a small navigation behavior, not the action registry.
8
+
9
+ ```css
10
+ @import '@ponchia/ui';
11
+ @import '@ponchia/ui/css/command.css';
12
+ ```
13
+
14
+ ```js
15
+ import { initCommand, initDialog } from '@ponchia/ui/behaviors';
16
+ initDialog(); // open/close the dialog the palette lives in
17
+ initCommand(); // filter + keyboard-navigate the list
18
+ ```
19
+
20
+ **Bronto** filters and keyboard-navigates a DOM-authored list. **The host** owns
21
+ the action registry, permission checks, routing, async effects, and execution —
22
+ it listens for `bronto:command:select` and runs the command. There is **no global
23
+ Cmd/Ctrl+K**; you open the palette yourself (e.g. a `<dialog>` opened by a button
24
+ or your own shortcut). Pairs with the [`ui-shortcut`](./reference.md) hint. Not in
25
+ the core bundle.
26
+
27
+ ## Markup
28
+
29
+ ```html
30
+ <dialog class="ui-modal" id="cmdk" data-bronto-dialog-light aria-label="Command palette">
31
+ <div class="ui-command" data-bronto-command>
32
+ <input class="ui-command__input" aria-label="Command" placeholder="Type a command…" />
33
+ <ul class="ui-command__list">
34
+ <li class="ui-command__group">Navigation</li>
35
+ <li class="ui-command__item" data-value="dashboard">
36
+ <span>Go to dashboard</span>
37
+ <span class="ui-command__shortcut"><kbd class="ui-kbd">G</kbd> <kbd class="ui-kbd">D</kbd></span>
38
+ </li>
39
+ <li class="ui-command__group">Actions</li>
40
+ <li class="ui-command__item" data-value="invoice">
41
+ <span>New invoice</span>
42
+ <span class="ui-command__meta">Create</span>
43
+ </li>
44
+ </ul>
45
+ <p class="ui-command__empty" hidden>No commands</p>
46
+ </div>
47
+ </dialog>
48
+ <button class="ui-button" data-bronto-open="cmdk" type="button">Commands</button>
49
+ ```
50
+
51
+ | Class | Role |
52
+ | --- | --- |
53
+ | `ui-command` | The palette shell (input + list + empty). |
54
+ | `ui-command__input` | The filter input (becomes `role="combobox"`). |
55
+ | `ui-command__list` | The listbox of commands. |
56
+ | `ui-command__group` | A non-selectable group label; auto-hidden when its items all filter out. |
57
+ | `ui-command__item` | A command row (`role="option"`); optional `data-value`. |
58
+ | `ui-command__shortcut` | A trailing shortcut hint (use `ui-kbd`). |
59
+ | `ui-command__meta` | Trailing secondary text (category, hint). |
60
+ | `ui-command__empty` | Shown when nothing matches. |
61
+
62
+ ## Behavior & events
63
+
64
+ `initCommand()` owns ids, `role`/`aria-activedescendant`, a roving active item,
65
+ substring filtering, the full keyboard (Down/Up/Home/End/Enter/Escape), and
66
+ pointer select. It emits:
67
+
68
+ - `bronto:command:select` — `{ value, label }`. The host executes and closes.
69
+ - `bronto:command:close` — on Escape. The host closes the dialog.
70
+
71
+ > **Permission boundary:** `initCommand()` owns the `hidden` attribute on items
72
+ > (that is how it filters), so it will reveal any item you pre-hid the moment the
73
+ > query changes. To gate a command by permission, **omit it from the DOM** —
74
+ > don't render it hidden.
75
+
76
+ ```js
77
+ const dialog = document.getElementById('cmdk');
78
+ document.querySelector('[data-bronto-command]').addEventListener('bronto:command:select', (e) => {
79
+ run(e.detail.value); // YOUR action registry
80
+ dialog.close();
81
+ });
82
+ document.querySelector('[data-bronto-command]').addEventListener('bronto:command:close', () =>
83
+ dialog.close(),
84
+ );
85
+ ```
86
+
87
+ Framework hook: `useCommand()` in `@ponchia/ui/react` · `/solid` · `/qwik`.
88
+
89
+ ## Accessibility
90
+
91
+ - The input is a `combobox`, the list a `listbox`, items `option`s, with
92
+ `aria-activedescendant` tracking the active row — standard APG listbox semantics.
93
+ - Focus stays in the input while arrows move the active item; Enter selects it.
94
+ - Open the palette in a focus-trapping `<dialog>` (Bronto's `initDialog`) so focus
95
+ returns to the trigger on close.
@@ -0,0 +1,91 @@
1
+ # Connectors (leader lines)
2
+
3
+ `@ponchia/ui/css/connectors.css` + `@ponchia/ui/connectors` draw a **leader line
4
+ between two DOM elements** — connect a note to a card, a card to a chart point,
5
+ or two related regions. This is the page-coordinate, element-to-element cousin
6
+ of the figure-coordinate [annotations](./annotations.md) layer.
7
+
8
+ ```css
9
+ @import '@ponchia/ui';
10
+ @import '@ponchia/ui/css/connectors.css';
11
+ ```
12
+
13
+ Bronto computes the **geometry** (pure functions) and styles the line; the
14
+ optional `initConnectors` behavior draws and keeps it in sync. It owns no
15
+ layout, no scales — just the path between two rects.
16
+
17
+ ## Markup
18
+
19
+ An `.ui-connector` is an SVG that overlays a **positioned** container. Give it
20
+ the ids of the two elements to connect; `initConnectors` fills in the path.
21
+
22
+ ```html
23
+ <div style="position: relative">
24
+ <div id="card-a" class="ui-card">…</div>
25
+ <div id="note-b" class="ui-card">…</div>
26
+
27
+ <svg
28
+ class="ui-connector ui-connector--accent"
29
+ data-bronto-connector
30
+ data-from="card-a"
31
+ data-to="note-b"
32
+ data-shape="elbow"
33
+ data-end="arrow"
34
+ aria-hidden="true"
35
+ ></svg>
36
+ </div>
37
+ ```
38
+
39
+ ```js
40
+ import { initConnectors } from '@ponchia/ui/behaviors';
41
+ const stop = initConnectors(); // redraws on resize/scroll; returns a cleanup
42
+ ```
43
+
44
+ A connector is decorative — mark it `aria-hidden="true"` and make sure the
45
+ relationship it depicts is also clear from the content/DOM order.
46
+
47
+ | Attribute | Values |
48
+ | --- | --- |
49
+ | `data-from` / `data-to` | ids of the elements to connect (required). |
50
+ | `data-shape` | `straight` (default), `elbow`, `curve`. |
51
+ | `data-from-side` / `data-to-side` | `top`/`right`/`bottom`/`left`/`center`. Omit a side to auto-pick it (set neither for fully automatic facing edges). |
52
+ | `data-end` | `arrow` (default), `dot`, `none`. |
53
+
54
+ Tones: `ui-connector--accent` / `--muted` / `--success` / `--warning` /
55
+ `--danger` / `--info` (monochrome by default). `ui-connector--dashed` for a
56
+ dashed line; `ui-connector--draw` strokes it in once (reduced-motion-safe).
57
+ `ui.connector({ tone, dashed, motion })` builds the class string.
58
+
59
+ ## Geometry helpers (no DOM)
60
+
61
+ For SSR, canvas, or your own renderer, the pure helpers return SVG strings /
62
+ coordinates and never touch the DOM:
63
+
64
+ ```js
65
+ import { connectRects, arrowHead } from '@ponchia/ui/connectors';
66
+
67
+ const { d, to, angle } = connectRects({
68
+ fromRect: { x: 0, y: 0, width: 80, height: 32 },
69
+ toRect: { x: 220, y: 120, width: 80, height: 32 },
70
+ shape: 'curve',
71
+ });
72
+ const head = arrowHead(to, angle); // place at the endpoint
73
+ ```
74
+
75
+ - `anchorPoint(rect, side)` — a point on a rect's edge.
76
+ - `connectorPath({ from, to, shape, curvature, mid })` — path between two points.
77
+ - `straightPath` / `elbowPath` / `curvePath` — the individual shapes.
78
+ - `connectRects(opts)` → `{ d, from, to, angle }`.
79
+ - `arrowHead(point, angle, size)` / `dotMark(point, radius)` — end markers.
80
+ - `autoSides(fromRect, toRect)` / `angleBetween(from, to)`.
81
+ - `endTangentAngle(from, to, shape)` — the angle the path *arrives* at `to`
82
+ (chord for `straight`, axis-aligned for `elbow`/`curve`); rotate an end marker
83
+ by this so it points along the path. `connectRects().angle` already uses it.
84
+
85
+ ## Coordinate model
86
+
87
+ `initConnectors` measures the from/to elements relative to the connector SVG's
88
+ own box (via `getBoundingClientRect`), so place the SVG as an overlay
89
+ (`position: absolute; inset: 0`, which `.ui-connector` sets) inside a
90
+ `position: relative` container that holds both endpoints. The SVG has no
91
+ `viewBox`, so its user units are CSS pixels.
package/docs/contrast.md CHANGED
@@ -40,51 +40,57 @@ Overall: **all contractual pairings meet their floor ✅**.
40
40
 
41
41
  | Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
42
42
  | --- | --- | --- | --- | --- | --- | --- |
43
- | `--text` | `--bg` | Body text on page background | AA text (4.5:1) | 17.98:1 | Lc 99 | ✅ pass |
44
- | `--text` | `--surface` | Body text on a card/panel | AA text (4.5:1) | 19.80:1 | Lc 106 | ✅ pass |
45
- | `--text` | `--surface-muted` | Body text on a muted panel | AA text (4.5:1) | 16.74:1 | Lc 94 | ✅ pass |
46
- | `--text-soft` | `--bg` | Secondary text on page background | AA text (4.5:1) | 11.16:1 | Lc 91 | ✅ pass |
47
- | `--text-soft` | `--surface` | Secondary text on a card | AA text (4.5:1) | 12.29:1 | Lc 98 | ✅ pass |
48
- | `--text-soft` | `--surface-muted` | Secondary text on a muted panel | AA text (4.5:1) | 10.39:1 | Lc 87 | ✅ pass |
49
- | `--text-dim` | `--bg` | Dim/meta text on page background | AA text (4.5:1) | 5.09:1 | Lc 71 | ✅ pass |
50
- | `--text-dim` | `--surface` | Dim/meta text on a card | AA text (4.5:1) | 5.60:1 | Lc 78 | ✅ pass |
51
- | `--text-dim` | `--surface-muted` | Dim/meta text on a muted panel | AA text (4.5:1) | 4.74:1 | Lc 67 | ✅ pass |
52
- | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.32:1 | Lc 75 | ✅ pass |
53
- | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 6.96:1 | Lc 82 | ✅ pass |
54
- | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.18:1 | Lc 79 | pass |
55
- | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 4.71:1 | Lc 67 | pass |
56
- | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 5.18:1 | Lc 73 | ✅ pass |
57
- | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 4.71:1 | Lc 67 | ✅ pass |
58
- | `--success` | `--surface` | Success indicator vs a card | UI / large (3:1) | 5.04:1 | Lc 75 | ✅ pass |
59
- | `--warning` | `--surface` | Warning indicator vs a card | UI / large (3:1) | 5.60:1 | Lc 78 | ✅ pass |
60
- | `--danger` | `--surface` | Danger indicator vs a card | UI / large (3:1) | 6.21:1 | Lc 79 | ✅ pass |
61
- | `--info` | `--surface` | Info indicator vs a card | UI / large (3:1) | 5.77:1 | Lc 78 | ✅ pass |
62
- | `--line-strong` | `--surface` | Strong hairline vs a card | Decorative (1.4.11-exempt) | 2.39:1 | Lc 47 | ℹ️ not gated |
43
+ | `--text` | `--bg` | Body text on page background | AA text (4.5:1) | 17.98:1 | Lc 99.2 | ✅ pass |
44
+ | `--text` | `--surface` | Body text on a card/panel | AA text (4.5:1) | 19.80:1 | Lc 105.8 | ✅ pass |
45
+ | `--text` | `--surface-muted` | Body text on a muted panel | AA text (4.5:1) | 16.74:1 | Lc 94.5 | ✅ pass |
46
+ | `--text-soft` | `--bg` | Secondary text on page background | AA text (4.5:1) | 11.16:1 | Lc 91.4 | ✅ pass |
47
+ | `--text-soft` | `--surface` | Secondary text on a card | AA text (4.5:1) | 12.29:1 | Lc 98.1 | ✅ pass |
48
+ | `--text-soft` | `--surface-muted` | Secondary text on a muted panel | AA text (4.5:1) | 10.39:1 | Lc 86.7 | ✅ pass |
49
+ | `--text-dim` | `--bg` | Dim/meta text on page background | AA text (4.5:1) | 5.09:1 | Lc 71.4 | ✅ pass |
50
+ | `--text-dim` | `--surface` | Dim/meta text on a card | AA text (4.5:1) | 5.60:1 | Lc 78.0 | ✅ pass |
51
+ | `--text-dim` | `--surface-muted` | Dim/meta text on a muted panel | AA text (4.5:1) | 4.74:1 | Lc 66.7 | ✅ pass |
52
+ | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.32:1 | Lc 75.4 | ✅ pass |
53
+ | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 6.96:1 | Lc 82.1 | ✅ pass |
54
+ | `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 5.91:1 | Lc 71.0 | ℹ️ not gated |
55
+ | `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 6.31:1 | Lc 75.3 | ℹ️ not gated |
56
+ | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.18:1 | Lc 78.9 | ✅ pass |
57
+ | `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 5.18:1 | Lc 78.9 | ✅ pass |
58
+ | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 4.71:1 | Lc 66.8 | ✅ pass |
59
+ | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 5.18:1 | Lc 73.5 | ✅ pass |
60
+ | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 4.71:1 | Lc 66.8 | ✅ pass |
61
+ | `--success` | `--surface` | Success indicator vs a card | UI / large (3:1) | 5.04:1 | Lc 74.6 | ✅ pass |
62
+ | `--warning` | `--surface` | Warning indicator vs a card | UI / large (3:1) | 5.60:1 | Lc 77.8 | pass |
63
+ | `--danger` | `--surface` | Danger indicator vs a card | UI / large (3:1) | 6.21:1 | Lc 78.9 | ✅ pass |
64
+ | `--info` | `--surface` | Info indicator vs a card | UI / large (3:1) | 5.77:1 | Lc 78.3 | ✅ pass |
65
+ | `--line-strong` | `--surface` | Strong hairline vs a card | Decorative (1.4.11-exempt) | 2.39:1 | Lc 47.1 | ℹ️ not gated |
63
66
 
64
67
  ## Dark theme
65
68
 
66
69
  | Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
67
70
  | --- | --- | --- | --- | --- | --- | --- |
68
- | `--text` | `--bg` | Body text on page background | AA text (4.5:1) | 15.01:1 | Lc 91 | ✅ pass |
69
- | `--text` | `--surface` | Body text on a card/panel | AA text (4.5:1) | 13.65:1 | Lc 90 | ✅ pass |
70
- | `--text` | `--surface-muted` | Body text on a muted panel | AA text (4.5:1) | 12.44:1 | Lc 89 | ✅ pass |
71
- | `--text-soft` | `--bg` | Secondary text on page background | AA text (4.5:1) | 11.20:1 | Lc 73 | ✅ pass |
72
- | `--text-soft` | `--surface` | Secondary text on a card | AA text (4.5:1) | 10.19:1 | Lc 72 | ✅ pass |
73
- | `--text-soft` | `--surface-muted` | Secondary text on a muted panel | AA text (4.5:1) | 9.28:1 | Lc 71 | ✅ pass |
74
- | `--text-dim` | `--bg` | Dim/meta text on page background | AA text (4.5:1) | 7.16:1 | Lc 50 | ✅ pass |
75
- | `--text-dim` | `--surface` | Dim/meta text on a card | AA text (4.5:1) | 6.52:1 | Lc 49 | ✅ pass |
76
- | `--text-dim` | `--surface-muted` | Dim/meta text on a muted panel | AA text (4.5:1) | 5.94:1 | Lc 48 | ✅ pass |
77
- | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.14:1 | Lc 45 | ✅ pass |
78
- | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 5.58:1 | Lc 44 | ✅ pass |
79
- | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.95:1 | Lc 43 | pass |
80
- | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 5.31:1 | Lc 40 | pass |
81
- | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 4.83:1 | Lc 39 | ✅ pass |
82
- | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 5.31:1 | Lc 40 | ✅ pass |
83
- | `--success` | `--surface` | Success indicator vs a card | UI / large (3:1) | 7.58:1 | Lc 57 | ✅ pass |
84
- | `--warning` | `--surface` | Warning indicator vs a card | UI / large (3:1) | 9.29:1 | Lc 67 | ✅ pass |
85
- | `--danger` | `--surface` | Danger indicator vs a card | UI / large (3:1) | 5.23:1 | Lc 42 | ✅ pass |
86
- | `--info` | `--surface` | Info indicator vs a card | UI / large (3:1) | 7.33:1 | Lc 55 | ✅ pass |
87
- | `--line-strong` | `--surface` | Strong hairline vs a card | Decorative (1.4.11-exempt) | 2.29:1 | Lc 15 | ℹ️ not gated |
71
+ | `--text` | `--bg` | Body text on page background | AA text (4.5:1) | 15.01:1 | Lc 91.1 | ✅ pass |
72
+ | `--text` | `--surface` | Body text on a card/panel | AA text (4.5:1) | 13.65:1 | Lc 90.1 | ✅ pass |
73
+ | `--text` | `--surface-muted` | Body text on a muted panel | AA text (4.5:1) | 12.44:1 | Lc 88.9 | ✅ pass |
74
+ | `--text-soft` | `--bg` | Secondary text on page background | AA text (4.5:1) | 11.20:1 | Lc 72.7 | ✅ pass |
75
+ | `--text-soft` | `--surface` | Secondary text on a card | AA text (4.5:1) | 10.19:1 | Lc 71.7 | ✅ pass |
76
+ | `--text-soft` | `--surface-muted` | Secondary text on a muted panel | AA text (4.5:1) | 9.28:1 | Lc 70.6 | ✅ pass |
77
+ | `--text-dim` | `--bg` | Dim/meta text on page background | AA text (4.5:1) | 7.16:1 | Lc 50.3 | ✅ pass |
78
+ | `--text-dim` | `--surface` | Dim/meta text on a card | AA text (4.5:1) | 6.52:1 | Lc 49.3 | ✅ pass |
79
+ | `--text-dim` | `--surface-muted` | Dim/meta text on a muted panel | AA text (4.5:1) | 5.94:1 | Lc 48.1 | ✅ pass |
80
+ | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.14:1 | Lc 44.9 | ✅ pass |
81
+ | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 5.58:1 | Lc 43.9 | ✅ pass |
82
+ | `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 2.53:1 | Lc 43.8 | ℹ️ not gated |
83
+ | `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 2.75:1 | Lc 49.1 | ℹ️ not gated |
84
+ | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.95:1 | Lc 42.9 | ✅ pass |
85
+ | `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 5.95:1 | Lc 42.9 | ✅ pass |
86
+ | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 5.31:1 | Lc 40.0 | ✅ pass |
87
+ | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 4.83:1 | Lc 39.0 | ✅ pass |
88
+ | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 5.31:1 | Lc 40.0 | ✅ pass |
89
+ | `--success` | `--surface` | Success indicator vs a card | UI / large (3:1) | 7.58:1 | Lc 56.6 | ✅ pass |
90
+ | `--warning` | `--surface` | Warning indicator vs a card | UI / large (3:1) | 9.29:1 | Lc 66.6 | pass |
91
+ | `--danger` | `--surface` | Danger indicator vs a card | UI / large (3:1) | 5.23:1 | Lc 41.5 | ✅ pass |
92
+ | `--info` | `--surface` | Info indicator vs a card | UI / large (3:1) | 7.33:1 | Lc 54.8 | ✅ pass |
93
+ | `--line-strong` | `--surface` | Strong hairline vs a card | Decorative (1.4.11-exempt) | 2.29:1 | Lc 14.5 | ℹ️ not gated |
88
94
 
89
95
  ## Display colorways (skins)
90
96
 
@@ -101,67 +107,85 @@ palette untouched). Accents are authored in OKLCH; `--accent-text` is the
101
107
 
102
108
  | Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
103
109
  | --- | --- | --- | --- | --- | --- | --- |
104
- | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.75:1 | Lc 79 | ✅ pass |
105
- | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 7.44:1 | Lc 86 | ✅ pass |
106
- | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.66:1 | Lc 83 | pass |
107
- | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 5.14:1 | Lc 71 | pass |
108
- | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 5.66:1 | Lc 78 | ✅ pass |
109
- | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 5.14:1 | Lc 71 | ✅ pass |
110
+ | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.75:1 | Lc 78.9 | ✅ pass |
111
+ | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 7.44:1 | Lc 85.6 | ✅ pass |
112
+ | `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 6.31:1 | Lc 74.5 | ℹ️ not gated |
113
+ | `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 6.74:1 | Lc 78.8 | ℹ️ not gated |
114
+ | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.66:1 | Lc 83.3 | ✅ pass |
115
+ | `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 5.66:1 | Lc 83.3 | ✅ pass |
116
+ | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 5.14:1 | Lc 71.4 | ✅ pass |
117
+ | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 5.66:1 | Lc 78.0 | ✅ pass |
118
+ | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 5.14:1 | Lc 71.4 | ✅ pass |
110
119
 
111
120
  ### Amber CRT — dark
112
121
 
113
122
  | Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
114
123
  | --- | --- | --- | --- | --- | --- | --- |
115
- | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 11.57:1 | Lc 75 | ✅ pass |
116
- | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 10.52:1 | Lc 74 | ✅ pass |
117
- | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 11.88:1 | Lc 72 | pass |
118
- | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 10.60:1 | Lc 70 | pass |
119
- | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 9.64:1 | Lc 69 | ✅ pass |
120
- | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 10.60:1 | Lc 70 | ✅ pass |
124
+ | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 11.57:1 | Lc 74.8 | ✅ pass |
125
+ | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 10.52:1 | Lc 73.8 | ✅ pass |
126
+ | `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 1.34:1 | Lc 15.1 | ℹ️ not gated |
127
+ | `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 1.46:1 | Lc 20.3 | ℹ️ not gated |
128
+ | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 11.88:1 | Lc 71.6 | ✅ pass |
129
+ | `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 11.88:1 | Lc 71.6 | ✅ pass |
130
+ | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 10.60:1 | Lc 69.9 | ✅ pass |
131
+ | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 9.64:1 | Lc 68.9 | ✅ pass |
132
+ | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 10.60:1 | Lc 69.9 | ✅ pass |
121
133
 
122
134
  ### E-ink — light
123
135
 
124
136
  | Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
125
137
  | --- | --- | --- | --- | --- | --- | --- |
126
- | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 12.23:1 | Lc 93 | ✅ pass |
127
- | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 13.47:1 | Lc 100 | ✅ pass |
128
- | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 11.74:1 | Lc 101 | pass |
129
- | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 10.66:1 | Lc 90 | pass |
130
- | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 11.74:1 | Lc 97 | ✅ pass |
131
- | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 10.66:1 | Lc 90 | ✅ pass |
138
+ | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 12.23:1 | Lc 93.3 | ✅ pass |
139
+ | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 13.47:1 | Lc 99.9 | ✅ pass |
140
+ | `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 11.43:1 | Lc 88.8 | ℹ️ not gated |
141
+ | `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 12.22:1 | Lc 93.2 | ℹ️ not gated |
142
+ | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 11.74:1 | Lc 100.6 | ✅ pass |
143
+ | `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 11.74:1 | Lc 100.6 | ✅ pass |
144
+ | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 10.66:1 | Lc 90.4 | ✅ pass |
145
+ | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 11.74:1 | Lc 97.0 | ✅ pass |
146
+ | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 10.66:1 | Lc 90.4 | ✅ pass |
132
147
 
133
148
  ### E-ink — dark
134
149
 
135
150
  | Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
136
151
  | --- | --- | --- | --- | --- | --- | --- |
137
- | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 12.47:1 | Lc 79 | ✅ pass |
138
- | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 11.35:1 | Lc 78 | ✅ pass |
139
- | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 12.86:1 | Lc 76 | pass |
140
- | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 11.48:1 | Lc 74 | pass |
141
- | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 10.44:1 | Lc 73 | ✅ pass |
142
- | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 11.48:1 | Lc 74 | ✅ pass |
152
+ | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 12.47:1 | Lc 79.1 | ✅ pass |
153
+ | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 11.35:1 | Lc 78.1 | ✅ pass |
154
+ | `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 1.25:1 | Lc 11.0 | ℹ️ not gated |
155
+ | `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 1.35:1 | Lc 16.3 | ℹ️ not gated |
156
+ | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 12.86:1 | Lc 75.6 | ✅ pass |
157
+ | `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 12.86:1 | Lc 75.6 | ✅ pass |
158
+ | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 11.48:1 | Lc 74.1 | ✅ pass |
159
+ | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 10.44:1 | Lc 73.1 | ✅ pass |
160
+ | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 11.48:1 | Lc 74.1 | ✅ pass |
143
161
 
144
162
  ### Phosphor Green — light
145
163
 
146
164
  | Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
147
165
  | --- | --- | --- | --- | --- | --- | --- |
148
- | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.22:1 | Lc 77 | ✅ pass |
149
- | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 6.85:1 | Lc 83 | ✅ pass |
150
- | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.19:1 | Lc 81 | pass |
151
- | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 4.71:1 | Lc 69 | pass |
152
- | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 5.19:1 | Lc 75 | ✅ pass |
153
- | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 4.71:1 | Lc 69 | ✅ pass |
166
+ | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 6.22:1 | Lc 76.6 | ✅ pass |
167
+ | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 6.85:1 | Lc 83.3 | ✅ pass |
168
+ | `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 5.81:1 | Lc 72.2 | ℹ️ not gated |
169
+ | `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 6.21:1 | Lc 76.5 | ℹ️ not gated |
170
+ | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 5.19:1 | Lc 80.7 | ✅ pass |
171
+ | `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 5.19:1 | Lc 80.7 | ✅ pass |
172
+ | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 4.71:1 | Lc 68.6 | ✅ pass |
173
+ | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 5.19:1 | Lc 75.3 | ✅ pass |
174
+ | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 4.71:1 | Lc 68.6 | ✅ pass |
154
175
 
155
176
  ### Phosphor Green — dark
156
177
 
157
178
  | Foreground | Background | Role | Held to | Ratio | APCA _(advisory)_ | Verdict |
158
179
  | --- | --- | --- | --- | --- | --- | --- |
159
- | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 12.97:1 | Lc 82 | ✅ pass |
160
- | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 11.80:1 | Lc 81 | ✅ pass |
161
- | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 13.75:1 | Lc 80 | pass |
162
- | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 12.27:1 | Lc 79 | pass |
163
- | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 11.16:1 | Lc 78 | ✅ pass |
164
- | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 12.27:1 | Lc 79 | ✅ pass |
180
+ | `--accent-text` | `--bg` | Accent text on page background | AA text (4.5:1) | 12.97:1 | Lc 81.8 | ✅ pass |
181
+ | `--accent-text` | `--surface` | Accent text on a card | AA text (4.5:1) | 11.80:1 | Lc 80.8 | ✅ pass |
182
+ | `--accent-text` | `--accent-soft` | Accent text on an accent tint | Advisory (translucent tint — not gated) | 1.20:1 | Lc 8.4 | ℹ️ not gated |
183
+ | `--accent-text` | `--bg-accent` | Accent text on an accent-tinted surface | Advisory (translucent tint — not gated) | 1.30:1 | Lc 13.7 | ℹ️ not gated |
184
+ | `--button-text` | `--accent` | Label on the primary button | AA text (4.5:1) | 13.75:1 | Lc 79.7 | ✅ pass |
185
+ | `--on-accent` | `--accent` | Ink on an accent fill | AA text (4.5:1) | 13.75:1 | Lc 79.7 | ✅ pass |
186
+ | `--focus-ring` | `--bg` | Focus ring vs page background | UI / large (3:1) | 12.27:1 | Lc 78.5 | ✅ pass |
187
+ | `--focus-ring` | `--surface` | Focus ring vs a card | UI / large (3:1) | 11.16:1 | Lc 77.5 | ✅ pass |
188
+ | `--accent` | `--bg` | Accent fill vs page background | UI / large (3:1) | 12.27:1 | Lc 78.5 | ✅ pass |
165
189
 
166
190
  ## Data-viz palette (advisory)
167
191
 
@@ -178,27 +202,27 @@ the brand accent.
178
202
 
179
203
  | Series | Colour | Ratio _(advisory)_ | APCA _(advisory)_ |
180
204
  | --- | --- | --- | --- |
181
- | 1 _(accent)_ | `#d71921` | 4.71:1 | Lc 67 |
182
- | 2 | `#e69f00` | 2.05:1 | Lc 37 |
183
- | 3 | `#56b4e9` | 2.10:1 | Lc 39 |
184
- | 4 | `#009e73` | 3.11:1 | Lc 54 |
185
- | 5 | `#f0e442` | 1.20:1 | Lc 9 |
186
- | 6 | `#0072b2` | 4.71:1 | Lc 68 |
187
- | 7 | `#cc79a7` | 2.78:1 | Lc 51 |
188
- | 8 | `#4d5358` | 7.08:1 | Lc 80 |
205
+ | 1 _(accent)_ | `#d71921` | 4.71:1 | Lc 66.8 |
206
+ | 2 | `#e69f00` | 2.05:1 | Lc 37.4 |
207
+ | 3 | `#56b4e9` | 2.10:1 | Lc 38.6 |
208
+ | 4 | `#009e73` | 3.11:1 | Lc 54.4 |
209
+ | 5 | `#f0e442` | 1.20:1 | Lc 9.1 |
210
+ | 6 | `#0072b2` | 4.71:1 | Lc 68.4 |
211
+ | 7 | `#cc79a7` | 2.78:1 | Lc 50.6 |
212
+ | 8 | `#4d5358` | 7.08:1 | Lc 80.4 |
189
213
 
190
214
  ### Dark theme — categorical vs `--bg`
191
215
 
192
216
  | Series | Colour | Ratio _(advisory)_ | APCA _(advisory)_ |
193
217
  | --- | --- | --- | --- |
194
- | 1 _(accent)_ | `#ff3b41` | 5.31:1 | Lc 40 |
195
- | 2 | `#e69f00` | 8.32:1 | Lc 58 |
196
- | 3 | `#56b4e9` | 8.12:1 | Lc 56 |
197
- | 4 | `#009e73` | 5.48:1 | Lc 40 |
198
- | 5 | `#f0e442` | 14.17:1 | Lc 87 |
199
- | 6 | `#0072b2` | 3.61:1 | Lc 26 |
200
- | 7 | `#cc79a7` | 6.12:1 | Lc 44 |
201
- | 8 | `#4d5358` | 2.40:1 | Lc 14 |
218
+ | 1 _(accent)_ | `#ff3b41` | 5.31:1 | Lc 40.0 |
219
+ | 2 | `#e69f00` | 8.32:1 | Lc 57.5 |
220
+ | 3 | `#56b4e9` | 8.12:1 | Lc 56.3 |
221
+ | 4 | `#009e73` | 5.48:1 | Lc 40.0 |
222
+ | 5 | `#f0e442` | 14.17:1 | Lc 87.4 |
223
+ | 6 | `#0072b2` | 3.61:1 | Lc 26.1 |
224
+ | 7 | `#cc79a7` | 6.12:1 | Lc 43.9 |
225
+ | 8 | `#4d5358` | 2.40:1 | Lc 14.4 |
202
226
 
203
227
  ## Scope & caveats
204
228
 
@@ -0,0 +1,63 @@
1
+ # Crosshair & readout
2
+
3
+ `@ponchia/ui/css/crosshair.css` is an opt-in **ruler + readout** for reading a
4
+ value off a plot: pointer-tracking crosshair rules, axis value badges, and a
5
+ pinned readout chip.
6
+
7
+ ```css
8
+ @import '@ponchia/ui';
9
+ @import '@ponchia/ui/css/crosshair.css';
10
+ ```
11
+
12
+ Bronto draws the rules and (via `initCrosshair`) tracks the pointer — it reports
13
+ **where** the pointer is, as pixels and 0–1 fractions. It does **not** find the
14
+ nearest data point or map pixels to data values; that needs your scales. So this
15
+ is the *visual + pointer* layer, not a chart engine.
16
+
17
+ ## Markup
18
+
19
+ The plot is `[data-bronto-crosshair]` (a `position: relative` box); it contains a
20
+ `.ui-crosshair` overlay with the rule(s) you want.
21
+
22
+ ```html
23
+ <figure data-bronto-crosshair style="position: relative">
24
+ <!-- your chart / image / canvas -->
25
+ <div class="ui-crosshair" aria-hidden="true">
26
+ <div class="ui-crosshair__line ui-crosshair__line--x"></div>
27
+ <div class="ui-crosshair__line ui-crosshair__line--y"></div>
28
+ <div class="ui-readout" id="readout"></div>
29
+ </div>
30
+ </figure>
31
+ ```
32
+
33
+ ```js
34
+ import { initCrosshair } from '@ponchia/ui/behaviors';
35
+ const stop = initCrosshair(); // returns a cleanup
36
+ document.querySelector('[data-bronto-crosshair]').addEventListener(
37
+ 'bronto:crosshair:move',
38
+ (e) => {
39
+ const { x, y, fx, fy } = e.detail; // px + 0..1 fractions
40
+ // YOU map the fraction to a data value with your scale:
41
+ readout.textContent = `${(fx * 100).toFixed(0)}%`;
42
+ },
43
+ );
44
+ ```
45
+
46
+ | Class | Role |
47
+ | --- | --- |
48
+ | `ui-crosshair` | The overlay. Hidden until `.is-active` (set on the first pointer move over the plot). |
49
+ | `ui-crosshair__line` + `--x` / `--y` | Vertical / horizontal rule, positioned by `--crosshair-x` / `--crosshair-y`. |
50
+ | `ui-crosshair__badge` | An axis value chip (you set its text + edge). |
51
+ | `ui-readout` | A pinned readout chip; follows the crosshair point. |
52
+
53
+ `ui-crosshair--muted` is a subtler neutral crosshair. `ui.crosshair({ muted })`
54
+ builds the class string. Include only the lines you need (just `--x` for a
55
+ vertical scrubber, etc.).
56
+
57
+ ## Events
58
+
59
+ - `bronto:crosshair:move` — `{ x, y, fx, fy }` (px within the plot + fractions).
60
+ - `bronto:crosshair:leave` — pointer left the plot.
61
+
62
+ The crosshair is decorative (`aria-hidden`); expose the underlying values to
63
+ assistive tech through a data table or text, not the rules.