@ponchia/ui 0.5.0 → 0.6.3

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 (196) hide show
  1. package/CHANGELOG.md +386 -4
  2. package/MIGRATIONS.json +14 -0
  3. package/README.md +29 -6
  4. package/annotations/index.d.ts +398 -276
  5. package/annotations/index.d.ts.map +1 -0
  6. package/annotations/index.js +350 -77
  7. package/behaviors/carousel.d.ts +28 -0
  8. package/behaviors/carousel.d.ts.map +1 -0
  9. package/behaviors/carousel.js +20 -16
  10. package/behaviors/combobox.d.ts +40 -0
  11. package/behaviors/combobox.d.ts.map +1 -0
  12. package/behaviors/combobox.js +111 -29
  13. package/behaviors/command.d.ts +41 -0
  14. package/behaviors/command.d.ts.map +1 -0
  15. package/behaviors/command.js +27 -15
  16. package/behaviors/connectors.d.ts +17 -0
  17. package/behaviors/connectors.d.ts.map +1 -0
  18. package/behaviors/connectors.js +7 -5
  19. package/behaviors/crosshair.d.ts +42 -0
  20. package/behaviors/crosshair.d.ts.map +1 -0
  21. package/behaviors/crosshair.js +23 -6
  22. package/behaviors/dialog.d.ts +20 -0
  23. package/behaviors/dialog.d.ts.map +1 -0
  24. package/behaviors/dialog.js +6 -2
  25. package/behaviors/disclosure.d.ts +10 -0
  26. package/behaviors/disclosure.d.ts.map +1 -0
  27. package/behaviors/disclosure.js +6 -2
  28. package/behaviors/dismissible.d.ts +10 -0
  29. package/behaviors/dismissible.d.ts.map +1 -0
  30. package/behaviors/dismissible.js +6 -2
  31. package/behaviors/forms.d.ts +27 -0
  32. package/behaviors/forms.d.ts.map +1 -0
  33. package/behaviors/forms.js +54 -13
  34. package/behaviors/glyph.d.ts +14 -0
  35. package/behaviors/glyph.d.ts.map +1 -0
  36. package/behaviors/glyph.js +28 -5
  37. package/behaviors/index.d.ts +31 -237
  38. package/behaviors/index.d.ts.map +1 -0
  39. package/behaviors/index.js +17 -0
  40. package/behaviors/inert.d.ts +20 -0
  41. package/behaviors/inert.d.ts.map +1 -0
  42. package/behaviors/inert.js +46 -0
  43. package/behaviors/internal.d.ts +25 -0
  44. package/behaviors/internal.d.ts.map +1 -0
  45. package/behaviors/internal.js +77 -1
  46. package/behaviors/legend.d.ts +35 -0
  47. package/behaviors/legend.d.ts.map +1 -0
  48. package/behaviors/legend.js +32 -2
  49. package/behaviors/menu.d.ts +16 -0
  50. package/behaviors/menu.d.ts.map +1 -0
  51. package/behaviors/menu.js +6 -2
  52. package/behaviors/modal.d.ts +41 -0
  53. package/behaviors/modal.d.ts.map +1 -0
  54. package/behaviors/modal.js +124 -0
  55. package/behaviors/popover.d.ts +28 -0
  56. package/behaviors/popover.d.ts.map +1 -0
  57. package/behaviors/popover.js +78 -7
  58. package/behaviors/spotlight.d.ts +17 -0
  59. package/behaviors/spotlight.d.ts.map +1 -0
  60. package/behaviors/spotlight.js +7 -5
  61. package/behaviors/table.d.ts +36 -0
  62. package/behaviors/table.d.ts.map +1 -0
  63. package/behaviors/table.js +84 -17
  64. package/behaviors/tabs.d.ts +20 -0
  65. package/behaviors/tabs.d.ts.map +1 -0
  66. package/behaviors/tabs.js +17 -14
  67. package/behaviors/theme.d.ts +54 -0
  68. package/behaviors/theme.d.ts.map +1 -0
  69. package/behaviors/theme.js +22 -3
  70. package/behaviors/toast.d.ts +49 -0
  71. package/behaviors/toast.d.ts.map +1 -0
  72. package/behaviors/toast.js +47 -3
  73. package/classes/classes.json +2527 -0
  74. package/classes/index.d.ts +134 -15
  75. package/classes/index.js +280 -80
  76. package/classes/vscode.css-custom-data.json +12 -0
  77. package/connectors/index.d.ts +201 -69
  78. package/connectors/index.d.ts.map +1 -0
  79. package/connectors/index.js +142 -25
  80. package/css/app.css +69 -13
  81. package/css/base.css +15 -10
  82. package/css/bullet.css +108 -0
  83. package/css/code.css +98 -0
  84. package/css/connectors.css +17 -0
  85. package/css/content.css +22 -3
  86. package/css/crosshair.css +7 -7
  87. package/css/dataviz.css +5 -1
  88. package/css/diff.css +153 -0
  89. package/css/disclosure.css +53 -7
  90. package/css/dots.css +94 -7
  91. package/css/feedback.css +97 -7
  92. package/css/forms.css +113 -4
  93. package/css/legend.css +16 -9
  94. package/css/marks.css +38 -8
  95. package/css/motion.css +98 -53
  96. package/css/navigation.css +7 -0
  97. package/css/overlay.css +90 -3
  98. package/css/primitives.css +158 -13
  99. package/css/report.css +73 -56
  100. package/css/sidenote.css +67 -0
  101. package/css/site.css +16 -2
  102. package/css/sources.css +43 -1
  103. package/css/spark.css +62 -0
  104. package/css/spotlight.css +1 -1
  105. package/css/table.css +9 -2
  106. package/css/term.css +110 -0
  107. package/css/textref.css +63 -0
  108. package/css/toc.css +91 -0
  109. package/css/tokens.css +49 -1
  110. package/css/tree.css +134 -0
  111. package/css/workbench.css +1 -1
  112. package/dist/bronto.css +1 -1
  113. package/dist/css/analytical.css +1 -1
  114. package/dist/css/app.css +1 -1
  115. package/dist/css/base.css +1 -1
  116. package/dist/css/bullet.css +1 -0
  117. package/dist/css/code.css +1 -0
  118. package/dist/css/connectors.css +1 -1
  119. package/dist/css/content.css +1 -1
  120. package/dist/css/crosshair.css +1 -1
  121. package/dist/css/diff.css +1 -0
  122. package/dist/css/disclosure.css +1 -1
  123. package/dist/css/dots.css +1 -1
  124. package/dist/css/feedback.css +1 -1
  125. package/dist/css/forms.css +1 -1
  126. package/dist/css/legend.css +1 -1
  127. package/dist/css/marks.css +1 -1
  128. package/dist/css/motion.css +1 -1
  129. package/dist/css/navigation.css +1 -1
  130. package/dist/css/overlay.css +1 -1
  131. package/dist/css/primitives.css +1 -1
  132. package/dist/css/report.css +1 -1
  133. package/dist/css/sidenote.css +1 -0
  134. package/dist/css/site.css +1 -1
  135. package/dist/css/sources.css +1 -1
  136. package/dist/css/spark.css +1 -0
  137. package/dist/css/spotlight.css +1 -1
  138. package/dist/css/table.css +1 -1
  139. package/dist/css/term.css +1 -0
  140. package/dist/css/textref.css +1 -0
  141. package/dist/css/toc.css +1 -0
  142. package/dist/css/tokens.css +1 -1
  143. package/dist/css/tree.css +1 -0
  144. package/dist/css/workbench.css +1 -1
  145. package/docs/adr/0003-theme-model.md +1 -1
  146. package/docs/annotations.md +133 -14
  147. package/docs/architecture.md +49 -6
  148. package/docs/bullet.md +78 -0
  149. package/docs/code.md +76 -0
  150. package/docs/contrast.md +116 -92
  151. package/docs/d2.md +196 -0
  152. package/docs/diff.md +146 -0
  153. package/docs/legends.md +23 -3
  154. package/docs/marks.md +9 -2
  155. package/docs/mermaid.md +169 -0
  156. package/docs/reference.md +201 -26
  157. package/docs/reporting.md +416 -57
  158. package/docs/sidenote.md +64 -0
  159. package/docs/sources.md +27 -0
  160. package/docs/spark.md +78 -0
  161. package/docs/stability.md +10 -2
  162. package/docs/term.md +81 -0
  163. package/docs/textref.md +78 -0
  164. package/docs/theming.md +44 -5
  165. package/docs/toc.md +83 -0
  166. package/docs/tree.md +74 -0
  167. package/docs/usage.md +354 -16
  168. package/docs/vega.md +244 -0
  169. package/docs/workbench.md +7 -1
  170. package/glyphs/glyphs.js +13 -5
  171. package/llms.txt +285 -14
  172. package/package.json +95 -17
  173. package/qwik/index.d.ts +44 -59
  174. package/qwik/index.d.ts.map +1 -0
  175. package/qwik/index.js +65 -3
  176. package/react/index.d.ts +41 -61
  177. package/react/index.d.ts.map +1 -0
  178. package/react/index.js +63 -3
  179. package/solid/index.d.ts +68 -61
  180. package/solid/index.d.ts.map +1 -0
  181. package/solid/index.js +66 -3
  182. package/tokens/d2.d.ts +38 -0
  183. package/tokens/d2.js +71 -0
  184. package/tokens/d2.json +43 -0
  185. package/tokens/index.d.ts +5 -5
  186. package/tokens/index.js +15 -1
  187. package/tokens/index.json +9 -0
  188. package/tokens/mermaid.d.ts +23 -0
  189. package/tokens/mermaid.js +181 -0
  190. package/tokens/mermaid.json +163 -0
  191. package/tokens/resolved.json +45 -1
  192. package/tokens/skins.js +3 -2
  193. package/tokens/tokens.dtcg.json +26 -0
  194. package/tokens/vega.d.ts +34 -0
  195. package/tokens/vega.js +155 -0
  196. package/tokens/vega.json +179 -0
