@ponchia/ui 0.6.6 → 0.6.7

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 (108) hide show
  1. package/CHANGELOG.md +105 -6
  2. package/README.md +36 -23
  3. package/behaviors/carousel.d.ts.map +1 -1
  4. package/behaviors/carousel.js +3 -0
  5. package/behaviors/dialog.d.ts.map +1 -1
  6. package/behaviors/dialog.js +14 -8
  7. package/behaviors/forms.d.ts.map +1 -1
  8. package/behaviors/forms.js +11 -5
  9. package/behaviors/index.d.ts +2 -0
  10. package/behaviors/index.d.ts.map +1 -1
  11. package/behaviors/index.js +2 -0
  12. package/behaviors/internal.d.ts +2 -1
  13. package/behaviors/internal.d.ts.map +1 -1
  14. package/behaviors/internal.js +23 -3
  15. package/behaviors/legend.d.ts.map +1 -1
  16. package/behaviors/legend.js +41 -9
  17. package/behaviors/splitter.d.ts +26 -0
  18. package/behaviors/splitter.d.ts.map +1 -0
  19. package/behaviors/splitter.js +200 -0
  20. package/behaviors/table.js +3 -3
  21. package/behaviors/theme.js +2 -2
  22. package/classes/classes.json +230 -4
  23. package/classes/index.d.ts +49 -1
  24. package/classes/index.js +56 -1
  25. package/classes/vscode.css-custom-data.json +1 -1
  26. package/css/analytical.css +3 -1
  27. package/css/app.css +4 -4
  28. package/css/clamp.css +92 -0
  29. package/css/figure.css +102 -0
  30. package/css/highlights.css +50 -0
  31. package/css/interval.css +90 -0
  32. package/css/primitives.css +2 -3
  33. package/css/report-kit.css +38 -0
  34. package/css/report.css +23 -4
  35. package/css/sidenote.css +12 -2
  36. package/css/site.css +2 -1
  37. package/css/sources.css +5 -0
  38. package/css/state.css +120 -1
  39. package/css/table.css +4 -0
  40. package/css/tokens.css +9 -9
  41. package/css/workbench.css +101 -8
  42. package/dist/bronto.css +1 -1
  43. package/dist/css/analytical.css +1 -1
  44. package/dist/css/app.css +1 -1
  45. package/dist/css/clamp.css +1 -0
  46. package/dist/css/figure.css +1 -0
  47. package/dist/css/highlights.css +1 -0
  48. package/dist/css/interval.css +1 -0
  49. package/dist/css/primitives.css +1 -1
  50. package/dist/css/report-kit.css +1 -0
  51. package/dist/css/report.css +1 -1
  52. package/dist/css/sidenote.css +1 -1
  53. package/dist/css/site.css +1 -1
  54. package/dist/css/sources.css +1 -1
  55. package/dist/css/state.css +1 -1
  56. package/dist/css/table.css +1 -1
  57. package/dist/css/tokens.css +1 -1
  58. package/dist/css/workbench.css +1 -1
  59. package/docs/adr/0002-scope-and-2026-baseline.md +1 -1
  60. package/docs/architecture.md +67 -43
  61. package/docs/clamp.md +49 -0
  62. package/docs/contrast.md +34 -24
  63. package/docs/d2.md +37 -0
  64. package/docs/figure.md +71 -0
  65. package/docs/frontier-primitives.md +25 -24
  66. package/docs/highlights.md +52 -0
  67. package/docs/interop/tailwind.md +148 -0
  68. package/docs/interval.md +55 -0
  69. package/docs/legends.md +3 -2
  70. package/docs/mermaid.md +6 -0
  71. package/docs/migrations/0.2-to-0.3.md +80 -0
  72. package/docs/migrations/0.3-to-0.4.md +48 -0
  73. package/docs/migrations/0.4-to-0.5.md +96 -0
  74. package/docs/migrations/0.5-to-0.6.md +82 -0
  75. package/docs/package-contract.md +40 -2
  76. package/docs/reference.md +78 -5
  77. package/docs/reporting.md +113 -58
  78. package/docs/sidenote.md +7 -1
  79. package/docs/sources.md +1 -1
  80. package/docs/stability.md +5 -3
  81. package/docs/state.md +67 -10
  82. package/docs/theming.md +10 -2
  83. package/docs/usage.md +31 -11
  84. package/docs/workbench.md +59 -18
  85. package/llms.txt +80 -12
  86. package/package.json +66 -6
  87. package/qwik/index.d.ts +1 -0
  88. package/qwik/index.d.ts.map +1 -1
  89. package/qwik/index.js +26 -21
  90. package/react/index.d.ts +1 -0
  91. package/react/index.d.ts.map +1 -1
  92. package/react/index.js +4 -1
  93. package/schemas/report-claims.v1.schema.json +137 -0
  94. package/solid/index.d.ts +2 -0
  95. package/solid/index.d.ts.map +1 -1
  96. package/solid/index.js +3 -0
  97. package/svelte/index.d.ts +88 -0
  98. package/svelte/index.d.ts.map +1 -0
  99. package/svelte/index.js +166 -0
  100. package/tailwind.css +87 -0
  101. package/tokens/figma.variables.json +2241 -0
  102. package/tokens/index.js +1 -1
  103. package/tokens/index.json +2 -2
  104. package/tokens/resolved.json +3 -3
  105. package/tokens/tokens.dtcg.json +1 -1
  106. package/vue/index.d.ts +79 -0
  107. package/vue/index.d.ts.map +1 -0
  108. package/vue/index.js +197 -0
