@kerfjs/ui 5.0.0-beta.6 → 5.0.0-beta.60

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 (706) hide show
  1. package/README.md +493 -138
  2. package/ai/application-ui-diagnostic-ids-v1.json +109 -0
  3. package/ai/application-ui-profile-sync.cjs +529 -0
  4. package/ai/application-ui-profile.d.mts +1 -0
  5. package/ai/application-ui-profile.d.ts +102 -0
  6. package/ai/application-ui-profile.defaults.json +96 -0
  7. package/ai/application-ui-profile.mjs +626 -0
  8. package/ai/application-ui-profile.schema.json +130 -0
  9. package/ai/catalog-authoring.json +36 -0
  10. package/ai/catalog-authoring.schema.json +109 -0
  11. package/ai/compile-time-contracts-v1.json +149 -0
  12. package/ai/compile-time-contracts-v1.schema.json +52 -0
  13. package/ai/component-catalog-extension.schema.json +79 -0
  14. package/ai/component-catalog.json +3947 -607
  15. package/ai/component-catalog.schema.json +78 -1
  16. package/ai/component-class-owners.cjs +83 -0
  17. package/ai/component-composition-extension.d.ts +7 -0
  18. package/ai/component-composition-extension.schema.json +7 -0
  19. package/ai/component-composition-rules.json +516 -0
  20. package/ai/component-composition.d.ts +115 -0
  21. package/ai/component-composition.json +6972 -0
  22. package/ai/component-composition.schema.json +354 -0
  23. package/ai/components/README.md +170 -0
  24. package/ai/components/application-tabs.md +65 -0
  25. package/ai/components/badge.md +72 -0
  26. package/ai/components/collapsible-panel.md +102 -0
  27. package/ai/components/content-item.md +90 -0
  28. package/ai/components/disclosure-arrow.md +77 -0
  29. package/ai/components/document-baseline.md +75 -0
  30. package/ai/components/empty-state.md +80 -0
  31. package/ai/components/feedback.md +71 -0
  32. package/ai/components/floating-toolbar.md +82 -0
  33. package/ai/components/foundation.md +65 -0
  34. package/ai/components/grid.md +93 -0
  35. package/ai/components/headers.md +63 -0
  36. package/ai/components/layout.md +75 -0
  37. package/ai/components/list-action-row.md +91 -0
  38. package/ai/components/list-header.md +89 -0
  39. package/ai/components/list-inset-control.md +74 -0
  40. package/ai/components/list-inset-text.md +72 -0
  41. package/ai/components/list-item.md +91 -0
  42. package/ai/components/list.md +103 -0
  43. package/ai/components/loading-spinner.md +72 -0
  44. package/ai/components/lucide-icon.md +66 -0
  45. package/ai/components/nav-stack.md +103 -0
  46. package/ai/components/pane.md +100 -0
  47. package/ai/components/popup-menu.md +171 -0
  48. package/ai/components/recipe-app-shell.md +57 -0
  49. package/ai/components/recipe-collapsible-sidebar.md +57 -0
  50. package/ai/components/recipe-compact-toolbar.md +57 -0
  51. package/ai/components/recipe-composer-form.md +57 -0
  52. package/ai/components/recipe-list-detail-dialog.md +57 -0
  53. package/ai/components/recipe-list-workspace-states.md +57 -0
  54. package/ai/components/recipe-loading-inspector.md +57 -0
  55. package/ai/components/recipe-navigation-sidebar.md +57 -0
  56. package/ai/components/recipe-navigation-stack.md +57 -0
  57. package/ai/components/recipe-workspace-header.md +57 -0
  58. package/ai/components/resize.md +106 -0
  59. package/ai/components/row.md +96 -0
  60. package/ai/components/segmented-control.md +82 -0
  61. package/ai/components/select.md +109 -0
  62. package/ai/components/skeleton.md +84 -0
  63. package/ai/components/spacer.md +86 -0
  64. package/ai/components/split-view.md +102 -0
  65. package/ai/components/state-banner.md +94 -0
  66. package/ai/components/sunken-panel.md +79 -0
  67. package/ai/components/surface-scaffold.md +78 -0
  68. package/ai/components/tab-bar.md +112 -0
  69. package/ai/components/tab-scaffold.md +94 -0
  70. package/ai/components/tabs.md +91 -0
  71. package/ai/components/text.md +83 -0
  72. package/ai/components/token-search-field.md +103 -0
  73. package/ai/components/toolbar-control-group.md +102 -0
  74. package/ai/components/toolbar-text.md +78 -0
  75. package/ai/components/toolbar.md +96 -0
  76. package/ai/components/value-table.md +73 -0
  77. package/ai/components/wa-accordion-item.md +55 -0
  78. package/ai/components/wa-accordion.md +65 -0
  79. package/ai/components/wa-animated-image.md +55 -0
  80. package/ai/components/wa-animation.md +55 -0
  81. package/ai/components/wa-avatar.md +55 -0
  82. package/ai/components/wa-badge.md +55 -0
  83. package/ai/components/wa-breadcrumb-item.md +53 -0
  84. package/ai/components/wa-breadcrumb.md +55 -0
  85. package/ai/components/wa-button-group.md +59 -0
  86. package/ai/components/wa-button.md +55 -0
  87. package/ai/components/wa-callout.md +58 -0
  88. package/ai/components/wa-card.md +59 -0
  89. package/ai/components/wa-carousel-item.md +53 -0
  90. package/ai/components/wa-carousel.md +55 -0
  91. package/ai/components/wa-checkbox-group.md +55 -0
  92. package/ai/components/wa-checkbox.md +55 -0
  93. package/ai/components/wa-color-picker.md +55 -0
  94. package/ai/components/wa-comparison.md +55 -0
  95. package/ai/components/wa-copy-button.md +55 -0
  96. package/ai/components/wa-details.md +61 -0
  97. package/ai/components/wa-dialog.md +59 -0
  98. package/ai/components/wa-divider.md +53 -0
  99. package/ai/components/wa-drawer.md +55 -0
  100. package/ai/components/wa-dropdown-item.md +60 -0
  101. package/ai/components/wa-dropdown.md +60 -0
  102. package/ai/components/wa-format-bytes.md +53 -0
  103. package/ai/components/wa-format-date.md +53 -0
  104. package/ai/components/wa-format-number.md +53 -0
  105. package/ai/components/wa-icon.md +58 -0
  106. package/ai/components/wa-include.md +53 -0
  107. package/ai/components/wa-input.md +59 -0
  108. package/ai/components/wa-intersection-observer.md +53 -0
  109. package/ai/components/wa-known-date.md +53 -0
  110. package/ai/components/wa-markdown.md +53 -0
  111. package/ai/components/wa-mutation-observer.md +53 -0
  112. package/ai/components/wa-number-input.md +55 -0
  113. package/ai/components/wa-option.md +59 -0
  114. package/ai/components/wa-otp-input.md +53 -0
  115. package/ai/components/wa-page.md +53 -0
  116. package/ai/components/wa-pagination.md +55 -0
  117. package/ai/components/wa-popover.md +55 -0
  118. package/ai/components/wa-popup.md +53 -0
  119. package/ai/components/wa-progress-bar.md +53 -0
  120. package/ai/components/wa-progress-ring.md +53 -0
  121. package/ai/components/wa-qr-code.md +53 -0
  122. package/ai/components/wa-radio-group.md +59 -0
  123. package/ai/components/wa-radio.md +55 -0
  124. package/ai/components/wa-random-content.md +53 -0
  125. package/ai/components/wa-rating.md +55 -0
  126. package/ai/components/wa-relative-time.md +53 -0
  127. package/ai/components/wa-resize-observer.md +53 -0
  128. package/ai/components/wa-scroller.md +53 -0
  129. package/ai/components/wa-select.md +63 -0
  130. package/ai/components/wa-skeleton.md +53 -0
  131. package/ai/components/wa-slider.md +55 -0
  132. package/ai/components/wa-spinner.md +58 -0
  133. package/ai/components/wa-split-panel.md +58 -0
  134. package/ai/components/wa-switch.md +53 -0
  135. package/ai/components/wa-tab-group.md +69 -0
  136. package/ai/components/wa-tab-panel.md +60 -0
  137. package/ai/components/wa-tab.md +59 -0
  138. package/ai/components/wa-tag.md +59 -0
  139. package/ai/components/wa-textarea.md +53 -0
  140. package/ai/components/wa-time-input.md +55 -0
  141. package/ai/components/wa-toast-item.md +60 -0
  142. package/ai/components/wa-toast.md +60 -0
  143. package/ai/components/wa-tooltip.md +55 -0
  144. package/ai/components/wa-tree-item.md +55 -0
  145. package/ai/components/wa-tree.md +55 -0
  146. package/ai/components/wa-zoomable-frame.md +55 -0
  147. package/ai/components/workbench.md +114 -0
  148. package/ai/public-api-signatures-v1.md +2528 -197
  149. package/ai/skill.md +241 -65
  150. package/ai/webawesome-jsx-signatures-v1.md +9 -2
  151. package/analyzer/cli.mjs +49 -0
  152. package/analyzer/component-ownership.d.mts +56 -0
  153. package/analyzer/component-ownership.mjs +184 -0
  154. package/analyzer/index.d.mts +49 -0
  155. package/analyzer/index.mjs +1458 -0
  156. package/analyzer/loud-pairs.d.mts +49 -0
  157. package/analyzer/loud-pairs.mjs +357 -0
  158. package/analyzer/report.schema.json +66 -0
  159. package/analyzer/selectors.d.mts +21 -0
  160. package/analyzer/selectors.mjs +212 -0
  161. package/dist/app-tab.d.ts +31 -4
  162. package/dist/app-tab.js +3 -1
  163. package/dist/badge.d.ts +52 -0
  164. package/dist/badge.js +3 -0
  165. package/dist/badge.js.map +1 -0
  166. package/dist/browser/app-tab.js +1 -0
  167. package/dist/browser/badge.js +3 -0
  168. package/dist/browser/catalog.js +25 -0
  169. package/dist/browser/collapsible-panel.js +7 -0
  170. package/dist/browser/content-item.js +3 -0
  171. package/dist/browser/floating-toolbar.js +3 -0
  172. package/dist/browser/grid.js +3 -0
  173. package/dist/browser/list-action-row.js +5 -0
  174. package/dist/browser/list-header.js +8 -0
  175. package/dist/browser/list-inset-control.js +3 -0
  176. package/dist/browser/list-inset-text.js +3 -0
  177. package/dist/browser/list-item.js +5 -0
  178. package/dist/browser/list.js +3 -0
  179. package/dist/browser/{panel-header.js → nav-stack.js} +4 -2
  180. package/dist/browser/pane.js +3 -0
  181. package/dist/browser/popup-menu.js +3 -0
  182. package/dist/browser/row.js +3 -0
  183. package/dist/browser/select.js +2 -0
  184. package/dist/browser/skeleton.js +3 -0
  185. package/dist/browser/spacer.js +3 -0
  186. package/dist/browser/split-view.js +10 -0
  187. package/dist/browser/state-banner.js +2 -0
  188. package/dist/browser/sunken-panel.js +3 -0
  189. package/dist/browser/surface-scaffold.js +3 -0
  190. package/dist/browser/tab-scaffold.js +4 -0
  191. package/dist/browser/text.js +3 -0
  192. package/dist/browser/toolbar-text.js +1 -0
  193. package/dist/browser/value-table.js +1 -0
  194. package/dist/browser/workbench.js +10 -0
  195. package/dist/catalog-resources.d.ts +46 -0
  196. package/dist/catalog-resources.js +22 -0
  197. package/dist/catalog-resources.js.map +1 -0
  198. package/dist/catalog.d.ts +90 -0
  199. package/dist/catalog.js +501 -0
  200. package/dist/catalog.js.map +1 -0
  201. package/dist/chunk-2G2ZJQYK.js +152 -0
  202. package/dist/chunk-2G2ZJQYK.js.map +1 -0
  203. package/dist/chunk-2OXQHM7I.js +418 -0
  204. package/dist/chunk-2OXQHM7I.js.map +1 -0
  205. package/dist/chunk-3SXMEK3B.js +43 -0
  206. package/dist/chunk-3SXMEK3B.js.map +1 -0
  207. package/dist/{chunk-MQIZAD4A.js → chunk-46263QLK.js} +126 -22
  208. package/dist/chunk-46263QLK.js.map +1 -0
  209. package/dist/{chunk-W2FK7ZNY.js → chunk-4R777DUZ.js} +10 -2
  210. package/dist/chunk-4R777DUZ.js.map +1 -0
  211. package/dist/chunk-5I7OHCIP.js +96 -0
  212. package/dist/chunk-5I7OHCIP.js.map +1 -0
  213. package/dist/chunk-5I7QGQVT.js +24 -0
  214. package/dist/chunk-5I7QGQVT.js.map +1 -0
  215. package/dist/chunk-5PESG2VP.js +115 -0
  216. package/dist/chunk-5PESG2VP.js.map +1 -0
  217. package/dist/chunk-5Q3N6EV7.js +156 -0
  218. package/dist/chunk-5Q3N6EV7.js.map +1 -0
  219. package/dist/chunk-6ZXOQEAN.js +27 -0
  220. package/dist/chunk-6ZXOQEAN.js.map +1 -0
  221. package/dist/chunk-7JUBXOIV.js +103 -0
  222. package/dist/chunk-7JUBXOIV.js.map +1 -0
  223. package/dist/chunk-A3CKOW2R.js +622 -0
  224. package/dist/chunk-A3CKOW2R.js.map +1 -0
  225. package/dist/chunk-AGXQQFKZ.js +62 -0
  226. package/dist/chunk-AGXQQFKZ.js.map +1 -0
  227. package/dist/chunk-AHEG27M7.js +66 -0
  228. package/dist/chunk-AHEG27M7.js.map +1 -0
  229. package/dist/chunk-AUKT3KMG.js +37 -0
  230. package/dist/chunk-AUKT3KMG.js.map +1 -0
  231. package/dist/chunk-AYUHMAI6.js +48 -0
  232. package/dist/chunk-AYUHMAI6.js.map +1 -0
  233. package/dist/chunk-BFPBS5Y4.js +250 -0
  234. package/dist/chunk-BFPBS5Y4.js.map +1 -0
  235. package/dist/chunk-DG6G5EPF.js +35 -0
  236. package/dist/chunk-DG6G5EPF.js.map +1 -0
  237. package/dist/chunk-E36EVCKG.js +34 -0
  238. package/dist/chunk-E36EVCKG.js.map +1 -0
  239. package/dist/chunk-EZVNC4J6.js +104 -0
  240. package/dist/chunk-EZVNC4J6.js.map +1 -0
  241. package/dist/chunk-F4QMLOXJ.js +135 -0
  242. package/dist/chunk-F4QMLOXJ.js.map +1 -0
  243. package/dist/chunk-F7F7KRO2.js +187 -0
  244. package/dist/chunk-F7F7KRO2.js.map +1 -0
  245. package/dist/chunk-GLDWRCAM.js +49 -0
  246. package/dist/chunk-GLDWRCAM.js.map +1 -0
  247. package/dist/chunk-I3Q7KKYM.js +33 -0
  248. package/dist/chunk-I3Q7KKYM.js.map +1 -0
  249. package/dist/chunk-IA7TMD4Y.js +288 -0
  250. package/dist/chunk-IA7TMD4Y.js.map +1 -0
  251. package/dist/chunk-JCV4GXWB.js +249 -0
  252. package/dist/chunk-JCV4GXWB.js.map +1 -0
  253. package/dist/chunk-K47P7QZC.js +17 -0
  254. package/dist/chunk-K47P7QZC.js.map +1 -0
  255. package/dist/chunk-KLJKU6ZL.js +109 -0
  256. package/dist/chunk-KLJKU6ZL.js.map +1 -0
  257. package/dist/{chunk-56RJXPHG.js → chunk-KROD7KZK.js} +7 -4
  258. package/dist/chunk-KROD7KZK.js.map +1 -0
  259. package/dist/chunk-MA73TA6D.js +42 -0
  260. package/dist/chunk-MA73TA6D.js.map +1 -0
  261. package/dist/chunk-MERWRTBG.js +45 -0
  262. package/dist/chunk-MERWRTBG.js.map +1 -0
  263. package/dist/chunk-MEY33VWB.js +44 -0
  264. package/dist/chunk-MEY33VWB.js.map +1 -0
  265. package/dist/chunk-MP2GDBQZ.js +50 -0
  266. package/dist/chunk-MP2GDBQZ.js.map +1 -0
  267. package/dist/chunk-N3GKMPDW.js +51 -0
  268. package/dist/chunk-N3GKMPDW.js.map +1 -0
  269. package/dist/chunk-NAPE7WUS.js +75 -0
  270. package/dist/chunk-NAPE7WUS.js.map +1 -0
  271. package/dist/chunk-NN6GYOA2.js +47 -0
  272. package/dist/chunk-NN6GYOA2.js.map +1 -0
  273. package/dist/chunk-NNMLEDMD.js +399 -0
  274. package/dist/chunk-NNMLEDMD.js.map +1 -0
  275. package/dist/chunk-P7D3EMXM.js +52 -0
  276. package/dist/chunk-P7D3EMXM.js.map +1 -0
  277. package/dist/chunk-PSHUSWQH.js +18 -0
  278. package/dist/chunk-PSHUSWQH.js.map +1 -0
  279. package/dist/chunk-RRB26JRJ.js +247 -0
  280. package/dist/chunk-RRB26JRJ.js.map +1 -0
  281. package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
  282. package/dist/chunk-SRSJO5QE.js.map +1 -0
  283. package/dist/chunk-T57AXDFM.js +95 -0
  284. package/dist/chunk-T57AXDFM.js.map +1 -0
  285. package/dist/chunk-T66UNBXL.js +129 -0
  286. package/dist/chunk-T66UNBXL.js.map +1 -0
  287. package/dist/chunk-UIDKY4UB.js +14 -0
  288. package/dist/chunk-UIDKY4UB.js.map +1 -0
  289. package/dist/chunk-UWONG4QE.js +127 -0
  290. package/dist/chunk-UWONG4QE.js.map +1 -0
  291. package/dist/chunk-VZOFZPSO.js +71 -0
  292. package/dist/chunk-VZOFZPSO.js.map +1 -0
  293. package/dist/chunk-WPLIO5WJ.js +184 -0
  294. package/dist/chunk-WPLIO5WJ.js.map +1 -0
  295. package/dist/{chunk-TRNBHPNW.js → chunk-X5QSICJ3.js} +13 -7
  296. package/dist/chunk-X5QSICJ3.js.map +1 -0
  297. package/dist/chunk-ZLWQZUGF.js +27 -0
  298. package/dist/chunk-ZLWQZUGF.js.map +1 -0
  299. package/dist/collapsible-panel.d.ts +122 -0
  300. package/dist/collapsible-panel.js +115 -0
  301. package/dist/collapsible-panel.js.map +1 -0
  302. package/dist/content-item.d.ts +46 -0
  303. package/dist/content-item.js +4 -0
  304. package/dist/content-item.js.map +1 -0
  305. package/dist/css-values.d.ts +107 -0
  306. package/dist/css-values.js +3 -0
  307. package/dist/css-values.js.map +1 -0
  308. package/dist/device-class.js +33 -5
  309. package/dist/device-class.js.map +1 -1
  310. package/dist/disclosure-arrow.d.ts +6 -1
  311. package/dist/disclosure-arrow.js +1 -1
  312. package/dist/empty-state.d.ts +5 -2
  313. package/dist/empty-state.js +1 -1
  314. package/dist/flex-alignment-4ms8ZbV8.d.ts +5 -0
  315. package/dist/floating-toolbar.d.ts +51 -0
  316. package/dist/floating-toolbar.js +3 -0
  317. package/dist/floating-toolbar.js.map +1 -0
  318. package/dist/grid.d.ts +34 -0
  319. package/dist/grid.js +5 -0
  320. package/dist/grid.js.map +1 -0
  321. package/dist/help-tags-register.d.ts +13 -0
  322. package/dist/help-tags-register.js +9 -0
  323. package/dist/help-tags-register.js.map +1 -0
  324. package/dist/index.d.ts +34 -14
  325. package/dist/index.js +42 -22
  326. package/dist/list-action-row.d.ts +64 -0
  327. package/dist/list-action-row.js +8 -0
  328. package/dist/list-action-row.js.map +1 -0
  329. package/dist/list-header.d.ts +99 -0
  330. package/dist/list-header.js +10 -0
  331. package/dist/list-header.js.map +1 -0
  332. package/dist/list-inset-control.d.ts +22 -0
  333. package/dist/list-inset-control.js +3 -0
  334. package/dist/list-inset-control.js.map +1 -0
  335. package/dist/list-inset-text.d.ts +30 -0
  336. package/dist/list-inset-text.js +4 -0
  337. package/dist/list-inset-text.js.map +1 -0
  338. package/dist/list-item.d.ts +52 -0
  339. package/dist/list-item.js +8 -0
  340. package/dist/list-item.js.map +1 -0
  341. package/dist/list.d.ts +59 -0
  342. package/dist/list.js +6 -0
  343. package/dist/list.js.map +1 -0
  344. package/dist/loading-spinner.d.ts +3 -1
  345. package/dist/loading-spinner.js +1 -1
  346. package/dist/lucide-icon.d.ts +3 -1
  347. package/dist/lucide-icon.js +1 -1
  348. package/dist/nav-stack.d.ts +60 -10
  349. package/dist/nav-stack.js +6 -1
  350. package/dist/pane.d.ts +102 -0
  351. package/dist/pane.js +4 -0
  352. package/dist/pane.js.map +1 -0
  353. package/dist/panel-toolbar-DqwtBxQL.d.ts +63 -0
  354. package/dist/popup-menu-register.d.ts +9 -0
  355. package/dist/popup-menu-register.js +31 -0
  356. package/dist/popup-menu-register.js.map +1 -0
  357. package/dist/popup-menu.d.ts +99 -0
  358. package/dist/popup-menu.js +3 -0
  359. package/dist/popup-menu.js.map +1 -0
  360. package/dist/resizable-region.d.ts +32 -3
  361. package/dist/resizable-region.js +1 -1
  362. package/dist/row.d.ts +48 -0
  363. package/dist/row.js +6 -0
  364. package/dist/row.js.map +1 -0
  365. package/dist/segmented-control.d.ts +13 -8
  366. package/dist/segmented-control.js +1 -1
  367. package/dist/select-register.d.ts +5 -1
  368. package/dist/select-register.js +197 -2
  369. package/dist/select-register.js.map +1 -1
  370. package/dist/select.d.ts +87 -8
  371. package/dist/select.js +4 -1
  372. package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
  373. package/dist/sides-BPSWde0A.d.ts +4 -0
  374. package/dist/skeleton.d.ts +31 -0
  375. package/dist/skeleton.js +4 -0
  376. package/dist/skeleton.js.map +1 -0
  377. package/dist/spacer.d.ts +18 -0
  378. package/dist/spacer.js +4 -0
  379. package/dist/spacer.js.map +1 -0
  380. package/dist/split-view.d.ts +39 -7
  381. package/dist/split-view.js +120 -10
  382. package/dist/split-view.js.map +1 -1
  383. package/dist/state-banner.d.ts +19 -5
  384. package/dist/state-banner.js +4 -1
  385. package/dist/styles/app-tab.css +69 -0
  386. package/dist/styles/badge.css +118 -0
  387. package/dist/styles/catalog/components/catalog-example-stack.css +9 -0
  388. package/dist/styles/catalog/components/catalog-example.css +164 -0
  389. package/dist/styles/catalog/components/catalog-stage.css +55 -0
  390. package/dist/styles/catalog/components/catalog.css +12 -0
  391. package/dist/styles/catalog.css +5 -0
  392. package/dist/styles/collapsible-panel.css +575 -0
  393. package/dist/styles/content-item.css +30 -0
  394. package/dist/styles/document.css +23 -0
  395. package/dist/styles/floating-toolbar.css +119 -0
  396. package/dist/styles/foundation.css +178 -40
  397. package/dist/styles/grid.css +21 -0
  398. package/dist/styles/layout.css +3 -41
  399. package/dist/styles/list-action-row.css +257 -0
  400. package/dist/styles/list-header.css +228 -0
  401. package/dist/styles/list-inset-control.css +30 -0
  402. package/dist/styles/list-inset-text.css +29 -0
  403. package/dist/styles/list-item.css +240 -0
  404. package/dist/styles/list.css +133 -0
  405. package/dist/styles/nav-stack.css +158 -50
  406. package/dist/styles/pane.css +229 -0
  407. package/dist/styles/popup-menu.css +270 -0
  408. package/dist/styles/resizable-region.css +429 -4
  409. package/dist/styles/row.css +111 -0
  410. package/dist/styles/segmented-control.css +31 -8
  411. package/dist/styles/select.css +232 -9
  412. package/dist/styles/skeleton.css +92 -0
  413. package/dist/styles/spacer.css +11 -0
  414. package/dist/styles/split-view.css +108 -3
  415. package/dist/styles/state-banner.css +94 -19
  416. package/dist/styles/styles.css +13 -4
  417. package/dist/styles/sunken-panel.css +22 -0
  418. package/dist/styles/surface-scaffold.css +56 -0
  419. package/dist/styles/tab-bar.css +132 -9
  420. package/dist/styles/tab-scaffold.css +113 -3
  421. package/dist/styles/text.css +65 -0
  422. package/dist/styles/token-search-field.css +49 -43
  423. package/dist/styles/toolbar-control-group.css +465 -28
  424. package/dist/styles/toolbar-text.css +88 -4
  425. package/dist/styles/toolbar.css +319 -11
  426. package/dist/styles/value-table.css +34 -4
  427. package/dist/styles/webawesome.css +348 -10
  428. package/dist/styles/workbench.css +828 -7
  429. package/dist/sunken-panel.d.ts +22 -0
  430. package/dist/sunken-panel.js +3 -0
  431. package/dist/sunken-panel.js.map +1 -0
  432. package/dist/surface-scaffold.d.ts +30 -0
  433. package/dist/surface-scaffold.js +3 -0
  434. package/dist/surface-scaffold.js.map +1 -0
  435. package/dist/tab-bar.d.ts +31 -6
  436. package/dist/tab-bar.js +1 -1
  437. package/dist/tab-scaffold.d.ts +54 -7
  438. package/dist/tab-scaffold.js +82 -9
  439. package/dist/tab-scaffold.js.map +1 -1
  440. package/dist/text.d.ts +38 -0
  441. package/dist/text.js +3 -0
  442. package/dist/text.js.map +1 -0
  443. package/dist/token-search-field-CCke1yvQ.d.ts +126 -0
  444. package/dist/token-search-field.d.ts +3 -43
  445. package/dist/token-search-field.js +1 -1
  446. package/dist/token-search-model.d.ts +3 -0
  447. package/dist/token-search-model.js +3 -0
  448. package/dist/token-search-model.js.map +1 -0
  449. package/dist/toolbar-control-group.d.ts +65 -7
  450. package/dist/toolbar-control-group.js +1 -1
  451. package/dist/toolbar-text.d.ts +37 -4
  452. package/dist/toolbar-text.js +3 -1
  453. package/dist/toolbar.d.ts +56 -7
  454. package/dist/toolbar.js +1 -1
  455. package/dist/types-BuHELdXR.d.ts +117 -0
  456. package/dist/value-table.d.ts +13 -3
  457. package/dist/value-table.js +3 -1
  458. package/dist/webawesome.d.ts +8 -1
  459. package/dist/wire-catalog.d.ts +82 -0
  460. package/dist/wire-catalog.js +327 -0
  461. package/dist/wire-catalog.js.map +1 -0
  462. package/dist/wire-nav-stack.d.ts +4 -1
  463. package/dist/wire-nav-stack.js +147 -7
  464. package/dist/wire-nav-stack.js.map +1 -1
  465. package/dist/wire-resizable-regions.d.ts +2 -1
  466. package/dist/wire-resizable-regions.js +2 -1
  467. package/dist/wire-scroll-dividers.d.ts +69 -0
  468. package/dist/wire-scroll-dividers.js +3 -0
  469. package/dist/wire-scroll-dividers.js.map +1 -0
  470. package/dist/wire-sidebar.d.ts +63 -0
  471. package/dist/wire-sidebar.js +231 -0
  472. package/dist/wire-sidebar.js.map +1 -0
  473. package/dist/wire-tab-bars.d.ts +19 -2
  474. package/dist/wire-tab-bars.js +1 -1
  475. package/dist/wire-tab-scaffold.js +9 -4
  476. package/dist/wire-tab-scaffold.js.map +1 -1
  477. package/dist/wire-token-search-fields.d.ts +65 -3
  478. package/dist/wire-token-search-fields.js +1 -1
  479. package/dist/wire-workbench.d.ts +109 -0
  480. package/dist/wire-workbench.js +6 -0
  481. package/dist/wire-workbench.js.map +1 -0
  482. package/dist/workbench.d.ts +218 -7
  483. package/dist/workbench.js +13 -15
  484. package/dist/workbench.js.map +1 -1
  485. package/docs/accessibility.md +386 -22
  486. package/docs/app-layouts.md +106 -8
  487. package/docs/catalog.md +440 -0
  488. package/docs/collapsible-panel.md +363 -0
  489. package/docs/component-contract.md +555 -52
  490. package/docs/component-integration.md +75 -0
  491. package/docs/component-selection.md +359 -89
  492. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  493. package/docs/design/templates/empty-state/filtered.svg +1 -0
  494. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  495. package/docs/design/templates/empty-state/with-action.svg +1 -0
  496. package/docs/design/templates/empty-state-dark.svg +1 -0
  497. package/docs/design/templates/empty-state.svg +1 -0
  498. package/docs/design/templates/list/compact-dark.svg +1 -0
  499. package/docs/design/templates/list/compact.svg +1 -0
  500. package/docs/design/templates/list/stack-dark.svg +1 -0
  501. package/docs/design/templates/list/stack.svg +1 -0
  502. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  503. package/docs/design/templates/list-action-row/default.svg +1 -0
  504. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  505. package/docs/design/templates/list-action-row/selected.svg +1 -0
  506. package/docs/design/templates/list-action-row-dark.svg +1 -0
  507. package/docs/design/templates/list-action-row.svg +1 -0
  508. package/docs/design/templates/list-dark.svg +1 -0
  509. package/docs/design/templates/list-header/action-dark.svg +1 -0
  510. package/docs/design/templates/list-header/action.svg +1 -0
  511. package/docs/design/templates/list-header/count-dark.svg +1 -0
  512. package/docs/design/templates/list-header/count.svg +1 -0
  513. package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
  514. package/docs/design/templates/list-header/inline-action.svg +1 -0
  515. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  516. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  517. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  518. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  519. package/docs/design/templates/list-header-dark.svg +1 -0
  520. package/docs/design/templates/list-header.svg +1 -0
  521. package/docs/design/templates/list-item/default-dark.svg +1 -0
  522. package/docs/design/templates/list-item/default.svg +1 -0
  523. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  524. package/docs/design/templates/list-item/multiline.svg +1 -0
  525. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  526. package/docs/design/templates/list-item/selected.svg +1 -0
  527. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  528. package/docs/design/templates/list-item/trailing.svg +1 -0
  529. package/docs/design/templates/list-item-dark.svg +1 -0
  530. package/docs/design/templates/list-item.svg +1 -0
  531. package/docs/design/templates/list.svg +1 -0
  532. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  533. package/docs/design/templates/segmented-control/equal.svg +1 -0
  534. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  535. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  536. package/docs/design/templates/segmented-control-dark.svg +1 -0
  537. package/docs/design/templates/segmented-control.svg +1 -0
  538. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  539. package/docs/design/templates/skeleton/avatar.svg +1 -0
  540. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  541. package/docs/design/templates/skeleton/block.svg +1 -0
  542. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  543. package/docs/design/templates/skeleton/lines.svg +1 -0
  544. package/docs/design/templates/skeleton-dark.svg +1 -0
  545. package/docs/design/templates/skeleton.svg +1 -0
  546. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  547. package/docs/design/templates/state-banner/danger.svg +1 -0
  548. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  549. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  550. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  551. package/docs/design/templates/state-banner/info.svg +1 -0
  552. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  553. package/docs/design/templates/state-banner/success.svg +1 -0
  554. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  555. package/docs/design/templates/state-banner/warning.svg +1 -0
  556. package/docs/design/templates/state-banner-dark.svg +1 -0
  557. package/docs/design/templates/state-banner.svg +1 -0
  558. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  559. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  560. package/docs/design/templates/tab-bar-dark.svg +1 -0
  561. package/docs/design/templates/tab-bar.svg +1 -0
  562. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  563. package/docs/design/templates/token-search-field/empty.svg +1 -0
  564. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  565. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  566. package/docs/design/templates/token-search-field-dark.svg +1 -0
  567. package/docs/design/templates/token-search-field.svg +1 -0
  568. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  569. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  570. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  571. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  572. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  573. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  574. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  575. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  576. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  577. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  578. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  579. package/docs/design/templates/toolbar-control-group.svg +1 -0
  580. package/docs/design/templates/toolbar-dark.svg +1 -0
  581. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  582. package/docs/design/templates/toolbar-text/default.svg +1 -0
  583. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  584. package/docs/design/templates/toolbar-text/large.svg +1 -0
  585. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  586. package/docs/design/templates/toolbar-text/small.svg +1 -0
  587. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  588. package/docs/design/templates/toolbar-text.svg +1 -0
  589. package/docs/design/templates/toolbar.svg +1 -0
  590. package/docs/design/templates/value-table/details-dark.svg +1 -0
  591. package/docs/design/templates/value-table/details.svg +1 -0
  592. package/docs/design/templates/value-table-dark.svg +1 -0
  593. package/docs/design/templates/value-table.svg +1 -0
  594. package/docs/design/templates.md +138 -0
  595. package/docs/design-philosophy.md +48 -20
  596. package/docs/device-class.md +10 -9
  597. package/docs/document-baseline.md +38 -0
  598. package/docs/examples/application-ui-profile.json +60 -0
  599. package/docs/examples/command-palette-adapter.tsx +69 -17
  600. package/docs/examples/component-catalog-extension.json +45 -0
  601. package/docs/examples/component-composition-extension.json +80 -0
  602. package/docs/layout.md +398 -59
  603. package/docs/nav-stack.md +126 -14
  604. package/docs/recipes.md +137 -54
  605. package/docs/split-view.md +69 -6
  606. package/docs/sunken-panel.md +53 -0
  607. package/docs/surface-scaffold.md +25 -0
  608. package/docs/tab-scaffold.md +94 -10
  609. package/docs/type-contracts.md +134 -0
  610. package/docs/ui-analyzer.md +306 -0
  611. package/docs/ui-doctor.md +107 -0
  612. package/docs/ui-evaluator.md +139 -0
  613. package/docs/ux-demo.md +40 -16
  614. package/docs/webawesome-theme.md +143 -16
  615. package/docs/workbench.md +534 -12
  616. package/doctor/cli.mjs +115 -0
  617. package/doctor/config.schema.json +68 -0
  618. package/doctor/eslint-diagnostics.d.mts +24 -0
  619. package/doctor/eslint-diagnostics.mjs +24 -0
  620. package/doctor/index.d.mts +116 -0
  621. package/doctor/index.mjs +1277 -0
  622. package/doctor/report.schema.json +118 -0
  623. package/evaluator/cli.mjs +89 -0
  624. package/evaluator/index.d.mts +90 -0
  625. package/evaluator/index.mjs +1203 -0
  626. package/evaluator/report.schema.json +152 -0
  627. package/llms.txt +395 -42
  628. package/package.json +235 -37
  629. package/traversal-exclusions.mjs +27 -0
  630. package/ux-demo/recipes/app-shell.tsx +205 -17
  631. package/ux-demo/recipes/collapsible-sidebar.tsx +260 -0
  632. package/ux-demo/recipes/compact-toolbar.tsx +135 -14
  633. package/ux-demo/recipes/composer-form.tsx +133 -21
  634. package/ux-demo/recipes/list-detail-dialog.tsx +282 -0
  635. package/ux-demo/recipes/list-workspace-states.tsx +183 -11
  636. package/ux-demo/recipes/loaders.ts +15 -5
  637. package/ux-demo/recipes/loading-inspector.tsx +204 -0
  638. package/ux-demo/recipes/mount-recipe.ts +28 -10
  639. package/ux-demo/recipes/navigation-sidebar.tsx +139 -13
  640. package/ux-demo/recipes/navigation-stack.tsx +79 -24
  641. package/ux-demo/recipes/types.ts +23 -2
  642. package/ux-demo/recipes/workspace-header.tsx +86 -5
  643. package/dist/browser/menu-action-row.js +0 -3
  644. package/dist/browser/menu-header.js +0 -5
  645. package/dist/browser/menu-item.js +0 -3
  646. package/dist/chunk-24Z2XE6C.js +0 -10
  647. package/dist/chunk-24Z2XE6C.js.map +0 -1
  648. package/dist/chunk-2RJBFNB6.js +0 -172
  649. package/dist/chunk-2RJBFNB6.js.map +0 -1
  650. package/dist/chunk-55DATV3A.js +0 -10
  651. package/dist/chunk-55DATV3A.js.map +0 -1
  652. package/dist/chunk-56RJXPHG.js.map +0 -1
  653. package/dist/chunk-7BR3DO2A.js.map +0 -1
  654. package/dist/chunk-7LDCTU7D.js +0 -134
  655. package/dist/chunk-7LDCTU7D.js.map +0 -1
  656. package/dist/chunk-AQNKJRFH.js +0 -79
  657. package/dist/chunk-AQNKJRFH.js.map +0 -1
  658. package/dist/chunk-CTQKI2XL.js +0 -28
  659. package/dist/chunk-CTQKI2XL.js.map +0 -1
  660. package/dist/chunk-DAFFDO33.js +0 -14
  661. package/dist/chunk-DAFFDO33.js.map +0 -1
  662. package/dist/chunk-EGHL5S4P.js +0 -33
  663. package/dist/chunk-EGHL5S4P.js.map +0 -1
  664. package/dist/chunk-KZKSBUKC.js +0 -19
  665. package/dist/chunk-KZKSBUKC.js.map +0 -1
  666. package/dist/chunk-M5JRI6MN.js +0 -27
  667. package/dist/chunk-M5JRI6MN.js.map +0 -1
  668. package/dist/chunk-MBR3TYNL.js +0 -14
  669. package/dist/chunk-MBR3TYNL.js.map +0 -1
  670. package/dist/chunk-MFKMHK5W.js +0 -10
  671. package/dist/chunk-MFKMHK5W.js.map +0 -1
  672. package/dist/chunk-MQIZAD4A.js.map +0 -1
  673. package/dist/chunk-NPPD2A6O.js +0 -40
  674. package/dist/chunk-NPPD2A6O.js.map +0 -1
  675. package/dist/chunk-R6JHHBKQ.js +0 -19
  676. package/dist/chunk-R6JHHBKQ.js.map +0 -1
  677. package/dist/chunk-RBTVBGRD.js +0 -23
  678. package/dist/chunk-RBTVBGRD.js.map +0 -1
  679. package/dist/chunk-S3TPM2LP.js +0 -23
  680. package/dist/chunk-S3TPM2LP.js.map +0 -1
  681. package/dist/chunk-TRNBHPNW.js.map +0 -1
  682. package/dist/chunk-VHW5YNIB.js +0 -17
  683. package/dist/chunk-VHW5YNIB.js.map +0 -1
  684. package/dist/chunk-W2FK7ZNY.js.map +0 -1
  685. package/dist/chunk-WVPYVDXY.js +0 -15
  686. package/dist/chunk-WVPYVDXY.js.map +0 -1
  687. package/dist/chunk-XZXRLY5Q.js +0 -33
  688. package/dist/chunk-XZXRLY5Q.js.map +0 -1
  689. package/dist/menu-action-row.d.ts +0 -50
  690. package/dist/menu-action-row.js +0 -4
  691. package/dist/menu-action-row.js.map +0 -1
  692. package/dist/menu-header.d.ts +0 -41
  693. package/dist/menu-header.js +0 -6
  694. package/dist/menu-header.js.map +0 -1
  695. package/dist/menu-item.d.ts +0 -31
  696. package/dist/menu-item.js +0 -4
  697. package/dist/menu-item.js.map +0 -1
  698. package/dist/panel-header.d.ts +0 -26
  699. package/dist/panel-header.js +0 -6
  700. package/dist/panel-header.js.map +0 -1
  701. package/dist/styles/menu-action-row.css +0 -160
  702. package/dist/styles/menu-header.css +0 -160
  703. package/dist/styles/menu-item.css +0 -123
  704. package/dist/styles/panel-header.css +0 -59
  705. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
  706. package/ux-demo/recipes/recipes.css +0 -355
