@aceshooting/lyra-ui 11.2.0 → 12.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/CHANGELOG.md +407 -0
  2. package/README.md +11 -8
  3. package/custom-elements.json +1 -1
  4. package/dist/all.d.ts +1 -0
  5. package/dist/all.js +1 -1
  6. package/dist/autoloader.js +1 -1
  7. package/dist/components/agent-tools/tool-param-form/tool-param-form.class.d.ts +0 -1
  8. package/dist/components/agent-tools/tool-param-form/tool-param-form.class.js +1 -1
  9. package/dist/components/charts/chart/chart.class.d.ts +13 -1
  10. package/dist/components/charts/chart/histogram-bin.d.ts +0 -1
  11. package/dist/components/charts/chart/histogram-bin.js +1 -1
  12. package/dist/components/charts/chart/lite-chart.class.d.ts +22 -9
  13. package/dist/components/charts/chart/lite-chart.class.js +6 -6
  14. package/dist/components/conversation/code-block/code-block-shared.d.ts +4 -54
  15. package/dist/components/conversation/code-block/code-block-shared.js +1 -1
  16. package/dist/components/conversation/code-block/shiki-types.d.ts +2 -1
  17. package/dist/components/conversation/generation-metrics/generation-metrics.class.js +1 -1
  18. package/dist/components/conversation/model-select/model-select.class.d.ts +0 -3
  19. package/dist/components/conversation/model-select/model-select.class.js +1 -1
  20. package/dist/components/conversation/selection-toolbar/selection-toolbar.class.js +1 -1
  21. package/dist/components/conversation/voice-picker/voice-picker.class.d.ts +0 -1
  22. package/dist/components/conversation/voice-picker/voice-picker.class.js +1 -1
  23. package/dist/components/data/data-grid/data-grid.class.js +1 -1
  24. package/dist/components/data/funnel/funnel.class.d.ts +92 -0
  25. package/dist/components/data/funnel/funnel.class.js +25 -0
  26. package/dist/components/data/funnel/funnel.d.ts +1 -0
  27. package/dist/components/data/funnel/funnel.js +1 -0
  28. package/dist/components/data/funnel/funnel.styles.d.ts +1 -0
  29. package/dist/components/data/funnel/funnel.styles.js +127 -0
  30. package/dist/components/data/graph-query-builder/graph-query-builder.class.d.ts +0 -1
  31. package/dist/components/data/graph-query-builder/graph-query-builder.class.js +1 -1
  32. package/dist/components/data/heatmap/heatmap.class.d.ts +199 -14
  33. package/dist/components/data/heatmap/heatmap.class.js +30 -22
  34. package/dist/components/data/heatmap/heatmap.styles.js +36 -0
  35. package/dist/components/data/index.d.ts +1 -0
  36. package/dist/components/data/index.js +1 -1
  37. package/dist/components/data/table/table.class.d.ts +18 -4
  38. package/dist/components/data/table/table.class.js +2 -2
  39. package/dist/components/data/table/table.styles.js +2 -1
  40. package/dist/components/data/timeline/timeline-cluster.d.ts +1 -0
  41. package/dist/components/data/timeline/timeline-cluster.js +1 -0
  42. package/dist/components/data/timeline/timeline-item.class.d.ts +17 -2
  43. package/dist/components/data/timeline/timeline-item.class.js +13 -2
  44. package/dist/components/data/timeline/timeline-item.styles.js +65 -0
  45. package/dist/components/data/timeline/timeline.class.d.ts +83 -27
  46. package/dist/components/data/timeline/timeline.class.js +3 -3
  47. package/dist/components/data/timeline/timeline.d.ts +1 -0
  48. package/dist/components/data/timeline/timeline.js +1 -1
  49. package/dist/components/data/timeline/timeline.styles.js +14 -0
  50. package/dist/components/forms/button/button.class.js +1 -1
  51. package/dist/components/forms/checkbox/checkbox.class.d.ts +0 -1
  52. package/dist/components/forms/checkbox/checkbox.class.js +1 -1
  53. package/dist/components/forms/checkbox-group/checkbox-group.class.d.ts +0 -1
  54. package/dist/components/forms/checkbox-group/checkbox-group.class.js +1 -1
  55. package/dist/components/forms/code-editor/code-editor.class.js +1 -1
  56. package/dist/components/forms/color-picker/color-picker.class.js +1 -1
  57. package/dist/components/forms/combobox/combobox.class.d.ts +7 -1
  58. package/dist/components/forms/combobox/combobox.class.js +3 -2
  59. package/dist/components/forms/combobox/combobox.styles.js +1 -1
  60. package/dist/components/forms/date-picker/date-input.class.d.ts +23 -0
  61. package/dist/components/forms/date-picker/date-input.class.js +1 -1
  62. package/dist/components/forms/emoji-picker/emoji-data-loader.d.ts +0 -1
  63. package/dist/components/forms/input/native-time-input.class.js +1 -1
  64. package/dist/components/forms/input/time-input-shared.d.ts +2 -1
  65. package/dist/components/forms/input/time-input.class.js +1 -1
  66. package/dist/components/forms/locale-picker/locale-picker.class.d.ts +0 -1
  67. package/dist/components/forms/locale-picker/locale-picker.class.js +1 -1
  68. package/dist/components/forms/radio/radio-button.styles.d.ts +0 -1
  69. package/dist/components/forms/radio/radio-button.styles.js +1 -1
  70. package/dist/components/forms/radio/radio-group.class.d.ts +0 -1
  71. package/dist/components/forms/radio/radio-group.class.js +1 -1
  72. package/dist/components/forms/radio/radio.class.d.ts +0 -1
  73. package/dist/components/forms/radio/radio.class.js +1 -1
  74. package/dist/components/forms/rubric-form/rubric-form.class.d.ts +0 -1
  75. package/dist/components/forms/rubric-form/rubric-form.class.js +1 -1
  76. package/dist/components/forms/select/select.class.d.ts +0 -1
  77. package/dist/components/forms/select/select.class.js +1 -1
  78. package/dist/components/forms/slider/slider.class.d.ts +0 -1
  79. package/dist/components/forms/slider/slider.class.js +1 -1
  80. package/dist/components/forms/switch/switch.class.d.ts +0 -1
  81. package/dist/components/forms/switch/switch.class.js +1 -1
  82. package/dist/components/forms/time-range/time-range.class.d.ts +0 -1
  83. package/dist/components/forms/time-range/time-range.class.js +1 -1
  84. package/dist/components/forms/token-input/token-input.class.d.ts +0 -1
  85. package/dist/components/forms/token-input/token-input.class.js +1 -1
  86. package/dist/components/layout/app-rail/app-rail-item.class.d.ts +16 -0
  87. package/dist/components/layout/app-rail/app-rail-item.class.js +4 -4
  88. package/dist/components/layout/dashboard-grid/layout-internal.d.ts +0 -1
  89. package/dist/components/layout/dashboard-grid/layout-internal.js +1 -1
  90. package/dist/components/layout/details/accordion-owner.d.ts +1 -1
  91. package/dist/components/layout/filter-bar/filter-bar.class.d.ts +32 -3
  92. package/dist/components/layout/filter-bar/filter-bar.class.js +2 -1
  93. package/dist/components/layout/menu/menu-item.class.js +1 -1
  94. package/dist/components/layout/virtual-list/virtual-list.class.js +1 -1
  95. package/dist/components/layout/widget/widget.class.d.ts +16 -0
  96. package/dist/components/layout/widget/widget.class.js +2 -2
  97. package/dist/components/lr-funnel.d.ts +1 -0
  98. package/dist/components/lr-funnel.js +1 -0
  99. package/dist/components/media/av-player/av-metadata.d.ts +0 -2
  100. package/dist/components/media/av-player/av-metadata.js +1 -1
  101. package/dist/components/media/file-input/file-input.class.d.ts +0 -1
  102. package/dist/components/media/file-input/file-input.class.js +1 -1
  103. package/dist/components/media/flag/flag-peer-bulk-standard.d.ts +25 -0
  104. package/dist/components/media/flag/flag-peer-bulk-standard.js +1 -0
  105. package/dist/components/media/flag/flag.class.d.ts +8 -0
  106. package/dist/components/media/flag/flag.class.js +1 -1
  107. package/dist/components/media/map/map-loader.d.ts +4 -1
  108. package/dist/components/media/map/map.class.d.ts +242 -23
  109. package/dist/components/media/map/map.class.js +2 -2
  110. package/dist/components/overlays/chip/chip-group.class.d.ts +16 -0
  111. package/dist/components/overlays/chip/chip-group.class.js +10 -3
  112. package/dist/components/overlays/overlay/popover.class.d.ts +7 -1
  113. package/dist/components/overlays/overlay/popover.class.js +1 -1
  114. package/dist/components/overlays/overlay/tooltip.class.d.ts +1 -0
  115. package/dist/components/overlays/overlay/tooltip.class.js +1 -1
  116. package/dist/components/overlays/progress/progress-shared.d.ts +0 -2
  117. package/dist/components/overlays/progress/progress-shared.js +1 -1
  118. package/dist/components/overlays/rating/rating.class.js +1 -1
  119. package/dist/components/retrieval/graph/graph-canvas.d.ts +10 -9
  120. package/dist/components/retrieval/mind-map/mind-map-layout.d.ts +0 -4
  121. package/dist/components/retrieval/mind-map/mind-map-layout.js +1 -1
  122. package/dist/components/utility/format/format-options.d.ts +0 -2
  123. package/dist/components/utility/format/format-options.js +1 -1
  124. package/dist/components/utility/icon/icon-resource.d.ts +0 -2
  125. package/dist/components/utility/icon/icon-resource.js +1 -1
  126. package/dist/components/viewers/archive-viewer/zip-resource-guard.d.ts +2 -1
  127. package/dist/components/viewers/docx-viewer/docx-resource-guard.d.ts +0 -3
  128. package/dist/components/viewers/docx-viewer/docx-resource-guard.js +1 -1
  129. package/dist/components/viewers/ebook-viewer/ebook-loader.d.ts +2 -1
  130. package/dist/components/viewers/ebook-viewer/epub-resource-guard.d.ts +0 -3
  131. package/dist/components/viewers/ebook-viewer/epub-resource-guard.js +1 -1
  132. package/dist/components/viewers/pdf-viewer/pdf-loader.d.ts +2 -2
  133. package/dist/components/viewers/pdf-viewer/pdf-viewer.class.js +2 -2
  134. package/dist/components/viewers/pptx-viewer/pptx-resource-guard.d.ts +0 -2
  135. package/dist/components/viewers/pptx-viewer/pptx-resource-guard.js +1 -1
  136. package/dist/components/viewers/spreadsheet-viewer/xlsx-resource-guard.d.ts +0 -5
  137. package/dist/components/viewers/spreadsheet-viewer/xlsx-resource-guard.js +1 -1
  138. package/dist/custom-elements-jsx.d.ts +16 -8
  139. package/dist/events.d.ts +15 -0
  140. package/dist/internal/accessibility-visibility.d.ts +1 -1
  141. package/dist/internal/anchor-target.d.ts +1 -3
  142. package/dist/internal/anchor-target.js +2 -2
  143. package/dist/internal/anchored-overlay-runtime.d.ts +7 -0
  144. package/dist/internal/anchored-overlay-runtime.js +1 -0
  145. package/dist/internal/announcement-text.d.ts +1 -1
  146. package/dist/internal/ansi.d.ts +0 -4
  147. package/dist/internal/ansi.js +1 -1
  148. package/dist/internal/aria-ownership.d.ts +2 -1
  149. package/dist/internal/autoloader-manifest.js +1 -1
  150. package/dist/internal/autoloader-tags.d.ts +1 -1
  151. package/dist/internal/autoloader-tags.js +1 -1
  152. package/dist/internal/default-strings.generated.d.ts +1 -0
  153. package/dist/internal/default-strings.generated.js +1 -1
  154. package/dist/internal/focus-navigation.d.ts +3 -2
  155. package/dist/internal/form-associated.d.ts +0 -1
  156. package/dist/internal/form-associated.js +1 -1
  157. package/dist/internal/form-control-labels.d.ts +0 -3
  158. package/dist/internal/form-control-labels.js +1 -1
  159. package/dist/internal/localization-runtime.js +1 -1
  160. package/dist/internal/localization-types.d.ts +1 -1
  161. package/dist/internal/localization.js +1 -1
  162. package/dist/internal/lyra-element.d.ts +7 -14
  163. package/dist/internal/lyra-element.js +1 -1
  164. package/dist/internal/nonmodal-overlay-manager.d.ts +9 -0
  165. package/dist/internal/nonmodal-overlay-manager.js +1 -0
  166. package/dist/internal/overlay-manager.d.ts +4 -20
  167. package/dist/internal/overlay-manager.js +1 -1
  168. package/dist/internal/overlay-stack.d.ts +66 -0
  169. package/dist/internal/overlay-stack.js +1 -0
  170. package/dist/internal/package-metadata.d.ts +1 -1
  171. package/dist/internal/package-metadata.js +1 -1
  172. package/dist/internal/positioner.js +1 -1
  173. package/dist/internal/root-registration-allowlist.d.ts +1 -1
  174. package/dist/internal/root-registration-allowlist.js +1 -1
  175. package/dist/internal/text-quote.d.ts +1 -1
  176. package/dist/lyra.d.ts +5 -4
  177. package/dist/lyra.js +1 -1
  178. package/dist/ssr/all.d.ts +1 -0
  179. package/dist/ssr/all.js +1 -1
  180. package/dist/ssr.d.ts +4 -4
  181. package/dist/styles/tokens-root.css +488 -0
  182. package/dist/svelte.d.ts +17 -8
  183. package/dist/translations/ar.js +1 -1
  184. package/dist/translations/de.js +1 -1
  185. package/dist/translations/es.js +1 -1
  186. package/dist/translations/fa.js +1 -1
  187. package/dist/translations/fr.js +1 -1
  188. package/dist/translations/he.js +1 -1
  189. package/dist/translations/ja.js +1 -1
  190. package/dist/translations/pt-BR.js +1 -1
  191. package/dist/translations/ru.js +1 -1
  192. package/dist/translations/zh-CN.js +1 -1
  193. package/dist/utilities/localization.js +1 -1
  194. package/dist/vue.d.ts +16 -8
  195. package/llms/components/lr-app-rail-item.md +8 -1
  196. package/llms/components/lr-app-rail.md +6 -0
  197. package/llms/components/lr-chip-group.md +7 -0
  198. package/llms/components/lr-chip.md +7 -0
  199. package/llms/components/lr-combobox.md +42 -0
  200. package/llms/components/lr-date-input.md +19 -1
  201. package/llms/components/lr-date-picker.md +19 -1
  202. package/llms/components/lr-filter-bar.md +50 -1
  203. package/llms/components/lr-flag.md +14 -0
  204. package/llms/components/lr-funnel.md +122 -0
  205. package/llms/components/lr-heatmap.md +55 -8
  206. package/llms/components/lr-lite-chart.md +20 -8
  207. package/llms/components/lr-map.md +109 -22
  208. package/llms/components/lr-option.md +42 -0
  209. package/llms/components/lr-popover.md +39 -0
  210. package/llms/components/lr-table.md +7 -3
  211. package/llms/components/lr-timeline-item.md +49 -24
  212. package/llms/components/lr-timeline.md +49 -24
  213. package/llms/components/lr-widget.md +7 -1
  214. package/llms/index.md +3 -2
  215. package/llms/peers.md +1 -1
  216. package/llms/shared.md +110 -1
  217. package/llms-full.txt +681 -67
  218. package/llms.txt +1 -1
  219. package/package.json +26 -8
  220. package/vscode-css-data.json +63 -0
  221. package/vscode-html-data.json +108 -23
  222. package/web-types.json +32738 -310
