@ponchia/ui 0.6.6 → 0.6.8

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 (160) hide show
  1. package/CHANGELOG.md +175 -6
  2. package/README.md +38 -25
  3. package/annotations/index.d.ts.map +1 -1
  4. package/annotations/index.js +21 -3
  5. package/behaviors/carousel.d.ts.map +1 -1
  6. package/behaviors/carousel.js +91 -32
  7. package/behaviors/combobox.d.ts.map +1 -1
  8. package/behaviors/combobox.js +117 -43
  9. package/behaviors/command.d.ts.map +1 -1
  10. package/behaviors/command.js +74 -14
  11. package/behaviors/connectors.d.ts.map +1 -1
  12. package/behaviors/connectors.js +92 -9
  13. package/behaviors/crosshair.d.ts.map +1 -1
  14. package/behaviors/crosshair.js +47 -1
  15. package/behaviors/dialog.d.ts.map +1 -1
  16. package/behaviors/dialog.js +37 -16
  17. package/behaviors/disclosure.d.ts.map +1 -1
  18. package/behaviors/disclosure.js +33 -3
  19. package/behaviors/dismissible.d.ts.map +1 -1
  20. package/behaviors/dismissible.js +3 -2
  21. package/behaviors/forms.d.ts.map +1 -1
  22. package/behaviors/forms.js +78 -5
  23. package/behaviors/glyph.d.ts.map +1 -1
  24. package/behaviors/glyph.js +17 -2
  25. package/behaviors/index.d.ts +2 -0
  26. package/behaviors/index.d.ts.map +1 -1
  27. package/behaviors/index.js +2 -0
  28. package/behaviors/inert.js +3 -2
  29. package/behaviors/internal.d.ts +2 -1
  30. package/behaviors/internal.d.ts.map +1 -1
  31. package/behaviors/internal.js +25 -4
  32. package/behaviors/legend.d.ts +0 -5
  33. package/behaviors/legend.d.ts.map +1 -1
  34. package/behaviors/legend.js +78 -14
  35. package/behaviors/menu.d.ts.map +1 -1
  36. package/behaviors/menu.js +13 -8
  37. package/behaviors/modal.d.ts.map +1 -1
  38. package/behaviors/modal.js +77 -19
  39. package/behaviors/popover.d.ts +4 -3
  40. package/behaviors/popover.d.ts.map +1 -1
  41. package/behaviors/popover.js +89 -9
  42. package/behaviors/sources.d.ts.map +1 -1
  43. package/behaviors/sources.js +14 -2
  44. package/behaviors/splitter.d.ts +26 -0
  45. package/behaviors/splitter.d.ts.map +1 -0
  46. package/behaviors/splitter.js +239 -0
  47. package/behaviors/spotlight.d.ts.map +1 -1
  48. package/behaviors/spotlight.js +28 -2
  49. package/behaviors/table.d.ts.map +1 -1
  50. package/behaviors/table.js +105 -13
  51. package/behaviors/tabs.d.ts.map +1 -1
  52. package/behaviors/tabs.js +82 -18
  53. package/behaviors/theme.d.ts.map +1 -1
  54. package/behaviors/theme.js +26 -6
  55. package/classes/classes.json +230 -4
  56. package/classes/index.d.ts +64 -3
  57. package/classes/index.js +56 -2
  58. package/classes/vscode.css-custom-data.json +1 -1
  59. package/connectors/index.d.ts +39 -6
  60. package/connectors/index.d.ts.map +1 -1
  61. package/connectors/index.js +67 -9
  62. package/css/analytical.css +3 -1
  63. package/css/annotations.css +12 -0
  64. package/css/app.css +4 -4
  65. package/css/clamp.css +92 -0
  66. package/css/crosshair.css +27 -2
  67. package/css/feedback.css +2 -30
  68. package/css/figure.css +102 -0
  69. package/css/highlights.css +50 -0
  70. package/css/interval.css +90 -0
  71. package/css/navigation.css +12 -0
  72. package/css/primitives.css +2 -3
  73. package/css/report-kit.css +38 -0
  74. package/css/report.css +23 -4
  75. package/css/sidenote.css +12 -2
  76. package/css/site.css +2 -1
  77. package/css/sources.css +5 -0
  78. package/css/state.css +120 -1
  79. package/css/table.css +4 -0
  80. package/css/tokens.css +25 -9
  81. package/css/workbench.css +101 -8
  82. package/dist/bronto.css +1 -1
  83. package/dist/css/analytical.css +1 -1
  84. package/dist/css/annotations.css +1 -1
  85. package/dist/css/app.css +1 -1
  86. package/dist/css/clamp.css +1 -0
  87. package/dist/css/crosshair.css +1 -1
  88. package/dist/css/feedback.css +1 -1
  89. package/dist/css/figure.css +1 -0
  90. package/dist/css/highlights.css +1 -0
  91. package/dist/css/interval.css +1 -0
  92. package/dist/css/navigation.css +1 -1
  93. package/dist/css/primitives.css +1 -1
  94. package/dist/css/report-kit.css +1 -0
  95. package/dist/css/report.css +1 -1
  96. package/dist/css/sidenote.css +1 -1
  97. package/dist/css/site.css +1 -1
  98. package/dist/css/sources.css +1 -1
  99. package/dist/css/state.css +1 -1
  100. package/dist/css/table.css +1 -1
  101. package/dist/css/tokens.css +1 -1
  102. package/dist/css/workbench.css +1 -1
  103. package/docs/adr/0001-color-system.md +3 -2
  104. package/docs/adr/0002-scope-and-2026-baseline.md +1 -1
  105. package/docs/annotations.md +12 -1
  106. package/docs/architecture.md +105 -48
  107. package/docs/clamp.md +49 -0
  108. package/docs/command.md +4 -1
  109. package/docs/connectors.md +16 -0
  110. package/docs/contrast.md +34 -24
  111. package/docs/crosshair.md +1 -1
  112. package/docs/d2.md +37 -0
  113. package/docs/dots.md +4 -1
  114. package/docs/figure.md +71 -0
  115. package/docs/frontier-primitives.md +25 -24
  116. package/docs/glyphs.md +11 -0
  117. package/docs/highlights.md +52 -0
  118. package/docs/interop/tailwind.md +148 -0
  119. package/docs/interval.md +55 -0
  120. package/docs/legends.md +3 -2
  121. package/docs/mermaid.md +6 -0
  122. package/docs/migrations/0.2-to-0.3.md +80 -0
  123. package/docs/migrations/0.3-to-0.4.md +48 -0
  124. package/docs/migrations/0.4-to-0.5.md +96 -0
  125. package/docs/migrations/0.5-to-0.6.md +82 -0
  126. package/docs/package-contract.md +44 -6
  127. package/docs/reference.md +78 -5
  128. package/docs/reporting.md +126 -60
  129. package/docs/sidenote.md +7 -1
  130. package/docs/sources.md +1 -1
  131. package/docs/stability.md +23 -5
  132. package/docs/state.md +67 -10
  133. package/docs/theming.md +12 -4
  134. package/docs/usage.md +47 -13
  135. package/docs/vega.md +4 -4
  136. package/docs/workbench.md +59 -18
  137. package/llms.txt +89 -16
  138. package/package.json +82 -6
  139. package/qwik/index.d.ts +1 -0
  140. package/qwik/index.d.ts.map +1 -1
  141. package/qwik/index.js +26 -21
  142. package/react/index.d.ts +1 -0
  143. package/react/index.d.ts.map +1 -1
  144. package/react/index.js +4 -1
  145. package/schemas/report-claims.v1.schema.json +137 -0
  146. package/solid/index.d.ts +2 -0
  147. package/solid/index.d.ts.map +1 -1
  148. package/solid/index.js +3 -0
  149. package/svelte/index.d.ts +114 -0
  150. package/svelte/index.d.ts.map +1 -0
  151. package/svelte/index.js +193 -0
  152. package/tailwind.css +87 -0
  153. package/tokens/figma.variables.json +2241 -0
  154. package/tokens/index.js +1 -1
  155. package/tokens/index.json +2 -2
  156. package/tokens/resolved.json +3 -3
  157. package/tokens/tokens.dtcg.json +1 -1
  158. package/vue/index.d.ts +116 -0
  159. package/vue/index.d.ts.map +1 -0
  160. package/vue/index.js +228 -0