@@ -6,18 +6,205 @@ These are normative contracts for package components and consuming applications.
6
6
 
7
7
  - All controls have accessible names and visible `:focus-visible` treatment.
8
8
  - Decorative `LucideIcon` output is `aria-hidden`; pass `label` only when the SVG itself conveys meaning.
9
- - `MenuItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
10
- - `MenuHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
11
- - In any `.kui-pane`, menu rows, `MenuHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `MenuHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-menu-header-action-icon-size`; do not reduce the target to the visible icon.
9
+ - `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Selected rows keep the normal foreground over their brand-tinted fill so text retains WCAG AA contrast in light and dark themes. Its leading icon visual is 18px while the row retains its 44px minimum target; multiline icons align to the first text line. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
10
+ - `ListHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
11
+ - Dispose `wireTabBars` and `wireTokenSearchFields` with their owning view. Disposal cancels queued controlled-render focus restoration, so a torn-down view cannot reclaim focus from its successor.
12
+ - `wireNavStack` moves focus into every newly active top view and remembers the last focused descendant of each mounted view for pop restoration. Mark a preferred initial heading or control with `data-nav-focus` (and `tabindex="-1"` when it is not normally tabbable); otherwise the helper uses the first enabled focusable descendant, then a programmatically focusable view-container fallback.
13
+ - In any `.kui-pane`, menu rows, `ListHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `ListHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-list-header-action-icon-size` inside a fitted 36px hover square; a transparent hit layer extends the pointer target to at least 44 × 44 around that square without moving layout or enlarging the painted surface. Compact headers are 36px tall like the rest of the compact List family, so their action target is 44px wide and clamped to the 36px header height instead of reaching into a neighboring row. Do not reduce the target to the visible icon, and do not force the button itself to 44px.
12
14
  - `LoadingSpinner` is either labeled (`role="img"`) or hidden. Its rotation stops for reduced motion.