@@ -20,6 +20,48 @@ Filterable single/multi-select combining a text input with a listbox. Mirrors th
20
20
  `<wa-combobox>` API under the `lr-` prefix. **Form-associated** (hand-rolled internals, not the
21
21
  shared `FormAssociated` mixin — see gotchas).
22
22
 
23
+ **First-interaction registration.** Where initial-route weight is stricter than a static combobox
24
+ registration allows, keep a labelled native `<input list>` as the working pre-JavaScript control
25
+ and import only the granular combobox registration on its first focus. Copy the native value after
26
+ the import resolves so typing that happens while the chunk is in flight is not lost, then transfer
27
+ focus explicitly — the browser does not replay the focus event after custom-element upgrade:
28
+
29
+ ```html
30
+ <div id="country-fallback">
31
+ <label for="country-native">Country</label>
32
+ <input id="country-native" name="country" list="country-options">
33
+ <datalist id="country-options"><option value="France"></option></datalist>
34
+ </div>
35
+ <lr-combobox id="country-enhanced" name="country" label="Country" hidden>
36
+ <lr-option value="France">France</lr-option>
37
+ </lr-combobox>
38
+ <script type="module">
39
+ const fallback = document.querySelector("#country-fallback");
40
+ const input = document.querySelector("#country-native");
41
+ const combobox = document.querySelector("#country-enhanced");
42
+ let registration;
43
+ input.addEventListener("focus", async () => {
44
+ if (!combobox.hidden) return;
45
+ registration ??= import(
46
+ "@aceshooting/lyra-ui/components/forms/combobox/combobox.js"
47
+ ).catch((error) => {
48
+ registration = undefined; // let a later interaction retry
49
+ throw error;
50
+ });
51
+ await registration;
52
+ await customElements.whenDefined("lr-combobox");
53
+ combobox.value = input.value;
54
+ fallback.hidden = true;
55
+ combobox.hidden = false;
56
+ combobox.focus();
57
+ });
58
+ </script>
59
+ ```
60
+
61
+ Leave the native control in place if registration fails. This pattern preserves the initial shell;
62
+ the full form-label, option, overlay, and first-open positioning contracts arrive in deferred
63
+ chunks instead of being weakened in a separate partial combobox implementation.
64
+
23
65
  An `lr-option` row remains bounded by its owning listbox: the default label ellipsizes and each
