@aceshooting/lyra-ui 27.0.0 → 28.0.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 (389) hide show
  1. package/CHANGELOG.md +55 -63
  2. package/README.md +7 -3
  3. package/dist/cli/css-declarations.mjs +1 -0
  4. package/dist/cli/lyra-rename-ledger.mjs +1 -1
  5. package/dist/cli/migrate-wa.mjs +9 -4
  6. package/dist/cli/migration-contract.json +1 -1
  7. package/dist/cli/migration-renames.mjs +2 -2
  8. package/dist/cli/migration-theme-scopes.mjs +3 -0
  9. package/dist/cli/theme-scope-vocabulary.generated.mjs +1 -0
  10. package/dist/components/agent-tools/activity-feed/activity-feed.class.js +1 -1
  11. package/dist/components/agent-tools/agent-eval-dashboard/agent-eval-dashboard.class.js +1 -1
  12. package/dist/components/agent-tools/agent-question/agent-question.class.js +1 -1
  13. package/dist/components/agent-tools/agent-run/agent-run.class.js +1 -1
  14. package/dist/components/agent-tools/agent-trace/agent-trace.class.js +1 -1
  15. package/dist/components/agent-tools/approval-queue/approval-queue.class.js +1 -1
  16. package/dist/components/agent-tools/artifact-panel/artifact-panel.class.js +1 -1
  17. package/dist/components/agent-tools/background-runs/background-runs.class.js +1 -1
  18. package/dist/components/agent-tools/browser-frame/browser-frame.class.js +1 -1
  19. package/dist/components/agent-tools/budget-meter/budget-meter.class.js +1 -1
  20. package/dist/components/agent-tools/change-review/change-review.class.js +1 -1
  21. package/dist/components/agent-tools/commit-card/commit-card.class.js +1 -1
  22. package/dist/components/agent-tools/compare-panel/compare-panel.class.js +1 -1
  23. package/dist/components/agent-tools/confirm-bar/confirm-bar.class.js +1 -1
  24. package/dist/components/agent-tools/connector-manager/connector-manager.class.js +1 -1
  25. package/dist/components/agent-tools/context-inspector/context-inspector.class.js +1 -1
  26. package/dist/components/agent-tools/eval-dataset/eval-dataset.class.js +1 -1
  27. package/dist/components/agent-tools/eval-result/eval-result.class.js +1 -1
  28. package/dist/components/agent-tools/evaluation-run/evaluation-run.class.js +1 -1
  29. package/dist/components/agent-tools/mcp-app/mcp-app.class.js +1 -1
  30. package/dist/components/agent-tools/permission-grant/permission-grant.class.js +1 -1
  31. package/dist/components/agent-tools/permission-rules/permission-rules.class.js +1 -1
  32. package/dist/components/agent-tools/policy-summary/policy-summary.class.js +1 -1
  33. package/dist/components/agent-tools/prompt-studio/prompt-studio.class.js +1 -1
  34. package/dist/components/agent-tools/result-card/result-field.class.js +1 -1
  35. package/dist/components/agent-tools/schema-viewer/schema-viewer.class.js +1 -1
  36. package/dist/components/agent-tools/span-waterfall/span-waterfall.class.js +1 -1
  37. package/dist/components/agent-tools/stack-trace/stack-trace.class.js +1 -1
  38. package/dist/components/agent-tools/subagent-panel/subagent-panel.class.js +2 -2
  39. package/dist/components/agent-tools/task-list/task-list.class.js +2 -2
  40. package/dist/components/agent-tools/terminal/terminal.class.js +1 -1
  41. package/dist/components/agent-tools/test-results/test-results.class.js +1 -1
  42. package/dist/components/agent-tools/thinking-panel/thinking-panel.class.js +1 -1
  43. package/dist/components/agent-tools/tool-approval-dialog/tool-approval-dialog.class.js +1 -1
  44. package/dist/components/agent-tools/tool-call-block/tool-call-block.class.js +1 -1
  45. package/dist/components/agent-tools/tool-call-chip/tool-call-chip.class.js +1 -1
  46. package/dist/components/agent-tools/tool-param-form/tool-param-form.class.js +1 -1
  47. package/dist/components/agent-tools/tool-result-dialog/tool-result-dialog.class.js +1 -1
  48. package/dist/components/agent-tools/tool-result-view/tool-result-view.class.js +1 -1
  49. package/dist/components/agent-tools/tool-select-dialog/tool-select-dialog.class.js +1 -1
  50. package/dist/components/agent-tools/tool-timeline/tool-timeline.class.js +1 -1
  51. package/dist/components/agent-tools/trace-tree/trace-tree.class.js +2 -2
  52. package/dist/components/charts/chart/box-plot.class.js +1 -1
  53. package/dist/components/charts/chart/chart-token-cache.js +2 -2
  54. package/dist/components/charts/chart/chart.class.d.ts +1 -1
  55. package/dist/components/charts/chart/chart.class.js +3 -3
  56. package/dist/components/charts/chart/histogram.class.js +1 -1
  57. package/dist/components/charts/chart/lite-chart.class.js +1 -1
  58. package/dist/components/conversation/agent-workspace/agent-workspace.class.js +1 -1
  59. package/dist/components/conversation/audio-visualizer/audio-visualizer.class.js +1 -1
  60. package/dist/components/conversation/branch-picker/branch-picker.class.js +1 -1
  61. package/dist/components/conversation/chat-composer/chat-composer.class.js +1 -1
  62. package/dist/components/conversation/chat-message/chat-message.class.js +1 -1
  63. package/dist/components/conversation/chat-viewport/chat-viewport.class.js +1 -1
  64. package/dist/components/conversation/checkpoint/checkpoint.class.js +1 -1
  65. package/dist/components/conversation/code-block/code-block-base.class.js +1 -1
  66. package/dist/components/conversation/code-block/code-block-core.class.js +1 -1
  67. package/dist/components/conversation/code-block/code-block.class.js +1 -1
  68. package/dist/components/conversation/conversation-item/conversation-item.class.js +1 -1
  69. package/dist/components/conversation/generation-metrics/generation-metrics.class.js +1 -1
  70. package/dist/components/conversation/handoff-divider/handoff-divider.class.js +1 -1
  71. package/dist/components/conversation/markdown/markdown-base.class.js +1 -1
  72. package/dist/components/conversation/markdown/markdown-core.class.js +1 -1
  73. package/dist/components/conversation/markdown/markdown.class.js +1 -1
  74. package/dist/components/conversation/message-actions/message-actions.class.js +1 -1
  75. package/dist/components/conversation/message-feedback/message-feedback.class.js +1 -1
  76. package/dist/components/conversation/message-parts/message-parts.class.js +1 -1
  77. package/dist/components/conversation/model-select/model-select.class.js +1 -1
  78. package/dist/components/conversation/model-settings-panel/model-settings-panel.class.js +1 -1
  79. package/dist/components/conversation/prompt-input/prompt-input.class.js +1 -1
  80. package/dist/components/conversation/prompt-queue/prompt-queue.class.js +1 -1
  81. package/dist/components/conversation/push-to-talk/push-to-talk.class.js +1 -1
  82. package/dist/components/conversation/realtime-session/realtime-session.class.js +1 -1
  83. package/dist/components/conversation/selection-toolbar/selection-toolbar.class.js +1 -1
  84. package/dist/components/conversation/stream-status/stream-status.class.js +1 -1
  85. package/dist/components/conversation/suggestion-chips/suggestion-chips.class.js +1 -1
  86. package/dist/components/conversation/thread-list/thread-list.class.js +1 -1
  87. package/dist/components/conversation/transcript-feed/transcript-feed.class.js +1 -1
  88. package/dist/components/conversation/typing-indicator/typing-indicator.class.js +1 -1
  89. package/dist/components/conversation/usage-badge/usage-badge.class.js +1 -1
  90. package/dist/components/conversation/voice-picker/voice-picker.class.js +1 -1
  91. package/dist/components/data/calendar/calendar.class.js +1 -1
  92. package/dist/components/data/condition-builder/condition-builder.class.js +1 -1
  93. package/dist/components/data/context-meter/context-meter.class.js +1 -1
  94. package/dist/components/data/data-grid/data-grid.class.d.ts +2 -2
  95. package/dist/components/data/data-grid/data-grid.class.js +3 -3
  96. package/dist/components/data/document-library/document-library.class.js +1 -1
  97. package/dist/components/data/env-list/env-list.class.js +1 -1
  98. package/dist/components/data/file-tree/file-tree.class.d.ts +2 -2
  99. package/dist/components/data/file-tree/file-tree.class.js +1 -1
  100. package/dist/components/data/flow-canvas/flow-canvas.class.js +1 -1
  101. package/dist/components/data/flow-controls/flow-controls.class.js +1 -1
  102. package/dist/components/data/flow-minimap/flow-minimap.class.js +1 -1
  103. package/dist/components/data/flow-node/flow-node.class.js +1 -1
  104. package/dist/components/data/flow-run-status/flow-run-status.class.js +1 -1
  105. package/dist/components/data/funnel/funnel.class.js +1 -1
  106. package/dist/components/data/gauge/gauge.class.js +1 -1
  107. package/dist/components/data/graph-query-builder/graph-query-builder.class.js +1 -1
  108. package/dist/components/data/heatmap/heatmap.class.js +1 -1
  109. package/dist/components/data/pagination/pagination.class.d.ts +2 -0
  110. package/dist/components/data/pagination/pagination.class.js +1 -1
  111. package/dist/components/data/pagination/pagination.styles.js +1 -1
  112. package/dist/components/data/sequence-strip/sequence-strip.class.js +1 -1
  113. package/dist/components/data/sparkline/sparkline.class.js +1 -1
  114. package/dist/components/data/stat/stat.class.js +1 -1
  115. package/dist/components/data/table/table.class.d.ts +23 -2
  116. package/dist/components/data/table/table.class.js +5 -3
  117. package/dist/components/data/table/table.styles.js +1 -1
  118. package/dist/components/data/timeline/timeline.class.js +1 -1
  119. package/dist/components/data/tree/tree.class.js +1 -1
  120. package/dist/components/data/word-cloud/word-cloud.class.js +1 -1
  121. package/dist/components/forms/button/button.class.js +1 -1
  122. package/dist/components/forms/checkbox/checkbox.class.js +1 -1
  123. package/dist/components/forms/checkbox-group/checkbox-group.class.js +1 -1
  124. package/dist/components/forms/code-editor/code-editor.class.js +1 -1
  125. package/dist/components/forms/color-picker/color-picker.class.d.ts +8 -4
  126. package/dist/components/forms/color-picker/color-picker.class.js +2 -2
  127. package/dist/components/forms/combobox/combobox.class.js +1 -1
  128. package/dist/components/forms/country-picker/country-picker.class.js +1 -1
  129. package/dist/components/forms/currency-picker/currency-picker.class.js +1 -1
  130. package/dist/components/forms/date-picker/date-input.class.js +1 -1
  131. package/dist/components/forms/date-picker/date-picker.class.js +1 -1
  132. package/dist/components/forms/emoji-picker/emoji-data-loader.js +1 -1
  133. package/dist/components/forms/emoji-picker/emoji-picker.class.d.ts +7 -4
  134. package/dist/components/forms/emoji-picker/emoji-picker.class.js +5 -5
  135. package/dist/components/forms/icon-button/icon-button.class.d.ts +4 -0
  136. package/dist/components/forms/icon-button/icon-button.class.js +1 -1
  137. package/dist/components/forms/icon-button/icon-button.styles.js +1 -1
  138. package/dist/components/forms/input/input-shared.js +1 -1
  139. package/dist/components/forms/input/input.class.js +1 -1
  140. package/dist/components/forms/input/native-time-input.class.js +1 -1
  141. package/dist/components/forms/input/number-input.class.js +1 -1
  142. package/dist/components/forms/input/time-input.class.js +1 -1
  143. package/dist/components/forms/locale-picker/locale-picker.class.js +1 -1
  144. package/dist/components/forms/otp-input/otp-input.class.js +1 -1
  145. package/dist/components/forms/phone-input/phone-input.class.js +1 -1
  146. package/dist/components/forms/radio/radio-group.class.js +1 -1
  147. package/dist/components/forms/radio/radio.class.js +1 -1
  148. package/dist/components/forms/rubric-form/rubric-form.class.js +4 -4
  149. package/dist/components/forms/select/select.class.js +1 -1
  150. package/dist/components/forms/signature-pad/signature-pad.class.js +1 -1
  151. package/dist/components/forms/slider/slider.class.js +1 -1
  152. package/dist/components/forms/switch/switch.class.js +1 -1
  153. package/dist/components/forms/textarea/textarea.class.js +1 -1
  154. package/dist/components/forms/time-range/time-range.class.js +1 -1
  155. package/dist/components/forms/time-zone-picker/time-zone-picker.class.js +1 -1
  156. package/dist/components/forms/token-input/token-input.class.js +1 -1
  157. package/dist/components/forms/unit-picker/unit-picker.class.js +1 -1
  158. package/dist/components/layout/app-rail/app-rail-item.class.js +1 -1
  159. package/dist/components/layout/app-rail/app-rail.class.d.ts +70 -7
  160. package/dist/components/layout/app-rail/app-rail.class.js +16 -4
  161. package/dist/components/layout/app-rail/app-rail.styles.js +1 -1
  162. package/dist/components/layout/app-rail-group/app-rail-group.class.js +1 -1
  163. package/dist/components/layout/breadcrumb/breadcrumb.class.js +1 -1
  164. package/dist/components/layout/card/card.class.js +1 -1
  165. package/dist/components/layout/carousel/carousel.class.js +1 -1
  166. package/dist/components/layout/command-palette/command-palette.class.js +1 -1
  167. package/dist/components/layout/dashboard-grid/dashboard-grid.class.js +1 -1
  168. package/dist/components/layout/details/accordion-item.class.js +1 -1
  169. package/dist/components/layout/details/details.class.js +1 -1
  170. package/dist/components/layout/dock-panel/dock-panel.class.js +1 -1
  171. package/dist/components/layout/drilldown-panel/drilldown-panel.class.js +1 -1
  172. package/dist/components/layout/filter-bar/filter-bar.class.js +1 -1
  173. package/dist/components/layout/menu/menu.class.js +1 -1
  174. package/dist/components/layout/menubar/menubar.class.js +1 -1
  175. package/dist/components/layout/multi-split/multi-split.class.js +1 -1
  176. package/dist/components/layout/navigation-menu/navigation-menu.class.js +1 -1
  177. package/dist/components/layout/page/page.class.js +1 -1
  178. package/dist/components/layout/reorder-list/reorder-item.class.js +1 -1
  179. package/dist/components/layout/reorder-list/reorder-list.class.js +1 -1
  180. package/dist/components/layout/responsive-panel/responsive-panel.class.js +1 -1
  181. package/dist/components/layout/scroller/scroller.class.js +1 -1
  182. package/dist/components/layout/segmented/segmented.class.d.ts +7 -4
  183. package/dist/components/layout/segmented/segmented.styles.js +1 -1
  184. package/dist/components/layout/split-panel/split-panel.class.js +1 -1
  185. package/dist/components/layout/tab-group/tab-group.class.js +1 -1
  186. package/dist/components/layout/tab-group/tab.class.js +1 -1
  187. package/dist/components/layout/virtual-list/virtual-list.styles.js +1 -1
  188. package/dist/components/layout/widget/widget.class.js +1 -1
  189. package/dist/components/media/animated-image/animated-image.class.js +1 -1
  190. package/dist/components/media/attachment-chip/attachment-chip.class.js +1 -1
  191. package/dist/components/media/attachment-trigger/attachment-trigger.class.js +2 -2
  192. package/dist/components/media/av-player/av-player.class.js +1 -1
  193. package/dist/components/media/avatar-group/avatar-group.class.js +1 -1
  194. package/dist/components/media/drop-zone/drop-zone.class.js +1 -1
  195. package/dist/components/media/file-icon/file-icon.class.js +1 -1
  196. package/dist/components/media/file-input/file-input.class.js +1 -1
  197. package/dist/components/media/flag/flag.class.js +1 -1
  198. package/dist/components/media/image-comparer/image-comparer.class.js +1 -1
  199. package/dist/components/media/image-viewer/image-viewer.class.d.ts +2 -1
  200. package/dist/components/media/image-viewer/image-viewer.class.js +4 -2
  201. package/dist/components/media/image-viewer/image-viewer.styles.js +1 -1
  202. package/dist/components/media/lightbox/lightbox.class.js +1 -1
  203. package/dist/components/media/map/map.class.js +1 -1
  204. package/dist/components/media/media-card/media-card.class.js +1 -1
  205. package/dist/components/media/pan-zoom/pan-zoom.class.js +1 -1
  206. package/dist/components/media/qr-code/qr-code.class.d.ts +2 -2
  207. package/dist/components/media/qr-code/qr-code.class.js +1 -1
  208. package/dist/components/media/sequence-playback/sequence-playback.class.js +1 -1
  209. package/dist/components/media/video/video.class.js +1 -1
  210. package/dist/components/media/video-playlist/video-playlist.class.js +1 -1
  211. package/dist/components/media/zoomable-frame/zoomable-frame.class.js +1 -1
  212. package/dist/components/overlays/alert/alert.class.js +1 -1
  213. package/dist/components/overlays/badge/tag.class.js +1 -1
  214. package/dist/components/overlays/callout/callout.class.js +1 -1
  215. package/dist/components/overlays/chip/chip-group.class.js +1 -1
  216. package/dist/components/overlays/chip/chip.class.js +1 -1
  217. package/dist/components/overlays/context-menu/context-menu.class.js +1 -1
  218. package/dist/components/overlays/dialog/confirm.js +1 -1
  219. package/dist/components/overlays/dialog/dialog.class.js +1 -1
  220. package/dist/components/overlays/kbd/kbd.class.js +1 -1
  221. package/dist/components/overlays/overlay/popover.class.js +1 -1
  222. package/dist/components/overlays/overlay/tooltip.class.js +1 -1
  223. package/dist/components/overlays/progress/progress-bar.class.js +1 -1
  224. package/dist/components/overlays/progress/progress-ring.class.js +1 -1
  225. package/dist/components/overlays/rating/rating.class.js +1 -1
  226. package/dist/components/overlays/skeleton/skeleton.class.js +1 -1
  227. package/dist/components/overlays/spinner/spinner.class.js +1 -1
  228. package/dist/components/overlays/toast/toast-item.class.js +1 -1
  229. package/dist/components/overlays/toast/toast.class.js +1 -1
  230. package/dist/components/retrieval/chunk-inspector/chunk-inspector.class.js +1 -1
  231. package/dist/components/retrieval/citation-badge/citation-badge.class.js +1 -1
  232. package/dist/components/retrieval/claim-evidence/claim-evidence.class.js +1 -1
  233. package/dist/components/retrieval/community-card/community-card.class.js +1 -1
  234. package/dist/components/retrieval/embedding-explorer/embedding-explorer.class.js +1 -1
  235. package/dist/components/retrieval/entity-card/entity-card.class.js +1 -1
  236. package/dist/components/retrieval/entity-chip/entity-chip.class.js +1 -1
  237. package/dist/components/retrieval/entity-dossier/entity-dossier.class.js +1 -1
  238. package/dist/components/retrieval/graph/graph.class.js +1 -1
  239. package/dist/components/retrieval/graph-legend/graph-legend.class.js +1 -1
  240. package/dist/components/retrieval/grounding-summary/grounding-summary.class.js +2 -2
  241. package/dist/components/retrieval/ingestion-queue/ingestion-queue.class.js +1 -1
  242. package/dist/components/retrieval/knowledge-base/knowledge-base.class.js +1 -1
  243. package/dist/components/retrieval/knowledge-base-admin/knowledge-base-admin.class.js +1 -1
  244. package/dist/components/retrieval/knowledge-graph-explorer/knowledge-graph-explorer.class.js +1 -1
  245. package/dist/components/retrieval/memory-panel/memory-panel.class.js +1 -1
  246. package/dist/components/retrieval/mind-map/mind-map.class.js +1 -1
  247. package/dist/components/retrieval/neighbor-list/neighbor-list.class.js +1 -1
  248. package/dist/components/retrieval/node-palette/node-palette.class.js +1 -1
  249. package/dist/components/retrieval/path-strip/path-strip.class.js +1 -1
  250. package/dist/components/retrieval/provenance-panel/provenance-panel.class.js +1 -1
  251. package/dist/components/retrieval/rag-answer/rag-answer.class.js +1 -1
  252. package/dist/components/retrieval/rag-eval-dashboard/rag-eval-dashboard.class.js +1 -1
  253. package/dist/components/retrieval/research-progress/research-progress.class.js +1 -1
  254. package/dist/components/retrieval/retrieval-compare/retrieval-compare.class.js +1 -1
  255. package/dist/components/retrieval/retrieval-results/retrieval-results.class.js +1 -1
  256. package/dist/components/retrieval/retrieval-search/retrieval-search.class.js +1 -1
  257. package/dist/components/retrieval/retrieval-trace/retrieval-trace.class.js +1 -1
  258. package/dist/components/retrieval/source-card/source-card.class.js +1 -1
  259. package/dist/components/retrieval/source-list/source-list.class.js +1 -1
  260. package/dist/components/retrieval/source-picker/source-picker.class.js +1 -1
  261. package/dist/components/utility/copy-button/copy-button.class.js +1 -1
  262. package/dist/components/utility/diff-view/diff-view.class.js +1 -1
  263. package/dist/components/utility/export-button/export-button.class.js +1 -1
  264. package/dist/components/utility/icon/icon.class.js +1 -1
  265. package/dist/components/utility/json-viewer/json-viewer.class.js +1 -1
  266. package/dist/components/utility/known-date/known-date.class.js +1 -1
  267. package/dist/components/utility/mention-popover/mention-popover.class.js +1 -1
  268. package/dist/components/utility/poll-status/poll-status.class.js +1 -1
  269. package/dist/components/utility/random-content/random-content.class.js +1 -1
  270. package/dist/components/utility/tour/tour.class.js +1 -1
  271. package/dist/components/viewers/archive-viewer/archive-viewer.class.js +1 -1
  272. package/dist/components/viewers/calendar-viewer/calendar-viewer.class.js +1 -1
  273. package/dist/components/viewers/contact-viewer/contact-viewer.class.js +1 -1
  274. package/dist/components/viewers/csv-viewer/csv-viewer.class.js +1 -1
  275. package/dist/components/viewers/dataset-viewer/dataset-viewer.class.js +2 -2
  276. package/dist/components/viewers/document-compare/document-compare.class.js +1 -1
  277. package/dist/components/viewers/document-preview/document-preview.class.js +1 -1
  278. package/dist/components/viewers/document-viewer/document-viewer.class.js +1 -1
  279. package/dist/components/viewers/docx-viewer/docx-viewer.class.js +1 -1
  280. package/dist/components/viewers/ebook-viewer/ebook-viewer.class.js +1 -1
  281. package/dist/components/viewers/email-viewer/email-viewer.class.js +1 -1
  282. package/dist/components/viewers/geojson-view/geojson-view.class.js +1 -1
  283. package/dist/components/viewers/geojson-view/geojson-viewer.class.js +1 -1
  284. package/dist/components/viewers/highlight-layer/highlight-layer.class.js +1 -1
  285. package/dist/components/viewers/html-viewer/html-viewer.class.js +1 -1
  286. package/dist/components/viewers/include/include.class.js +1 -1
  287. package/dist/components/viewers/notebook-viewer/notebook-viewer.class.js +1 -1
  288. package/dist/components/viewers/page-rail/page-rail.class.d.ts +1 -1
  289. package/dist/components/viewers/page-rail/page-rail.class.js +3 -3
  290. package/dist/components/viewers/pdf-viewer/pdf-viewer.class.d.ts +2 -2
  291. package/dist/components/viewers/pdf-viewer/pdf-viewer.class.js +2 -2
  292. package/dist/components/viewers/pptx-viewer/pptx-viewer.class.js +1 -1
  293. package/dist/components/viewers/spreadsheet-viewer/spreadsheet-viewer.class.js +1 -1
  294. package/dist/components/viewers/svg-viewer/svg-viewer.class.js +1 -1
  295. package/dist/components/viewers/xml-viewer/xml-viewer.class.js +1 -1
  296. package/dist/events.d.ts +6 -1
  297. package/dist/framework-types.d.ts +1 -1
  298. package/dist/internal/accessibility-visibility.js +1 -1
  299. package/dist/internal/collection-snapshot.js +1 -1
  300. package/dist/internal/default-strings/agent-tools.generated.js +1 -0
  301. package/dist/internal/default-strings/charts.generated.js +1 -0
  302. package/dist/internal/default-strings/conversation.generated.js +1 -0
  303. package/dist/internal/default-strings/data.generated.js +1 -0
  304. package/dist/internal/default-strings/forms.generated.js +1 -0
  305. package/dist/internal/default-strings/layout.generated.js +1 -0
  306. package/dist/internal/default-strings/media.generated.js +1 -0
  307. package/dist/internal/default-strings/overlays.generated.js +1 -0
  308. package/dist/internal/default-strings/retrieval.generated.js +1 -0
  309. package/dist/internal/default-strings/shared.generated.js +1 -0
  310. package/dist/internal/default-strings/utility.generated.js +1 -0
  311. package/dist/internal/default-strings/viewers.generated.js +5 -0
  312. package/dist/internal/dev-mode-attribute-warning.development.js +1 -1
  313. package/dist/internal/dev-mode-attribute-warning.production.js +1 -1
  314. package/dist/internal/document-tokens.d.ts +11 -0
  315. package/dist/internal/document-tokens.generated.js +15 -0
  316. package/dist/internal/document-tokens.js +3 -0
  317. package/dist/internal/form-associated.d.ts +1 -1
  318. package/dist/internal/form-associated.js +1 -1
  319. package/dist/internal/glass-surface.styles.js +1 -1
  320. package/dist/internal/host-tokens.styles.js +1 -0
  321. package/dist/internal/image-zoom.js +1 -0
  322. package/dist/internal/intl-cache.js +1 -1
  323. package/dist/internal/localization-types.d.ts +1 -1
  324. package/dist/internal/localization.js +2 -2
  325. package/dist/internal/lyra-element.d.ts +4 -2
  326. package/dist/internal/lyra-element.js +2 -2
  327. package/dist/internal/opaque-content-border.styles.js +1 -1
  328. package/dist/internal/open-overlay-raise.styles.js +1 -0
  329. package/dist/internal/optional-peer-failure.js +1 -0
  330. package/dist/internal/package-metadata.js +1 -1
  331. package/dist/internal/prefix.js +1 -1
  332. package/dist/internal/specialist-host-tokens.styles.js +1 -0
  333. package/dist/internal/specialist-tokens.generated.js +2 -0
  334. package/dist/internal/step-value.js +1 -1
  335. package/dist/internal/theme-observation.js +1 -1
  336. package/dist/internal/theme-scopes.d.ts +11 -0
  337. package/dist/internal/theme-scopes.js +1 -0
  338. package/dist/looks/shadcn.css +1 -1
  339. package/dist/lyra.d.ts +1 -1
  340. package/dist/lyra.js +1 -1
  341. package/dist/styles/design-tokens.css +1 -1
  342. package/dist/styles/tokens-root.css +1 -1
  343. package/dist/surfaces/glass.css +1 -1
  344. package/dist/testing/happy-dom-shims.d.ts +21 -2
  345. package/dist/testing/happy-dom-shims.js +1 -1
  346. package/dist/testing/index.d.ts +1 -1
  347. package/dist/testing/index.js +1 -1
  348. package/dist/testing/interaction-drivers.d.ts +66 -7
  349. package/dist/testing/interaction-drivers.js +1 -1
  350. package/dist/testing/jsdom-shims.d.ts +70 -0
  351. package/dist/testing/jsdom-shims.js +1 -0
  352. package/dist/theme/look-inputs.d.ts +1 -1
  353. package/dist/theme/look-inputs.js +37 -41
  354. package/dist/theme/theme.d.ts +13 -0
  355. package/dist/theme/theme.js +1 -1
  356. package/dist/theme.css +1 -1
  357. package/dist/utilities/index.d.ts +1 -1
  358. package/dist/utilities/index.js +1 -1
  359. package/dist/utilities/scoped-registry.d.ts +3 -0
  360. package/dist/utilities/scoped-registry.js +1 -1
  361. package/dist/utilities/theme-scopes.d.ts +1 -0
  362. package/dist/utilities/theme-scopes.js +1 -0
  363. package/dist/utilities/tokens.d.ts +1 -0
  364. package/dist/utilities/tokens.js +1 -0
  365. package/llms/components/lr-app-rail.md +51 -4
  366. package/llms/components/lr-emoji-picker.md +13 -8
  367. package/llms/components/lr-icon-button.md +13 -1
  368. package/llms/components/lr-image-viewer.md +8 -0
  369. package/llms/components/lr-pagination.md +4 -2
  370. package/llms/components/lr-segmented.md +7 -3
  371. package/llms/components/lr-table.md +14 -12
  372. package/llms/components/lr-thread-list.md +4 -3
  373. package/llms/migration.md +9 -0
  374. package/llms/peers.md +1 -1
  375. package/llms/shared/events-and-types.md +41 -0
  376. package/llms/shared/frameworks-and-ssr.md +16 -0
  377. package/llms/shared/imports-and-registration.md +4 -3
  378. package/llms/shared/native-styles-and-utilities.md +38 -3
  379. package/llms/shared/styles-and-tokens.md +242 -102
  380. package/llms/shared/testing-and-utilities.md +90 -6
  381. package/llms/shared.md +431 -114
  382. package/llms/tokens.md +31 -49
  383. package/locales.json +138 -138
  384. package/package.json +14 -7
  385. package/registrations.json +1 -1
  386. package/dist/internal/default-strings.generated.js +0 -5
  387. package/dist/internal/specialist-tokens.styles.js +0 -1
  388. package/dist/internal/tokens/palette.styles.js +0 -1
  389. package/dist/internal/tokens.styles.js +0 -1