@@ -1,13 +1,17 @@
1
- import { hasDom, noop, bindOnce, byIdInHost } from './internal.js';
1
+ import { hasDom, resolveHost, noop, bindOnce, byIdInHost } from './internal.js';
2
2
 
3
3
  /**
4
4
  * Disclosure: a `[data-bronto-disclosure]` trigger toggles the element
5
5
  * referenced by its `aria-controls` id, keeping `aria-expanded` and the
6
6
  * panel's `hidden` attribute in sync.
7
+ *
8
+ * @param {import('./internal.js').DelegateOpts} [opts]
9
+ * @returns {import('./internal.js').Cleanup}
7
10
  */
8
11
  export function initDisclosure({ root } = {}) {
9
12
  if (!hasDom()) return noop;
10
- const host = root || document;
13
+ const host = resolveHost(root);
14
+ if (!host) return noop;
11
15
  const onClick = (e) => {
12
16
  const trigger = e.target.closest('[data-bronto-disclosure]');
13
17
  if (!trigger || !host.contains(trigger)) return;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Click on `[data-bronto-dismiss]` removes the nearest ancestor matching
3
+ * `[data-bronto-dismissible]` (or the selector given as the attribute
4
+ * value). Emits a cancelable `bronto:dismiss` event first.
5
+ *
6
+ * @param {import('./internal.js').DelegateOpts} [opts]
7
+ * @returns {import('./internal.js').Cleanup}
8
+ */
9
+ export function dismissible({ root }?: import("./internal.js").DelegateOpts): import("./internal.js").Cleanup;
10
+ //# sourceMappingURL=dismissible.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dismissible.d.ts","sourceRoot":"","sources":["dismissible.js"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,uCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAmB3C"}
@@ -1,13 +1,17 @@
1
- import { hasDom, noop, bindOnce, closestSafe } from './internal.js';
1
+ import { hasDom, resolveHost, noop, bindOnce, closestSafe } from './internal.js';
2
2
 
3
3
  /**
4
4
  * Click on `[data-bronto-dismiss]` removes the nearest ancestor matching
5
5
  * `[data-bronto-dismissible]` (or the selector given as the attribute
6
6
  * value). Emits a cancelable `bronto:dismiss` event first.
7
+ *
8
+ * @param {import('./internal.js').DelegateOpts} [opts]
9
+ * @returns {import('./internal.js').Cleanup}
7
10
  */
8
11
  export function dismissible({ root } = {}) {
9
12
  if (!hasDom()) return noop;
10
- const host = root || document;
13
+ const host = resolveHost(root);
14
+ if (!host) return noop;
11
15
  const onClick = (e) => {
12
16
  const btn = e.target.closest('[data-bronto-dismiss]');
13
17
  if (!btn || !host.contains(btn)) return;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Accessible form validation glue for `<form data-bronto-validate>`.
3
+ * Progressive enhancement over the native Constraint Validation API —
4
+ * the framework already ships the `[aria-invalid]` / `.ui-hint--error`
5
+ * styling; this wires the a11y plumbing every consumer would otherwise
6
+ * re-implement (and usually get wrong):
7
+ *
8
+ * - suppresses the native error bubbles (`form.noValidate`),
9
+ * - on blur and on submit sets `aria-invalid` and writes the browser's
10
+ * `validationMessage` into the field's error slot
11
+ * (`[data-bronto-error]` inside the `.ui-field`, falling back to a
12
+ * `.ui-hint`), linked via `aria-describedby`. When it falls back to a
13
+ * `.ui-hint` the original help text is snapshotted and restored once the
14
+ * field is valid again (so the error does not eat the help permanently); a
15
+ * dedicated empty `[data-bronto-error]` node is still the recommended slot,
16
+ * - on an invalid submit, fills the form's
17
+ * `[data-bronto-error-summary]` (a `.ui-error-summary`) with
18
+ * in-page links to each bad field, focuses it, and blocks submit.
19
+ *
20
+ * Pure enhancement: with JS off the form still submits and the browser
21
+ * validates natively. SSR-safe, idempotent; returns a cleanup function.
22
+ *
23
+ * @param {import('./internal.js').DelegateOpts} [opts]
24
+ * @returns {import('./internal.js').Cleanup}
25
+ */
26
+ export function initFormValidation({ root }?: import("./internal.js").DelegateOpts): import("./internal.js").Cleanup;
27
+ //# sourceMappingURL=forms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"forms.d.ts","sourceRoot":"","sources":["forms.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,8CAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CA6K3C"}
@@ -1,4 +1,4 @@
1
- import { hasDom, noop, bindOnce, nextFieldUid } from './internal.js';
1
+ import { hasDom, resolveHost, noop, bindOnce, nextFieldUid, collectHosts } from './internal.js';
2
2
 
3
3
  /**
4
4
  * Accessible form validation glue for `<form data-bronto-validate>`.
@@ -11,27 +11,42 @@ import { hasDom, noop, bindOnce, nextFieldUid } from './internal.js';
11
11
  * - on blur and on submit sets `aria-invalid` and writes the browser's
12
12
  * `validationMessage` into the field's error slot
13
13
  * (`[data-bronto-error]` inside the `.ui-field`, falling back to a
14
- * `.ui-hint`), linked via `aria-describedby`,
14
+ * `.ui-hint`), linked via `aria-describedby`. When it falls back to a
15
+ * `.ui-hint` the original help text is snapshotted and restored once the
16
+ * field is valid again (so the error does not eat the help permanently); a
17
+ * dedicated empty `[data-bronto-error]` node is still the recommended slot,
15
18
  * - on an invalid submit, fills the form's
16
19
  * `[data-bronto-error-summary]` (a `.ui-error-summary`) with
17
20
  * in-page links to each bad field, focuses it, and blocks submit.
18
21
  *
19
22
  * Pure enhancement: with JS off the form still submits and the browser
20
23
  * validates natively. SSR-safe, idempotent; returns a cleanup function.
24
+ *
25
+ * @param {import('./internal.js').DelegateOpts} [opts]
26
+ * @returns {import('./internal.js').Cleanup}
21
27
  */
22
28
  export function initFormValidation({ root } = {}) {
23
29
  if (!hasDom()) return noop;
24
- const host = root || document;
30
+ const host = resolveHost(root);
31
+ if (!host) return noop;
25
32
 
26
33
  const ensureId = (el, prefix) => {
27
34
  if (!el.id) el.id = `${prefix}-${nextFieldUid()}`;
28
35
  return el.id;
29
36
  };
30
37
 
38
+ // When the field has no dedicated `[data-bronto-error]` node we fall back to
39
+ // the shared `.ui-hint` help slot. Snapshot its original help text the first
40
+ // time we overwrite it with an error, so the valid branch can RESTORE the help
41
+ // rather than blanking it permanently (component-audit C8).
42
+ const hintHelp = new WeakMap();
43
+
31
44
  const slotFor = (control) => {
32
45
  const field = control.closest('.ui-field');
33
46
  if (!field) return null;
34
- return field.querySelector('[data-bronto-error]') || field.querySelector('.ui-hint');
47
+ const dedicated = field.querySelector('[data-bronto-error]');
48
+ if (dedicated) return dedicated;
49
+ return field.querySelector('.ui-hint');
35
50
  };
36
51
 
37
52
  const link = (control, slot) => {
@@ -43,21 +58,52 @@ export function initFormValidation({ root } = {}) {
43
58
  }
44
59
  };
45
60
 
61
+ const unlink = (control, slot) => {
62
+ if (!slot.id) return;
63
+ const ids = (control.getAttribute('aria-describedby') || '')
64
+ .split(/\s+/)
65
+ .filter((id) => id && id !== slot.id);
66
+ if (ids.length) control.setAttribute('aria-describedby', ids.join(' '));
67
+ else control.removeAttribute('aria-describedby');
68
+ };
69
+
46
70
  const validateField = (control) => {
47
71
  if (!control.willValidate) return true;
48
72
  const ok = control.validity.valid;
49
73
  const slot = slotFor(control);
74
+ // Decide the slot TYPE by the `[data-bronto-error]` attribute, NOT the
75
+ // `.ui-hint` class: the canonical markup is `<p class="ui-hint"
76
+ // data-bronto-error>`, which carries BOTH. Keying off `.ui-hint` sent that
77
+ // dedicated error node down the help-hint branch, which never unlink()s — so
78
+ // the field kept a dangling aria-describedby to an empty error node after it
79
+ // was fixed (component-audit C6). Only a *borrowed* plain `.ui-hint` (a help
80
+ // slot with no dedicated error node) snapshots/restores its help text and
81
+ // stays linked in the valid state.
82
+ const dedicated = !!slot?.matches?.('[data-bronto-error]');
83
+ const hasHintClass = !!slot?.classList.contains('ui-hint');
84
+ const borrowedHint = hasHintClass && !dedicated;
50
85
  if (ok) {
51
86
  control.removeAttribute('aria-invalid');
52
87
  if (slot) {
53
- slot.textContent = '';
54
- if (slot.classList.contains('ui-hint')) slot.classList.remove('ui-hint--error');
88
+ if (hasHintClass) slot.classList.remove('ui-hint--error');
89
+ if (borrowedHint) {
90
+ // Restore the snapshotted help text (or clear if there was none); a
91
+ // help-bearing hint stays linked via aria-describedby (it describes
92
+ // the field in the valid state too).
93
+ slot.textContent = hintHelp.get(slot) ?? '';
94
+ } else {
95
+ // Dedicated error node: clear it and drop the now-stale describedby
96
+ // so AT doesn't announce an empty error association.
97
+ slot.textContent = '';
98
+ unlink(control, slot);
99
+ }
55
100
  }
56
101
  } else {
57
102
  control.setAttribute('aria-invalid', 'true');
58
103
  if (slot) {
104
+ if (borrowedHint && !hintHelp.has(slot)) hintHelp.set(slot, slot.textContent);
59
105
  slot.textContent = control.validationMessage;
60
- if (slot.classList.contains('ui-hint')) slot.classList.add('ui-hint--error');
106
+ if (hasHintClass) slot.classList.add('ui-hint--error');
61
107
  link(control, slot);
62
108
  }
63
109
  }
@@ -136,12 +182,7 @@ export function initFormValidation({ root } = {}) {
136
182
  // otherwise show the native UA bubble instead of the Bronto
137
183
  // summary — contradicting the documented contract. (Forms added
138
184
  // after init are still covered by the in-handler set.)
139
- // Feature-detect rather than `instanceof Element` — `Element` is not
140
- // a guaranteed global (SSR / the no-DOM test env), and `host` is
141
- // either `document` (no `.matches`) or a root Element.
142
- const selfForm =
143
- typeof host.matches === 'function' && host.matches('[data-bronto-validate]') ? [host] : [];
144
- const forms = [...selfForm, ...(host.querySelectorAll?.('[data-bronto-validate]') ?? [])];
185
+ const forms = collectHosts(host, '[data-bronto-validate]');
145
186
  const priorNoValidate = new Map();
146
187
  for (const f of forms) {
147
188
  priorNoValidate.set(f, f.noValidate);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Expand `[data-bronto-glyph="name"]` placeholders into a `.ui-dotmatrix`
3
+ * grid of GLYPH_SIZE² cells — the DOM counterpart to renderGlyph() from
4
+ * `@ponchia/ui/glyphs`, for when you'd rather drop a placeholder than inline
5
+ * the markup. Decorative by default (`aria-hidden`); add
6
+ * `data-bronto-glyph-label` to expose it as `role="img"`. An unknown glyph
7
+ * name is left untouched. Idempotent (skips an already-expanded host); the
8
+ * returned cleanup removes the cells and restores the original attributes.
9
+ *
10
+ * @param {import('./internal.js').DelegateOpts} [opts]
11
+ * @returns {import('./internal.js').Cleanup}
12
+ */
13
+ export function initDotGlyph({ root }?: import("./internal.js").DelegateOpts): import("./internal.js").Cleanup;
14
+ //# sourceMappingURL=glyph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glyph.d.ts","sourceRoot":"","sources":["glyph.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;GAWG;AACH,wCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAiH3C"}
@@ -1,4 +1,4 @@
1
- import { hasDom, noop } from './internal.js';
1
+ import { hasDom, resolveHost, noop, collectHosts } from './internal.js';
2
2
  import { GLYPH_SIZE, glyphCells } from '../glyphs/glyphs.js';
3
3
 
4
4
  function restoreAttr(el, name, prev) {
@@ -14,13 +14,15 @@ function restoreAttr(el, name, prev) {
14
14
  * `data-bronto-glyph-label` to expose it as `role="img"`. An unknown glyph
15
15
  * name is left untouched. Idempotent (skips an already-expanded host); the
16
16
  * returned cleanup removes the cells and restores the original attributes.
17
+ *
18
+ * @param {import('./internal.js').DelegateOpts} [opts]
19
+ * @returns {import('./internal.js').Cleanup}
17
20
  */
18
21
  export function initDotGlyph({ root } = {}) {
19
22
  if (!hasDom()) return noop;
20
- const host = root || document;
21
- const els = [];
22
- if (host !== document && host.matches?.('[data-bronto-glyph]')) els.push(host);
23
- els.push(...(host.querySelectorAll?.('[data-bronto-glyph]') ?? []));
23
+ const host = resolveHost(root);
24
+ if (!host) return noop;
25
+ const els = collectHosts(host, '[data-bronto-glyph]');
24
26
  const cleanups = [];
25
27
 
26
28
  for (const el of els) {
@@ -60,6 +62,25 @@ export function initDotGlyph({ root } = {}) {
60
62
  el.style.setProperty('--dotmatrix-dot-radius', '0');
61
63
  el.style.setProperty('--dotmatrix-gap', '0');
62
64
  }
65
+ // Without a track size the grid cells default to `1fr`, so the 16×16 matrix
66
+ // balloons to fill its container (full-bleed) — asymmetric with the mask
67
+ // path's safe 1em. If the author set no `--dotmatrix-dot` (inline OR via the
68
+ // cascade), default it to an intrinsic icon scale so a forgotten size
69
+ // degrades to ~icon, not full-bleed. (component audit C9.)
70
+ const authoredDot =
71
+ el.style.getPropertyValue('--dotmatrix-dot') ||
72
+ (typeof getComputedStyle === 'function'
73
+ ? getComputedStyle(el).getPropertyValue('--dotmatrix-dot').trim()
74
+ : '');
75
+ const setDefaultDot = !authoredDot;
76
+ let setDefaultGap = false;
77
+ if (setDefaultDot) {
78
+ el.style.setProperty('--dotmatrix-dot', '0.08em');
79
+ if (!solid && !hadGap) {
80
+ el.style.setProperty('--dotmatrix-gap', '0.02em'); // tight, so it reads as one glyph
81
+ setDefaultGap = true;
82
+ }
83
+ }
63
84
  if (label) {
64
85
  el.setAttribute('role', 'img');
65
86
  el.setAttribute('aria-label', label);
@@ -94,6 +115,8 @@ export function initDotGlyph({ root } = {}) {
94
115
  if (hadGap) el.style.setProperty('--dotmatrix-gap', hadGap);
95
116
  else el.style.removeProperty('--dotmatrix-gap');
96
117
  }
118
+ if (setDefaultDot) el.style.removeProperty('--dotmatrix-dot');
119
+ if (setDefaultGap) el.style.removeProperty('--dotmatrix-gap');
97
120
  if (hadCols) el.style.setProperty('--dotmatrix-cols', hadCols);
98
121
  else el.style.removeProperty('--dotmatrix-cols');
99
122
  restoreAttr(el, 'aria-hidden', hadAriaHidden);
@@ -1,237 +1,31 @@
1
- /** @ponchia/ui optional, framework-agnostic behaviors. */
2
-
3
- /** Cleanup function returned by every initializer. */
4
- export type Cleanup = () => void;
5
-
6
- export interface ThemeStorageOpts {
7
- /** localStorage key for the persisted theme. Default: "bronto-theme". */
8
- storageKey?: string;
9
- }
10
-
11
- export interface ApplyThemeOpts extends ThemeStorageOpts {
12
- /** Element to set `data-theme` on. Default: <html>. */
13
- root?: Element;
14
- }
15
-
16
- export interface DelegateOpts {
17
- /** Event-delegation root; also scopes which controls are queried. Default: document. */
18
- root?: Document | Element;
19
- }
20
-
21
- /** `bronto:themechange` CustomEvent detail. */
22
- export interface ThemeChangeDetail {
23
- theme: 'light' | 'dark';
24
- }
25
-
26
- /** Apply the persisted theme to <html data-theme>. Call before paint. */
27
- export declare function applyStoredTheme(opts?: ApplyThemeOpts): void;
28
-
29
- /**
30
- * Wire `[data-bronto-theme-toggle]` controls. Theme is always applied to
31
- * <html>; `root` only scopes delegation/queried controls. Dispatches
32
- * `bronto:themechange` on <html>. Returns a cleanup function.
33
- */
34
- export declare function initThemeToggle(opts?: ThemeStorageOpts & DelegateOpts): Cleanup;
35
-
36
- /** Wire `[data-bronto-dismiss]` controls. Returns a cleanup function. */
37
- export declare function dismissible(opts?: DelegateOpts): Cleanup;
38
-
39
- /** Wire `[data-bronto-disclosure]` triggers. Returns a cleanup function. */
40
- export declare function initDisclosure(opts?: DelegateOpts): Cleanup;
41
-
42
- /**
43
- * Close affordances (Escape, outside-click, close-on-activate) for a
44
- * native `<details data-bronto-menu>` dropdown holding a `.ui-menu`.
45
- * Not a full ARIA menu by design. Returns a cleanup function.
46
- */
47
- export declare function initMenu(opts?: DelegateOpts): Cleanup;
48
-
49
- /**
50
- * Accessible validation glue for `<form data-bronto-validate>`:
51
- * progressive enhancement over the Constraint Validation API. Sets
52
- * `aria-invalid`, writes `validationMessage` into the field's
53
- * `[data-bronto-error]` / `.ui-hint` slot (linked via
54
- * `aria-describedby`), and on invalid submit fills the form's
55
- * `[data-bronto-error-summary]` with focusable links and blocks submit.
56
- * Works without JS (native validation). Returns a cleanup function.
57
- */
58
- export declare function initFormValidation(opts?: DelegateOpts): Cleanup;
59
-
60
- /**
61
- * Editable combobox with a filtered listbox popup (WAI-ARIA APG
62
- * pattern), dependency-free and CSS-anchored. Wires
63
- * `[data-bronto-combobox]` (input `role=combobox` +
64
- * `.ui-combobox__list` of `role=option`): ids, `aria-expanded` /
65
- * `aria-controls` / `aria-activedescendant`, type-to-filter, full
66
- * keyboard, pointer select, outside-click close. Emits `bronto:change`
67
- * ({ detail: { value } }) on selection. SSR-safe, idempotent per
68
- * instance. Returns a cleanup function.
69
- */
70
- export declare function initCombobox(opts?: DelegateOpts): Cleanup;
71
-
72
- /**
73
- * Collision-aware popover, dependency-free. A `[data-bronto-popover]`
74
- * trigger toggles the `.ui-popover` panel it names; the panel flips
75
- * above when it would overflow the viewport and its inline edge is
76
- * clamped on-screen. Uses the native top layer when the panel has
77
- * `popover` and the Popover API exists, else an `.is-open` class.
78
- * Manages `aria-expanded`/`aria-controls`, Escape + outside-click
79
- * close, scroll/resize reposition. Returns a cleanup function.
80
- */
81
- export declare function initPopover(opts?: DelegateOpts): Cleanup;
82
-
83
- /**
84
- * Client-side sortable + selectable data table for
85
- * `[data-bronto-sortable]`. Header `.ui-table__sort` / `th[data-sort]`
86
- * cycles `aria-sort` and reorders the tbody (numeric- or
87
- * locale-string-aware); `[data-bronto-select-all]` toggles
88
- * `[data-bronto-select]` rows + `aria-selected` with synced
89
- * checked/indeterminate state. Emits `bronto:selectionchange`
90
- * ({ detail: { count } }). SSR-safe, idempotent per table. Returns a
91
- * cleanup function.
92
- */
93
- export declare function initTableSort(opts?: DelegateOpts): Cleanup;
94
-
95
- /**
96
- * Wire `[data-bronto-tabs]` groups with the WAI-ARIA Tabs keyboard
97
- * pattern (roving tabindex, Arrow/Home/End, aria-selected, panel sync).
98
- * Returns a cleanup function.
99
- */
100
- export declare function initTabs(opts?: DelegateOpts): Cleanup;
101
-
102
- /**
103
- * Wire native <dialog> open/close glue: `[data-bronto-open="id"]`,
104
- * `[data-bronto-close]`, and backdrop light-dismiss for dialogs marked
105
- * `[data-bronto-dialog-light]`. `root` scopes delegated controls; dialog ids
106
- * resolve root-first, then document-wide for body/portal-mounted overlays.
107
- * Returns a cleanup function.
108
- */
109
- export declare function initDialog(opts?: DelegateOpts): Cleanup;
110
-
111
- /**
112
- * Image carousel / gallery built on CSS scroll-snap (touch/trackpad swipe
113
- * is the browser's). Wires `[data-bronto-carousel]`: prev/next
114
- * (`[data-bronto-carousel-prev|next]`), keyboard (Arrow/Home/End on the
115
- * focused `.ui-carousel__viewport`), a `.ui-carousel__thumb` strip with
116
- * `aria-current` sync, the `.ui-carousel__status` counter, and ARIA. Keeps
117
- * a JS index in sync with the scroll position both ways (via
118
- * IntersectionObserver where available). `data-bronto-carousel-loop` wraps
119
- * at the ends; `data-bronto-carousel-label` names the region. A
120
- * full-screen lightbox is the same markup inside a native
121
- * `<dialog class="ui-lightbox">` opened by `initDialog` (focus-trap/Escape
122
- * come from the dialog). Emits `bronto:change` ({ detail: { index } }).
123
- * SSR-safe, idempotent per carousel. Returns a cleanup function.
124
- */
125
- export declare function initCarousel(opts?: DelegateOpts): Cleanup;
126
-
127
- export interface ToastOpts {
128
- /** Status tone — maps to `ui-toast--<tone>`. */
129
- tone?: 'accent' | 'success' | 'warning' | 'danger' | 'info';
130
- /** Optional uppercase label rendered above the message. */
131
- title?: string;
132
- /** Auto-dismiss delay in ms. 0 keeps it until dismissed. Default: 4000. */
133
- duration?: number;
134
- /**
135
- * Route to the assertive live region so AT interrupts immediately.
136
- * Defaults to `true` when `tone === 'danger'`.
137
- */
138
- assertive?: boolean;
139
- /** Render a dismiss button on the toast. */
140
- closable?: boolean;
141
- }
142
-
143
- /**
144
- * Push a transient toast into a shared, body-anchored stack. Returns a
145
- * function that dismisses it early. No-op (returns noop) without a DOM.
146
- */
147
- export declare function toast(message: string, opts?: ToastOpts): Cleanup;
148
-
149
- /**
150
- * Expand `[data-bronto-glyph="name"]` placeholders into a `.ui-dotmatrix`
151
- * grid of cells — the DOM counterpart to `renderGlyph` from
152
- * `@ponchia/ui/glyphs`. Decorative by default (`aria-hidden`); add
153
- * `data-bronto-glyph-label` to expose it as `role="img"`. Unknown glyph names
154
- * are left untouched. SSR-safe, idempotent (skips an already-expanded host).
155
- * Returns a cleanup that removes the cells and restores the original
156
- * attributes.
157
- */
158
- export declare function initDotGlyph(opts?: DelegateOpts): Cleanup;
159
-
160
- /** `bronto:legend:toggle` CustomEvent detail. `series` is the entry's
161
- * `data-series`, or its 0-based index when unset. `active` is the new state
162
- * (`true` ⇒ series shown). */
163
- export interface LegendToggleDetail {
164
- series: string | number;
165
- active: boolean;
166
- }
167
-
168
- /**
169
- * Wire `[data-bronto-legend]` interactive legends. Each `.ui-legend__item` is a
170
- * `<button aria-pressed>`; activating it flips `aria-pressed`, toggles
171
- * `.is-inactive`, and dispatches `bronto:legend:toggle`
172
- * ({@link LegendToggleDetail}) on the legend. Bronto owns the control + its
173
- * state only — the host hides its own series and owns any `aria-live`
174
- * announcement (`aria-pressed="true"` ⇒ series shown). SSR-safe, idempotent per
175
- * host. Returns a cleanup function.
176
- */
177
- export declare function initLegend(opts?: DelegateOpts): Cleanup;
178
-
179
- /**
180
- * Draw + keep leader lines in sync. Each `[data-bronto-connector]` is a
181
- * `.ui-connector` SVG overlaying a positioned container; `data-from`/`data-to`
182
- * are the ids of the elements to connect (with optional `data-shape`,
183
- * `data-from-side`/`data-to-side`, `data-end`). Computes geometry via the
184
- * `@ponchia/ui/connectors` helpers and redraws on resize/scroll. Bronto owns no
185
- * layout. SSR-safe, idempotent per host; returns a cleanup that disconnects
186
- * observers/listeners. Re-run after adding/removing connectors.
187
- */
188
- export declare function initConnectors(opts?: DelegateOpts): Cleanup;
189
-
190
- /**
191
- * Position a spotlight cutout over a target. Each `[data-bronto-spotlight]` is a
192
- * `.ui-spotlight` overlay; `data-target` is the id of the element to highlight.
193
- * Sets `--spot-x/y/w/h` and re-places on resize/scroll and when `data-target`
194
- * changes. NOT a tour engine — the host owns step order/advancing/visibility.
195
- * SSR-safe, idempotent per host; returns a cleanup function.
196
- */
197
- export declare function initSpotlight(opts?: DelegateOpts): Cleanup;
198
-
199
- /** `bronto:crosshair:move` CustomEvent detail — pointer position over the plot
200
- * in pixels and as 0..1 fractions. Bronto reports where; mapping to data is
201
- * the host's. */
202
- export interface CrosshairMoveDetail {
203
- x: number;
204
- y: number;
205
- fx: number;
206
- fy: number;
207
- }
208
-
209
- /**
210
- * Track the pointer over `[data-bronto-crosshair]` plots and drive a contained
211
- * `.ui-crosshair` overlay: sets `--crosshair-x/y` (px), marks `.is-active`, and
212
- * dispatches `bronto:crosshair:move` ({@link CrosshairMoveDetail}) /
213
- * `bronto:crosshair:leave`. Reports the pointer position only — it does not find
214
- * the nearest datum or map pixels to data. SSR-safe, idempotent per plot;
215
- * returns a cleanup function.
216
- */
217
- export declare function initCrosshair(opts?: DelegateOpts): Cleanup;
218
-
219
- /** `bronto:command:select` CustomEvent detail — the chosen command's value and
220
- * visible label. The host executes it; Bronto only filters and navigates. */
221
- export interface CommandSelectDetail {
222
- value: string;
223
- label: string;
224
- }
225
-
226
- /**
227
- * Filter + keyboard-navigate a DOM-authored command list inside
228
- * `[data-bronto-command]` (the `.ui-command` shell). Owns ids,
229
- * `role=combobox/listbox/option`, `aria-activedescendant`, a roving active item,
230
- * substring filtering (hiding empty groups), full keyboard
231
- * (Down/Up/Home/End/Enter/Escape), and pointer select. Emits
232
- * `bronto:command:select` ({@link CommandSelectDetail}) on choose and
233
- * `bronto:command:close` on Escape; the host owns the action registry, routing,
234
- * and execution. No global Cmd/Ctrl+K. SSR-safe, idempotent per instance;
235
- * returns a cleanup function.
236
- */
237
- export declare function initCommand(opts?: DelegateOpts): Cleanup;
1
+ export { dismissible } from "./dismissible.js";
2
+ export { initDisabledGuard } from "./inert.js";
3
+ export { initTabs } from "./tabs.js";
4
+ export { initDialog } from "./dialog.js";
5
+ export { initModal } from "./modal.js";
6
+ export { toast } from "./toast.js";
7
+ export { initDisclosure } from "./disclosure.js";
8
+ export { initMenu } from "./menu.js";
9
+ export { initFormValidation } from "./forms.js";
10
+ export { initCombobox } from "./combobox.js";
11
+ export { initPopover } from "./popover.js";
12
+ export { initTableSort } from "./table.js";
13
+ export { initCarousel } from "./carousel.js";
14
+ export { initDotGlyph } from "./glyph.js";
15
+ export { initLegend } from "./legend.js";
16
+ export { initConnectors } from "./connectors.js";
17
+ export { initSpotlight } from "./spotlight.js";
18
+ export { initCrosshair } from "./crosshair.js";
19
+ export { initCommand } from "./command.js";
20
+ export type Cleanup = import("./internal.js").Cleanup;
21
+ export type DelegateOpts = import("./internal.js").DelegateOpts;
22
+ export type ThemeStorageOpts = import("./theme.js").ThemeStorageOpts;
23
+ export type ApplyThemeOpts = import("./theme.js").ApplyThemeOpts;
24
+ export type ThemeChangeDetail = import("./theme.js").ThemeChangeDetail;
25
+ export type ToastOpts = import("./toast.js").ToastOpts;
26
+ export type ModalCloseDetail = import("./modal.js").ModalCloseDetail;
27
+ export type LegendToggleDetail = import("./legend.js").LegendToggleDetail;
28
+ export type CrosshairMoveDetail = import("./crosshair.js").CrosshairMoveDetail;
29
+ export type CommandSelectDetail = import("./command.js").CommandSelectDetail;
30
+ export { applyStoredTheme, initThemeToggle } from "./theme.js";
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;sBAmBa,OAAO,eAAe,EAAE,OAAO;2BAC/B,OAAO,eAAe,EAAE,YAAY;+BACpC,OAAO,YAAY,EAAE,gBAAgB;6BACrC,OAAO,YAAY,EAAE,cAAc;gCACnC,OAAO,YAAY,EAAE,iBAAiB;wBACtC,OAAO,YAAY,EAAE,SAAS;+BAC9B,OAAO,YAAY,EAAE,gBAAgB;iCACrC,OAAO,aAAa,EAAE,kBAAkB;kCACxC,OAAO,gBAAgB,EAAE,mBAAmB;kCAC5C,OAAO,cAAc,EAAE,mBAAmB"}
@@ -12,11 +12,28 @@
12
12
  * import { applyStoredTheme, initThemeToggle } from '@ponchia/ui/behaviors';
13
13
  * applyStoredTheme(); // before paint, avoids theme flash
14
14
  * const stop = initThemeToggle(); // wire [data-bronto-theme-toggle]
15
+ *
16
+ * The public option/detail types are JSDoc `@typedef`s on the modules below,
17
+ * re-exported here by name; the shipped `index.d.ts` is generated from this
18
+ * source by `tsc --emitDeclarationOnly` (so the declarations cannot drift).
19
+ *
20
+ * @typedef {import('./internal.js').Cleanup} Cleanup
21
+ * @typedef {import('./internal.js').DelegateOpts} DelegateOpts
22
+ * @typedef {import('./theme.js').ThemeStorageOpts} ThemeStorageOpts
23
+ * @typedef {import('./theme.js').ApplyThemeOpts} ApplyThemeOpts
24
+ * @typedef {import('./theme.js').ThemeChangeDetail} ThemeChangeDetail
25
+ * @typedef {import('./toast.js').ToastOpts} ToastOpts
26
+ * @typedef {import('./modal.js').ModalCloseDetail} ModalCloseDetail
27
+ * @typedef {import('./legend.js').LegendToggleDetail} LegendToggleDetail
28
+ * @typedef {import('./crosshair.js').CrosshairMoveDetail} CrosshairMoveDetail
29
+ * @typedef {import('./command.js').CommandSelectDetail} CommandSelectDetail
15
30
  */
16
31
  export { applyStoredTheme, initThemeToggle } from './theme.js';
17
32
  export { dismissible } from './dismissible.js';
33
+ export { initDisabledGuard } from './inert.js';
18
34
  export { initTabs } from './tabs.js';
19
35
  export { initDialog } from './dialog.js';
36
+ export { initModal } from './modal.js';
20
37
  export { toast } from './toast.js';
21
38
  export { initDisclosure } from './disclosure.js';
22
39
  export { initMenu } from './menu.js';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Make `aria-disabled="true"` controls keyboard-inert, not just pointer-inert.
3
+ *
4
+ * CSS can dim an `aria-disabled` control and set `pointer-events: none`, but it
5
+ * cannot block keyboard activation: a focused `<a aria-disabled>` or
6
+ * `<button aria-disabled>` still fires on Enter/Space, so a keyboard user can
7
+ * activate a control that looks dead. (Native `disabled` is already fully inert;
8
+ * this guard brings the ARIA path up to parity.) The element intentionally stays
9
+ * focusable and announced — the WAI-ARIA disabled pattern keeps it in the tab
10
+ * order — but it no longer *acts*.
11
+ *
12
+ * Wire once near the root, like {@link applyStoredTheme}. Capturing listeners
13
+ * intercept activation before any component handler (tabs, pagination, menus)
14
+ * sees it. (component audit C4.)
15
+ *
16
+ * @param {import('./internal.js').DelegateOpts} [opts]
17
+ * @returns {import('./internal.js').Cleanup}
18
+ */
19
+ export function initDisabledGuard({ root }?: import("./internal.js").DelegateOpts): import("./internal.js").Cleanup;
20
+ //# sourceMappingURL=inert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inert.d.ts","sourceRoot":"","sources":["inert.js"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;GAiBG;AACH,6CAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAyB3C"}