24
66
  `start`/`end` (or `prefix`/`suffix`) adornment is capped at 40% of the row. Unbroken metadata
25
67
  therefore cannot widen a 320px LTR or RTL picker.
@@ -17,6 +17,45 @@
17
17
 
18
18
  A click-triggered, light-dismiss floating surface positioned with the shared Floating UI positioner.
19
19
 
20
+ **First-interaction registration.** A performance-sensitive navigation shell can keep a native
21
+ `<details>` disclosure working before JavaScript, then load only the granular popover registration
22
+ when that fallback first opens. The interaction that starts an async import is not replayed after
23
+ the custom element upgrades, so finish the handoff explicitly with `show()`:
24
+
25
+ ```html
26
+ <details id="account-fallback">
27
+ <summary>Account</summary>
28
+ <nav aria-label="Account"><a href="/profile">Profile</a></nav>
29
+ </details>
30
+ <lr-popover id="account-popover" popup-role="none" hidden>
31
+ <button slot="trigger">Account</button>
32
+ <nav aria-label="Account"><a href="/profile">Profile</a></nav>
33
+ </lr-popover>
34
+ <script type="module">
35
+ const fallback = document.querySelector("#account-fallback");
36
+ const popover = document.querySelector("#account-popover");
37
+ let registration;
38
+ fallback.addEventListener("toggle", async () => {
39
+ if (!fallback.open || !popover.hidden) return;
40
+ registration ??= import(
41
+ "@aceshooting/lyra-ui/components/overlays/overlay/popover.js"
42
+ ).catch((error) => {
43
+ registration = undefined; // let a later interaction retry
44
+ throw error;
45
+ });
46
+ await registration;
47
+ await customElements.whenDefined("lr-popover");
48
+ fallback.hidden = true;
49
+ popover.hidden = false;
50
+ await popover.show();
51
+ });
52
+ </script>
53
+ ```
54
+
55
+ This keeps the fallback's markup and semantics in the initial document. The registration, Lyra
56
+ base, nonmodal stack, and first-open positioning runtime remain outside that initial module graph.
57
+ If the import fails, leave the native disclosure visible and usable.
58
+
20
59
  **Properties:**
21
60
 
22
61
  - `open: boolean = false` (reflected) — assigning it runs the same `lr-show`/`lr-hide` lifecycle as
@@ -331,7 +331,7 @@ no-rows states return the empty element as the shadow root's own root, with no `
331
331
  wrapper around it — `::part(base)` does not apply in those two states, only in the filtered-to-zero
332
332
  one — and that `empty` disappears entirely once the `empty` slot is filled.
333
333
 
334
- - `scrollMode: 'self' | 'page' = 'self'` (attribute `scroll-mode`, reflected) — which element
334
+ - `scrollMode: 'self' | 'page' | 'auto' = 'self'` (attribute `scroll-mode`, reflected) — which element
335
335
  scrolls when the table overflows. `'self'` makes `[part="base"]` the scroll container, which is
336
336
  what pairs with `--lr-table-max-height` and makes the sticky header pin inside the table's own
337
337
  viewport. `'page'` hands scrolling back to the document. Needed because a scroll container clips
@@ -339,8 +339,12 @@ one — and that `empty` disappears entirely once the `empty` slot is filled.
339
339
  still `overflow: auto` becomes a sticky containing block that never scrolls, and its header
340
340
  scrolls away with the page. With `'page'` the header's nearest scrollport is the page, so it pins
341
341
  there; the cost is that a table wider than its host overflows the page instead of scrolling
342
- inside itself. Named `scrollMode` rather than `scroll` because a `scroll` property would shadow
343
- `Element.prototype.scroll()`
342
+ inside itself. The opt-in `'auto'` mode resolves between those two behaviors from the rendered
343
+ allocation: while content fits it uses page flow, and only while content actually overflows
344
+ horizontally does `[part="base"]` become the contained scrollport. It re-evaluates when either
345
+ the allocated width or the rendered table's intrinsic width changes, so the same table can flow
346
+ with a desktop page and contain itself in a 320px panel. The default remains `'self'`. Named
347
+ `scrollMode` rather than `scroll` because a `scroll` property would shadow `Element.prototype.scroll()`
344
348
 
345
349
  **Themeable custom properties:** `--lr-table-cell-color` (default `inherit`),
346
350
  `--lr-table-cell-link-color` (default `var(--lr-color-brand)`) and
@@ -8,7 +8,7 @@
8
8
  - **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
9
9
  - **Deprecations** none
10
10
  - **Optional peers** none
11
- - **Themeable via** 9 parts, 5 custom properties — see this component's own `@csspart`/`@cssprop` list below
11
+ - **Themeable via** 11 parts, 8 custom properties — see this component's own `@csspart`/`@cssprop` list below
12
12
  - **Documented with** `lr-timeline` (same section below)