package/llms/shared.md CHANGED
@@ -184,11 +184,12 @@ The entry points, then:
184
184
  - **`lr-flag`** registers from the barrel, but resolving a flag by `country`/`language` (rather than
185
185
  a pre-resolved `src`) additionally needs
186
186
  `import '@aceshooting/lyra-ui/components/media/flag/flag-peer.js';` once.
187
- - **Other subpaths.** `@aceshooting/lyra-ui/theme.css` (ready-made light/dark theme),
187
+ - **Other subpaths.** `@aceshooting/lyra-ui/theme.css` (ready-made light/dark theme, including the
188
+ document token layer),
188
189
  `@aceshooting/lyra-ui/looks/shadcn.css` (opt-in shadcn/ui look, imported after `theme.css` — see
189
190
  [The shadcn look](#the-shadcn-look--looksshadcncss)),
190
- `@aceshooting/lyra-ui/tokens-root.css` (opt-in: the curated resolved `--lr-*` tokens at `:root`,
191
- so your own components can read them),
191
+ `@aceshooting/lyra-ui/tokens-root.css` (the document token layer alone, for pages that do not
192
+ use `theme.css`; link one of the two, not both),
192
193
  `@aceshooting/lyra-ui/native.css` (opt-in native-element styles inside `.lr-native`),
193
194
  `@aceshooting/lyra-ui/utilities.css` (opt-in light-DOM layout/text/typography utilities),
194
195
  `@aceshooting/lyra-ui/theme.js` (the zero-dependency style runtime),
@@ -880,17 +881,18 @@ Three layers, and **which one you set decides how far the override reaches**:
880
881
  3. **`--lr-<component>-*`** — per-component properties, for one element at a time. Listed in each
881
882
  component's own section.
882
883
 
883
- **Layer 2 (`--lr-*`) is declared only on each `lr-*` element's own shadow `:host`.** It never
884
- reaches plain application CSS, and it never reaches your own custom elements, since neither is a
885
- descendant of an `lr-*` shadow root — `body { color: var(--lr-color-text) }` in application CSS
886
- resolves to nothing, silently, not an error. Retheme through layer 1 (`--lr-theme-*`), which
887
- `theme.css` supplies at document scope and which inherits normally into every nested shadow root.
888
- To _read_ (not retheme) the resolved values from your own components, import the opt-in
889
- [`tokens-root.css`](#reading-the-resolved-tokens-from-your-own-components--tokens-rootcss), which
890
- declares a curated subset of layer 2 at `:root`.
884
+ **Layer 2 (`--lr-*`) is declared once per document, not on each component** (since 28.0.0). The
885
+ _document token layer_ declares every shared output on `:root` and re-derives it only at
886
+ [theme scopes](#theme-scopes). `theme.css` carries it, so a page that imports `theme.css` has it from
887
+ first paint; on a page without `theme.css`, registering the first `lr-*` element adopts the same text
888
+ as a constructed stylesheet. Components inherit the result, and so do your own elements —
889
+ `body { color: var(--lr-color-text) }` resolves everywhere the layer applies.
890
+ [`tokens-root.css`](#reading-the-resolved-tokens-from-your-own-components--tokens-rootcss) is the
891
+ layer alone, for pages that do not use `theme.css`. Retheme through layer 1 (`--lr-theme-*`) **on a theme scope**: `:root`, a
892
+ mode scope, a style-axis boundary, or any element marked `data-lr-theme-scope`.
891
893
  See [Where an override actually reaches](#where-an-override-actually-reaches) below for the full
892
- inheritance rules, including the one documented exception (per-component `--lr-<component>-*`
893
- hooks, layer 3, which do inherit through wrappers).
894
+ inheritance rules, including per-component `--lr-<component>-*` hooks (layer 3), which inherit
895
+ through wrappers.
894
896
 
895
897
  ### Lyra signature starter
896
898
 
@@ -904,7 +906,6 @@ mode; `accent: null` explicitly clears the accent, while `resetLyraStyle()` rest
904
906
  @layer lr-base, lr-theme, lr-theme-preset, lr-utilities, lr-overrides;
905
907
  @layer lr-theme-preset.look, lr-theme-preset.density, lr-theme-preset.surface, lr-theme-preset.accent, lr-theme-preset.mode;
906
908
  @import "@aceshooting/lyra-ui/theme.css";
907
- @import "@aceshooting/lyra-ui/tokens-root.css";
908
909
  ```
909
910
 
910
911
  Keep both layer-order statements before these imports. Bundlers can hoist statements from imported
@@ -1084,20 +1085,35 @@ design launcher. Keep these layout rules shared across application headers and a
1084
1085
 
1085
1086
  ### Reading the resolved tokens from your own components — `tokens-root.css`
1086
1087
 
1087
- The paragraph above is a real problem for any application that has custom elements of its own: they
1088
- are not descendants of an `lr-*` shadow root either, so `var(--lr-color-border)` inside **your**
1089
- component resolves to nothing, and `var(--lr-space-m, 0.5rem)` quietly runs on its literal fallback
1090
- forever. Both failures are invisible without reading computed styles in a browser.
1091
-
1092
- Import one optional stylesheet and the curated part of layer 2 exists at document scope:
1088
+ Since 28.0.0 the resolved layer lives in the document, so your own elements read it like any
1089
+ component does: `var(--lr-color-border)` inside **your** component resolves wherever the layer
1090
+ applies. `theme.css` carries the layer, so importing `theme.css` is enough, including for
1091
+ server-rendered pages and for application elements that paint before any Lyra element exists.
1092
+ `tokens-root.css` is the same layer alone, as a static stylesheet, for pages that do not use
1093
+ `theme.css`:
1094
+
1095
+ - **Server-rendered pages.** Declarative shadow roots no longer carry the layer, so link
1096
+ `theme.css` (or `tokens-root.css`) for a correct first paint before hydration (see
1097
+ [Theme scopes](#theme-scopes) for application shadow roots).
1098
+ - **Application elements that paint before the first Lyra element is registered**, for example
1099
+ ahead of a lazily loaded route; otherwise they change value when the layer is adopted.
1100
+ - **Large pages that load Lyra late.** Without a static copy, the layer is adopted when the first
1101
+ Lyra element is registered, which usually happens while the document is still small. A route that
1102
+ imports Lyra only after the page already holds a large DOM pays a one-off, whole-document style
1103
+ invalidation then. A document whose root already resolves the layer (it links `theme.css` or
1104
+ `tokens-root.css`) keeps the static copy and adopts no constructed one.
1105
+
1106
+ Link one of the two, not both: the second copy changes no value, but every scope matches the layer
1107
+ twice.
1093
1108
 
1094
1109
  ```css
1095
- @import "@aceshooting/lyra-ui/theme.css"; /* the --lr-theme-* input layer */
1096
- @import "@aceshooting/lyra-ui/tokens-root.css"; /* the resolved --lr-* layer, at :root */
1110
+ @import "@aceshooting/lyra-ui/theme.css"; /* the --lr-theme-* inputs, Lyra's layer order, and the resolved --lr-* layer */
1111
+ /* or, without theme.css: */
1112
+ @import "@aceshooting/lyra-ui/tokens-root.css"; /* the resolved --lr-* layer alone */
1097
1113
  ```
1098
1114
 
1099
1115
  ```css
1100
- /* Now valid in your own component's stylesheet, in plain application CSS, anywhere. */
1116
+ /* Valid in your own component's stylesheet, in plain application CSS, anywhere. */
1101
1117
  .app-panel {
1102
1118
  padding: var(--lr-space-m);
1103
1119
  border: var(--lr-border-width-thin) solid var(--lr-color-border);
@@ -1112,10 +1128,9 @@ Import one optional stylesheet and the curated part of layer 2 exists at documen
1112
1128
  }
1113
1129
  ```
1114
1130
 
1115
- **It is opt-in, and it is a curated subset — not all of layer 2.** `--lr-*` is internal precisely so
1116
- it can change without a major version; publishing all of it at `:root` would freeze several hundred
1117
- internal decisions as permanent API. What ships is what an application's own component needs to sit
1118
- inside a Lyra UI without looking foreign:
1131
+ **Every output is visible on `:root`; only a subset is public API.** `--lr-*` is internal precisely
1132
+ so it can change without a major version. The stable subset is what an application's own component
1133
+ needs to sit inside a Lyra UI without looking foreign, and the file's header lists it:
1119
1134
 
1120
1135
  | Family | Names |
1121
1136
  | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -1128,53 +1143,122 @@ inside a Lyra UI without looking foreign:
1128
1143
  | Typography | `--lr-font`, `--lr-font-mono`, the ten `--lr-font-size-*` steps, the four `--lr-font-weight-*` steps |
1129
1144
  | 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`, `--lr-transition-interactive` |
1130
1145
 
1131
- The grid ships whole because its contrast guarantee is **per tier** — a `fill-quiet` background is
1132
- only guaranteed legible under the matching `on-quiet` foreground — so shipping the flat aliases
1133
- alone would hand you a pairing with nothing behind it.
1146
+ The grid is stable as a whole because its contrast guarantee is **per tier** — a `fill-quiet`
1147
+ background is only guaranteed legible under the matching `on-quiet` foreground.
1134
1148
 
1135
- **Deliberately not published**, and each for a reason that makes reading it a bug rather than a
1136
- convenience: `--lr-ramp-*` (tooling-only numeric inputs, absent from component host styles),
1137
- `--lr-size-*` (value-named geometry constants, frozen internals), the chart, graph and terminal
1138
- palettes (generated ramps that move with the palette tooling), `--lr-layer-*` (stacking order is
1139
- your decision), `--lr-color-mix-*` and `--lr-hover-brightness` (inputs to the library's own
1140
- interaction recipe), `--lr-line-height-*`, the per-control internals (`--lr-icon-button-size`,
1149
+ **Visible but internal** (read them at your own risk; they may change in any release): `--lr-size-*`,
1150
+ `--lr-layer-*`, `--lr-color-mix-*`, `--lr-hover-brightness`, `--lr-line-height-*`,
1141
1151
  `--lr-otp-input-segment-size`, `--lr-scroll-fade-size`, `--lr-popover-viewport-clamp`,
1142
- `--lr-safe-area-*`, `--lr-mask-opaque`, `--lr-color-no-data`), and the nine variant-following slots
1143
- (`--lr-color-fill-loud` and friends), which mean "the variant _this_ element is set to" and are
1144
- meaningless on `:root`. If you need one of these, ask for it to be added rather than reading it out
1145
- of a component's shadow root.
1152
+ `--lr-mask-opaque`, `--lr-color-no-data`, and the private `--_lr-*` names. **Not in the layer at all:**
1153
+ `--lr-ramp-*` (tooling-only), the chart, graph and terminal palettes (per host, on the components that
1154
+ draw with them), the form-control ladder, and the per-element names `--lr-icon-button-size`,
1155
+ `--lr-radius-button` and `--lr-safe-area-*`. If you need one of these, ask for it to be added to the
1156
+ stable subset rather than reading it from the document.
1146
1157
 
1147
1158
  **Stability promise.** Every name in the table is public API from the release that introduced it: it
1148
1159
  will not be renamed or removed outside a major version, and its meaning will not change. Its _value_
1149
1160
  may change in a minor exactly as it may inside a component — a palette retune moves your elements
1150
- and the kit's together, which is the point. Names absent from the file stay internal and may change
1151
- in any release.
1152
-
1153
- **Modes follow the inherited style context.** Standalone `tokens-root.css` follows the OS at the
1154
- root and accepts `.lr-light` / `.lr-dark`, `data-lr-theme` and `data-lr-mode` scopes. When `theme.css`
1155
- is also installed, its requested mode owns the switches instead. Shared outputs re-resolve on style
1156
- and contrast/motion preference boundaries; forced-colors and reduced-motion overrides use those
1157
- same boundaries, including under a dark OS. Each output reads the local `--lr-theme-*` input before
1158
- its built-in fallback. A preference-only boundary never overwrites inherited theme inputs.
1159
-
1160
- **One caveat, and it is the same shape as layer 2's rule everywhere else.** A `--lr-theme-*` input
1161
- set on a mid-tree element retunes every `lr-*` component below it, because each component re-derives
1162
- the resolved layer on its own `:host`. The document-scope copy cannot: it is substituted where it is
1163
- declared, and what inherits past that point is the finished value. So if an application element
1164
- carries a subtree override and expects its **own** descendants to follow, give that element a mode
1165
- scope too — `data-lr-mode`, `data-lr-theme-scope`, or the compatibility `lr-light`/`lr-dark`
1166
- classes — so the whole subset resolves again there:
1161
+ and the kit's together, which is the point.
1162
+
1163
+ **Modes follow the nearest mode scope.** The layer follows the OS preference at the root and accepts
1164
+ `.lr-light` / `.lr-dark`, `data-lr-theme` and `data-lr-mode` scopes, in every engine, with or without
1165
+ `theme.css`. When `theme.css` is installed its requested mode owns the switches. A mode-neutral
1166
+ scope keeps its ancestor's mode. Forced colours, increased contrast and reduced motion apply at every
1167
+ scope and again on every component host.
1168
+
1169
+ Public outputs sit in the `lr-theme` cascade layer (the fallback mode switches in `lr-base`), so any
1170
+ unlayered application rule beats them regardless of load order, and the file declares custom
1171
+ properties only — notably not `color-scheme` — so importing it paints nothing by itself.
1172
+
1173
+ ### Theme scopes
1174
+
1175
+ A **theme scope** is an element that re-derives the whole token layer from the `--lr-theme-*` inputs
1176
+ and the mode it sees. The list is closed: `:root`; the mode scopes `.lr-light`, `.lr-dark` and
1177
+ `[data-lr-theme]` (any value); the style-axis boundaries `[data-lr-mode]`, `[data-lr-look]`,
1178
+ `[data-lr-accent]`, `[data-lr-density]`, `[data-lr-contrast]`, `[data-lr-motion]`; a look's scoped
1179
+ `.light` / `.dark` aliases; the design-token fixture scopes `.lr-token-light`, `.lr-token-dark`,
1180
+ `[data-lr-design-token-mode]`; and the mode-neutral marker **`data-lr-theme-scope`**.
1167
1181
 
1168
1182
  ```html
1169
- <!-- Both the lr-* components and the app's own elements below follow the override. -->
1170
- <section class="lr-light" style="--lr-theme-color-brand-fill-loud: #7c3aed">…</section>
1183
+ <!-- Re-theme one region: mark it, then set inputs on it. -->
1184
+ <section data-lr-theme-scope style="--lr-theme-color-brand-fill-loud: #7c3aed">
1185
+ <lr-button variant="brand">Save</lr-button>
1186
+ </section>
1187
+
1188
+ <!-- Mode scopes are already scopes; a marked region inside one keeps its mode. -->
1189
+ <aside class="lr-dark"><div data-lr-theme-scope style="--lr-theme-space-m: 1rem">…</div></aside>
1190
+
1191
+ <!-- A single re-themed component. -->
1192
+ <lr-button data-lr-theme-scope style="--lr-theme-color-brand-fill-loud: #7c3aed">Save</lr-button>
1193
+ ```
1194
+
1195
+ - **The marker follows HTML presence semantics:** `data-lr-theme-scope="false"` still marks. React
1196
+ renders `data-*={false}` as `"false"`, so write `data-lr-theme-scope=""` (or omit the attribute);
1197
+ the development build warns on the literal `"false"`.
1198
+ - **Nesting** works like inheritance: a scope re-derives from what it inherits plus what it sets, and
1199
+ its descendants inherit the result until the next scope. The marker only re-derives the `--lr-*`
1200
+ outputs, so an input set on `:root` (inline, in your stylesheet, or by `setLyraStyle()`) or on an
1201
+ outer scope reaches a marked region exactly as it reaches an unmarked one, and adding a marker
1202
+ where nothing needs it changes nothing. With `theme.css`, mode scopes and the style-axis
1203
+ boundaries also re-resolve the inputs from the selected look, accent and mode, so an input set
1204
+ above one of them stops there (set it on that boundary, or below it). A scope costs a little less
1205
+ than one component did before 28.0.0, so put inputs on a common ancestor rather than marking every
1206
+ row of a list.
1207
+ - **`applyLyraStyleScope()`** writes the marker whenever it writes inline inputs.
1208
+ - **Brand regions in the default look.** The marker re-derives outputs but is not a style boundary,
1209
+ so the roles that follow brand in the default Shadcn look (the neutral loud tier) keep the brand
1210
+ of the nearest boundary inside the first example above. For a brand region call
1211
+ `applyLyraStyleScope(el, { accent: '#7c3aed' })`, which writes the `data-lr-accent="custom"`
1212
+ boundary, or set the `--lr-theme-color-neutral-*-loud` inputs on the region too.
1213
+ - **Which inputs need a scope.** Only the `--lr-theme-*` inputs the layer consumes (listed in
1214
+ `llms/tokens.md`). Inputs read on the host itself keep working on any element: the chart,
1215
+ graph and terminal palettes, the form-control heights and radius, the icon-button size, the
1216
+ button radius, scrollbar and progress-ring inputs.
1217
+
1218
+ **Application shadow roots.** Custom properties inherit across shadow boundaries; selectors do not.
1219
+ A scope _inside_ your own component's shadow root therefore needs the layer adopted in that root.
1220
+ Lyra does that on demand: when a Lyra element connects inside an application shadow root and it is,
1221
+ or sits below, a scope in that root, an unlayered copy of the layer with zero-specificity
1222
+ (`:where()`) selectors is appended to the root's `adoptedStyleSheets`, so your component's own rules
1223
+ there still win (unless you put them in a cascade layer).
1224
+ "Below" follows the flat tree, so a Lyra element slotted into your component (directly, or through
1225
+ forwarded slots) counts as below the scopes around its slot, also when your component renders its
1226
+ slots right after connecting, as Lit does. A Lyra element inside a slotted non-Lyra wrapper does not
1227
+ trigger it. Library components' own shadow roots never receive it. Call
1228
+ `adoptLyraTokens(root)` (from `@aceshooting/lyra-ui/utilities/tokens.js`, or the package root) for a
1229
+ root whose scopes appear after its Lyra elements connected, a root that has scopes but no Lyra
1230
+ element yet, or an iframe that holds application elements only. Server-rendered application roots
1231
+ that contain scopes include `<link rel="stylesheet" href="…/tokens-root.css">`.
1232
+
1233
+ ```js
1234
+ // An application component whose shadow root holds scopes before any Lyra element connects in it.
1235
+ import { adoptLyraTokens } from "@aceshooting/lyra-ui/utilities/tokens.js";
1236
+ adoptLyraTokens(this.shadowRoot); // adoptLyraTokens(root: Document | ShadowRoot): void, idempotent
1171
1237
  ```
1172
1238
 
1173
- Public outputs sit in the `lr-theme` cascade layer, like `theme.css`, so any unlayered application rule
1174
- beats it regardless of load order, and the file declares custom properties only — notably not
1175
- `color-scheme` — so importing it paints nothing by itself. `--lr-focus-ring` and its three parts are
1176
- also declared at document scope by `theme.css`; both spell the same chain, so importing both is a
1177
- no-op either way round.
1239
+ **Append, never replace.** Lyra appends its sheet to `document.adoptedStyleSheets` and to an
1240
+ application root's. An application that later assigns a new array wholesale removes it until the next
1241
+ Lyra element connects there; append to the existing array instead.
1242
+ In Chromium, Lyra also appends a one-rule sheet to `document.adoptedStyleSheets`: a private root
1243
+ property (`--_lr-root-inputs`) that follows every theme input. It lets Chromium skip a full
1244
+ custom-property comparison per element when the root is re-themed (an accent or spacing input
1245
+ written on `<html>` with dark islands or other scopes on the page costs about half as much). Firefox
1246
+ and Safari do not get it; they already stop at unchanged scopes, which the property would defeat.
1247
+ When `theme.css` or `tokens-root.css` provides the layer, that sheet goes in only at the first
1248
+ attribute change on `<html>` (an inline input, a mode or look attribute, a class), so a route that registers Lyra late on a large page does not restyle every element for it.
1249
+
1250
+ **Layer order.** On a page with no Lyra stylesheet, the adopted sheet's layer statement is the last
1251
+ the browser sees, so Lyra's layers sort after yours. Declare Lyra's order first (import `theme.css`,
1252
+ or repeat `@layer lr-base, lr-theme, lr-theme-preset, lr-utilities, lr-overrides;` before your own
1253
+ layers), or leave application overrides of `--lr-*` outputs unlayered.
1254
+
1255
+ **Finding unscoped inputs.** `findUnscopedThemeInputs(root?)` (from
1256
+ `@aceshooting/lyra-ui/utilities/theme-scopes.js`; `findUnscopedThemeInputs(root?: Document |
1257
+ DocumentFragment | Element): Element[]`) returns, without logging, every element under `root`
1258
+ (through open shadow roots) whose inline style sets a layer-consumed input and that is not a scope. `lyra-ui-migrate --rule=theme-scopes`
1259
+ adds the marker in HTML, Lit and JSX templates and reports dynamic inputs and stylesheet rules;
1260
+ `lyra-ui-migrate --origin=lyra-v26` runs the same rule together with the other Lyra 27 moves (relocated
1261
+ locale and editor-data specifiers, `ToolStatus`) and reports removed localization keys.
1178
1262
 
1179
1263
  ### The colour ramp and the semantic grid
1180
1264
 
@@ -1215,8 +1299,9 @@ These guarantees apply in both modes.
1215
1299
  rethemed without forking anything beneath it:
1216
1300
 
1217
1301
  ```css
1218
- /* Both the grid slot and the flat alias below now resolve to this. */
1219
- .invoice-panel {
1302
+ /* Both the grid slot and the flat alias below now resolve to this, for every component inside
1303
+ the panel (mark the panel <div class="invoice-panel" data-lr-theme-scope>, or use :root). */
1304
+ .invoice-panel[data-lr-theme-scope] {
1220
1305
  --lr-theme-color-brand-fill-loud: #7c3aed;
1221
1306
  }
1222
1307
  ```
@@ -1391,6 +1476,13 @@ first statement the browser sees is appended at the end, so an older four-name s
1391
1476
  `lr-theme-preset`) would rank an imported look preset above `lr-overrides` and your own layer. Every Lyra stylesheet that declares an order repeats the same five names for exactly this
1392
1477
  reason.
1393
1478
 
1479
+ The same rule decides application **fallback fills**. A page that keeps plain `background` rules
1480
+ for dialogs or panels (so it also renders without the component bundle) and uses an unlayered rule
1481
+ on an `.lr-surface-chrome` element overrides the Glass fill; declare a low-priority layer before
1482
+ Lyra's names (`@layer app-base, lr-base, lr-theme, lr-theme-preset, lr-utilities, lr-overrides;`)
1483
+ for those fallbacks, or set `--lr-surface-background`. See
1484
+ [Application fallback fills and cascade layers](native-styles-and-utilities.md#application-fallback-fills-and-cascade-layers).
1485
+
1394
1486
  <a id="the-shadcn-look--themesshadcncss"></a>
1395
1487
 
1396
1488
  ### The shadcn look — looks/shadcn.css
@@ -1437,25 +1529,33 @@ compatibility aliases above.
1437
1529
 
1438
1530
  ### Where an override actually reaches
1439
1531
 
1440
- **Shared computed `--lr-*` design-token outputs are declared on every `lr-*` element's `:host`.**
1441
- That includes palette, spacing, radius, typography, and motion outputs such as
1442
- `--lr-color-brand`, `--lr-space-m`, and `--lr-radius`. A value for one of those shared outputs set
1443
- on an ancestor is re-declared — and lost — at the first `lr-*` element between that ancestor and
1444
- the component you meant to style. It never reaches anything nested inside another component.
1445
-
1446
- **`--lr-theme-*` inputs are never redeclared inside a component's shadow styles.** `theme.css`
1447
- supplies them on its root and light/dark mode selectors, so an application override inherits normally
1448
- through every nested shadow root. **Setting a `--lr-theme-*` input on a wrapper element is the
1449
- supported way to retheme one subtree.** Setting a `--lr-*` token there only works for that wrapper's
1450
- direct children.
1532
+ **Shared computed `--lr-*` outputs are resolved at theme scopes and inherited everywhere else.** That
1533
+ includes palette, spacing, radius, typography and motion outputs such as `--lr-color-brand`,
1534
+ `--lr-space-m` and `--lr-radius`. No component re-declares them on its host (since 28.0.0), so:
1535
+
1536
+ - **A `--lr-theme-*` input** retunes everything below it **when it is set on a theme scope**. Set on a
1537
+ plain wrapper, a component's inline style, or an application component's own `:host` rule, it no
1538
+ longer re-derives the components below; add `data-lr-theme-scope` to that element.
1539
+ - **A `--lr-*` output** set on an ancestor now reaches every component below it, until the next theme
1540
+ scope (which re-derives every output). The outputs _derived_ from it (`--lr-focus-ring` from
1541
+ `--lr-focus-ring-color`, `--lr-color-brand` from `--lr-color-brand-fill-loud`,
1542
+ `--lr-transition-interactive` from `--lr-transition-fast`) keep the scope's values unless the
1543
+ element is itself a scope.
1544
+ - **Border outputs stop at an opaque content interior.** The bodies of `lr-card`, `lr-details` and
1545
+ `lr-accordion-item` restore the nearest scope's unqualified `--lr-color-border` and
1546
+ `--lr-color-border-strong` for their content (inside a glass surface the surface would otherwise
1547
+ hand its translucency-qualified borders to opaque content). A `--lr-color-border` set on such a
1548
+ component, or above it, still applies to the chrome that reads it, but not to the content; set
1549
+ `--lr-theme-color-surface-border` on a theme scope, or a component hook such as
1550
+ `--lr-input-border-color`, to reach the content.
1451
1551
 
1452
1552
  ```css
1453
- /* Reaches everything in the subtree, however deeply nested. */
1454
- .invoice-panel {
1553
+ /* Reaches everything in the subtree, however deeply nested: the element is a theme scope. */
1554
+ .invoice-panel[data-lr-theme-scope] {
1455
1555
  --lr-theme-color-brand-fill-loud: #7c3aed;
1456
1556
  }
1457
1557
 
1458
- /* A shared computed output reaches direct lr-* children only — shadowed at the first nested host. */
1558
+ /* An output reaches the subtree down to the next scope; its dependants do not follow it. */
1459
1559
  .invoice-panel {
1460
1560
  --lr-color-brand: #7c3aed;
1461
1561
  }
@@ -1478,8 +1578,9 @@ reliable way to theme a group of matching components:
1478
1578
  State and size selectors change only the private fallback. An inherited or direct public value
1479
1579
  continues to win in every state and size tier.
1480
1580
 
1481
- **Diagnostic:** if a shared design-token output has no effect on a nested component, check which
1482
- layer you set before assuming the component is at fault. If a documented component-specific hook
1581
+ **Diagnostic:** if a `--lr-theme-*` input has no effect on a nested component, check that the element
1582
+ carrying it is a theme scope (the development build warns once per page about an inline input on an
1583
+ element that is not, and `findUnscopedThemeInputs()` lists them). If a documented component-specific hook
1483
1584
  fails when inherited from a wrapper, that is a component bug; setting the same hook directly on
1484
1585
  every host should not be necessary.
1485
1586
 
@@ -1589,28 +1690,16 @@ consult it rather than guessing a token name.
1589
1690
  }
1590
1691
  ```
1591
1692
 
1592
- With `theme.css` imported, switch modes by putting `class="lr-light"`/`class="lr-dark"` (or
1593
- `data-lr-theme="light"`/`"dark"`) on any ancestor; it also sets `color-scheme`. Without it, the token
1594
- layer still ships a `prefers-color-scheme: dark` fallback that re-points the hardcoded defaults at a
1595
- dark palette. Two things switch that fallback off:
1596
-
1597
- - **A real `--lr-theme-*` value**, which the fallback only substitutes for.
1598
- - **`data-lr-theme="light"` on the component itself**, which pins light mode regardless of the OS.
1599
- Both layers honour it now: the palette layer always did, and the token layer — the hardcoded
1600
- surface/text/border defaults — does too, so `<lr-card data-lr-theme="light">` on a dark machine is
1601
- light throughout rather than light chrome over a dark colour grid. The mirror-image
1602
- `data-lr-theme="dark"` pins dark on a light machine the same way, and a `.lr-dark` /
1603
- `data-lr-theme="dark"` _ancestor_ is followed as well (through `:host-context()` where the engine
1604
- has it, and through `theme.css`'s inheriting custom properties everywhere else).
1605
-
1606
- Note the asymmetry: the _light_ pin is read on the component itself (`:host([data-lr-theme='light'])`),
1607
- while a _dark_ ancestor is followed through `:host-context()`. Putting `data-lr-theme="light"` on
1608
- `<html>` — what `setLyraStyle({ mode: 'light' })` does — pins the page through
1609
- `theme.css`'s real `--lr-theme-*` values, which inherit into every shadow root. Without `theme.css`
1610
- there are no such values to inherit, so put the attribute on the components you actually need
1611
- pinned.
1612
-
1613
- The token layer also sets `:host([hidden]) { display: none !important; }` and an inherited
1693
+ Switch modes by putting `class="lr-light"`/`class="lr-dark"` (or `data-lr-theme="light"`/`"dark"`,
1694
+ or `data-lr-mode`) on any element, including a component itself; with `theme.css` imported it also
1695
+ sets `color-scheme`. **Every engine follows the nearest mode scope, with or without `theme.css`:** the
1696
+ document layer carries mode to every scope through two inherited private switches, so a `.lr-light`
1697
+ island inside a `.lr-dark` region is light, a mode-neutral scope keeps its ancestor's mode, and the
1698
+ OS preference decides the root unless the root carries an explicit mode. A real `--lr-theme-*` value
1699
+ still wins over the built-in light or dark default. (Before 28.0.0 an ancestor mode reached
1700
+ components without `theme.css` only in Chromium, through `:host-context()`.)
1701
+
1702
+ Each component host also keeps `:host([hidden]) { display: none !important; }` and an inherited
1614
1703
  `box-sizing: border-box` reset.
1615
1704
 
1616
1705
  ### Style runtime — @aceshooting/lyra-ui/theme.js
@@ -1623,8 +1712,11 @@ import {
1623
1712
  getLyraStyle,
1624
1713
  resetLyraStyle,
1625
1714
  setLyraStyle,
1715
+ startLyraStyle,
1626
1716
  } from "@aceshooting/lyra-ui/theme.js";
1627
1717
 
1718
+ startLyraStyle(); // adopt the saved style and follow the OS without writing storage
1719
+
1628
1720
  setLyraStyle({ mode: "dark" }); // unspecified axes keep their current value
1629
1721
  setLyraStyle({ accent: "sapphire" }); // named palette accent
1630
1722
  setLyraStyle({ accent: "#7c3aed" }); // absolute CSS color
@@ -1695,6 +1787,27 @@ initial snapshot; it does not install a live system-mode listener. This option c
1695
1787
  the bootstrap. Later runtime calls such as `setLyraStyle()` follow their own style and persistence
1696
1788
  policy, so choose that policy explicitly if the application initializes the runtime afterward.
1697
1789
 
1790
+ **Starting the runtime after the bootstrap — `startLyraStyle()`.** A page that inlines only the
1791
+ bootstrap and loads `theme.js` later in a deferred bundle calls `startLyraStyle()` once. It adopts
1792
+ the saved style (the defaults when nothing is saved) on the document root and, while the mode is
1793
+ System, attaches the live `prefers-color-scheme` listener. It never writes storage: no record is
1794
+ created, a v1 record is not migrated, and an operating-system flip afterwards repaints without
1795
+ saving. Repeated calls do not stack listeners. The call emits no `lr-style-change`; a later
1796
+ operating-system flip emits `changed: ["resolvedMode"]`. The first `setLyraStyle()` takes over the
1797
+ same listener. Do not call `setLyraStyle({})` for this: it persists the current record.
1798
+
1799
+ ~~~ts
1800
+ // deferred bundle, after <script>lyraThemeBootstrap</script> in <head>
1801
+ import { startLyraStyle } from "@aceshooting/lyra-ui/theme.js";
1802
+
1803
+ startLyraStyle(); // adopts the saved style, follows the OS while mode is System, never writes
1804
+ ~~~
1805
+
1806
+ `startLyraStyle()` always reads the runtime's own `localStorage['lyra-theme']` record and applies
1807
+ the whole profile in it. A page whose bootstrap uses `data-lr-theme-restore="mode"` or an
1808
+ application-owned storage key does not pair with it: the start would apply axes or a record the
1809
+ bootstrap deliberately ignored. Such pages call `setLyraStyle()` with the policy they want.
1810
+
1698
1811
  **Migrating the retired theme facade.** New code uses setLyraStyle() and getLyraStyle(). Map old auto mode to system, an old surface reference color to accentBackground, and an old token map to overrides; review custom CSS colors that share a gemstone name before choosing a named accent. Replace preset definitions with a LyraLook plus explicit style choices. Replace selectors for data-lr-theme-preset with the actual axis they need, such as data-lr-look="shadcn". Import theme.css in place of the removed fixed themes/shadcn.css facade and select the look with setLyraStyle({ look: "shadcn" }) or a scoped data-lr-look attribute. Listen for lr-style-change and read event.detail.style and event.detail.changed; the old theme and preset events are no longer emitted. For the complete project-by-project sequence, see [Upgrading from v23 to v24](v23-to-v24-migration.md).
1699
1812
 
1700
1813
  Historical v1 stored records remain readable by the runtime and bootstrap. The removed JavaScript functions, preset subpaths, preset event, root marker, and fixed stylesheet are not available in the current API.
@@ -1825,8 +1938,8 @@ uses a substring class selector, so a class such as `app-lr-flex-preview` does n
1825
1938
  assets repeat `@layer lr-base, lr-theme, lr-theme-preset, lr-utilities, lr-overrides`; an ordinary unlayered
1826
1939
  application rule therefore beats them regardless of load order. A third opt-in asset,
1827
1940
  [`tokens-root.css`](#reading-the-resolved-tokens-from-your-own-components--tokens-rootcss), is not a
1828
- style sheet in the same sense — it declares custom properties only, and exists so your own
1829
- components can read the resolved `--lr-*` tokens these two are written against.
1941
+ style sheet in the same sense — it declares custom properties only: the document token layer that
1942
+ the first connected Lyra element would otherwise adopt, available before any component connects.
1830
1943
 
1831
1944
  ### Utility class inventory
1832
1945
 
@@ -1908,7 +2021,8 @@ keeps that surface's text colour. Heading tracking reads `--lr-heading-letter-sp
1908
2021
  | Muted | `lr-text-sm lr-text-quiet` |
1909
2022
 
1910
2023
  For an exact 0.875rem muted/small size, use a class of your own with the token chain (resolved
1911
- `--lr-*` tokens are undefined at document scope unless `tokens-root.css` is loaded):
2024
+ `--lr-*` tokens exist at document scope once a Lyra element has connected or `tokens-root.css` is
2025
+ loaded; the chain covers the moment before):
1912
2026
 
1913
2027
  ```css
1914
2028
  .app-muted {
@@ -2098,6 +2212,40 @@ contrast/motion choices. Nested chrome suppresses repeated blur; independently p
2098
2212
  surfaces begin their own material root. Keep cards, data tables, charts, map layers and editing
2099
2213
  fields outside the automatic chrome class.
2100
2214
 
2215
+ #### Application fallback fills and cascade layers
2216
+
2217
+ Lyra paints the `.lr-surface-chrome` fill from a rule inside its cascade layers
2218
+ (`lr-theme-preset.surface`). An **unlayered** application rule that sets `background` on the same
2219
+ element — a generic `dialog { background: … }`, a `.panel` class — beats every layered rule
2220
+ whatever its specificity, so the element keeps the application's opaque fill and the Glass
2221
+ treatment disappears without a warning. No `revert-layer` workaround is needed; put the
2222
+ application's fallback where it ranks below Lyra:
2223
+
2224
+ ```css
2225
+ /* 1. A low-priority application layer named before Lyra's. This order statement must be the first
2226
+ the browser sees: put it at the top of the entry stylesheet, before the theme.css import. */
2227
+ @layer app-base, lr-base, lr-theme, lr-theme-preset, lr-utilities, lr-overrides;
2228
+ @import "@aceshooting/lyra-ui/theme.css";
2229
+
2230
+ @layer app-base {
2231
+ dialog,
2232
+ .panel {
2233
+ background: #fff; /* still paints when the Lyra stylesheets are absent */
2234
+ }
2235
+ }
2236
+
2237
+ /* 2. Or choose the chrome's base fill through its token; Glass mixes it at the surface opacity. */
2238
+ .panel.lr-surface-chrome {
2239
+ --lr-surface-background: #fff;
2240
+ }
2241
+ ```
2242
+
2243
+ An application layer declared after Lyra's (or unnamed until after `theme.css` loads) ranks above
2244
+ the surface layer and disables Glass in the same way as an unlayered rule. Keep unlayered
2245
+ `background` rules off `.lr-surface-chrome` elements, or exclude them with
2246
+ `:not(.lr-surface-chrome)`. See [Cascade layers](styles-and-tokens.md#cascade-layers) for the full
2247
+ order.
2248
+
2101
2249
  ## Localization: `locale`, `strings`, and the locale runtime
2102
2250
 
2103
2251
  Every built-in string — button labels, accessible names, descriptions, validation messages, status
@@ -2622,6 +2770,22 @@ browser-bundle argument for excluding them does not apply to a server render. Us
2622
2770
  template; the component renders when its definition upgrades in the browser. Use this for initial
2623
2771
  renders that require light-DOM traversal, layout, canvas, observers, media, or other browser APIs.
2624
2772
 
2773
+ **Link the token layer in `<head>`.** Since 28.0.0 declarative shadow roots no longer carry the
2774
+ shared `--lr-*` layer (one server-rendered `lr-button` is about 34 KB smaller). `theme.css` carries
2775
+ it, so for a correct first paint before hydration link `theme.css` after the no-flash bootstrap (a
2776
+ page that does not use `theme.css` links `styles/tokens-root.css`, the layer alone, instead):
2777
+
2778
+ ```html
2779
+ <link rel="stylesheet" href="/node_modules/@aceshooting/lyra-ui/dist/theme.css" />
2780
+ ```
2781
+
2782
+ Hydration then skips the constructed copy. An application's **own** declarative shadow roots that
2783
+ contain theme scopes (for example a `.lr-dark` region or a `data-lr-theme-scope` wrapper) include the
2784
+ same `<link rel="stylesheet" href="…/tokens-root.css">` inside their template: document styles do not
2785
+ reach into a shadow root until hydration adopts the layer there. Without any link and without
2786
+ JavaScript, server-rendered components paint without resolved tokens, as a page without Lyra's styles
2787
+ always did.
2788
+
2625
2789
  Server setup (the fallback must precede Lit's renderer):
2626
2790
 
2627
2791
  ```ts
@@ -2698,6 +2862,60 @@ only targets in the element's own or an ancestor shadow scope (sibling and desce
2698
2862
  targets are dropped); `null` clears it and removes the attribute; a changed `aria-controls`
2699
2863
  attribute resolves ids in the element's own root instead.
2700
2864
 
2865
+ `installHappyDomShims()` also calls `installHappyDomCascadeLayerShim()`. Happy DOM 20 drops every
2866
+ rule inside an `@layer` block from its CSSOM, while `CSS.supports('at-rule(@layer)')` still reports
2867
+ support. Lyra's stylesheets declare their probe properties (`--_lr-style-resolver`, the
2868
+ `--_lr-*-installed` markers, the token-layer sentinel) inside cascade layers, so under Happy DOM
2869
+ `theme.css` looked absent: `setLyraStyle()` warned that a stylesheet was missing and took its
2870
+ no-stylesheet fallback. The shim passes the text that `CSSStyleSheet.replaceSync()`/`replace()`
2871
+ parse (the path used by `<style>`, `<link rel="stylesheet">` and constructed sheets) through the
2872
+ exported `flattenCascadeLayers(css)`. That function drops `@layer a, b;` statements and unwraps
2873
+ each layer block in place, leaving comments, strings, `url(...)` and `@import … layer(x)` as they
2874
+ are. The probes then resolve as they do in a browser. Layer precedence is not modelled: the
2875
+ unwrapped rules cascade in source order, so assert behaviour, not values that depend on which layer
2876
+ wins. `insertRule()` is unchanged. The shim detects the defect by parsing a layered rule, so it is
2877
+ a no-op in every browser and without a `CSSStyleSheet` global; it installs once and returns a
2878
+ function that restores the original methods. Install it before any stylesheet is parsed (in the
2879
+ same `setupFiles` entry): a sheet parsed earlier keeps the rules it dropped.
2880
+
2881
+ ### jsdom: `installJsdomShims()`
2882
+
2883
+ jsdom implements no `adoptedStyleSheets`, so Lit renders one `<style>` element per component
2884
+ shadow root. jsdom registers each of those sheets on the document, not the shadow root, and every
2885
+ `getComputedStyle()` call then matches the element against every rule of every sheet. With a few
2886
+ hundred Lyra elements on the page a single computed-style read can take over 100 ms, and Lyra
2887
+ reads computed style to decide which slotted text is visible, so whole tests slow to a crawl.
2888
+
2889
+ `installJsdomShims()` installs the two jsdom shims. `installJsdomAdoptedStyleSheetsShim()` adds an inert
2890
+ `adoptedStyleSheets` to `Document` and `ShadowRoot`, so Lit adopts each component's constructed
2891
+ stylesheet instead of appending `<style>` elements. Adopted sheets are stored and read back (the
2892
+ same array until reassigned, so `push()` persists) but never applied; jsdom could not cascade them
2893
+ per shadow root anyway. `getComputedStyle()` then sees only the document's own sheets and inline
2894
+ styles, so assert behaviour and DOM rather than component styling. A jsdom release without
2895
+ `CSSStyleSheet.prototype.replace()`/`replaceSync()`, which Lit also requires, gets inert versions
2896
+ that record the text. The shim installs only when `navigator.userAgent` carries jsdom's
2897
+ `jsdom/<version>` signature and `adoptedStyleSheets` is missing, so it is a no-op in browsers,
2898
+ under Happy DOM and in plain Node projects. It installs once and returns a function that removes
2899
+ what it added.
2900
+
2901
+ jsdom's `ElementInternals` has no form association, so every Lyra form control (`lr-button`,
2902
+ `lr-input`, `lr-checkbox`, …) throws in its constructor there. `installJsdomFormAssociatedShim()`
2903
+ adds, only where missing, an inert `setFormValue()`, a `setValidity()` that `validity`,
2904
+ `validationMessage`, `checkValidity()` and `reportValidity()` reflect, `willValidate`, `form` (the
2905
+ host's closest `<form>`) and a `Set` for `states`. Nothing is submitted and no `invalid` event
2906
+ fires. It has the same jsdom detection, install-once and restore behaviour.
2907
+
2908
+ Lit decides whether it can adopt stylesheets when its module is first evaluated, so call the shim
2909
+ in a `setupFiles` entry before anything imports Lit or a Lyra component. Importing
2910
+ `@aceshooting/lyra-ui/testing` does not load Lit:
2911
+
2912
+ ```ts
2913
+ // vitest.setup.ts (environment: 'jsdom')
2914
+ import { installJsdomShims } from '@aceshooting/lyra-ui/testing';
2915
+
2916
+ installJsdomShims();
2917
+ ```
2918
+
2701
2919
  ## Constructing a validated test event: `createLyraEvent()`
2702
2920
 
2703
2921
  `@aceshooting/lyra-ui/testing` also exports
@@ -2730,8 +2948,9 @@ semantics of their own that this factory does not model.
2730
2948
 
2731
2949
  ## Driving a component's real activation path: interaction drivers
2732
2950
 
2733
- For the exact gap `createLyraEvent()` leaves open — choosing an option, submitting a confirm
2734
- decision, toggling a switch, activating a step — `@aceshooting/lyra-ui/testing` exports a small
2951
+ For the exact gap `createLyraEvent()` leaves open — choosing an option, a swatch or a currency,
2952
+ submitting a confirm decision, toggling a switch, activating a step, opening and closing a
2953
+ popover — `@aceshooting/lyra-ui/testing` exports a small
2735
2954
  set of typed interaction drivers, one per interaction, that go through the real component's own
2736
2955
  activation path (its own shadow-part lookup and `.click()`, the same as its own tests) instead of
2737
2956
  a downstream suite reverse-engineering internal detail shapes or shadow-part selectors itself:
@@ -2739,17 +2958,43 @@ a downstream suite reverse-engineering internal detail shapes or shadow-part sel
2739
2958
  ```ts
2740
2959
  import {
2741
2960
  chooseOption,
2961
+ chooseSwatch,
2962
+ chooseCurrency,
2963
+ openPopover,
2964
+ closePopover,
2742
2965
  submitConfirmDecision,
2743
2966
  toggleSwitch,
2744
2967
  activateStep,
2745
2968
  } from '@aceshooting/lyra-ui/testing';
2746
2969
 
2747
2970
  await chooseOption(combobox, 'banana'); // opens the listbox, clicks the matching [part="option"] row
2971
+ await chooseSwatch(swatchPicker, 'ruby'); // clicks the matching swatch radio
2972
+ await chooseCurrency(currencyPicker, 'EUR'); // opens the composed listbox, clicks the EUR option
2973
+ await openPopover(popover); // clicks the slotted (or `for`) trigger, waits for lr-after-show
2974
+ await closePopover(popover); // presses Escape, waits for lr-after-hide; { via: 'trigger' } clicks instead
2748
2975
  await submitConfirmDecision(confirmBar, 'approved'); // clicks [part="approve-button"]
2749
2976
  await toggleSwitch(switchEl); // calls switchEl.click(), lr-switch's own activation path
2750
2977
  await activateStep(stepper, 'review'); // clicks the [part="step"] button for that stepId (or pass an index)
2751
2978
  ```
2752
2979
 
2980
+ `chooseSwatch(picker, value)` clicks the `<lr-swatch-picker>` radio whose value matches (the first
2981
+ enabled one when values repeat). When it resolves, `lr-change` (only if the selection moved) and
2982
+ `lr-activate` have fired. `chooseCurrency(picker, code)` drives the select, or the lazily loaded
2983
+ combobox of a `searchable` picker, that `<lr-currency-picker>` composes. The code must match the
2984
+ catalog exactly (`"EUR"`). When it resolves, the picker has emitted `input`, `lr-input`, `change`
2985
+ and `lr-change` once if the value changed.
2986
+
2987
+ `openPopover(popover, { timeoutMs? })` clicks the `<lr-popover>` interaction trigger (the slotted
2988
+ `trigger` element, else its `for` target) and resolves after `lr-show` and `lr-after-show`. A
2989
+ `hover`/`focus` popover opens and pins, as for a user's click. `closePopover(popover, { via?,
2990
+ timeoutMs? })` presses Escape on the focused element by default, which closes the topmost overlay,
2991
+ or clicks the trigger again with `via: 'trigger'`. It resolves after `lr-hide` and `lr-after-hide`,
2992
+ with focus back on the trigger. Both resolve at once when the popover is already in the requested
2993
+ state. They throw when the interaction is refused: an `lr-show`/`lr-hide` veto, a disabled popover
2994
+ or trigger, `trigger="manual"`, no trigger, or Escape going to a newer overlay. Use `show()`/`hide()`
2995
+ for manual and `showAt()` popovers. A missing `lr-after-*` event rejects after `timeoutMs`
2996
+ (default 2000).
2997
+
2753
2998
  `chooseOption()` accepts any of `<lr-combobox>`, `<lr-select>`, `<lr-model-select>`,
2754
2999
  `<lr-locale-picker>` and `<lr-voice-picker>` — every component that independently implements the
2755
3000
  same `[part="option"]` row plus `data-value` delegated-click pattern for its own listbox/popup.
@@ -2765,13 +3010,14 @@ immediately after it see the render reached by that interaction. Host-driven asy
2765
3010
  still need their own completion check. Each driver also throws a plain `Error` — never a
2766
3011
  silent no-op — when the requested interaction cannot actually happen: the target is disabled, the
2767
3012
  option owner/stepper is read-only or has no matching row/step currently rendered, or the confirm
2768
- bar is already decided. That is the exact failure mode a hand-rolled event misses (for example
3013
+ bar is already decided, or a popover's lifecycle event was vetoed. That is the exact failure mode a hand-rolled event misses (for example
2769
3014
  dispatching a confirm event without `cancelable`, which makes a `preventDefault()`-based
2770
3015
  pending-state handler a silent no-op that still looks tested) — these drivers exercise the
2771
3016
  component for real, so a precondition that blocks the interaction is surfaced as a thrown error
2772
3017
  instead of quietly doing nothing.
2773
3018
 
2774
- Pure DOM operations only (`Element.click()`, shadow-part queries, public properties) — no
3019
+ Pure DOM operations only (`Element.click()`, an Escape `KeyboardEvent`, shadow-part queries,
3020
+ public properties) — no
2775
3021
  `@web/test-runner`/CDP-only helper, so they also run under a downstream suite's own
2776
3022
  happy-dom/jsdom environment, not only a real browser.
2777
3023
 
@@ -3120,7 +3366,8 @@ arrowPadding?: number; hoverBridge?: HTMLElement; onPlaced?: (result: PlacementR
3120
3366
  (`next`); the `value` getter/setter (`next`); `defaultValue`; `customError`; `disabled`; `required`; readonly
3121
3367
  `effectiveDisabled`; the `form` getter/setter (`owner`); readonly `labels`, `validity`,
3122
3368
  `validationMessage`, and `willValidate`; `setFormValue(next)`; `getForm()`; `checkValidity()`;
3123
- `reportValidity()`; `setCustomValidity(message)`; `resetValidity()`; `formResetCallback()`; and
3369
+ `reportValidity()`; `setCustomValidity(message)`; `resetValidity()`; `formResetCallback()`;
3370
+ `formDisabledCallback(disabled)`; and
3124
3371
  `formStateRestoreCallback(state, reason)`.
3125
3372
  Writing `null` to `defaultValue` removes its markup value and resets the default to the adapter's
3126
3373
  empty value; reading it still returns `TValue`. This does not overwrite a dirty live value.
@@ -3143,7 +3390,8 @@ HTMLFormElement | null; set form(owner: FormOwnerValue); readonly labels: NodeLi
3143
3390
  validity: ValidityState; readonly validationMessage: string; readonly willValidate: boolean;
3144
3391
  setFormValue(next: TValue): void; getForm(): HTMLFormElement | null; checkValidity(): boolean;
3145
3392
  reportValidity(): boolean; setCustomValidity(message: string): void; resetValidity(): void;
3146
- formResetCallback(): void; formStateRestoreCallback(state: FormSubmissionValue, reason:
3393
+ formResetCallback(): void; formDisabledCallback(disabled: boolean): void;
3394
+ formStateRestoreCallback(state: FormSubmissionValue, reason:
3147
3395
  'autocomplete' | 'restore'): void }`. The exported subclass seam is
3148
3396
  `FormAssociatedSubclassInterface<TValue> { protected captureLiveValueCheckpoint(): { readonly
3149
3397
  value: TValue; readonly dirty: boolean }; protected restoreLiveValueCheckpoint(checkpoint: {
@@ -4245,6 +4493,16 @@ These named interfaces and helper signatures are available to typed integrations
4245
4493
  `resolveCanvasColor(/* public names: scope, color, fallback */): unknown`
4246
4494
  `resolveCanvasColors(/* public names: scope, colors, fallback */): unknown`
4247
4495
 
4496
+ - **`internal-document-tokens-contracts`** — Shared utility contracts.
4497
+ `adoptLyraTokens(root: Document | ShadowRoot): void`
4498
+ Adopts the document token layer into a document or shadow root. Idempotent; see "Application
4499
+ shadow roots" in the styles and tokens guide for when a root needs it.
4500
+
4501
+ - **`internal-theme-scopes-contracts`** — Shared utility contracts.
4502
+ `findUnscopedThemeInputs(root?: Document | DocumentFragment | Element): Element[]`
4503
+ Lists elements that set an inline `--lr-theme-*` input the layer consumes without being a theme
4504
+ scope. Reads inline styles only and logs nothing.
4505
+
4248
4506
  - **`internal-localization-runtime-contracts`** — Shared utility contracts.
4249
4507
  `getLyraLocaleDirection(/* public names: locale */): unknown`
4250
4508
  `getLyraLocale(): unknown`
@@ -4980,15 +5238,45 @@ These named interfaces and helper signatures are available to typed integrations
4980
5238
  - **`testing-happy-dom-shims-contracts`** — Shared utility contracts.
4981
5239
  `installHappyDomFormAssociatedShims(): unknown`
4982
5240
  `installHappyDomShadowFocusShim(): unknown`
5241
+ `flattenCascadeLayers(/* public names: css */): unknown`
4983
5242
  `installHappyDomAriaControlsShim(/* public names: proto */): unknown`
5243
+ `installHappyDomCascadeLayerShim(/* public names: proto, misbehaves */): unknown`
4984
5244
  `installHappyDomShims(): unknown`
4985
5245
  `installStubInternalsForTest(/* public names: host */): unknown`
4986
5246
 
5247
+ - **`testing-jsdom-shims-contracts`** — Shared utility contracts.
5248
+ `JsdomAdoptedStyleSheetsTargets {
5249
+ documentPrototype?: object;
5250
+ shadowRootPrototype?: object;
5251
+ styleSheetPrototype?: object;
5252
+ isJsdom?: () => boolean;
5253
+ }`
5254
+ `installJsdomAdoptedStyleSheetsShim(targets?: JsdomAdoptedStyleSheetsTargets): () => void`
5255
+ `JsdomFormAssociatedTargets {
5256
+ internalsPrototype?: object;
5257
+ elementPrototype?: object;
5258
+ isJsdom?: () => boolean;
5259
+ }`
5260
+ `installJsdomFormAssociatedShim(targets?: JsdomFormAssociatedTargets): () => void`
5261
+ `installJsdomShims(): () => void`
5262
+ See "jsdom: `installJsdomShims()`" above for the full contract.
5263
+
4987
5264
  - **`testing-interaction-drivers-contracts`** — Shared utility contracts.
4988
5265
  `chooseOption(/* public names: owner, value */): unknown`
4989
5266
  `submitConfirmDecision(/* public names: owner, decision */): unknown`
4990
5267
  `toggleSwitch(/* public names: switchEl */): unknown`
4991
5268
  `activateStep(/* public names: stepper, target */): unknown`
5269
+ `chooseSwatch(/* public names: picker, value */): unknown`
5270
+ `chooseCurrency(/* public names: picker, code */): unknown`
5271
+ `PopoverDriverOptions {
5272
+ timeoutMs: unknown;
5273
+ }`
5274
+ `ClosePopoverOptions {
5275
+ via: unknown;
5276
+ timeoutMs: unknown;
5277
+ }`
5278
+ `openPopover(/* public names: popover, options */): unknown`
5279
+ `closePopover(/* public names: popover, options */): unknown`
4992
5280
  See "Driving a component's real activation path: interaction drivers" above for the full contract.
4993
5281
 
4994
5282
  - **`testing-wait-for-mount-contracts`** — Shared utility contracts.
@@ -5085,6 +5373,7 @@ These named interfaces and helper signatures are available to typed integrations
5085
5373
  `parseLyraStyleRecord(value: unknown): Readonly<LyraStyle>`
5086
5374
  `resetLyraStyle(fields?: readonly LyraStyleField[]): Readonly<LyraStyle>`
5087
5375
  `setLyraStyle(choices: LyraStyleChoices): Readonly<LyraStyle>`
5376
+ `startLyraStyle(): Readonly<LyraStyle>`
5088
5377
 
5089
5378
  - **`vue-contracts`** — Framework integration type contracts, including the multi-split
5090
5379
  `for` launcher id and `trigger` element reference.
@@ -5873,7 +6162,35 @@ of the backdrop; the default foreground qualification covers 60%, while a custom
5873
6162
  cannot guarantee contrast against every backdrop. Solid and accessibility preferences retain
5874
6163
  opaque fills regardless of this input.
5875
6164
  Dark Glass deepens the selected fill at opacity below 70% so light foregrounds remain readable
5876
- against bright backdrops; at 70% and above, the selected fill keeps its original color.
6165
+ against bright backdrops; at 70% and above, the selected fill keeps its original color. The
6166
+ deepening blends toward a darkened `--lr-color-surface` (10% of the surface, the rest black), not
6167
+ pure black, so a lifted dark palette (for example a neutral grey `--lr-theme-color-surface-default`
6168
+ and overlay tokens) lifts its Glass chrome with it. That share is small on purpose: the stock
6169
+ palettes are qualified for 4.5:1 text over a pure white backdrop at 60% opacity, which caps how
6170
+ light the pane can get. To paint a lifted palette's own overlay colour, set `--lr-theme-surface-opacity`
6171
+ to 0.7 or higher.
6172
+
6173
+ `--lr-theme-surface-glass-dark-share` is the public input for that share. It is a percentage,
6174
+ default `10%`, read in dark mode only, below 70% opacity. It sets how much of `--lr-color-surface`
6175
+ the deepening anchor keeps (the rest is black), so raising it lifts dark Glass chrome toward the
6176
+ surface. It inherits, so set it on `:root` for the whole app or on any container for that subtree;
6177
+ values outside 0% to 100% clamp. It is deliberately not registered with `@property` (a registered
6178
+ inherited property slows every re-theme in Chromium), so a value that is not a percentage voids the
6179
+ dark fill; the development build warns about one set on `:root`. The default paints exactly as before. For a softer grey dark mode:
6180
+
6181
+ ```css
6182
+ :root {
6183
+ --lr-theme-color-surface-default: #1f2023;
6184
+ --lr-theme-color-surface-raised: #1f2023;
6185
+ --lr-theme-color-surface-overlay: #1f2023;
6186
+ --lr-theme-surface-glass-dark-share: 60%;
6187
+ }
6188
+ ```
6189
+
6190
+ The trade-off is contrast: the 4.5:1 qualification over a white backdrop at 60% opacity holds for
6191
+ the stock palettes only at the 10% default, and a lighter pane lowers light-text contrast over bright
6192
+ backdrops. After raising the share, re-measure your own text colours, or raise
6193
+ `--lr-theme-surface-opacity` to 0.7 or higher, where the fill keeps the palette's own colour.
5877
6194
  Necessary control borders use the effective painted opacity: their glass adjustment grows as
5878
6195
  transparency increases and disappears at 100% opacity. Ordinary and strong borders share this
5879
6196
  adjustment; decorative borders and explicit component border overrides keep their own tokens.