@@ -0,0 +1,200 @@
1
+ import { hasDom, resolveHost, noop, bindOnce, collectHosts } from './internal.js';
2
+
3
+ const SELECTOR = '[data-bronto-splitter]';
4
+ const HANDLE_SELECTOR = '.ui-splitter__handle';
5
+ const DEFAULT_MIN = 20;
6
+ const DEFAULT_MAX = 80;
7
+ const DEFAULT_VALUE = 50;
8
+ const STEP = 2;
9
+ const LARGE_STEP = 10;
10
+
11
+ /**
12
+ * @typedef {object} SplitterResizeDetail
13
+ * @property {number} value The first pane size as a 0..100 percentage.
14
+ * @property {'vertical' | 'horizontal'} orientation Splitter orientation.
15
+ */
16
+
17
+ const num = (v, fallback) => {
18
+ const n = Number.parseFloat(String(v ?? '').trim());
19
+ return Number.isFinite(n) ? n : fallback;
20
+ };
21
+
22
+ const fmt = (v) => String(Math.round(v * 10) / 10);
23
+
24
+ const clamp = (v, min, max) => Math.min(max, Math.max(min, v));
25
+
26
+ const readCssValue = (splitter) => splitter.style.getPropertyValue('--splitter-pos');
27
+
28
+ const readOrientation = (splitter, handle) => {
29
+ const data = splitter.getAttribute('data-bronto-splitter');
30
+ if (data === 'horizontal' || data === 'vertical') return data;
31
+ if (splitter.classList.contains('ui-splitter--horizontal')) return 'horizontal';
32
+ if (splitter.classList.contains('ui-splitter--vertical')) return 'vertical';
33
+ return handle.getAttribute('aria-orientation') === 'horizontal' ? 'horizontal' : 'vertical';
34
+ };
35
+
36
+ const getView = (el) => el.ownerDocument?.defaultView || null;
37
+
38
+ const dispatchResize = (splitter, detail) => {
39
+ splitter.dispatchEvent(
40
+ new CustomEvent('bronto:splitter:resize', {
41
+ bubbles: true,
42
+ detail,
43
+ }),
44
+ );
45
+ };
46
+
47
+ function wireSplitter(splitter) {
48
+ const handle = splitter.querySelector(HANDLE_SELECTOR);
49
+ if (!handle) return noop;
50
+
51
+ const orientation = readOrientation(splitter, handle);
52
+ const min = num(handle.getAttribute('aria-valuemin'), DEFAULT_MIN);
53
+ const max = Math.max(min, num(handle.getAttribute('aria-valuemax'), DEFAULT_MAX));
54
+ let value = clamp(
55
+ num(handle.getAttribute('aria-valuenow'), num(readCssValue(splitter), DEFAULT_VALUE)),
56
+ min,
57
+ max,
58
+ );
59
+ let activePointer = null;
60
+
61
+ const apply = (next, { emit = true } = {}) => {
62
+ value = clamp(next, min, max);
63
+ const label = fmt(value);
64
+ splitter.style.setProperty('--splitter-pos', `${label}%`);
65
+ handle.setAttribute('aria-valuenow', label);
66
+ if (emit) dispatchResize(splitter, { value, orientation });
67
+ };
68
+
69
+ if (!handle.hasAttribute('role')) handle.setAttribute('role', 'separator');
70
+ if (!handle.hasAttribute('tabindex')) handle.tabIndex = 0;
71
+ if (!handle.hasAttribute('aria-orientation'))
72
+ handle.setAttribute('aria-orientation', orientation);
73
+ if (!handle.hasAttribute('aria-valuemin')) handle.setAttribute('aria-valuemin', fmt(min));
74
+ if (!handle.hasAttribute('aria-valuemax')) handle.setAttribute('aria-valuemax', fmt(max));
75
+ apply(value, { emit: false });
76
+
77
+ const fromPointer = (event) => {
78
+ const rect = splitter.getBoundingClientRect();
79
+ const size = orientation === 'horizontal' ? rect.height : rect.width;
80
+ if (!size) return value;
81
+ if (orientation === 'horizontal') {
82
+ return ((event.clientY - rect.top) / size) * 100;
83
+ }
84
+ const view = getView(splitter);
85
+ const dir = view?.getComputedStyle?.(splitter).direction;
86
+ const x = dir === 'rtl' ? rect.right - event.clientX : event.clientX - rect.left;
87
+ return (x / size) * 100;
88
+ };
89
+
90
+ const capturePointer = (pointerId) => {
91
+ if (pointerId === undefined || pointerId === null) return;
92
+ try {
93
+ handle.setPointerCapture?.(pointerId);
94
+ } catch {
95
+ /* Pointer capture is an affordance; drag still works through document listeners. */
96
+ }
97
+ };
98
+
99
+ const releasePointer = (pointerId = activePointer) => {
100
+ if (pointerId === undefined || pointerId === null) return;
101
+ try {
102
+ if (!handle.hasPointerCapture || handle.hasPointerCapture(pointerId)) {
103
+ handle.releasePointerCapture?.(pointerId);
104
+ }
105
+ } catch {
106
+ /* The element may have been removed or capture may already be gone. */
107
+ }
108
+ };
109
+
110
+ const onKeydown = (event) => {
111
+ let next = value;
112
+ if (event.key === 'Home') next = min;
113
+ else if (event.key === 'End') next = max;
114
+ else if (event.key === 'PageUp') next += LARGE_STEP;
115
+ else if (event.key === 'PageDown') next -= LARGE_STEP;
116
+ else if (event.key === 'ArrowRight' || event.key === 'ArrowDown')
117
+ next += event.shiftKey ? LARGE_STEP : STEP;
118
+ else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp')
119
+ next -= event.shiftKey ? LARGE_STEP : STEP;
120
+ else return;
121
+ event.preventDefault();
122
+ apply(next);
123
+ };
124
+
125
+ const onPointerMove = (event) => {
126
+ if (
127
+ activePointer !== null &&
128
+ event.pointerId !== undefined &&
129
+ event.pointerId !== activePointer
130
+ )
131
+ return;
132
+ apply(fromPointer(event));
133
+ };
134
+
135
+ const onPointerUp = (event) => {
136
+ if (
137
+ activePointer !== null &&
138
+ event.pointerId !== undefined &&
139
+ event.pointerId !== activePointer
140
+ )
141
+ return;
142
+ releasePointer(event.pointerId);
143
+ activePointer = null;
144
+ handle.classList.remove('is-active');
145
+ splitter.ownerDocument.removeEventListener('pointermove', onPointerMove);
146
+ splitter.ownerDocument.removeEventListener('pointerup', onPointerUp);
147
+ splitter.ownerDocument.removeEventListener('pointercancel', onPointerUp);
148
+ };
149
+
150
+ const onPointerDown = (event) => {
151
+ if (event.button !== undefined && event.button !== 0) return;
152
+ event.preventDefault();
153
+ activePointer = event.pointerId ?? null;
154
+ capturePointer(activePointer);
155
+ handle.classList.add('is-active');
156
+ apply(fromPointer(event));
157
+ splitter.ownerDocument.addEventListener('pointermove', onPointerMove);
158
+ splitter.ownerDocument.addEventListener('pointerup', onPointerUp);
159
+ splitter.ownerDocument.addEventListener('pointercancel', onPointerUp);
160
+ };
161
+
162
+ return bindOnce(splitter, 'splitter', () => {
163
+ handle.addEventListener('keydown', onKeydown);
164
+ handle.addEventListener('pointerdown', onPointerDown);
165
+ return () => {
166
+ handle.removeEventListener('keydown', onKeydown);
167
+ handle.removeEventListener('pointerdown', onPointerDown);
168
+ splitter.ownerDocument.removeEventListener('pointermove', onPointerMove);
169
+ splitter.ownerDocument.removeEventListener('pointerup', onPointerUp);
170
+ splitter.ownerDocument.removeEventListener('pointercancel', onPointerUp);
171
+ releasePointer();
172
+ handle.classList.remove('is-active');
173
+ activePointer = null;
174
+ };
175
+ });
176
+ }
177
+
178
+ /**
179
+ * Wire focusable ARIA splitters. Each `[data-bronto-splitter]` host contains
180
+ * two `.ui-splitter__pane` elements separated by one `.ui-splitter__handle`
181
+ * (`role="separator"`). The behavior keeps `--splitter-pos` and
182
+ * `aria-valuenow` in sync for keyboard and pointer resizing, then dispatches
183
+ * `bronto:splitter:resize` with `{ value, orientation }`.
184
+ *
185
+ * Bronto owns the control affordance only. The host owns pane content,
186
+ * persistence, min/max policy, collapse behavior, and any saved layout state.
187
+ * SSR-safe and idempotent per splitter; returns a cleanup function.
188
+ *
189
+ * @param {import('./internal.js').DelegateOpts} [opts]
190
+ * @returns {import('./internal.js').Cleanup}
191
+ */
192
+ export function initSplitter({ root } = {}) {
193
+ if (!hasDom()) return noop;
194
+ const host = resolveHost(root);
195
+ if (!host) return noop;
196
+ const splitters = collectHosts(host, SELECTOR);
197
+ if (!splitters.length) return noop;
198
+ const cleanups = splitters.map(wireSplitter).filter(Boolean);
199
+ return () => cleanups.forEach((fn) => fn());
200
+ }
@@ -49,6 +49,7 @@ export function initTableSort({ root } = {}) {
49
49
  // announces the column as sortable from the start (it was unset until the
50
50
  // first click — C10).
51
51
  for (const sort of table.querySelectorAll('.ui-table__sort')) {
52
+ if (sort.tagName === 'BUTTON' && !sort.hasAttribute('type')) sort.type = 'button';
52
53
  const th = sort.closest('th');
53
54
  if (th && !th.hasAttribute('aria-sort')) th.setAttribute('aria-sort', 'none');
54
55
  }
@@ -110,9 +111,8 @@ export function initTableSort({ root } = {}) {
110
111
  return cmp * sign;
111
112
  });