13
13
  - **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`
14
14
 
@@ -16,20 +16,21 @@
16
16
 
17
17
  ## `lr-timeline` and `lr-timeline-item`
18
18
 
19
- Read-only chronological sequence. `lr-timeline` is a `role="list"` flex container; each
19
+ Read-only chronological sequence by default. `lr-timeline` is a `role="list"` flex container; each
20
20
  `lr-timeline-item` is a light-DOM child that sets `role="listitem"` on itself and renders its own
21
21
  marker plus the trailing rail segment reaching toward the next item's marker. The last item's rail is
22
22
  suppressed purely in CSS (`::slotted([role='listitem']:last-child)`) — no JS coordination anywhere.
23
- Neither element has events, keyboard navigation, or a selection model: a passive record display, by
24
- design (an item's `title`/`description` routinely hold focusable content, so wrapping the row in
25
- `role="button"` would trip `nested-interactive`).
23
+ Items have no keyboard navigation or selection model: a passive record display, by design (an
24
+ item's `title`/`description` routinely hold focusable content, so wrapping the row in `role="button"`
25
+ would trip `nested-interactive`). The opt-in clustered time scale adds only native count-marker
26
+ buttons; it does not make the individual rows interactive.
26
27
 
27
28
  **`lr-timeline` properties:** `orientation: 'vertical' | 'horizontal' = 'vertical'` — note the
28
29
  opposite default from `lr-stepper`; `horizontal` makes `[part='base']` a horizontally scrollable row.
29
30
  `accessibleLabel: string = ''` (attribute `aria-label`) overrides the localized `"Timeline"` name
30
31
  (the `role="list"` element is in the shadow root and never inherits a host attribute). Read-only
31
- `itemCount: number` is the live count of direct default-slot `<lr-timeline-item>` children;
32
- unrelated slotted elements and text nodes are ignored.
32
+ `itemCount: number` is the live count of default-slot `<lr-timeline-item>` assignments (including
33
+ flattened forwarding slots); unrelated slotted elements and text nodes are ignored.
33
34
  `scale: 'flow' | 'time' = 'flow'` (attribute `scale`, type `LyraTimelineScale`) chooses how items
34
35
  are distributed along the main axis. `'flow'` is the default even sequence, where `timestamp` is
35
36
  rendered as text but carries no positional meaning. `'time'` positions each item at its true
@@ -37,23 +38,34 @@ proportion of the range, so a gap of weeks and a gap of decades stop looking ide
37
38
  needs a definite extent to distribute along — `--lr-timeline-time-extent` (default
38
39
  `var(--lr-size-20rem)`), applied as `block-size` when vertical and `inline-size` when horizontal —
39
40
  because items are absolutely positioned and a percentage against an auto-sized track resolves to
40
- zero. `collision: 'overlap' | 'stack' = 'overlap'` (attribute `collision`, type `LyraTimelineCollision`)
41
- chooses what `scale="time"` does with items landing on nearly the same position: `'overlap'` leaves
42
- them stacked on one another, `'stack'` steps each colliding item one lane along the **cross** axis
43
- (indent per lane: `--lr-timeline-collision-offset`, default `var(--lr-space-l)`), which is what a
44
- dense chronology needs items within 1.5% of the axis of each other count as colliding, and an
45
- isolated item returns to lane 0 rather than inheriting a preceding run's depth. There is
46
- deliberately no `'cluster'` mode: collapsing coincident items into one expandable marker needs a
47
- selection model and click events this passive component does not have.
41
+ zero. `collision: 'overlap' | 'stack' | 'cluster' = 'overlap'` (attribute `collision`, type
42
+ `LyraTimelineCollision`) chooses what `scale="time"` does with items landing on nearly the same
43
+ position: `'overlap'` leaves them stacked on one another, `'stack'` steps each colliding item one
44
+ lane along the **cross** axis (indent per lane: `--lr-timeline-collision-offset`, default
45
+ `var(--lr-space-l)`), and `'cluster'` replaces every group of at least two colliding items with one
46
+ count marker. The group's first member in author order becomes its representative and renders the
47
+ button in that item's existing list/Tab position; the remaining members are hidden, so visual,
48
+ semantic, and keyboard order do not diverge merely because clustering is enabled. Stack mode and
49
+ the cluster floor treat items within 1.5% of the axis as colliding.
50
+ Cluster mode widens that floor to the rendered count action's footprint on the currently allocated
51
+ axis, then reclusters when the allocation or rendered action size changes, so interactive marker
52
+ hit areas do not overlap.
53
+ Cluster windows are bounded from their first sorted position rather than transitively chaining every
54
+ dense neighbour, so a large history becomes a useful sequence of markers rather than one axis-wide
55
+ cluster. An isolated item remains an ordinary timeline item. Activating a count marker is a
56
+ notification only; it never expands or selects items internally.
48
57
  `rangeStart` / `rangeEnd` (`Date | string | number`, attribute: false) pin the axis instead of
49
58
  deriving it from the earliest/latest item; a reversed or non-finite pair falls back to the derived
50
59
  range. An item with no parseable `timestamp` (including one supplied only through the `timestamp`
51
60
  slot, which carries no machine-readable instant) keeps document order and is spread evenly, so a
52
- partially-timestamped list degrades rather than stacking at the origin. Items sharing an instant
53
- overlap rather than being fanned into lanes: lane assignment, brushing, zooming and per-event
54
- selection belong to a denser component than this deliberately passive one. Positions are written to
61
+ partially-timestamped list degrades rather than stacking at the origin. Reassigning an item's
62
+ `timestamp` reclusters without requiring a slot mutation. Positions are written to
55
63
  each child as a private `--_lr-timeline-item-offset` custom property and removed again on a switch
56
- back to `'flow'`, so the component still never alters its children's content or structure.
64
+ back to `'flow'`. Cluster mode adds and removes a private visibility marker on non-representative
65
+ members and temporarily changes only the representative item's shadow presentation; switching
66
+ mode, shrinking the data, or disconnecting restores every ordinary row. If regrouping would remove
67
+ the focused row content or count action, focus moves to the replacement cluster action, the first
68
+ surviving item action, or the timeline list as a programmatic fallback.
57
69
 
58
70
  **`lr-timeline-item` properties:** `timestamp?: Date | string | number` (attribute: false — `Date`
59
71
  isn't attribute-serializable; invalid input normalizes to unset and renders no timestamp UI),
@@ -63,8 +75,16 @@ isn't attribute-serializable; invalid input normalizes to unset and renders no t
63
75
  optional pulse disabled under `prefers-reduced-motion: reduce`, with explicit `aria-current="true"`
64
76
  or `"false"` on the host).
65
77
 
66
- **Events:** none on either element. Read the reactive `itemCount` property after changing direct
67
- children; the internal slot's non-composed `slotchange` event is not a host-level public signal.
78
+ **Events:** `lr-timeline` emits `lr-cluster-activate` when a `collision="cluster"` marker is
79
+ activated by pointer, Enter, or Space. Its non-cancelable, bubbling, composed
80
+ `detail: LyraTimelineClusterActivateDetail` is `{ items: readonly LyraTimelineItem[] }`: a fresh
81
+ frozen snapshot of the cluster members in document order, preserving each element's identity.
82
+ `LyraTimelineClusterActivateDetail {
83
+ items: unknown;
84
+ }`
85
+ Use it to open a consumer-owned popover, dialog, or detail view. `lr-timeline-item` emits no events.
86
+ Read the reactive `itemCount` property after changing assigned items; the internal slot's
87
+ non-composed `slotchange` event is not a host-level public signal.
68
88
 
69
89
  **Slots:** `lr-timeline`'s default slot holds the items, in display order. On an item the **default
70
90
  slot is the title** (there is no `title` slot), plus `marker-icon` (marker glyph override; an empty
@@ -73,7 +93,9 @@ slot falls back to a color-coded dot), `timestamp` (wins outright over the
73
93
  entirely when empty).
74
94
 
75
95
  **CSS parts:** timeline `base` — the `role="list"` flex container (no separate `list` part). Item:
76
- `base`, `track` (marker + rail spine, always the opposite axis from `base`), `marker`
96
+ `cluster` the native count button rendered while that item represents a cluster, with a 40px
97
+ minimum action surface; `cluster-count` — its painted count pill; `base`, `track`
98
+ (marker + rail spine, always the opposite axis from `base`), `marker`
77
99
  (`aria-hidden="true"`, decorative), `rail` (the connecting segment; `visibility: hidden` rather than
78
100
  removed on the last item, so marker alignment stays consistent), `content`, `header` (flex row
79
101
  wrapping `title` and `timestamp`; wraps at narrow widths rather than truncating), `title`,
@@ -81,8 +103,11 @@ wrapping `title` and `timestamp`; wraps at narrow widths rather than truncating)
81
103
 
82
104
  **Themeable custom properties:** `--lr-timeline-gap` (default `var(--lr-space-l)`) — declared on
83
105
  `lr-timeline` but consumed inside each item via inheritance across the slot boundary; it is both the
84
- inter-item spacing and the length each rail bridges. `--lr-scroll-fade-size` (default `2rem`) controls each
85
- horizontal-overflow edge fade; forced-colors mode removes the masks while retaining native
106
+ inter-item spacing and the length each rail bridges. `--lr-timeline-cluster-size` (default
107
+ `var(--lr-size-2rem)`) sizes the painted count pill, `--lr-timeline-cluster-bg` (default
108
+ `var(--lr-color-brand)`) sets its background, and `--lr-timeline-cluster-color` (default
109
+ `var(--lr-color-on-brand)`) sets its foreground. `--lr-scroll-fade-size` (default `2rem`) controls
110
+ each horizontal-overflow edge fade; forced-colors mode removes the masks while retaining native
86
111
  scrolling. On the item: `--lr-timeline-marker-size`
87
112
  (default `var(--lr-size-1-25rem)`, both dimensions so the dot stays circular),
88
113
  `--lr-timeline-rail-width` (default `var(--lr-border-width-medium)`), `--lr-timeline-rail-color`
@@ -8,7 +8,7 @@
8
8
  - **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
9
9
  - **Deprecations** none
10
10
  - **Optional peers** none
11
- - **Themeable via** 1 part, 4 custom properties — see this component's own `@csspart`/`@cssprop` list below
11
+ - **Themeable via** 1 part, 7 custom properties — see this component's own `@csspart`/`@cssprop` list below
12
12
  - **Documented with** `lr-timeline-item` (same section below)
13
13
  - **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`