13
- - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure.
15
+ - An unlabeled `Skeleton` (including the value slots a component's `placeholder` prop renders) is decorative loading chrome: it is `aria-hidden`, conveys no information a user needs to act on, and is deliberately exempt from the WCAG 3:1 non-text-contrast target. Inside a solid `Badge` it stays subtle (about 2.1–4.4:1 in light, 2.4–2.8:1 in dark, depending on tone); `tests/browser/tone-contrast.spec.ts` holds those values as a regression floor rather than an accessibility threshold. Announce loading through a labeled `Skeleton`, `LoadingSpinner`, or `aria-busy` on the owning region, never through the skeleton's visibility.
16
+ - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure. Its optional `badge` is persistent inline status/count content beside the title and is announced as part of the banner; keep it terse and do not rely on color alone for its meaning.
14
17
  - `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
15
18
  - `Select` follows the Web Awesome host's standard `input`/`change` events. Application tests verify the live `value`, focus, and events—not attributes alone. Its decorative option icons and value-dependent selected content remain present after controlled rerenders, so the visible choice does not silently lose its non-text cue.
16
- - `DisclosureArrow` is an 18px root-scaled decorative visual by default, not an interaction target. Its owning native control supplies a stable accessible name, pointer and keyboard interaction, and `aria-expanded`; multiple arrows keep independently controlled state. Consumers may override `--kui-disclosure-arrow-size` without changing that ownership. Configured directions animate over the shortest path; a 180-degree closed-to-open tie uses counterclockwise rotation.
19
+ - `DisclosureArrow` is an 18px root-scaled decorative visual by default, not an interaction target. Its owning native control supplies a stable accessible name, pointer and keyboard interaction, and `aria-expanded`; multiple arrows keep independently controlled state. Consumers may resize it with the typed `size` prop without changing that ownership. Configured directions animate over the shortest path; a 180-degree closed-to-open tie uses counterclockwise rotation.
20
+ - `List` is layout-only and adds no `list` role. Children own their native or ARIA semantics. Give a scrollable List a bounded block size, avoid nested scroll owners, and keep focused children visible while scrolling.
17
21
 
18
- ## MenuActionRow
22
+ ## Select
19
23
 
20
- `MenuActionRow` renders a noninteractive visual root containing primary and
24
+ Supply a visible `label`, or `ariaLabel` when the surrounding interface already
25
+ provides visual context. A nonempty `label` takes precedence when both are
26
+ supplied. Kerf forwards that name through Web Awesome's internal label contract;
27
+ an `aria-label` on the custom-element host alone cannot name its shadow
28
+ combobox. With `ariaLabel` alone, Kerf visually hides the internal label without
29
+ adding height or spacing. This also applies to `renderSelected`: selected content
30
+ stays separate from the control's stable accessible name. Applications do not
31
+ need shadow-DOM patches or extra label styling.
32
+
33
+ Set `multiple` (with an array `value`) to let the person choose any number
34
+ of choices. The popup stays open while they toggle choices with the pointer,
35
+ Enter, or Space, and closes on an outside press, Escape, or focus leaving,
36
+ returning focus to the combobox. The listbox is `aria-multiselectable`, each
37
+ chosen option shows its check, and the closed control reads the chosen labels
38
+ in choice order as one short localized list (the `placeholderText` when none
39
+ are chosen) instead of a removable tag per choice. `change` and `input` report
40
+ the value array; in a form, every chosen value is submitted under `name`.
41
+
42
+ A toolbar filter menu (a "Filter by label" funnel) is a multiple Select with
43
+ `selectedPresentation="icon-only"` and a fixed `triggerIcon`; the types accept
44
+ `triggerIcon` only there, and require it there, because no single chosen
45
+ choice has an icon to show. The trigger keeps the icon-only pill geometry
46
+ below: the fixed icon, then, while any choice is chosen, a compact brand
47
+ `Badge` with the count 4px after it, then the caret. The badge is hidden from
48
+ assistive technology; instead the combobox's accessible name ends with the
49
+ chosen labels in choice order ("Filter by label: Bug, Docs", just "Filter by
50
+ label" when none are chosen), and its hidden value text stays empty so screen
51
+ readers do not announce the list twice. With a visible `label` the same summary
52
+ reads in the label, standing in for the hidden value.
53
+
54
+ Use `presentation="toolbar-borderless"` inside a `ToolbarControlGroup`, with
55
+ `size="compact"` in a compact group; set `focusRingOwner="group"` when that
56
+ parent paints the composed focus ring. `selectedPresentation="icon-only"` hides
57
+ only the visible selected label—the required `label` or `ariaLabel` still names
58
+ the combobox. An icon-only trigger keeps its disclosure caret: the selected icon
59
+ and caret form one pill with the same geometry as a `PopupMenu`
60
+ trigger (8px inline padding on both sides, 12px from the icon to the caret, a
61
+ 40px default or 32px compact control height), so the caret still signals a
62
+ choice list. Inside a group the trigger is inset evenly on every side (2px in a
63
+ default group, 1px in a compact one) with a concentric radius; the group grows
64
+ to the trigger's width, so a single group is wider than it is tall and its
65
+ focus ring follows the pill while focused. While the listbox is open the
66
+ trigger drops its ring, as a `PopupMenu` trigger does: the open popup and its
67
+ current option show where focus is, and the ring returns when the listbox
68
+ closes with focus still on the control. A group that owns the ring
69
+ (`focusRing="outline"` or `"halo"`) drops it the same way while a Select
70
+ listbox or `PopupMenu` inside the group is open.
71
+ Beside other controls in one group, the icon-only trigger is one more segment:
72
+ it keeps the group's gap and its siblings' block inset, height, item radius,
73
+ and hover pill. In a group with per-control rings (`focusRing="control"`, the
74
+ default) it paints the same ring as its sibling buttons, even when
75
+ `focusRingOwner="group"` was set, because no group ring would show focus.
76
+ Navigation selects can use intrinsic `presentation="navigation"` plus
77
+ `labelMaxWidth` for component-owned ellipsis. These props own the control's
78
+ appearance; its parent continues to own outer placement.
79
+
80
+ Use `hint` for persistent supporting text below the control; use
81
+ `placeholderText` only for the empty value shown inside the closed control.
82
+ Kerf passes hint text through Web Awesome's form-control contract, which renders
83
+ the hint part and connects the shadow combobox to it with `aria-describedby`.
84
+ The accessible control is that shadow combobox, not the `wa-select` wrapper;
85
+ inspect or test the element returned by the `combobox` role. Both the `hint`
86
+ attribute used by Kerf and Web Awesome's explicit `hint` slot produce a computed
87
+ accessible description in Chromium, Firefox, and WebKit, so applications do not
88
+ need an `aria-description` mirror or a shadow-DOM patch.
89
+ Loading placeholders keep the same visible hint while replacing the interactive
90
+ control with inert chrome.
91
+
92
+ `tests/unit/components.test.tsx` covers name projection and visible/hidden label
93
+ variants. `tests/browser/select-accessibility.spec.ts` verifies actual accessible
94
+ names, unchanged unlabeled geometry, keyboard selection, controlled rerenders,
95
+ native hint wiring, and wide/narrow presentation in Chromium, Firefox, and WebKit.
96
+
97
+ The explicit `@kerfjs/ui/select/register` import also installs the canonical
98
+ Select lifecycle adapter. The latest open/close request owns animation completion
99
+ and deferred option focus. A stale close cannot hide a reopened popup after
100
+ resize; stale completion events are not emitted. Preventing `wa-show` or
101
+ `wa-hide` retains the accepted closed/open state, and removal prevents deferred
102
+ activation. Native option selection, keyboard behavior, dismissal listeners,
103
+ and popup anchor placement remain with Web Awesome. Raw `wa-select` elements
104
+ without the canonical `data-component="select"` marker are unchanged.
105
+ `tests/unit/select-lifecycle.test.ts` and `animate-select-popup.test.ts` cover
106
+ repeated and out-of-order completions, cancellation, instance isolation, and
107
+ removal. `tests/browser/select-lifecycle.spec.ts` repeats animation/resize/reopen
108
+ sequences at wide and narrow widths in Chromium, Firefox, and WebKit.
109
+
110
+ ### Help tags on icon-only triggers
111
+
112
+ An icon-only control has no visible title, so sighted pointer and keyboard users
113
+ would otherwise see only its icon (and a filter's count). Following the Apple
114
+ HIG convention for toolbar items, three kinds of icon-only control show a help
115
+ tag: a Web Awesome `wa-tooltip` in the theme's tooltip palette, below the
116
+ control.
117
+
118
+ - an icon-only `Select` (`selectedPresentation="icon-only"`, single or
119
+ `multiple`);
120
+ - an icon-only `PopupMenu` (named by `label` rather than visible `text`);
121
+ - an icon-only `<button>` or link (`<a href>`) inside a `ToolbarControlGroup`,
122
+ named by a non-empty `aria-label`, with no visible text, no native `title`,
123
+ and no Web Awesome tooltip `for` it.
124
+
125
+ Tags are on by default and take no props. One document-level installer covers
126
+ all three kinds, and any of `@kerfjs/ui/select/register`,
127
+ `@kerfjs/ui/popup-menu/register`, or `@kerfjs/ui/help-tags/register` installs
128
+ it (once per document, however many are imported). An application whose
129
+ toolbars have icon-only buttons but no registered `Select` or `PopupMenu`
130
+ imports `@kerfjs/ui/help-tags/register`, which registers only the tooltip
131
+ element. Pure `SafeHtml` rendering stays zero-JS: without an installer, nothing
132
+ shows a tag.
133
+
134
+ - **Text.** The tag says what the accessible name says: a filter's name and
135
+ chosen labels ("Filter by label: Bug, Docs", or "Filter by label" when none
136
+ are chosen), a single Select's name and current choice ("Rendering balance:
137
+ Balanced"), a menu's `label` ("Sort tickets"), or a toolbar button's
138
+ `aria-label` ("Pin view"). It updates with the selection.
139
+ - **Showing.** It appears after the pointer rests on the control for 500ms, at
140
+ once when a neighboring tag closed moments ago (so scanning along a toolbar
141
+ stays quick, across buttons, Selects, and menus alike), and immediately on
142
+ keyboard focus (`:focus-visible`). A pointer press that focuses the control
143
+ and touch input never show it.
144
+ - **Hiding.** It hides when the pointer and keyboard focus have both left the
145
+ control, on a press, on Escape, and whenever a popup opens. It does not come
146
+ back when the popup closes and focus returns to the trigger, or while the
147
+ pointer stays on a pressed button; it returns after the pointer moves onto
148
+ something else or focus leaves and comes back.
149
+ - **Assistive technology.** The tag is `aria-hidden` and never joins the
150
+ control's `aria-labelledby` or `aria-describedby` (kerf anchors it directly
151
+ rather than through `for`), so the accessible name is unchanged and announced
152
+ once; the accessibility tree is identical with the tag open or closed.
153
+ - **Isolation.** A Select's or PopupMenu's tag lives in the control's own
154
+ shadow root, so the morph never touches it and light-DOM `:has([open])`
155
+ selectors do not see it. A plain button cannot host a shadow root, so its tag
156
+ lives in one `aria-hidden`, `data-morph-skip` layer at the end of `<body>`
157
+ (fixed and zero-sized, so it never joins a grid or flex body's flow, and
158
+ re-attached if a body re-render drops it) and leaves that layer once hidden.
159
+ It copies the button's `color-scheme`, so it matches a subtree theme, and its
160
+ popup renders in the browser's top layer, so it shows above a modal dialog
161
+ that holds the button. Tags' `wa-show` / `wa-hide` lifecycle events stop at
162
+ the tag, so a listener on the control (or a delegated one above it) hears only
163
+ the popup; a document capture-phase listener still observes them. A long tag
164
+ wraps at the tooltip's maximum width and keeps 8px clear of the viewport
165
+ edge.
166
+
167
+ A labeled Select, a `PopupMenu` with visible `text`, and a toolbar button with
168
+ visible text get no tag: their visible text already names them. A button that
169
+ carries a native `title`, or that the application has already given a
170
+ `wa-tooltip` `for` its `id`, keeps that and gets no second tag, so nothing is
171
+ shown twice. Zero-JS components that name an icon control with a native `title`
172
+ (the `ListHeader` action, the `AppTab` close button, the `TokenSearchField`
173
+ reveal and clear buttons, and `SegmentedControl` choice titles) keep the
174
+ browser's own tooltip, which needs no script; the installer deliberately skips
175
+ them rather than stacking a second tag on the first. Icon-only buttons outside
176
+ a `ToolbarControlGroup` are application markup and keep whatever naming the
177
+ application gives them.
178
+ `tests/unit/help-tag.test.ts` walks the hover, focus, press, Escape, popup, and
179
+ warm-window transitions for every kind; `tests/browser/select-help-tag.spec.ts`
180
+ and `tests/browser/toolbar-help-tag.spec.ts` check the real elements in
181
+ Chromium.
182
+
183
+ ## PopupMenu
184
+
185
+ `PopupMenu` renders a Web Awesome dropdown, so the menu roles, arrow-key and
186
+ typeahead navigation, Escape, and focus return to the trigger come from
187
+ `wa-dropdown`. Kerf's part is the trigger's name. A Web Awesome button takes its
188
+ name from its content and ignores an `aria-label` on the host, so an icon-only
189
+ trigger requires `label`, which renders as visually hidden text inside the
190
+ button; a trigger with visible `text` is named by that text. An icon-only
191
+ trigger also shows its `label` as an `aria-hidden` help tag on hover and
192
+ keyboard focus (see [Help tags on icon-only triggers](#help-tags-on-icon-only-triggers)). Group headings
193
+ render as the same uppercase group titles a grouped `Select` shows, and dividers
194
+ are presentational. A keyboard-focused item is the menu's current row, painted
195
+ with the Select's current-option fill rather than a second focus ring inside the
196
+ open popup. A disabled item stays in the menu but cannot be
197
+ chosen; disable the whole trigger only when every command is unavailable.
198
+ Nested items use Web Awesome's submenu pointer and Left/Right arrow routing;
199
+ checked items expose checkbox menu semantics. `details` supplies optional
200
+ trailing visual content without changing a command's accessible label. A
201
+ `context` menu's invisible anchor is outside the accessibility tree and normal
202
+ Tab order; opening the menu moves focus into its commands and Escape dismisses
203
+ it. Keep a separate visible row action named even when it is disabled.
204
+
205
+ ## ListActionRow
206
+
207
+ `ListActionRow` renders a noninteractive visual root containing primary and
21
208
  trailing native buttons as siblings. The primary comes first in DOM and Tab
22
209
  order and alone receives `aria-current` or `aria-pressed`; the trailing button
23
210
  has its own required accessible name and disabled state. Both controls carry
@@ -25,37 +212,119 @@ the row's item id for application delegation, while the root deliberately has
25
212
  no action or role. `label`, `icon`, and `trailingActionIcon` are dormant visual
26
213
  content and must not contain buttons, links, interactive roles, or other
27
214
  controls. A trailing click, double click, context menu, Enter, or Space
28
- must never activate the primary control. The application owns controlled
215
+ must never activate the primary control. Selected rows keep the normal
216
+ foreground over their brand-tinted fill so text retains WCAG AA contrast in
217
+ light and dark themes. The application owns controlled
29
218
  selection and the lifecycle, focus, and dismissal policy of any related
30
219
  popover or context menu.
31
220
 
32
221
  ## ResizableRegion
33
222
 
223
+ Set `responsiveFillAt="narrow"` or `"compact"` when a responsive composition
224
+ shows the region as its only inline track. The component then owns its
225
+ full-width geometry and removes its dormant separator at that container
226
+ breakpoint; the application owns which pane is visible.
227
+
34
228
  The handle exposes separator role, orientation, name, minimum, maximum, and current values. `wireResizableRegions()` adds:
35
229
 
36
230
  - the axis arrow keys in 16 px steps by default;
37
231
  - Shift+Arrow in 64 px steps;
38
232
  - Home and End for minimum and maximum;
39
233
  - primary-pointer drag with clamping;
40
- - preview callbacks while dragging and one commit callback on release.
234
+ - a visible maximum: when a parent clamps the track below the requested size
235
+ (a `max-width` stage, a narrow container), pointer and keyboard resizing stop
236
+ at the size actually shown, and the handle announces that size in
237
+ `aria-valuenow` and that bound in `aria-valuemax`. Focusing the handle
238
+ reports a stale larger committed size as the shown size without committing
239
+ it, so the first key press moves the visible separator. The reported values
240
+ also stay current at rest: a `ResizeObserver` on each region and its parent
241
+ re-clamps them when the space changes, and a `MutationObserver` re-clamps
242
+ them after a re-render writes the rendered props back, so assistive
243
+ technology never reads an unclamped size between interactions (the
244
+ disposer disconnects both observers);
245
+ - a truthful value below the minimum: when the container shows the track at
246
+ less than `min` (a narrowing `Workbench` squeezes its resizable rails), the
247
+ handle reports that shown size in `aria-valuenow` and pins `aria-valuemin`
248
+ and `aria-valuemax` to it — WAI-ARIA requires the value to lie inside the
249
+ range, and the separator cannot move a track its container holds. A pinned
250
+ separator — squeezed, or with a reachable range collapsed to one size —
251
+ commits nothing: arrow keys, Home/End, and drags leave the app's size (and
252
+ its storage) as it was, so the track returns to it, with the rendered range,
253
+ once it has room;
254
+ - a whole hit target and focus ring at a clamped edge: when the separator has
255
+ less room past it than the handle's 10px overhang (a parent clamps the track
256
+ to its own edge), the region carries `data-handle-inset` and the handle sits
257
+ wholly inside it, so a clipping ancestor cannot cut off half the handle or
258
+ its focus ring. The ring is drawn inside the handle's box, so the region's
259
+ ends meeting a clipping edge never shave it either;
260
+ - preview callbacks while dragging and one commit callback on release;
261
+ - a live size that also resizes slide-motion content, so the content tracks the
262
+ separator during a drag or key press instead of waiting for the app to
263
+ re-render the committed size.
41
264
 
42
265
  The application owns persistence and collapsed/expanded policy. Keep the last expanded size outside the component and restore it when reopening. An optional `handleIcon` replaces only decorative dormant content; it must not contain controls or interactive roles because the separator remains the sole focus and interaction owner.
43
266
 
44
- ## PanelHeader
267
+ ## Toolbar headings
268
+
269
+ For headings and paragraphs outside toolbar identity/title zones, use `Text`.
270
+ It renders the selected native `h1`–`h6` or `p` element, so heading navigation
271
+ follows the variant directly; choose a level that preserves the document
272
+ outline. Native global, `data-*`, and `aria-*` attributes pass through.
45
273
 
46
- `PanelHeader` is a plain `Toolbar` heading. Its leading zone holds an optional
47
- icon (a normal bordered `ToolbarControlGroup`) and the title as extra-large
48
- `ToolbarText`, and the app's trailing controls go straight into the trailing
49
- zone. The title carries no native heading role, so the application connects
50
- `titleId` and an optional `summaryId` to the owning dialog or panel through
51
- `aria-labelledby` and `aria-describedby` (and provides a document heading
52
- separately when one is required). Pass the trailing controls as a labeled
53
- `ToolbarControlGroup` when that group needs an accessible name.
274
+ Compose panel, dialog, and page headings as a plain `Toolbar`. Its leading zone
275
+ holds an optional icon `ToolbarControlGroup` and a direct extra-large
276
+ `ToolbarText`; controls belong in a trailing `ToolbarControlGroup`. By default
277
+ the title carries no native heading role, so for a **dialog or panel** the
278
+ application connects the title id and optional supporting-copy id to the owning
279
+ host through `aria-labelledby` and `aria-describedby`. For a **page or view**
280
+ title, pass `headingLevel` (usually `1`) to `ToolbarText`: it then exposes
281
+ `role="heading"` with a matching `aria-level`, giving the view a heading landmark
282
+ so screen-reader heading navigation and "main heading" semantics work — the same
283
+ role/level pair used throughout Kerf UI. Keep the levels meaningful and
284
+ non-skipping within a view. Pass trailing
285
+ controls as a labeled `ToolbarControlGroup` when that group needs an accessible
286
+ name. Keep supporting copy as app-owned content below the toolbar.
287
+
288
+ ## FloatingToolbar
289
+
290
+ `FloatingToolbar` is a `role="toolbar"` region with a **required** accessible
291
+ `label`; it floats over the content of its nearest positioned ancestor and is
292
+ forced to a dark color scheme, but it is **not** in the top layer, so it never
293
+ covers dialogs, popovers, or other overlays. Its children are the app's controls
294
+ (normally `ToolbarControlGroup`s), which keep their own names, focus, and
295
+ keyboard behavior; the application owns their actions, the toolbar's visibility,
296
+ and — via `position` and the typed `inset` prop — where it sits. Inside a `Workbench`, `CollapsiblePanel`, or
297
+ `ResizableRegion` `restoreControl` the restore corner owns the inset, so the
298
+ toolbar floats from that corner. While one of those layouts has a side overlay
299
+ open over the toolbar's region (a Workbench rail overlay, a `wireSidebar`
300
+ compact overlay, an overlay `CollapsiblePanel` or horizontal
301
+ `ResizableRegion`), the toolbar is hidden (`visibility: hidden`), so it is
302
+ unfocusable and out of the accessibility tree rather than a control stranded
303
+ behind the overlay's focus trap; it returns when the overlay closes. The
304
+ overlay's own floating toolbars stay.
54
305
 
55
306
  ## Tabs
56
307
 
57
308
  `AppTab` renders one controlled tab. `TabBar` supplies the containing tab list,
58
- fixed leading/trailing regions, and a horizontally scrollable strip.
309
+ fixed leading/trailing/end regions, and a horizontally scrollable strip.
310
+ Use `TabBar.presentation` for rail, segmented, or inspector chrome,
311
+ `allocation="fill"` when peers should divide the strip, and
312
+ `trailingPlacement="adjacent"` when an action belongs beside the final tab.
313
+ When that local action must coexist with a workspace-level action, put the
314
+ workspace action in `end`; TabBar keeps the adjacent action immediately after
315
+ the shrinkable/scrolling tabs and pins `end` at the far edge. Leading,
316
+ trailing, and end actions remain visible and do not shrink. The component owns
317
+ that geometry; applications must not override its anatomy to create the split.
318
+ `AppTab` provides compact 32px, segmented, truncating-label, and icon-only
319
+ presentations. Icon-only tabs keep the required `name` as the tab button's
320
+ accessible name while visually hiding the duplicate label. A `pending` tab (known,
321
+ still opening) keeps its visible name as the tab button's accessible name, so a
322
+ trailing spinner's own label is not folded into it. It is `aria-busy` and stays
323
+ selectable (in the roving tab order like a live tab), so the application can
324
+ show placeholder content in its panel until loading completes; its close
325
+ button is disabled and hidden and it is not draggable. A `placeholder` tab
326
+ (unknown) is instead disabled, out of the tab order, and not draggable. These props own
327
+ component appearance only; the application still owns the bar's outer placement.
59
328
  `AppTab.rootAttributes` accepts application `data-*` metadata only; runtime
60
329
  filtering rejects roles plus case variants of component- or wiring-owned
61
330
  action, identity, selection, drag, drop, and component attributes. An optional
@@ -65,8 +334,21 @@ and must not contain interactive descendants.
65
334
  activation, same-bar pointer reordering, `Alt+Shift+ArrowLeft/ArrowRight`
66
335
  reordering, focus restoration, scroll-into-view, and pointer-proximity
67
336
  autoscroll at either horizontal edge. Edge autoscroll is direct manipulation,
68
- stops on drop/drag end/disposal, and does not change keyboard behavior. It
69
- returns a disposer.
337
+ stops on drop/drag end/disposal, and does not change keyboard behavior. When
338
+ the application changes a bar's selected tab without focus (adding and
339
+ selecting a tab), the wiring scrolls that tab's strip — never the page — so
340
+ the new selection is fully visible; a re-render that keeps the same selection
341
+ leaves a strip the user scrolled where it is. It returns a disposer.
342
+
343
+ Arrow / Home / End follow the ARIA Tabs **automatic-activation** pattern by
344
+ default: they move roving focus and select the focused tab. If selection synchronously
345
+ replaces the strip, focus returns to the replacement with the same bar and tab IDs. Pass
346
+ `activation: 'manual'` (or set `data-tab-activation="manual"` on a strip via the
347
+ `TabBar` `activation` prop, which overrides the option per bar) for
348
+ **manual activation**: arrow keys move roving focus only and the user selects
349
+ with Enter / Space (native on the tab button) or click. Use manual activation
350
+ when selecting a tab is a heavy or side-effecting action — e.g. a tab that loads
351
+ a project — so arrowing through the strip does not trigger it on every tab.
70
352
 
71
353
  The application owns the ordered tab array, selection, panels, close policy,
72
354
  routing, and persistence. On `onReorder`, synchronously render the reported
@@ -97,7 +379,12 @@ and `placeTokenSearchCaret()` to restore a text caret without landing inside a
97
379
  chip. Call `wireTokenSearchFields()` once at a stable root so Enter submits
98
380
  without inserting a contenteditable line break and keyboard chip deletion
99
381
  restores focus plus the text-relative caret after controlled rendering replaces
100
- the editor; the editor still wraps text visually at its inline edge. Editable text is DOM-owned between token changes; a clear handler empties
382
+ the editor. Restoration finishes after synchronous input listeners and before
383
+ another keystroke; it does not wait for an animation frame that could overwrite
384
+ a later selection. Select All + Backspace/Delete shares that replacement path
385
+ and keeps the adopted expanded signal open while managed focus is enabled.
386
+ Real outside focus, empty Escape, disposal, and removed fields retain their usual
387
+ behavior. The editor still wraps text visually at its inline edge. Editable text is DOM-owned between token changes; a clear handler empties
101
388
  the editor's `textContent` before updating application state. Leading and
102
389
  trailing controls share the first text line's fixed vertical center and remain
103
390
  there as the editor wraps. In `collapsible` mode, the closed state is one named
@@ -108,6 +395,83 @@ trigger into the revealed editor, and clear `expanded` only after focus leaves
108
395
  the complete component. Text or tokens keep the field expanded even when that
109
396
  transient state is false. Width animation is disabled under reduced motion.
110
397
 
398
+ When the field is composed with a caller-owned surface rendered outside it — a
399
+ suggestions listbox, date picker, or help popover — mark that surface (or wrap
400
+ it) with `data-token-search-keep-open`, or pass `collapsible.keepOpenOn(target)`,
401
+ so focus or pointer activation moving into it does not collapse an empty field.
402
+ The helper preserves the pointer target when a browser omits
403
+ `focusout.relatedTarget`, preventing the field or target from disappearing
404
+ between pointerdown and click. `wireTokenSearchFields`
405
+ can also, opt-in via `keyboard`, own atomic-chip editing keys: from a collapsed
406
+ caret with no selection, Backspace removes the token before the caret and Delete
407
+ the token after it (reported through `onRemoveToken` for the app to apply to its
408
+ controlled state), and ArrowRight moves the caret past a trailing chip so typed
409
+ text lands after it. An optional `onEdit({ id, editor, event })` — with the
410
+ originating `InputEvent` so a caller can gate on `inputType`/`data` — fires on every editor
411
+ `input`, letting a caller drop its own `input` listener; the application still
412
+ owns query parsing and result-count/loading announcements.
413
+
414
+ With a `createTokenSearchModel` passed to the field and registered in
415
+ `wireTokenSearchFields(root, { models })`, grammar parsing, chips, clear/edit/
416
+ remove actions, and suggestions are managed. Arrow Down moves from the editor
417
+ to suggestions, arrow keys move between suggestions, Enter selects one, and
418
+ Escape returns focus to the editor. The application still announces results
419
+ when needed and can supply an `evaluate` callback to compute them. Omitting the
420
+ model leaves the manual behavior above intact.
421
+
422
+ Managed clear captures the action before application handlers run, keeps the adopted
423
+ expanded signal open during editor replacement, and restores focus at the actual
424
+ mutation checkpoint before the next input task. It also returns keyboard activation
425
+ from the clear button to a surviving editor immediately. No animation frame owns
426
+ clear focus, so fast typing cannot escape to page shortcuts or lose its first letter. The app still owns clearing query/tokens and emptying
427
+ DOM-owned text. This focus step respects `manageFocus: false`, disposal, removed fields,
428
+ and focus deliberately moved to another control; no app-level reopen callback is needed.
429
+
430
+ ## CollapsiblePanel / sidebar
431
+
432
+ `CollapsiblePanel` is a labeled `aside` region that renders `inert` and
433
+ `aria-hidden` while collapsed, straight from the app's `collapsed` flag, so its
434
+ controls leave the Tab order and the accessibility tree even while the content
435
+ is still sliding out, and focusing one can never scroll clipped content back into
436
+ view (`aria-hidden` alone left them focusable inside a hidden subtree). Its
437
+ content slides via transform (disabled under reduced motion); `inert` does not
438
+ affect rendering, so the slide still plays. A `restoreControl` renders outside
439
+ the panel and stays reachable. A collapsed `ResizableRegion` renders `inert` and
440
+ `aria-hidden` on the region itself the same way (its content wrapper is `inert`
441
+ too, and its separator is already hidden and not displayed), so no empty
442
+ labeled region landmark stays behind; its `restoreControl` stays outside the
443
+ region.
444
+ `CollapsiblePanelToggle` is a named button carrying `aria-expanded` and the
445
+ standard per-side collapse/expand glyph (`PanelLeft*` / `PanelRight*` /
446
+ `PanelBottom*`); place a collapse toggle inside the panel and an expand toggle in
447
+ an always-visible location so it is reachable while collapsed. `wireSidebar()`
448
+ moves focus into the panel when it opens and restores it to the trigger when it
449
+ closes; a trigger inside the now-inert panel hands focus to the panel's toggle
450
+ outside it instead (and focus stranded there by a wide → compact crossing is
451
+ rescued the same way), so an app may render the expand toggle only while the panel
452
+ is collapsed. When a `deviceClass()` reports `compact`, an open panel becomes an overlay
453
+ with a dismissable backdrop, Escape and backdrop-click collapse it, and Tab is
454
+ trapped within it (the ARIA dialog pattern). That modal state only ever begins
455
+ from a user action: at wire-up on a compact device and on a wide → compact
456
+ crossing, every panel starts collapsed, so no page load or resize traps focus. The application owns each `collapsed`
457
+ signal, the panels, their sizes, and content; the wire may persist the collapsed
458
+ state per panel.
459
+
460
+ ## Workbench
461
+
462
+ Each `Workbench` rail and drawer is a labeled region (`aside` for a rail, `section`
463
+ for the drawer). While a panel is collapsed the region itself renders `inert` and
464
+ `aria-hidden="true"`, and so does not linger as an empty landmark, and its content
465
+ wrapper is `inert`, all rendered from the app's `collapsed` flag, so its controls
466
+ leave the Tab order and the accessibility tree even while the content is still
467
+ sliding out, and focusing one can never scroll clipped content back into view. The panel's `restoreControl` renders
468
+ outside that content and stays reachable; a resizable panel's separator also leaves
469
+ the Tab order while collapsed or not inline. `wireWorkbench`, given a panel's
470
+ `collapsed` signal, returns focus stranded in a closing panel to the control that
471
+ opened it, else the restore control, else a control whose `aria-controls` names the
472
+ panel; an open overlay panel takes focus and keeps Tab inside it (the ARIA dialog
473
+ pattern). See [`workbench.md`](workbench.md).
474
+
111
475
  ## Verification matrix
112
476
 
113
477
  For each changed component, inspect default, hover, focus, disabled, selected/pressed, busy/error, long-content, wide, narrow, light, dark, increased-contrast, reduced-motion, keyboard-only, and 200%-zoom states where applicable. DOM order must match reading and focus order, with no clipping or unreachable action.
@@ -10,20 +10,101 @@ changes. The layouts:
10
10
  - [`SplitView`](split-view.md) — list-detail (two panes, collapsing to a stack).
11
11
  - [`Workbench`](workbench.md) — the Xcode-like collapsible rails + drawer.
12
12
  - [`TabScaffold`](tab-scaffold.md) — the iOS bottom tab bar (each tab a stack).
13
+ - [`CollapsiblePanel` + `wireSidebar`](collapsible-panel.md) — a standalone
14
+ collapsible rail or bottom drawer (with `CollapsiblePanelToggle`), outside a full
15
+ shell: the standard collapse animation, icon convention, and `wireSidebar`
16
+ semantics (focus, compact overlay, persistence).
13
17
 
14
18
  Derive responsiveness from `deviceClass()`: `compact` (a handset or portrait
15
19
  tablet) means "one pane at a time"; `atLeast('tablet')` / `atLeast('desktop')`
16
20
  gate the roomier presentations.
17
21
 
22
+ Full-height shells retain `height: 100%` so they remain embeddable. Give a
23
+ top-level shell a definite height chain by importing `@kerfjs/ui/document.css`
24
+ and applying `.kui-app-root` to the direct mount container; see the
25
+ [document baseline](document-baseline.md). Do not replace an embeddable shell's
26
+ height with `100dvh`.
27
+
28
+ ## Safe areas
29
+
30
+ On a device with unsafe areas (a notch, rounded corners, a home indicator) and a
31
+ page that opts into them with `<meta name="viewport" content="…, viewport-fit=cover">`,
32
+ the layouts handle the insets for you. There is nothing to configure:
33
+
34
+ - **Surfaces paint edge to edge.** Pane and panel backgrounds, separators, and
35
+ dividers run through the unsafe area. A collapsible rail or drawer grows by
36
+ the inset it reaches, so its content keeps its configured width or height.
37
+ - **Content is padded on the sides it touches.** Inline insets pad the sides a
38
+ region actually reaches. Block insets become padding _inside_ the scroll
39
+ container (with matching `scroll-padding`), so content scrolls under the
40
+ status bar or home indicator but its first and last items can always be
41
+ scrolled clear.
42
+ - **Interior edges get nothing.** A `Workbench` center next to an expanded rail,
43
+ a `SplitView` detail beside its list, or a `NavStack` view under its chrome is
44
+ not inset on that edge. Collapse the rail and the center picks up the edge.
45
+ - **No double inset.** Whichever region applies an inset clears it for its
46
+ descendants. A layout region whose only child is a `Pane` or another layout
47
+ lets that child own the insets. That child fills the region — a `Workbench`
48
+ area or panel, a `NavStack` view, a `TabScaffold` scene — so a sole `Pane`'s
49
+ header stays pinned while its own content scrolls. A `Pane` header or footer whose only child is
50
+ a `Toolbar` hands the inline insets to the toolbar, so the toolbar's dividers
51
+ still reach the edge.
52
+
53
+ A plain `Pane` follows the same rules. It assumes it may touch every screen edge
54
+ unless its layout says otherwise. When your own markup puts panes side by side,
55
+ say which sides each pane reaches with `safeAreaEdges`:
56
+
57
+ ```tsx
58
+ <div class="app-columns">
59
+ <Pane label="Library" safeAreaEdges={['block-start', 'block-end', 'inline-start']}>…</Pane>
60
+ <Pane label="Reader" safeAreaEdges={['block-start', 'block-end', 'inline-end']}>…</Pane>
61
+ </div>
62
+ ```
63
+
64
+ An app bar or bottom bar that sits directly at a screen edge, outside any
65
+ `Pane` header or footer, claims its edges the same way. A `Toolbar` only pads
66
+ the inline edges an owner hands it until you list the sides it reaches:
67
+
68
+ ```tsx
69
+ <List fill>
70
+ <Toolbar
71
+ label="App bar"
72
+ safeAreaEdges={['block-start', 'inline-start', 'inline-end']}
73
+ leading={<ToolbarText text="Atlas" />}
74
+ />
75
+ <Row gap="none" flex>…panes that list block-end and their outer side…</Row>
76
+ </List>
77
+ ```
78
+
79
+ Each claimed side pads by the edge context, or the full device inset when
80
+ nothing routes that edge, while the bar's surface and divider still reach the
81
+ screen edge. A claimed toolbar inside a `Pane` header or footer adds nothing
82
+ extra, because the pane already owns those edges.
83
+
84
+ Pass `safeAreaEdges={[]}` for a pane that never sits at a screen edge. In an
85
+ arrangement you own, give each `Pane` (and each edge-claiming `Toolbar`)
86
+ `safeAreaEdges` listing only the sides its region reaches. The toolbar
87
+ configurations layouts accept (a Workbench `mainToolbar` / `mainBottomToolbar`,
88
+ and a Workbench panel's or `CollapsiblePanel`'s `toolbar`) forward
89
+ `safeAreaEdges` to their `Toolbar` the same way.
90
+ A `CollapsiblePanel` clears the edge it covers for its direct flex siblings
91
+ automatically; a panel wrapped in your own grid cell cannot see its siblings,
92
+ so pass the neighboring panes' `safeAreaEdges` yourself.
93
+
94
+ The device insets come from `--kui-safe-area-block-start`, `-block-end`,
95
+ `-inline-start`, and `-inline-end`, which default to `env(safe-area-inset-*)`.
96
+ Override them on `:root` to reserve room for app-owned chrome or to simulate a
97
+ device in tests.
98
+
18
99
  ## Decision matrix
19
100
 
20
- | Situation | Layout | Device threshold |
21
- | --- | --- | --- |
22
- | Simple app, a few flat sections | `NavStack` with one entry (single pane); add `TabScaffold` for 2–5 co-equal sections on handset | `TabScaffold` on `compact`; promote its tabs to a `Workbench` rail / sidebar `atLeast('desktop')` |
23
- | Drill-down browsing (list → item → sub-item) | `NavStack`; upgrade to `SplitView` once list + detail fit together | `SplitView` two-pane `atLeast('tablet')` landscape / non-`compact`; `NavStack` form on `compact` |
24
- | Two related panes, selecting on the left updates the right | `SplitView` | two panes when not `compact`; collapses to `NavStack` (list → detail) on `compact` |
25
- | Complex tool / editor with peripheral panels (navigator, inspector, console) | `Workbench` | full three-panel `atLeast('desktop')`; on smaller classes present the rails via `NavStack` / overlay drawers, not a shrunken shell |
26
- | Mobile app with 2–5 top-level destinations, each its own drill-down | `TabScaffold`, each tab a `NavStack` | bottom bar on `compact`; promote to a rail / sidebar `atLeast('desktop')` |
101
+ | Situation | Layout | Device threshold |
102
+ | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
103
+ | Simple app, a few flat sections | `NavStack` with one entry (single pane); add `TabScaffold` for 2–5 co-equal sections on handset | `TabScaffold` on `compact`; promote its tabs to a `Workbench` rail / sidebar `atLeast('desktop')` |
104
+ | Drill-down browsing (list → item → sub-item) | `NavStack`; upgrade to `SplitView` once list + detail fit together | `SplitView` two-pane `atLeast('tablet')` landscape / non-`compact`; `NavStack` form on `compact` |
105
+ | Two related panes, selecting on the left updates the right | `SplitView` | two panes when not `compact`; collapses to `NavStack` (list → detail) on `compact` |
106
+ | Complex tool / editor with peripheral panels (navigator, inspector, console) | `Workbench` | full three-panel `atLeast('desktop')`; on smaller classes present the rails via `NavStack` / overlay drawers, not a shrunken shell |
107
+ | Mobile app with 2–5 top-level destinations, each its own drill-down | `TabScaffold`, each tab a `NavStack` | bottom bar on `compact`; promote to a rail / sidebar `atLeast('desktop')` |
27
108
 
28
109
  ### Worked examples
29
110
 
@@ -36,11 +117,28 @@ gate the roomier presentations.
36
117
  selection).
37
118
  - **IDE (complex tool):** `Workbench` with a left navigator rail, a right
38
119
  inspector rail, and a bottom console drawer, each `collapsed` bound to a
39
- signal. Only offer this `atLeast('desktop')`.
120
+ signal. Only offer this `atLeast('desktop')`. To let people size the
121
+ navigator or console, opt those panels in with `resizable` and call
122
+ `wireWorkbench` with their size signals and `deviceClass`; resizing is
123
+ suspended on `compact`, where the rails become overlay drawers — or give each
124
+ rail `responsiveOverlayAt: "narrow"` and let the Workbench's own width decide
125
+ when it overlays, with no device check; pass each rail's `collapsed` signal
126
+ to `wireWorkbench` so the overlay starts collapsed, takes focus and keeps Tab
127
+ inside it while open, and closes on Escape or an outside press. Inline
128
+ rails, fixed or resizable, leave the editor its `mainMinSize` (320px by
129
+ default) and shrink in proportion when the window narrows; the bottom drawer
130
+ leaves it `mainMinHeight` (120px by default) and shrinks when the window gets
131
+ shorter.
40
132
  - **Social app (tabbed):** `TabScaffold` with Home / Search / Profile tabs, each
41
133
  `content` a `NavStack`. On a tablet/desktop, render the same sections as a
42
134
  `Workbench` left rail instead of a bottom bar.
43
135
 
136
+ Whatever the layout, call `wireScrollDividers(appRoot)` from
137
+ `@kerfjs/ui/wire-scroll-dividers` once: every `Pane` a layout renders, every
138
+ `NavStack`'s top chrome and bottom toolbar, and every `TabScaffold` bar then
139
+ draw the line between the pinned chrome and the scrolling content only while
140
+ content is scrolled beneath them, and toolbars need no divider of their own. See [Scroll dividers](layout.md#scroll-dividers).
141
+
44
142
  ## Dialogs
45
143
 
46
144
  Pick the dialog's inner layout by the same complexity axis, then apply the device