112
113
  // Re-parent in document order: sorted data rows, then any empty/sentinel
113
- // row last. A single appendChild pass over the existing <tr> nodes (no
114
- // markup is created — these are trusted DOM elements being moved).
115
- for (const r of [...rows, ...emptyRows]) tbody.appendChild(r);
114
+ // row last. These are existing <tr> nodes being moved; no markup is parsed.
115
+ tbody.append(...rows, ...emptyRows);
116
116
  };
117
117
 
118
118
  const allBox = table.querySelector('[data-bronto-select-all]');
@@ -1,4 +1,4 @@
1
- import { hasDom, resolveHost, noop, bindOnce } from './internal.js';
1
+ import { hasDom, resolveHost, noop, bindOnce, collectHosts } from './internal.js';
2
2
 
3
3
  const THEMES = ['light', 'dark'];
4
4
 
@@ -66,7 +66,7 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
66
66
 
67
67
  const reflect = () => {
68
68
  const c = current();
69
- host.querySelectorAll('[data-bronto-theme-toggle]').forEach((el) => {
69
+ collectHosts(host, '[data-bronto-theme-toggle]').forEach((el) => {
70
70
  const forced = el.getAttribute('data-bronto-theme-toggle');
71
71
  // A forced control is "pressed" when its theme is the active one;
72
72
  // a plain toggle reflects whether dark is active.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$comment": "@ponchia/ui class vocabulary as language-neutral data — validate emitted markup without executing the ESM cls map or parsing the .d.ts. Generated from classes/index.js — do not edit by hand; run `npm run classes:json:build`. Drift-checked in CI. `groups[].base` is null for a parts-only namespace (no standalone base class — do NOT emit it). A modifier whose name contains `__` (e.g. `ui-spark__bar--pos`) attaches to THAT part, not the base host. `states` is the author-applied `is-*` hooks (runtime/behavior-managed hooks are excluded); `behaviorAttributes` are the `data-bronto-*` wiring hooks the optional behaviors delegate on; `requiredAria` is the role/aria a generator must emit per component. `states` + `customProperties` are documented in docs/reference.md and ship outside `cls` by design.",
3
3
  "counts": {
4
- "classes": 593,
5
- "groups": 171
4
+ "classes": 634,
5
+ "groups": 177
6
6
  },
7
7
  "groups": {
8
8
  "ui-accordion": {
@@ -322,6 +322,17 @@
322
322
  "ui-claim__status"
323
323
  ]
324
324
  },
325
+ "ui-clamp": {
326
+ "base": "ui-clamp",
327
+ "modifiers": [],
328
+ "parts": [
329
+ "ui-clamp__body",
330
+ "ui-clamp__control",
331
+ "ui-clamp__less",
332
+ "ui-clamp__more",
333
+ "ui-clamp__toggle"
334
+ ]
335
+ },
325
336
  "ui-cluster": {
326
337
  "base": "ui-cluster",
327
338
  "modifiers": [
@@ -583,6 +594,21 @@
583
594
  "modifiers": [],
584
595
  "parts": []
585
596
  },
597
+ "ui-figure": {
598
+ "base": "ui-figure",
599
+ "modifiers": [
600
+ "ui-figure__body--key-right"
601
+ ],
602
+ "parts": [
603
+ "ui-figure__body",
604
+ "ui-figure__caption",
605
+ "ui-figure__data",
606
+ "ui-figure__key",
607
+ "ui-figure__media",
608
+ "ui-figure__overlay",
609
+ "ui-figure__stage"
610
+ ]
611
+ },
586
612
  "ui-file": {
587
613
  "base": "ui-file",
588
614
  "modifiers": [],
@@ -613,6 +639,11 @@
613
639
  "modifiers": [],
614
640
  "parts": []
615
641
  },
642
+ "ui-highlights": {
643
+ "base": "ui-highlights",
644
+ "modifiers": [],
645
+ "parts": []
646
+ },
616
647
  "ui-hint": {
617
648
  "base": "ui-hint",
618
649
  "modifiers": [
@@ -654,6 +685,37 @@
654
685
  "ui-inspector__head"
655
686
  ]
656
687
  },
688
+ "ui-interval": {
689
+ "base": "ui-interval",
690
+ "modifiers": [],
691
+ "parts": [
692
+ "ui-interval__bounds",
693
+ "ui-interval__label",
694
+ "ui-interval__point",
695
+ "ui-interval__range",
696
+ "ui-interval__track"
697
+ ]
698
+ },
699
+ "ui-job": {
700
+ "base": "ui-job",
701
+ "modifiers": [
702
+ "ui-job--blocked",
703
+ "ui-job--compact",
704
+ "ui-job--complete",
705
+ "ui-job--failed",
706
+ "ui-job--queued",
707
+ "ui-job--running"
708
+ ],
709
+ "parts": [
710
+ "ui-job__actions",
711
+ "ui-job__bar",
712
+ "ui-job__body",
713
+ "ui-job__head",
714
+ "ui-job__meta",
715
+ "ui-job__progress",
716
+ "ui-job__title"
717
+ ]
718
+ },
657
719
  "ui-kbd": {
658
720
  "base": "ui-kbd",
659
721
  "modifiers": [],
@@ -1134,6 +1196,17 @@
1134
1196
  "modifiers": [],
1135
1197
  "parts": []
1136
1198
  },
1199
+ "ui-splitter": {
1200
+ "base": "ui-splitter",
1201
+ "modifiers": [
1202
+ "ui-splitter--horizontal",
1203
+ "ui-splitter--vertical"
1204
+ ],
1205
+ "parts": [
1206
+ "ui-splitter__handle",
1207
+ "ui-splitter__pane"
1208
+ ]
1209
+ },
1137
1210
  "ui-spotlight": {
1138
1211
  "base": "ui-spotlight",
1139
1212
  "modifiers": [
@@ -1554,6 +1627,12 @@
1554
1627
  "ui-claim__scope",
1555
1628
  "ui-claim__statement",
1556
1629
  "ui-claim__status",
1630
+ "ui-clamp",
1631
+ "ui-clamp__body",
1632
+ "ui-clamp__control",
1633
+ "ui-clamp__less",
1634
+ "ui-clamp__more",
1635
+ "ui-clamp__toggle",
1557
1636
  "ui-cluster",
1558
1637
  "ui-cluster--between",
1559
1638
  "ui-code",
@@ -1667,6 +1746,15 @@
1667
1746
  "ui-eyebrow--muted",
1668
1747
  "ui-eyebrow--sm",
1669
1748
  "ui-field",
1749
+ "ui-figure",
1750
+ "ui-figure__body",
1751
+ "ui-figure__body--key-right",
1752
+ "ui-figure__caption",
1753
+ "ui-figure__data",
1754
+ "ui-figure__key",
1755
+ "ui-figure__media",
1756
+ "ui-figure__overlay",
1757
+ "ui-figure__stage",
1670
1758
  "ui-file",
1671
1759
  "ui-generated",
1672
1760
  "ui-generated__label",
@@ -1675,6 +1763,7 @@
1675
1763
  "ui-glossary__term",
1676
1764
  "ui-grid",
1677
1765
  "ui-halftone",
1766
+ "ui-highlights",
1678
1767
  "ui-hint",
1679
1768
  "ui-hint--error",
1680
1769
  "ui-icon",
@@ -1687,6 +1776,26 @@
1687
1776
  "ui-inspector",
1688
1777
  "ui-inspector__body",
1689
1778
  "ui-inspector__head",
1779
+ "ui-interval",
1780
+ "ui-interval__bounds",
1781
+ "ui-interval__label",
1782
+ "ui-interval__point",
1783
+ "ui-interval__range",
1784
+ "ui-interval__track",
1785
+ "ui-job",
1786
+ "ui-job--blocked",
1787
+ "ui-job--compact",
1788
+ "ui-job--complete",
1789
+ "ui-job--failed",
1790
+ "ui-job--queued",
1791
+ "ui-job--running",
1792
+ "ui-job__actions",
1793
+ "ui-job__bar",
1794
+ "ui-job__body",
1795
+ "ui-job__head",
1796
+ "ui-job__meta",
1797
+ "ui-job__progress",
1798
+ "ui-job__title",
1690
1799
  "ui-kbd",
1691
1800
  "ui-keep",
1692
1801
  "ui-key-value",
@@ -1895,6 +2004,11 @@
1895
2004
  "ui-spark__bar--neg",
1896
2005
  "ui-spark__bar--pos",
1897
2006
  "ui-spinner",
2007
+ "ui-splitter",
2008
+ "ui-splitter--horizontal",
2009
+ "ui-splitter--vertical",
2010
+ "ui-splitter__handle",
2011
+ "ui-splitter__pane",
1898
2012
  "ui-spotlight",
1899
2013
  "ui-spotlight--ring",
1900
2014
  "ui-spotlight__hole",
@@ -2026,7 +2140,7 @@
2026
2140
  {
2027
2141
  "class": "is-open",
2028
2142
  "scope": "controlled .ui-modal / .ui-popover",
2029
- "effect": "open state (focus-trap is the host's)"
2143
+ "effect": "open visibility state; focus management belongs to the matching behavior/host"
2030
2144
  },
2031
2145
  {
2032
2146
  "class": "is-active",
@@ -2090,6 +2204,13 @@
2090
2204
  "example": "92",
2091
2205
  "note": "measured value as a UNITLESS number 0..100 (registered <number>, never a %). Set it on the meter/progress host — it inherits to the __fill/__bar. Prefer attrs.meter(value)/attrs.progress(value) from @ponchia/ui/classes, which sets it plus role + aria-valuenow/min/max together."
2092
2206
  },
2207
+ {
2208
+ "name": "--job-progress",
2209
+ "on": ".ui-job",
2210
+ "type": "percentage",
2211
+ "example": "64%",
2212
+ "note": "determinate background-job progress. Set it on the .ui-job host; pair the .ui-job__progress element with role=\"progressbar\" and matching aria-valuenow/min/max when the value is known."
2213
+ },
2093
2214
  {
2094
2215
  "name": "--icon-mask",
2095
2216
  "on": ".ui-icon",
@@ -2142,6 +2263,27 @@
2142
2263
  "example": "var(--accent-soft)",
2143
2264
  "note": "the ::target-text highlight wash for the matched cited sentence (default var(--accent-soft)). The host builds the #:~:text= href; Bronto owns the paint — see docs/textref.md."
2144
2265
  },
2266
+ {
2267
+ "name": "--figure-max-inline",
2268
+ "on": ".ui-figure__stage",
2269
+ "type": "length",
2270
+ "example": "32rem",
2271
+ "note": "maximum inline size of the centered figure stage (default 42rem). The host still owns chart/media dimensions and data mapping."
2272
+ },
2273
+ {
2274
+ "name": "--figure-min-block",
2275
+ "on": ".ui-figure__stage",
2276
+ "type": "length",
2277
+ "example": "16rem",
2278
+ "note": "minimum block size reserved for a live or late-rendered figure stage (default 0px)."
2279
+ },
2280
+ {
2281
+ "name": "--figure-key-width",
2282
+ "on": ".ui-figure__body--key-right",
2283
+ "type": "length",
2284
+ "example": "12rem",
2285
+ "note": "width of the right-side key column before the figure body collapses to one column (default 14rem)."
2286
+ },
2145
2287
  {
2146
2288
  "name": "--v",
2147
2289
  "on": ".ui-bullet__measure",
@@ -2178,6 +2320,78 @@
2178
2320
  "example": "1rem",
2179
2321
  "note": "the sticky inset from the top of the scroll container (default var(--space-md))."
2180
2322
  },
2323
+ {
2324
+ "name": "--lo",
2325
+ "on": ".ui-interval",
2326
+ "type": "number 0..1",
2327
+ "example": "0.22",
2328
+ "required": true,
2329
+ "note": "REQUIRED — normalised lower bound of the interval. The host normalises; Bronto only paints."
2330
+ },
2331
+ {
2332
+ "name": "--hi",
2333
+ "on": ".ui-interval",
2334
+ "type": "number 0..1",
2335
+ "example": "0.68",
2336
+ "required": true,
2337
+ "note": "REQUIRED — normalised upper bound of the interval. The host normalises; Bronto only paints."
2338
+ },
2339
+ {
2340
+ "name": "--v",
2341
+ "on": ".ui-interval__point",
2342
+ "type": "number 0..1",
2343
+ "example": "0.52",
2344
+ "note": "optional normalised point estimate inside the interval. Omit the point element when there is no point estimate."
2345
+ },
2346
+ {
2347
+ "name": "--clamp-lines",
2348
+ "on": ".ui-clamp",
2349
+ "type": "integer",
2350
+ "example": "3",
2351
+ "note": "number of lines shown before the optional reveal control expands the excerpt (default 4)."
2352
+ },
2353
+ {
2354
+ "name": "--highlight-evidence",
2355
+ "on": ".ui-highlights",
2356
+ "type": "color",
2357
+ "example": "var(--accent-soft)",
2358
+ "note": "CSS Custom Highlight API wash for host-registered bronto-evidence ranges."
2359
+ },
2360
+ {
2361
+ "name": "--highlight-search",
2362
+ "on": ".ui-highlights",
2363
+ "type": "color",
2364
+ "example": "var(--warning-soft)",
2365
+ "note": "CSS Custom Highlight API wash for host-registered bronto-search ranges."
2366
+ },
2367
+ {
2368
+ "name": "--highlight-current",
2369
+ "on": ".ui-highlights",
2370
+ "type": "color",
2371
+ "example": "color-mix(in srgb, var(--accent) 22%, transparent)",
2372
+ "note": "CSS Custom Highlight API wash for the host-registered bronto-current range."
2373
+ },
2374
+ {
2375
+ "name": "--splitter-pos",
2376
+ "on": ".ui-splitter",
2377
+ "type": "percentage",
2378
+ "example": "36%",
2379
+ "note": "first pane size in the splitter axis. initSplitter keeps this and aria-valuenow in sync; the host may persist the last value."
2380
+ },
2381
+ {
2382
+ "name": "--splitter-handle",
2383
+ "on": ".ui-splitter",
2384
+ "type": "length",
2385
+ "example": "0.75rem",
2386
+ "note": "fixed size of the separator handle track (default 0.75rem)."
2387
+ },
2388
+ {
2389
+ "name": "--splitter-pane-min",
2390
+ "on": ".ui-splitter",
2391
+ "type": "length",
2392
+ "example": "12rem",
2393
+ "note": "minimum first-pane size before the grid track clamps (default 0px); pair with matching aria-valuemin when the limit is user-visible."
2394
+ },
2181
2395
  {
2182
2396
  "name": "--stack-gap",
2183
2397
  "on": ".ui-stack",
@@ -2488,7 +2702,7 @@
2488
2702
  "name": "data-bronto-modal",
2489
2703
  "on": "a controlled (non-<dialog>) .ui-modal overlay",
2490
2704
  "behavior": "initModal",
2491
- "note": "inert focus-trap + .is-open toggling for a modal that is not a native <dialog>; needs an accessible name"
2705
+ "note": "inert focus-trap, focus-return, and Escape close signal for a consumer-owned .is-open modal; needs an accessible name"
2492
2706
  },
2493
2707
  {
2494
2708
  "name": "data-bronto-menu",
@@ -2515,6 +2729,13 @@
2515
2729
  "behavior": "initSources",
2516
2730
  "note": "scopes citation preview metadata and source-card focus/highlight behavior; the host still owns numbering, fetching, and trust decisions."
2517
2731
  },
2732
+ {
2733
+ "name": "data-bronto-splitter",
2734
+ "on": "a .ui-splitter host",
2735
+ "value": "optional 'vertical' | 'horizontal' (otherwise inferred from .ui-splitter--horizontal / aria-orientation)",
2736
+ "behavior": "initSplitter",
2737
+ "note": "keyboard + pointer ARIA window-splitter behavior; updates --splitter-pos and aria-valuenow, then emits bronto:splitter:resize. The host owns persistence and pane state."
2738
+ },
2518
2739
  {
2519
2740
  "name": "data-bronto-source-ref",
2520
2741
  "on": "a button/control that references a source card",
@@ -2728,6 +2949,11 @@
2728
2949
  "on": ".ui-error-summary",
2729
2950
  "require": "role=\"alert\" + tabindex=\"-1\" on the hand-authored summary so it is announced and focusable when validation fails",
2730
2951
  "helper": "initFormValidation wires this for the dynamic summary; a static summary needs it hand-set"
2952
+ },
2953
+ {
2954
+ "on": ".ui-splitter__handle",
2955
+ "require": "role=\"separator\" + tabindex=\"0\" + aria-controls=\"<first-pane-id>\" + an accessible name + aria-orientation + aria-valuemin/max/now. The aria-valuenow value mirrors --splitter-pos.",
2956
+ "helper": "initSplitter fills missing role/tabindex/orientation/value defaults and updates aria-valuenow; author the accessible name, aria-controls, and meaningful min/max."
2731
2957
  }
2732
2958
  ]
2733
2959
  }
@@ -330,6 +330,15 @@ export declare const cls: {
330
330
  readonly compare2up: 'ui-compare--2up';
331
331
  readonly compareCol: 'ui-compare__col';
332
332
  readonly compareHead: 'ui-compare__head';
333
+ readonly figure: 'ui-figure';
334
+ readonly figureCaption: 'ui-figure__caption';
335
+ readonly figureBody: 'ui-figure__body';
336
+ readonly figureBodyKeyRight: 'ui-figure__body--key-right';
337
+ readonly figureStage: 'ui-figure__stage';
338
+ readonly figureMedia: 'ui-figure__media';
339
+ readonly figureOverlay: 'ui-figure__overlay';
340
+ readonly figureKey: 'ui-figure__key';
341
+ readonly figureData: 'ui-figure__data';
333
342
  readonly legend: 'ui-legend';
334
343
  readonly legendVertical: 'ui-legend--vertical';
335
344
  readonly legendCompact: 'ui-legend--compact';
@@ -459,6 +468,19 @@ export declare const cls: {
459
468
  readonly srcReviewed: 'ui-src--reviewed';
460
469
  readonly srcStale: 'ui-src--stale';
461
470
  readonly srcConflict: 'ui-src--conflict';
471
+ readonly interval: 'ui-interval';
472
+ readonly intervalTrack: 'ui-interval__track';
473
+ readonly intervalRange: 'ui-interval__range';
474
+ readonly intervalPoint: 'ui-interval__point';
475
+ readonly intervalLabel: 'ui-interval__label';
476
+ readonly intervalBounds: 'ui-interval__bounds';
477
+ readonly clamp: 'ui-clamp';
478
+ readonly clampBody: 'ui-clamp__body';
479
+ readonly clampToggle: 'ui-clamp__toggle';
480
+ readonly clampControl: 'ui-clamp__control';
481
+ readonly clampMore: 'ui-clamp__more';
482
+ readonly clampLess: 'ui-clamp__less';
483
+ readonly highlights: 'ui-highlights';
462
484
  readonly diff: 'ui-diff';
463
485
  readonly diffSplit: 'ui-diff--split';
464
486
  readonly diffPane: 'ui-diff__pane';
@@ -524,6 +546,20 @@ export declare const cls: {
524
546
  readonly stateReviewed: 'ui-state--reviewed';
525
547
  readonly stateNeedsReview: 'ui-state--needs-review';
526
548
  readonly syncbar: 'ui-syncbar';
549
+ readonly job: 'ui-job';
550
+ readonly jobHead: 'ui-job__head';
551
+ readonly jobTitle: 'ui-job__title';
552
+ readonly jobMeta: 'ui-job__meta';
553
+ readonly jobBody: 'ui-job__body';
554
+ readonly jobProgress: 'ui-job__progress';
555
+ readonly jobBar: 'ui-job__bar';
556
+ readonly jobActions: 'ui-job__actions';
557
+ readonly jobCompact: 'ui-job--compact';
558
+ readonly jobQueued: 'ui-job--queued';
559
+ readonly jobRunning: 'ui-job--running';
560
+ readonly jobBlocked: 'ui-job--blocked';
561
+ readonly jobFailed: 'ui-job--failed';
562
+ readonly jobComplete: 'ui-job--complete';
527
563
  readonly generated: 'ui-generated';
528
564
  readonly generatedLabel: 'ui-generated__label';
529
565
  readonly originLabel: 'ui-origin-label';
@@ -544,6 +580,11 @@ export declare const cls: {
544
580
  readonly selectionbar: 'ui-selectionbar';
545
581
  readonly selectionbarCount: 'ui-selectionbar__count';
546
582
  readonly selectionbarActions: 'ui-selectionbar__actions';
583
+ readonly splitter: 'ui-splitter';
584
+ readonly splitterVertical: 'ui-splitter--vertical';
585
+ readonly splitterHorizontal: 'ui-splitter--horizontal';
586
+ readonly splitterPane: 'ui-splitter__pane';
587
+ readonly splitterHandle: 'ui-splitter__handle';
547
588
  readonly command: 'ui-command';
548
589
  readonly commandInput: 'ui-command__input';
549
590
  readonly commandList: 'ui-command__list';
@@ -699,7 +740,7 @@ export interface DotbarOpts {
699
740
  }
700
741
  export interface ModalOpts {
701
742
  drawer?: boolean;
702
- /** Controlled non-dialog usage — adds the is-open state (focus-trap is yours). */
743
+ /** Controlled non-dialog usage — adds only the is-open state; pair with initModal for focus management. */
703
744
  open?: boolean;
704
745
  }
705
746
  export interface TabOpts {
@@ -810,6 +851,12 @@ export interface StateOpts {
810
851
  /** Pulse the indicator for an in-progress state (saving / syncing / retrying). Reduced-motion-safe. */
811
852
  busy?: boolean;
812
853
  }
854
+ export type JobState = 'queued' | 'running' | 'blocked' | 'failed' | 'complete';
855
+ export interface JobOpts {
856
+ /** Persistent background-job state. Pair with written status text; tone is only a scanning aid. */
857
+ state?: JobState;
858
+ compact?: boolean;
859
+ }
813
860
  export interface OriginLabelOpts {
814
861
  /** Accent-tint the label for AI/model-generated origin (vs a neutral tag). */
815
862
  ai?: boolean;
@@ -892,6 +939,7 @@ export interface Ui {
892
939
  sparkBar(opts?: SparkBarOpts): string;
893
940
  bulletMeasure(opts?: BulletMeasureOpts): string;
894
941
  state(opts?: StateOpts): string;
942
+ job(opts?: JobOpts): string;
895
943
  originLabel(opts?: OriginLabelOpts): string;
896
944
  }
897
945