14
14
 
@@ -16,20 +16,21 @@
16
16
 
17
17
  ## `lr-timeline` and `lr-timeline-item`
18
18
 
19
- Read-only chronological sequence. `lr-timeline` is a `role="list"` flex container; each
19
+ Read-only chronological sequence by default. `lr-timeline` is a `role="list"` flex container; each
20
20
  `lr-timeline-item` is a light-DOM child that sets `role="listitem"` on itself and renders its own
21
21
  marker plus the trailing rail segment reaching toward the next item's marker. The last item's rail is
22
22
  suppressed purely in CSS (`::slotted([role='listitem']:last-child)`) — no JS coordination anywhere.
23
- Neither element has events, keyboard navigation, or a selection model: a passive record display, by
24
- design (an item's `title`/`description` routinely hold focusable content, so wrapping the row in
25
- `role="button"` would trip `nested-interactive`).
23
+ Items have no keyboard navigation or selection model: a passive record display, by design (an
24
+ item's `title`/`description` routinely hold focusable content, so wrapping the row in `role="button"`
25
+ would trip `nested-interactive`). The opt-in clustered time scale adds only native count-marker
26
+ buttons; it does not make the individual rows interactive.
26
27
 
27
28
  **`lr-timeline` properties:** `orientation: 'vertical' | 'horizontal' = 'vertical'` — note the
28
29
  opposite default from `lr-stepper`; `horizontal` makes `[part='base']` a horizontally scrollable row.
29
30
  `accessibleLabel: string = ''` (attribute `aria-label`) overrides the localized `"Timeline"` name
30
31
  (the `role="list"` element is in the shadow root and never inherits a host attribute). Read-only
31
- `itemCount: number` is the live count of direct default-slot `<lr-timeline-item>` children;
32
- unrelated slotted elements and text nodes are ignored.
32
+ `itemCount: number` is the live count of default-slot `<lr-timeline-item>` assignments (including
33
+ flattened forwarding slots); unrelated slotted elements and text nodes are ignored.
33
34
  `scale: 'flow' | 'time' = 'flow'` (attribute `scale`, type `LyraTimelineScale`) chooses how items
34
35
  are distributed along the main axis. `'flow'` is the default even sequence, where `timestamp` is
35
36
  rendered as text but carries no positional meaning. `'time'` positions each item at its true
@@ -37,23 +38,34 @@ proportion of the range, so a gap of weeks and a gap of decades stop looking ide
37
38
  needs a definite extent to distribute along — `--lr-timeline-time-extent` (default
38
39
  `var(--lr-size-20rem)`), applied as `block-size` when vertical and `inline-size` when horizontal —
39
40
  because items are absolutely positioned and a percentage against an auto-sized track resolves to
40
- zero. `collision: 'overlap' | 'stack' = 'overlap'` (attribute `collision`, type `LyraTimelineCollision`)
41
- chooses what `scale="time"` does with items landing on nearly the same position: `'overlap'` leaves
42
- them stacked on one another, `'stack'` steps each colliding item one lane along the **cross** axis
43
- (indent per lane: `--lr-timeline-collision-offset`, default `var(--lr-space-l)`), which is what a
44
- dense chronology needs items within 1.5% of the axis of each other count as colliding, and an
45
- isolated item returns to lane 0 rather than inheriting a preceding run's depth. There is
46
- deliberately no `'cluster'` mode: collapsing coincident items into one expandable marker needs a
47
- selection model and click events this passive component does not have.
41
+ zero. `collision: 'overlap' | 'stack' | 'cluster' = 'overlap'` (attribute `collision`, type
42
+ `LyraTimelineCollision`) chooses what `scale="time"` does with items landing on nearly the same
43
+ position: `'overlap'` leaves them stacked on one another, `'stack'` steps each colliding item one
44
+ lane along the **cross** axis (indent per lane: `--lr-timeline-collision-offset`, default
45
+ `var(--lr-space-l)`), and `'cluster'` replaces every group of at least two colliding items with one
46
+ count marker. The group's first member in author order becomes its representative and renders the
47
+ button in that item's existing list/Tab position; the remaining members are hidden, so visual,
48
+ semantic, and keyboard order do not diverge merely because clustering is enabled. Stack mode and
49
+ the cluster floor treat items within 1.5% of the axis as colliding.
50
+ Cluster mode widens that floor to the rendered count action's footprint on the currently allocated
51
+ axis, then reclusters when the allocation or rendered action size changes, so interactive marker
52
+ hit areas do not overlap.
53
+ Cluster windows are bounded from their first sorted position rather than transitively chaining every
54
+ dense neighbour, so a large history becomes a useful sequence of markers rather than one axis-wide
55
+ cluster. An isolated item remains an ordinary timeline item. Activating a count marker is a
56
+ notification only; it never expands or selects items internally.
48
57
  `rangeStart` / `rangeEnd` (`Date | string | number`, attribute: false) pin the axis instead of
49
58
  deriving it from the earliest/latest item; a reversed or non-finite pair falls back to the derived
50
59
  range. An item with no parseable `timestamp` (including one supplied only through the `timestamp`
51
60
  slot, which carries no machine-readable instant) keeps document order and is spread evenly, so a