@@ -48,12 +48,26 @@
48
48
  // builders below.
49
49
  export const PRECISION = 1000;
50
50
 
51
+ /**
52
+ * Resolve a numeric option with an optional fallback.
53
+ * @param {string} name
54
+ * @param {number | null | undefined} value
55
+ * @param {number | null | undefined} [fallback]
56
+ * @returns {number}
57
+ */
51
58
  export function finite(name, value, fallback) {
52
59
  const v = value ?? fallback;
53
60
  if (!Number.isFinite(v)) throw new TypeError(`${name} must be a finite number`);
54
61
  return v;
55
62
  }
56
63
 
64
+ /**
65
+ * Resolve a non-negative numeric option with an optional fallback.
66
+ * @param {string} name
67
+ * @param {number | null | undefined} value
68
+ * @param {number | null | undefined} [fallback]
69
+ * @returns {number}
70
+ */
57
71
  export function dimension(name, value, fallback) {
58
72
  const v = finite(name, value, fallback);
59
73
  if (v < 0) throw new RangeError(`${name} must be greater than or equal to 0`);
@@ -63,26 +77,64 @@ export function dimension(name, value, fallback) {
63
77
  // Round to PRECISION, normalising -0 → 0, and return the NUMBER (the numeric
64
78
  // core `fmt` stringifies). Shared with the annotations layer for the rounded
65
79
  // coordinates it echoes back to the host. (code-quality audit Q5.)
80
+ /**
81
+ * @param {number} value
82
+ * @returns {number}
83
+ */
66
84
  export function roundNumber(value) {
67
85
  const rounded = Math.round((Object.is(value, -0) ? 0 : value) * PRECISION) / PRECISION;
68
86
  return Object.is(rounded, -0) ? 0 : rounded;
69
87
  }
70
88
 
89
+ /**
90
+ * @param {number} value
91
+ * @returns {string}
92
+ */
71
93
  export function fmt(value) {
72
94
  return String(roundNumber(value));
73
95
  }
74
96
 
97
+ /**
98
+ * @param {number} x
99
+ * @param {number} y
100
+ * @returns {string}
101
+ */
75
102
  export function point(x, y) {
76
103
  return `${fmt(x)},${fmt(y)}`;
77
104
  }
78
105
 
79
106
  // Guarded form (returns min when the range is inverted) — the reconciled body;
80
107
  // connectors only ever calls clamp(v, 0, 1) so this is output-identical here.
108
+ /**
109
+ * @param {number} value
110
+ * @param {number} min
111
+ * @param {number} max
112
+ * @returns {number}
113
+ */
81
114
  export function clamp(value, min, max) {
82
115
  if (max < min) return min;
83
116
  return Math.min(max, Math.max(min, value));
84
117
  }
85
118
 
119
+ function connectorShape(value) {
120
+ const shape = value ?? 'straight';
121
+ if (shape === 'straight' || shape === 'elbow' || shape === 'curve') return shape;
122
+ throw new TypeError('shape must be "straight", "elbow" or "curve"');
123
+ }
124
+
125
+ function sideValue(value) {
126
+ const side = value ?? 'center';
127
+ if (
128
+ side === 'top' ||
129
+ side === 'right' ||
130
+ side === 'bottom' ||
131
+ side === 'left' ||
132
+ side === 'center'
133
+ )
134
+ return side;
135
+ throw new TypeError('side must be "top", "right", "bottom", "left" or "center"');
136
+ }
137
+
86
138
  /**
87
139
  * A point on a rect's edge (or centre). `rect` is `{ x, y, width, height }`.
88
140
  * @param {Rect} rect
@@ -90,11 +142,11 @@ export function clamp(value, min, max) {
90
142
  * @returns {Point}
91
143
  */
92
144
  export function anchorPoint(rect, side = 'center') {
93
- const x = finite('rect.x', rect?.x, 0);
94
- const y = finite('rect.y', rect?.y, 0);
95
- const w = dimension('rect.width', rect?.width, 0);
96
- const h = dimension('rect.height', rect?.height, 0);
97
- switch (side) {
145
+ const x = finite('rect.x', rect?.x);
146
+ const y = finite('rect.y', rect?.y);
147
+ const w = dimension('rect.width', rect?.width);
148
+ const h = dimension('rect.height', rect?.height);
149
+ switch (sideValue(side)) {
98
150
  case 'top':
99
151
  return { x: x + w / 2, y };
100
152
  case 'bottom':
@@ -185,7 +237,8 @@ export function curvePath(from, to, opts = {}) {
185
237
  * @returns {string}
186
238
  */
187
239
  export function connectorPath(opts = {}) {
188
- const { from, to, shape = 'straight' } = opts;
240
+ const { from, to } = opts;
241
+ const shape = connectorShape(opts.shape);
189
242
  if (shape === 'elbow') return elbowPath(from, to, opts);
190
243
  if (shape === 'curve') return curvePath(from, to, opts);
191
244
  return straightPath(from, to);
@@ -270,7 +323,8 @@ export function autoSides(fromRect, toRect) {
270
323
  * @returns {number}
271
324
  */
272
325
  export function endTangentAngle(from, to, shape = 'straight') {
273
- if (shape === 'straight') return angleBetween(from, to);
326
+ const resolved = connectorShape(shape);
327
+ if (resolved === 'straight') return angleBetween(from, to);
274
328
  const dx = finite('to.x', to?.x) - finite('from.x', from?.x);
275
329
  const dy = finite('to.y', to?.y) - finite('from.y', from?.y);
276
330
  if (Math.abs(dx) >= Math.abs(dy)) return dx >= 0 ? 0 : Math.PI;
@@ -285,10 +339,14 @@ export function endTangentAngle(from, to, shape = 'straight') {
285
339
  * @returns {ConnectRectsResult}
286
340
  */
287
341
  export function connectRects(opts = {}) {
288
- const { fromRect, toRect, shape = 'straight', curvature, mid } = opts;
342
+ const { fromRect, toRect, curvature, mid } = opts;
343
+ const shape = connectorShape(opts.shape);
289
344
  // Honor each side override independently; auto-pick whichever is unset.
290
345
  const auto = autoSides(fromRect, toRect);
291
- const sides = { from: opts.fromSide || auto.from, to: opts.toSide || auto.to };
346
+ const sides = {
347
+ from: opts.fromSide == null ? auto.from : sideValue(opts.fromSide),
348
+ to: opts.toSide == null ? auto.to : sideValue(opts.toSide),
349
+ };
292
350
  const from = anchorPoint(fromRect, sides.from);
293
351
  const to = anchorPoint(toRect, sides.to);
294
352
  const d = connectorPath({ from, to, shape, curvature, mid });
@@ -1,7 +1,7 @@
1
1
  /* ==========================================================================
2
2
  analytical — convenience roll-up of the opt-in analytical-primitive layers.
3
3
 
4
- Import this one file instead of the seven leaves when you're building
4
+ Import this one file instead of the nine leaves when you're building
5
5
  analytical / generated-report UI:
6
6
 
7
7
  @import '@ponchia/ui';
@@ -13,9 +13,11 @@
13
13
  ========================================================================== */
14
14
 
15
15
  @import url('./annotations.css') layer(bronto);
16
+ @import url('./figure.css') layer(bronto);
16
17
  @import url('./legend.css') layer(bronto);
17
18
  @import url('./marks.css') layer(bronto);
18
19
  @import url('./connectors.css') layer(bronto);
19
20
  @import url('./spotlight.css') layer(bronto);
20
21
  @import url('./crosshair.css') layer(bronto);
21
22
  @import url('./selection.css') layer(bronto);
23
+ @import url('./highlights.css') layer(bronto);
@@ -278,8 +278,20 @@
278
278
  animation: none !important;
279
279
  opacity: 1;
280
280
  stroke-dashoffset: 0;
281
+ }
282
+
283
+ .ui-annotation__subject,
284
+ .ui-annotation__connector,
285
+ .ui-annotation__note-line,
286
+ .ui-annotation__badge {
281
287
  transform: none;
282
288
  }
289
+
290
+ .ui-annotation--draw .ui-annotation__connector,
291
+ .ui-annotation--draw .ui-annotation__note-line {
292
+ stroke-dasharray: none;
293
+ stroke-dashoffset: 0;
294
+ }
283
295
  }
284
296
 
285
297
  @media (forced-colors: active) {
package/css/app.css CHANGED
@@ -1,6 +1,6 @@
1
1
  /* ==========================================================================
2
- app — admin shell (sidebar + topbar + panels + metrics)
3
- For admin / operator dashboards.
2
+ app — service shell (sidebar + topbar + panels + metrics)
3
+ For app, service, admin, and operator dashboards.
4
4
  ========================================================================== */
5
5
 
6
6
  .ui-app-shell {
@@ -103,7 +103,7 @@
103
103
  .ui-app-nav a[aria-current]:not([aria-current='false']) {
104
104
  background: var(--accent-soft);
105
105
  border-inline-start-color: var(--accent);
106
- color: var(--accent-text);
106
+ color: var(--text);
107
107
  }
108
108
 
109
109
  @media (pointer: coarse) {
@@ -257,7 +257,7 @@
257
257
  /* --- Metric tiles ---
258
258
  The metric primitive itself is shell-agnostic and now lives in
259
259
  primitives.css as `.ui-stat` / `.ui-statgrid`; `.ui-app-metric*` and
260
- `.ui-app-metrics` remain as permanent admin-shell aliases (grouped on
260
+ `.ui-app-metrics` remain as permanent app-shell aliases (grouped on
261
261
  the canonical rules there — identical output). Nothing app-specific
262
262
  left to define here. */
263
263
 
package/css/clamp.css ADDED
@@ -0,0 +1,92 @@
1
+ /* ==========================================================================
2
+ clamp — opt-in bounded excerpt + reveal.
3
+
4
+ For source excerpts, claim basis, caveats, and evidence text that should
5
+ scan as a bounded block but remain reachable. Bronto owns the visual clamp
6
+ and optional CSS-only reveal affordance; the host owns content, IDs, and
7
+ whether truncation is appropriate. Not imported by core.css.
8
+ ========================================================================== */
9
+
10
+ .ui-clamp {
11
+ --clamp-lines: 4;
12
+
13
+ display: grid;
14
+ gap: var(--space-2xs);
15
+ min-inline-size: 0;
16
+ position: relative;
17
+ }
18
+
19
+ .ui-clamp__body {
20
+ -webkit-box-orient: vertical;
21
+ -webkit-line-clamp: var(--clamp-lines);
22
+ display: -webkit-box;
23
+ line-clamp: var(--clamp-lines);
24
+ mask-image: linear-gradient(to bottom, #000 72%, transparent 100%);
25
+ overflow: hidden;
26
+ }
27
+
28
+ .ui-clamp:has(> .ui-clamp__toggle:checked) .ui-clamp__body {
29
+ -webkit-line-clamp: unset;
30
+ display: block;
31
+ line-clamp: none;
32
+ mask-image: none;
33
+ }
34
+
35
+ .ui-clamp__toggle {
36
+ block-size: 1px;
37
+ clip-path: inset(50%);
38
+ inline-size: 1px;
39
+ overflow: hidden;
40
+ position: absolute;
41
+ white-space: nowrap;
42
+ }
43
+
44
+ .ui-clamp__control {
45
+ color: var(--accent-text);
46
+ cursor: pointer;
47
+ font-family: var(--mono);
48
+ font-size: var(--text-2xs);
49
+ inline-size: max-content;
50
+ letter-spacing: var(--tracking-wide);
51
+ text-decoration: underline;
52
+ text-decoration-color: color-mix(in srgb, var(--accent) 55%, transparent);
53
+ text-underline-offset: 0.2rem;
54
+ text-transform: uppercase;
55
+ }
56
+
57
+ .ui-clamp__toggle:focus-visible + .ui-clamp__body + .ui-clamp__control {
58
+ outline: 2px solid var(--focus-ring);
59
+ outline-offset: 2px;
60
+ }
61
+
62
+ .ui-clamp__less {
63
+ display: none;
64
+ }
65
+
66
+ .ui-clamp:has(> .ui-clamp__toggle:checked) .ui-clamp__more {
67
+ display: none;
68
+ }
69
+
70
+ .ui-clamp:has(> .ui-clamp__toggle:checked) .ui-clamp__less {
71
+ display: inline;
72
+ }
73
+
74
+ @media (hover: hover) {
75
+ .ui-clamp__control:hover {
76
+ text-decoration-color: currentColor;
77
+ }
78
+ }
79
+
80
+ @media print {
81
+ .ui-clamp__body {
82
+ -webkit-line-clamp: unset;
83
+ display: block;
84
+ line-clamp: none;
85
+ mask-image: none;
86
+ }
87
+
88
+ .ui-clamp__toggle,
89
+ .ui-clamp__control {
90
+ display: none;
91
+ }
92
+ }
package/css/crosshair.css CHANGED
@@ -13,6 +13,9 @@
13
13
  --crosshair-x: 0;
14
14
  --crosshair-y: 0;
15
15
  --crosshair-color: var(--accent);
16
+ --crosshair-readout-gap: 0.35rem;
17
+ --crosshair-readout-x: var(--crosshair-readout-gap);
18
+ --crosshair-readout-y: var(--crosshair-readout-gap);
16
19
 
17
20
  inset: 0;
18
21
  opacity: 0;
@@ -25,6 +28,22 @@
25
28
  opacity: 1;
26
29
  }
27
30
 
31
+ .ui-crosshair[data-readout-inline='before'] {
32
+ --crosshair-readout-x: calc(-100% - var(--crosshair-readout-gap));
33
+ }
34
+
35
+ .ui-crosshair[data-readout-block='above'] {
36
+ --crosshair-readout-y: calc(-100% - var(--crosshair-readout-gap));
37
+ }
38
+
39
+ .ui-crosshair:dir(rtl) {
40
+ --crosshair-readout-x: calc(100% + var(--crosshair-readout-gap));
41
+ }
42
+
43
+ .ui-crosshair:dir(rtl)[data-readout-inline='before'] {
44
+ --crosshair-readout-x: calc(-1 * var(--crosshair-readout-gap));
45
+ }
46
+
28
47
  @media (prefers-reduced-motion: reduce) {
29
48
  .ui-crosshair {
30
49
  transition: none;
@@ -76,8 +95,9 @@
76
95
  color: var(--text);
77
96
  }
78
97
 
79
- /* A pinned readout chip — host fills the content; it follows the crosshair. */
80
- .ui-readout {
98
+ /* A pinned readout chip — host fills the content; it follows the crosshair.
99
+ Scoped so report-kit/crosshair imports do not restyle standalone dot readouts. */
100
+ .ui-crosshair .ui-readout {
81
101
  background: var(--panel);
82
102
  border: 1px solid var(--line);
83
103
  border-radius: var(--radius-sm);
@@ -87,10 +107,15 @@
87
107
  font-size: var(--text-xs);
88
108
  inset-block-start: var(--crosshair-y);
89
109
  inset-inline-start: var(--crosshair-x);
110
+ max-inline-size: calc(100% - var(--crosshair-readout-gap) * 2);
111
+ overflow: hidden;
90
112
  padding-block: 0.2rem;
91
113
  padding-inline: 0.4rem;
92
114
  pointer-events: none;
93
115
  position: absolute;
116
+ text-overflow: ellipsis;
117
+ transform: translate(var(--crosshair-readout-x), var(--crosshair-readout-y));
118
+ white-space: nowrap;
94
119
  }
95
120
 
96
121
  @media (forced-colors: active) {
package/css/feedback.css CHANGED
@@ -335,9 +335,7 @@
335
335
  position: absolute;
336
336
  text-transform: uppercase;
337
337
  transform: translate(-50%, 4px);
338
- transition:
339
- opacity var(--duration-fast) var(--ease-standard),
340
- transform var(--duration-fast) var(--ease-standard);
338
+ transition: opacity var(--duration-fast) var(--ease-standard);
341
339
  white-space: nowrap;
342
340
  z-index: var(--z-popover);
343
341
  }
@@ -348,33 +346,6 @@
348
346
  transform: translate(-50%, 0);
349
347
  }
350
348
 
351
- /* Progressive enhancement: where CSS anchor positioning exists, lift
352
- the bubble out of the normal flow so it can't be clipped by an
353
- ancestor's overflow/scroll and auto-flips at the viewport edge.
354
- Unsupported browsers keep the absolutely-positioned fallback above
355
- (fine for short labels; use .ui-popover + initPopover for rich or
356
- edge-critical content). */
357
- @supports (anchor-name: --x) {
358
- .ui-tooltip {
359
- anchor-name: --ui-tooltip;
360
- }
361
-
362
- .ui-tooltip__bubble {
363
- inset: auto;
364
- margin-block-end: 0.5rem;
365
- position: fixed;
366
- position-anchor: --ui-tooltip;
367
- position-area: block-start center;
368
- position-try-fallbacks: flip-block;
369
- transform: translateY(4px);
370
- }
371
-
372
- .ui-tooltip:hover .ui-tooltip__bubble,
373
- .ui-tooltip:focus-within .ui-tooltip__bubble {
374
- transform: translateY(0);
375
- }
376
- }
377
-
378
349
  /* Popover surface — a top-layer panel positioned by initPopover (JS
379
350
  collision-aware, dependency-free). Uses the native [popover] top
380
351
  layer when available so it never clips; the class styles it either
@@ -389,6 +360,7 @@
389
360
  inline-size: max-content;
390
361
  margin: 0;
391
362
  max-inline-size: min(22rem, calc(100vw - 2rem));
363
+ overflow: auto;
392
364
  padding: var(--space-sm) var(--space-md);
393
365
  position: fixed;
394
366
  z-index: var(--z-popover);
package/css/figure.css ADDED
@@ -0,0 +1,102 @@
1
+ /* ==========================================================================
2
+ figure — opt-in analytical/report figure stage.
3
+
4
+ A frame for chart/diagram/media evidence: caption, stable stage, optional
5
+ overlay, key, and fallback data. Bronto owns only the layout grammar. The
6
+ host still owns data, scales, chart rendering, annotation content, and the
7
+ fallback table. Not imported by core.css.
8
+ ========================================================================== */
9
+
10
+ .ui-figure {
11
+ display: grid;
12
+ gap: var(--space-sm);
13
+ margin: 0;
14
+ min-inline-size: 0;
15
+ }
16
+
17
+ .ui-figure > *,
18
+ .ui-figure__body > *,
19
+ .ui-figure__stage > * {
20
+ min-inline-size: 0;
21
+ }
22
+
23
+ .ui-figure__caption {
24
+ color: var(--text-dim);
25
+ font-family: var(--mono);
26
+ font-size: var(--text-2xs);
27
+ letter-spacing: 0;
28
+ line-height: 1.5;
29
+ text-transform: uppercase;
30
+ }
31
+
32
+ .ui-figure__body {
33
+ align-items: start;
34
+ display: grid;
35
+ gap: var(--space-sm);
36
+ }
37
+
38
+ .ui-figure__body--key-right {
39
+ grid-template-columns: minmax(0, 1fr) minmax(min(100%, 10rem), var(--figure-key-width, 14rem));
40
+ }
41
+
42
+ .ui-figure__stage {
43
+ --figure-max-inline: 42rem;
44
+ --figure-min-block: 0px;
45
+
46
+ display: grid;
47
+ inline-size: min(100%, var(--figure-max-inline));
48
+ margin-inline: auto;
49
+ min-block-size: var(--figure-min-block);
50
+ place-items: center;
51
+ position: relative;
52
+ }
53
+
54
+ .ui-figure__media {
55
+ display: block;
56
+ inline-size: 100%;
57
+ max-inline-size: 100%;
58
+ }
59
+
60
+ .ui-figure__stage > :is(canvas, img, picture, svg):not(.ui-figure__overlay) {
61
+ display: block;
62
+ max-inline-size: 100%;
63
+ }
64
+
65
+ .ui-figure__overlay {
66
+ block-size: 100%;
67
+ inline-size: 100%;
68
+ inset: 0;
69
+ overflow: visible;
70
+ pointer-events: none;
71
+ position: absolute;
72
+ }
73
+
74
+ .ui-figure__key,
75
+ .ui-figure__data {
76
+ display: grid;
77
+ gap: var(--space-xs);
78
+ }
79
+
80
+ .ui-figure__data {
81
+ margin-block-start: var(--space-xs);
82
+ }
83
+
84
+ @media (max-width: 44rem) {
85
+ .ui-figure__body--key-right {
86
+ grid-template-columns: 1fr;
87
+ }
88
+ }
89
+
90
+ @media print {
91
+ .ui-figure {
92
+ break-inside: avoid;
93
+ }
94
+
95
+ .ui-figure__body {
96
+ display: block;
97
+ }
98
+
99
+ .ui-figure__body > * + * {
100
+ margin-block-start: var(--space-sm);
101
+ }
102
+ }
@@ -0,0 +1,50 @@
1
+ /* ==========================================================================
2
+ highlights — opt-in CSS Custom Highlight API paint.
3
+
4
+ Named text ranges for cited evidence, search hits, and current match. The
5
+ HOST creates Range objects and registers Highlight instances in CSS.highlights
6
+ under the documented names. Bronto only paints the registered ranges. Not
7
+ imported by core.css.
8
+ ========================================================================== */
9
+
10
+ .ui-highlights {
11
+ --highlight-evidence: var(--accent-soft);
12
+ --highlight-search: var(--warning-soft);
13
+ --highlight-current: color-mix(in srgb, var(--accent) 22%, transparent);
14
+ }
15
+
16
+ .ui-highlights::highlight(bronto-evidence) {
17
+ background-color: var(--highlight-evidence);
18
+ color: inherit;
19
+ text-decoration-color: var(--accent);
20
+ text-decoration-line: underline;
21
+ text-decoration-thickness: 0.1em;
22
+ text-underline-offset: 0.18em;
23
+ }
24
+
25
+ .ui-highlights::highlight(bronto-search) {
26
+ background-color: var(--highlight-search);
27
+ color: inherit;
28
+ }
29
+
30
+ .ui-highlights::highlight(bronto-current) {
31
+ background-color: var(--highlight-current);
32
+ color: var(--text);
33
+ }
34
+
35
+ @media (forced-colors: active) {
36
+ .ui-highlights::highlight(bronto-evidence),
37
+ .ui-highlights::highlight(bronto-search),
38
+ .ui-highlights::highlight(bronto-current) {
39
+ background-color: Highlight;
40
+ color: HighlightText;
41
+ text-decoration-color: HighlightText;
42
+ }
43
+ }
44
+
45
+ @media print {
46
+ .ui-highlights {
47
+ -webkit-print-color-adjust: exact;
48
+ print-color-adjust: exact;
49
+ }
50
+ }
@@ -0,0 +1,90 @@
1
+ /* ==========================================================================
2
+ interval — opt-in low/high uncertainty span.
3
+
4
+ A compact report primitive for "the value is somewhere in this range" —
5
+ confidence intervals, estimates, budget windows, and measured uncertainty.
6
+ Boundary: the HOST normalises `--lo`, `--hi`, and optional `--v` to 0..1 and
7
+ writes the accessible label. Bronto only paints geometry. Not imported by
8
+ core.css.
9
+ ========================================================================== */
10
+
11
+ .ui-interval {
12
+ --lo: 0;
13
+ --hi: 1;
14
+
15
+ display: grid;
16
+ gap: 0.35rem;
17
+ inline-size: 100%;
18
+ min-inline-size: 0;
19
+ }
20
+
21
+ .ui-interval__track {
22
+ background: var(--surface-4);
23
+ block-size: 0.7rem;
24
+ border: 1px solid var(--line);
25
+ border-radius: var(--radius-pill);
26
+ min-inline-size: 8rem;
27
+ position: relative;
28
+ }
29
+
30
+ .ui-interval__range {
31
+ background: color-mix(in srgb, var(--accent) 26%, transparent);
32
+ border: 1px solid color-mix(in srgb, var(--accent) 58%, var(--line));
33
+ border-radius: var(--radius-pill);
34
+ inline-size: max(2px, calc((var(--hi) - var(--lo)) * 100%));
35
+ inset-block: 18%;
36
+ inset-inline-start: calc(var(--lo) * 100%);
37
+ position: absolute;
38
+ }
39
+
40
+ .ui-interval__point {
41
+ background: var(--text);
42
+ border: 2px solid var(--panel);
43
+ border-radius: 50%;
44
+ block-size: 0.72rem;
45
+ box-shadow: 0 0 0 1px var(--line-strong);
46
+ inline-size: 0.72rem;
47
+ inset-block-start: 50%;
48
+ inset-inline-start: calc(var(--v, var(--lo)) * 100%);
49
+ position: absolute;
50
+ transform: translate(-50%, -50%);
51
+ }
52
+
53
+ .ui-interval__label,
54
+ .ui-interval__bounds {
55
+ color: var(--text-dim);
56
+ font-family: var(--mono);
57
+ font-size: var(--text-2xs);
58
+ letter-spacing: 0;
59
+ }
60
+
61
+ .ui-interval__label {
62
+ color: var(--text-soft);
63
+ }
64
+
65
+ .ui-interval__bounds {
66
+ display: flex;
67
+ gap: var(--space-sm);
68
+ justify-content: space-between;
69
+ }
70
+
71
+ @media (forced-colors: active) {
72
+ .ui-interval__track,
73
+ .ui-interval__range,
74
+ .ui-interval__point {
75
+ border-color: CanvasText;
76
+ }
77
+
78
+ .ui-interval__range,
79
+ .ui-interval__point {
80
+ background: CanvasText;
81
+ }
82
+ }
83
+
84
+ @media print {
85
+ .ui-interval__range,
86
+ .ui-interval__point {
87
+ -webkit-print-color-adjust: exact;
88
+ print-color-adjust: exact;
89
+ }
90
+ }
@@ -70,6 +70,18 @@
70
70
  transform: translateX(0.6rem);
71
71
  }
72
72
 
73
+ @media (prefers-color-scheme: dark) {
74
+ :root:not([data-theme='light']) .ui-themetoggle__thumb {
75
+ background: var(--accent);
76
+ transform: translateX(0.6rem);
77
+ }
78
+
79
+ :root[dir='rtl']:not([data-theme='light']) .ui-themetoggle__thumb,
80
+ :root:not([data-theme='light']) [dir='rtl'] .ui-themetoggle__thumb {
81
+ transform: translateX(-0.6rem);
82
+ }
83
+ }
84
+
73
85
  [dir='rtl'][data-theme='dark'] .ui-themetoggle__thumb,
74
86
  [dir='rtl'] [data-theme='dark'] .ui-themetoggle__thumb {
75
87
  transform: translateX(-0.6rem);
@@ -569,9 +569,8 @@
569
569
  }
570
570
 
571
571
  .ui-chip--accent {
572
- /* accent-strong, not accent: chip text is small uppercase mono and must
573
- clear WCAG AA 4.5:1 — same rule as .ui-eyebrow above. */
574
- color: var(--accent-text);
572
+ /* Tone rides the border + hover tint; text stays neutral-readable. */
573
+ color: var(--text-soft);
575
574
  border-color: var(--accent);
576
575
  }
577
576