52
- partially-timestamped list degrades rather than stacking at the origin. Items sharing an instant
53
- overlap rather than being fanned into lanes: lane assignment, brushing, zooming and per-event
54
- selection belong to a denser component than this deliberately passive one. Positions are written to
61
+ partially-timestamped list degrades rather than stacking at the origin. Reassigning an item's
62
+ `timestamp` reclusters without requiring a slot mutation. Positions are written to
55
63
  each child as a private `--_lr-timeline-item-offset` custom property and removed again on a switch
56
- back to `'flow'`, so the component still never alters its children's content or structure.
64
+ back to `'flow'`. Cluster mode adds and removes a private visibility marker on non-representative
65
+ members and temporarily changes only the representative item's shadow presentation; switching
66
+ mode, shrinking the data, or disconnecting restores every ordinary row. If regrouping would remove
67
+ the focused row content or count action, focus moves to the replacement cluster action, the first
68
+ surviving item action, or the timeline list as a programmatic fallback.
57
69
 
58
70
  **`lr-timeline-item` properties:** `timestamp?: Date | string | number` (attribute: false — `Date`
59
71
  isn't attribute-serializable; invalid input normalizes to unset and renders no timestamp UI),
@@ -63,8 +75,16 @@ isn't attribute-serializable; invalid input normalizes to unset and renders no t
63
75
  optional pulse disabled under `prefers-reduced-motion: reduce`, with explicit `aria-current="true"`
64
76
  or `"false"` on the host).
65
77
 
66
- **Events:** none on either element. Read the reactive `itemCount` property after changing direct
67
- children; the internal slot's non-composed `slotchange` event is not a host-level public signal.
78
+ **Events:** `lr-timeline` emits `lr-cluster-activate` when a `collision="cluster"` marker is
79
+ activated by pointer, Enter, or Space. Its non-cancelable, bubbling, composed
80
+ `detail: LyraTimelineClusterActivateDetail` is `{ items: readonly LyraTimelineItem[] }`: a fresh
81
+ frozen snapshot of the cluster members in document order, preserving each element's identity.
82
+ `LyraTimelineClusterActivateDetail {
83
+ items: unknown;
84
+ }`
85
+ Use it to open a consumer-owned popover, dialog, or detail view. `lr-timeline-item` emits no events.
86
+ Read the reactive `itemCount` property after changing assigned items; the internal slot's
87
+ non-composed `slotchange` event is not a host-level public signal.
68
88
 
69
89
  **Slots:** `lr-timeline`'s default slot holds the items, in display order. On an item the **default
70
90
  slot is the title** (there is no `title` slot), plus `marker-icon` (marker glyph override; an empty
@@ -73,7 +93,9 @@ slot falls back to a color-coded dot), `timestamp` (wins outright over the
73
93
  entirely when empty).
74
94
 
75
95
  **CSS parts:** timeline `base` — the `role="list"` flex container (no separate `list` part). Item:
76
- `base`, `track` (marker + rail spine, always the opposite axis from `base`), `marker`
96
+ `cluster` the native count button rendered while that item represents a cluster, with a 40px
97
+ minimum action surface; `cluster-count` — its painted count pill; `base`, `track`
98
+ (marker + rail spine, always the opposite axis from `base`), `marker`
77
99
  (`aria-hidden="true"`, decorative), `rail` (the connecting segment; `visibility: hidden` rather than
78
100
  removed on the last item, so marker alignment stays consistent), `content`, `header` (flex row
79
101
  wrapping `title` and `timestamp`; wraps at narrow widths rather than truncating), `title`,
@@ -81,8 +103,11 @@ wrapping `title` and `timestamp`; wraps at narrow widths rather than truncating)
81
103
 
82
104
  **Themeable custom properties:** `--lr-timeline-gap` (default `var(--lr-space-l)`) — declared on
83
105
  `lr-timeline` but consumed inside each item via inheritance across the slot boundary; it is both the
84
- inter-item spacing and the length each rail bridges. `--lr-scroll-fade-size` (default `2rem`) controls each
85
- horizontal-overflow edge fade; forced-colors mode removes the masks while retaining native
106
+ inter-item spacing and the length each rail bridges. `--lr-timeline-cluster-size` (default
107
+ `var(--lr-size-2rem)`) sizes the painted count pill, `--lr-timeline-cluster-bg` (default
108
+ `var(--lr-color-brand)`) sets its background, and `--lr-timeline-cluster-color` (default
109
+ `var(--lr-color-on-brand)`) sets its foreground. `--lr-scroll-fade-size` (default `2rem`) controls
110
+ each horizontal-overflow edge fade; forced-colors mode removes the masks while retaining native
86
111
  scrolling. On the item: `--lr-timeline-marker-size`
87
112
  (default `var(--lr-size-1-25rem)`, both dimensions so the dot stays circular),
88
113
  `--lr-timeline-rail-width` (default `var(--lr-border-width-medium)`), `--lr-timeline-rail-color`
@@ -6,7 +6,7 @@
6
6
  - **Class** `LyraWidget`, also available unregistered from `@aceshooting/lyra-ui/components/layout/widget/widget.class.js`
7
7
  - **Family** `components/layout/` — see `llms/index.md` for its siblings
8
8
  - **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
9
- - **Deprecations** none
9
+ - **Deprecated property** `activeView` since `11.2.0`; use property `activeViewId`; removal not before `13.0.0` — `activeView` was this property's original public name; the string does not appear anywhere in CHANGELOG.md, so the rename to `activeViewId` was never announced at all. A shipped consumer's `.activeView=` binding became a dead expando and the widget fell back to its first view with no error. The alias seeds `activeViewId` rather than being read alongside it, because the component itself writes that property (a view-toggle click, and the fallback when `views` drops the active id), and a read-alongside alias would undo a later interactive change on the next update.
10
10
  - **Optional peers** none
11
11
  - **Themeable via** 16 parts, 9 custom properties — see this component's own `@csspart`/`@cssprop` list below
12
12
  - **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`
@@ -49,6 +49,12 @@ TemplateResult; ariaLabel?: string }`. Each entry gets a header toggle button
49
49
  - `activeViewId: string = ''` (attribute: false) — the currently active view's `viewId`; defaults to the
50
50
  first entry of `views` (or `''` when `views` is empty). Settable directly to control the active
51
51
  view externally; also updated internally when a view toggle is clicked.
52
+ - `activeView: string = ''` (attribute: false) — **deprecated alias for `activeViewId`**, which it
53
+ seeds. `activeView` was this member's original public name and the rename was never announced, so
54
+ a shipped `.activeView=${…}` binding silently became inert and the widget fell back to its first
55
+ view. It seeds rather than being read alongside, because the component itself writes
56
+ `activeViewId` (a toggle click, and the fallback when `views` drops the active id) — so a stale
57
+ alias must not undo a later interactive change. Prefer `activeViewId` in new code.
52
58
  - `accessibleLabel: string | null = null` (attribute `aria-label`) — overrides the label-derived
53
59
  fullscreen dialog name. An explicitly empty value is retained; property, slotted-label, and
54
60
  localized fallbacks apply only when it is absent.
package/llms/index.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Component index
4
4
 
5
- 284 custom elements, grouped by the source family they live in.
5
+ 285 custom elements, grouped by the source family they live in.
6
6
 
7
7
  **Reading one component.** Its reference file path is derived from the tag — no search needed:
8
8
  `llms/components/<tag>.md` (e.g. `llms/components/lr-table.md`). Each is self-contained: import
@@ -50,7 +50,7 @@ Optional peers: `llms/peers.md`. Safe `wa-*`/`sl-*` migration: `llms/migration.m
50
50
  - `lr-rubric-form` · lr-rubric-form.js · `stable` since `4.0.0` — a configurable annotation rubric (LangSmith annotation-queue style): score, category, and freeform-comment keys with a submit-and-next fl...
51
51
  - `lr-locale-picker` · lr-locale-picker.js · `stable` since `6.0.0` — a closed-list locale switcher over the library's own locale registry.
52
52
 
53
- ## Data display, dashboards & flow canvas — `components/data/` (25)
53
+ ## Data display, dashboards & flow canvas — `components/data/` (26)
54
54
 
55
55
  - `lr-sparkline` · lr-sparkline.js · `stable` since `4.0.0` — a zero-dependency inline SVG trend chart.
56
56
  - `lr-stat` · lr-stat.js · `stable` since `4.0.0` — a KPI/stat card.
@@ -58,6 +58,7 @@ Optional peers: `llms/peers.md`. Safe `wa-*`/`sl-*` migration: `llms/migration.m
58
58
  - `lr-table` · lr-table.js · `stable` since `4.0.0` — a sort/select-aware data table.
59
59
  - `lr-pagination` · lr-pagination.js · `stable` since `4.0.0` — controlled, server-friendly page navigation: a numbered page list with elided gaps, optional first/last controls, an optional item-range...
60
60
  - `lr-gauge` · lr-gauge.js · `stable` since `4.0.0` — a radial, full-circle ring, or linear meter.
61
+ - `lr-funnel` · lr-funnel.js · `experimental` since `12.0.0` — a dependency-free conversion funnel: an ordered set of stages, each drawn as a bar whose length is that stage's share of the FIRST stage,...
61
62
  - `lr-word-cloud` · lr-word-cloud.js · `stable` since `4.0.0` — a zero-dependency SVG word/tag cloud.
62
63
  - `lr-heatmap` · lr-heatmap.js · `stable` since `4.0.0` — a Canvas heatmap with a DPR-aware, resize-aware redraw loop.
63
64
  - `lr-sequence-strip` · lr-sequence-strip.js · `stable` since `4.0.0` — a compact, one-thin-cell-per-item strip visualizing a sequence of categorical states, with an optional secondary per-cell marker.
package/llms/peers.md CHANGED
@@ -37,7 +37,7 @@ for the page.
37
37
 
38
38
  | Peer | Range | Needed by |
39
39
  |---|---|---|
40
- | `@aceshooting/lyra-flags` | `workspace:^2.1.0` | `lr-flag`, `lr-locale-picker`, `lr-phone-input` |
40
+ | `@aceshooting/lyra-flags` | `workspace:^2.2.0` | `lr-flag`, `lr-locale-picker`, `lr-phone-input` |
41
41
  | `@aiden0z/pptx-renderer` | `^1.2.4` | `lr-pptx-viewer` |
42
42
  | `@sgratzl/chartjs-chart-boxplot` | `^4.4.5` | `lr-box-plot` |
43
43
  | `chart.js` | `^4.5.1` | `lr-bar-chart`, `lr-box-plot`, `lr-bubble-chart`, `lr-chart`, `lr-doughnut-chart`, `lr-histogram`, `lr-line-chart`, `lr-pie-chart`, `lr-polar-area-chart`, `lr-radar-chart`, `lr-scatter-chart` |
package/llms/shared.md CHANGED
@@ -150,6 +150,8 @@ The entry points, then:
150
150
  a pre-resolved `src`) additionally needs
151
151
  `import '@aceshooting/lyra-ui/components/media/flag/flag-peer.js';` once.
152
152
  - **Other subpaths.** `@aceshooting/lyra-ui/theme.css` (ready-made light/dark theme),
153
+ `@aceshooting/lyra-ui/tokens-root.css` (opt-in: the curated resolved `--lr-*` tokens at `:root`,
154
+ so your own components can read them),
153
155
  `@aceshooting/lyra-ui/native.css` (opt-in native-element styles inside `.lr-native`),
154
156
  `@aceshooting/lyra-ui/utilities.css` (opt-in light-DOM layout/text utilities),
155
157
  `@aceshooting/lyra-ui/theme.js` (the zero-dependency mode/accent runtime),
@@ -644,10 +646,110 @@ reaches plain application CSS, and it never reaches your own custom elements, si
644
646
  descendant of an `lr-*` shadow root — `body { color: var(--lr-color-text) }` in application CSS
645
647
  resolves to nothing, silently, not an error. Retheme through layer 1 (`--lr-theme-*`), which
646
648
  `theme.css` supplies at document scope and which inherits normally into every nested shadow root.
649
+ To _read_ (not retheme) the resolved values from your own components, import the opt-in
650
+ [`tokens-root.css`](#reading-the-resolved-tokens-from-your-own-components--tokens-rootcss), which
651
+ declares a curated subset of layer 2 at `:root`.
647
652
  See [Where an override actually reaches](#where-an-override-actually-reaches) below for the full
648
653
  inheritance rules, including the one documented exception (per-component `--lr-<component>-*`
649
654
  hooks, layer 3, which do inherit through wrappers).
650
655
 
656
+ ### Reading the resolved tokens from your own components — `tokens-root.css`
657
+
658
+ The paragraph above is a real problem for any application that has custom elements of its own: they
659
+ are not descendants of an `lr-*` shadow root either, so `var(--lr-color-border)` inside **your**
660
+ component resolves to nothing, and `var(--lr-space-m, 0.5rem)` quietly runs on its literal fallback
661
+ forever. Both failures are invisible without reading computed styles in a browser.
662
+
663
+ Import one optional stylesheet and the curated part of layer 2 exists at document scope:
664
+
665
+ ```css
666
+ @import "@aceshooting/lyra-ui/theme.css"; /* the --lr-theme-* input layer */
667
+ @import "@aceshooting/lyra-ui/tokens-root.css"; /* the resolved --lr-* layer, at :root */
668
+ ```
669
+
670
+ ```css
671
+ /* Now valid in your own component's stylesheet, in plain application CSS, anywhere. */
672
+ .app-panel {
673
+ padding: var(--lr-space-m);
674
+ border: var(--lr-border-width-thin) solid var(--lr-color-border);
675
+ border-radius: var(--lr-radius);
676
+ background: var(--lr-color-surface-raised);
677
+ color: var(--lr-color-text);
678
+ font-family: var(--lr-font);
679
+ }
680
+ .app-panel:focus-visible {
681
+ outline: var(--lr-focus-ring);
682
+ outline-offset: var(--lr-focus-ring-offset);
683
+ }
684
+ ```
685
+
686
+ **It is opt-in, and it is a curated subset — not all of layer 2.** `--lr-*` is internal precisely so
687
+ it can change without a major version; publishing all of it at `:root` would freeze several hundred
688
+ internal decisions as permanent API. What ships is what an application's own component needs to sit
689
+ inside a Lyra UI without looking foreign:
690
+
691
+ | Family | Names |
692
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
693
+ | Ambient colour | `--lr-color-surface`, `--lr-color-surface-raised`, `--lr-color-surface-overlay`, `--lr-color-overlay`, `--lr-color-text`, `--lr-color-text-quiet`, `--lr-color-border`, `--lr-color-border-strong` |
694
+ | The semantic grid | all 45 `--lr-color-{brand,success,warning,danger,neutral}-{fill,border,on}-{quiet,normal,loud}` slots |
695
+ | Flat colour aliases | `--lr-color-{brand,success,warning,danger,neutral}`, `--lr-color-{brand,success,warning,danger}-quiet`, `--lr-color-on-{brand,success,warning,danger,neutral}` |
696
+ | Spacing | `--lr-space-2xs`, `--lr-space-xs`, `--lr-space-s`, `--lr-space-m`, `--lr-space-l`, `--lr-space-2xl` |
697
+ | Geometry | `--lr-radius-xs`, `--lr-radius`, `--lr-radius-pill`, `--lr-border-width-thin`, `--lr-border-width-medium`, `--lr-border-width-thick` |
698
+ | Elevation | `--lr-shadow-color`, `--lr-shadow-xs`, `--lr-shadow-s`, `--lr-shadow-m`, `--lr-shadow-l`, `--lr-shadow-xl`, `--lr-shadow` |
699
+ | Typography | `--lr-font`, `--lr-font-mono`, the ten `--lr-font-size-*` steps, the four `--lr-font-weight-*` steps |
700
+ | State and motion | `--lr-focus-ring`, `--lr-focus-ring-color`, `--lr-focus-ring-width`, `--lr-focus-ring-offset`, `--lr-opacity-disabled`, `--lr-opacity-muted`, `--lr-duration-fast`, `--lr-duration-base`, `--lr-easing-standard`, `--lr-easing-emphasized`, `--lr-transition-fast`, `--lr-transition-base` |
701
+
702
+ The grid ships whole because its contrast guarantee is **per tier** — a `fill-quiet` background is
703
+ only guaranteed legible under the matching `on-quiet` foreground — so shipping the flat aliases
704
+ alone would hand you a pairing with nothing behind it.
705
+
706
+ **Deliberately not published**, and each for a reason that makes reading it a bug rather than a
707
+ convenience: `--lr-ramp-*` (a step encodes a light-mode choice and has no theme hook),
708
+ `--lr-size-*` (value-named geometry constants, frozen internals), the chart, graph and terminal
709
+ palettes (generated ramps that move with the palette tooling), `--lr-layer-*` (stacking order is
710
+ your decision), `--lr-color-mix-*` and `--lr-hover-brightness` (inputs to the library's own
711
+ interaction recipe), `--lr-line-height-*`, the per-control internals (`--lr-icon-button-size`,
712
+ `--lr-otp-input-segment-size`, `--lr-scroll-fade-size`, `--lr-popover-viewport-clamp`,
713
+ `--lr-safe-area-*`, `--lr-mask-opaque`, `--lr-color-no-data`), and the nine variant-following slots
714
+ (`--lr-color-fill-loud` and friends), which mean "the variant _this_ element is set to" and are
715
+ meaningless on `:root`. If you need one of these, ask for it to be added rather than reading it out
716
+ of a component's shadow root.
717
+
718
+ **Stability promise.** Every name in the table is public API from the release that introduced it: it
719
+ will not be renamed or removed outside a major version, and its meaning will not change. Its _value_
720
+ may change in a minor exactly as it may inside a component — a palette retune moves your elements
721
+ and the kit's together, which is the point. Names absent from the file stay internal and may change
722
+ in any release.
723
+
724
+ **Modes work the way the components' do.** Light on `:root` and on `.lr-light` /
725
+ `[data-lr-theme="light"]`, dark under `prefers-color-scheme: dark` (unless an explicit light scope
726
+ opts out) and on `.lr-dark` / `[data-lr-theme="dark"]`, plus the same `forced-colors` and
727
+ `prefers-reduced-motion` overrides the components apply — and those two reach you on **every** one
728
+ of those routes, including a dark OS with no explicit scope, because they repeat the compound
729
+ `:root:not(.lr-light):not([data-lr-theme="light"])` selector the dark route uses rather than a bare
730
+ `:root` that the dark route would out-specify. Every declaration keeps its `--lr-theme-*`
731
+ input in front of a resolved fallback, so `theme.css` and your own overrides still win here exactly
732
+ as they do inside a component.
733
+
734
+ **One caveat, and it is the same shape as layer 2's rule everywhere else.** A `--lr-theme-*` input
735
+ set on a mid-tree element retunes every `lr-*` component below it, because each component re-derives
736
+ the resolved layer on its own `:host`. The document-scope copy cannot: it is substituted where it is
737
+ declared, and what inherits past that point is the finished value. So if an application element
738
+ carries a subtree override and expects its **own** descendants to follow, give that element a mode
739
+ scope too — `class="lr-light"`, `class="lr-dark"`, or `data-lr-theme` — which is what the file's
740
+ mode rules key on, and the whole subset resolves again there:
741
+
742
+ ```html
743
+ <!-- Both the lr-* components and the app's own elements below follow the override. -->
744
+ <section class="lr-light" style="--lr-theme-color-brand-fill-loud: #7c3aed">…</section>
745
+ ```
746
+
747
+ Everything sits in the `lr-theme` cascade layer, like `theme.css`, so any unlayered application rule
748
+ beats it regardless of load order, and the file declares custom properties only — notably not
749
+ `color-scheme` — so importing it paints nothing by itself. `--lr-focus-ring` and its three parts are
750
+ also declared at document scope by `theme.css`; both spell the same chain, so importing both is a
751
+ no-op either way round.
752
+
651
753
  ### The colour ramp and the semantic grid
652
754
 
653
755
  Colour has two layers beneath the `--lr-*` tokens you normally read.
@@ -1194,7 +1296,10 @@ stay in light DOM: they do not pierce a component's shadow root.
1194
1296
  `utilities.css` places exact, zero-specificity `:where(.lr-*)` classes in `lr-utilities`. It never
1195
1297
  uses a substring class selector, so a class such as `app-lr-flex-preview` does not opt in. Both
1196
1298
  assets repeat `@layer lr-base, lr-theme, lr-utilities, lr-overrides`; an ordinary unlayered
1197
- application rule therefore beats them regardless of load order.
1299
+ application rule therefore beats them regardless of load order. A third opt-in asset,
1300
+ [`tokens-root.css`](#reading-the-resolved-tokens-from-your-own-components--tokens-rootcss), is not a
1301
+ style sheet in the same sense — it declares custom properties only, and exists so your own
1302
+ components can read the resolved `--lr-*` tokens these two are written against.
1198
1303
 
1199
1304
  ### Utility class inventory
1200
1305
 
@@ -2705,6 +2810,7 @@ These named interfaces and helper signatures are available to typed integrations
2705
2810
  "lr-format-bytes": unknown;
2706
2811
  "lr-format-date": unknown;
2707
2812
  "lr-format-number": unknown;
2813
+ "lr-funnel": unknown;
2708
2814
  "lr-gauge": unknown;
2709
2815
  "lr-generation-metrics": unknown;
2710
2816
  "lr-geojson-view": unknown;
@@ -3090,6 +3196,7 @@ These named interfaces and helper signatures are available to typed integrations
3090
3196
  "lr-format-bytes": unknown;
3091
3197
  "lr-format-date": unknown;
3092
3198
  "lr-format-number": unknown;
3199
+ "lr-funnel": unknown;
3093
3200
  "lr-gauge": unknown;
3094
3201
  "lr-generation-metrics": unknown;
3095
3202
  "lr-geojson-view": unknown;
@@ -3376,6 +3483,7 @@ These named interfaces and helper signatures are available to typed integrations
3376
3483
  "lr-format-bytes": unknown;
3377
3484
  "lr-format-date": unknown;
3378
3485
  "lr-format-number": unknown;
3486
+ "lr-funnel": unknown;
3379
3487
  "lr-gauge": unknown;
3380
3488
  "lr-generation-metrics": unknown;
3381
3489
  "lr-geojson-view": unknown;
@@ -3702,6 +3810,7 @@ These named interfaces and helper signatures are available to typed integrations
3702
3810
  "lr-format-bytes": unknown;
3703
3811
  "lr-format-date": unknown;
3704
3812
  "lr-format-number": unknown;
3813
+ "lr-funnel": unknown;
3705
3814
  "lr-gauge": unknown;
3706
3815
  "lr-generation-metrics": unknown;
3707
3816
  "lr-geojson-view": unknown;