@kerfjs/ui 5.0.0-beta.97 → 5.0.0-beta.99

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 (416) hide show
  1. package/README.md +42 -1
  2. package/ai/application-ui-diagnostic-ids-v1.json +1 -0
  3. package/ai/component-catalog.json +487 -66
  4. package/ai/component-catalog.schema.json +1 -1
  5. package/ai/component-composition-rules.json +22 -2
  6. package/ai/component-composition.json +360 -33
  7. package/ai/component-composition.schema.json +1 -1
  8. package/ai/components/README.md +18 -17
  9. package/ai/components/anchored-surface.md +13 -0
  10. package/ai/components/application-tabs.md +1 -1
  11. package/ai/components/chip.md +9 -5
  12. package/ai/components/choice-chips.md +15 -8
  13. package/ai/components/collapsible-panel.md +3 -2
  14. package/ai/components/composer.md +83 -0
  15. package/ai/components/content-item.md +2 -2
  16. package/ai/components/corner-status.md +1 -1
  17. package/ai/components/document-baseline.md +3 -3
  18. package/ai/components/foundation.md +2 -2
  19. package/ai/components/layout.md +1 -1
  20. package/ai/components/list-action-row.md +5 -1
  21. package/ai/components/list-item.md +5 -1
  22. package/ai/components/list.md +19 -3
  23. package/ai/components/nav-stack.md +11 -2
  24. package/ai/components/pane.md +13 -4
  25. package/ai/components/popup-menu.md +4 -4
  26. package/ai/components/recipe-app-shell.md +3 -3
  27. package/ai/components/recipe-collapsible-sidebar.md +3 -3
  28. package/ai/components/recipe-compact-toolbar.md +1 -1
  29. package/ai/components/recipe-composer-form.md +3 -3
  30. package/ai/components/recipe-list-detail-dialog.md +2 -2
  31. package/ai/components/recipe-list-workspace-states.md +3 -3
  32. package/ai/components/recipe-loading-inspector.md +1 -1
  33. package/ai/components/recipe-navigation-sidebar.md +3 -3
  34. package/ai/components/recipe-navigation-stack.md +3 -3
  35. package/ai/components/recipe-workspace-header.md +3 -3
  36. package/ai/components/resize.md +2 -2
  37. package/ai/components/split-view.md +4 -2
  38. package/ai/components/sunken-panel.md +2 -2
  39. package/ai/components/surface-scaffold.md +15 -3
  40. package/ai/components/tab-bar.md +1 -0
  41. package/ai/components/tab-navigator.md +4 -0
  42. package/ai/components/toolbar-control-group.md +2 -0
  43. package/ai/components/toolbar-text.md +1 -0
  44. package/ai/components/toolbar.md +1 -0
  45. package/ai/components/workbench.md +1 -0
  46. package/ai/public-api-signatures-v1.md +504 -80
  47. package/ai/skill.md +41 -14
  48. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  49. package/analyzer/component-ownership.mjs +1 -1
  50. package/analyzer/index.mjs +41 -14
  51. package/dist/anchored-surface.d.ts +30 -2
  52. package/dist/anchored-surface.js +1 -1
  53. package/dist/app-tab.js +1 -1
  54. package/dist/browser/composer.js +4 -0
  55. package/dist/catalog-resources.d.ts +3 -2
  56. package/dist/catalog-resources.js +4 -4
  57. package/dist/catalog-resources.js.map +1 -1
  58. package/dist/catalog.d.ts +39 -8
  59. package/dist/catalog.js +55 -32
  60. package/dist/catalog.js.map +1 -1
  61. package/dist/chip.d.ts +41 -9
  62. package/dist/chip.js +1 -1
  63. package/dist/choice-chips.d.ts +40 -16
  64. package/dist/choice-chips.js +1 -1
  65. package/dist/chunk-34U4ANDJ.js +119 -0
  66. package/dist/chunk-34U4ANDJ.js.map +1 -0
  67. package/dist/{chunk-36TL6BCR.js → chunk-3GLYIIFR.js} +34 -6
  68. package/dist/chunk-3GLYIIFR.js.map +1 -0
  69. package/dist/{chunk-HWY4BSLS.js → chunk-3VXDSIBJ.js} +2 -2
  70. package/dist/{chunk-HWY4BSLS.js.map → chunk-3VXDSIBJ.js.map} +1 -1
  71. package/dist/chunk-3WYREXW4.js +120 -0
  72. package/dist/chunk-3WYREXW4.js.map +1 -0
  73. package/dist/chunk-AV7CPAPT.js +82 -0
  74. package/dist/chunk-AV7CPAPT.js.map +1 -0
  75. package/dist/{chunk-XK4E225T.js → chunk-B53PS2LE.js} +2 -2
  76. package/dist/{chunk-XK4E225T.js.map → chunk-B53PS2LE.js.map} +1 -1
  77. package/dist/{chunk-R42D2BAO.js → chunk-CA7LNX3J.js} +2 -2
  78. package/dist/{chunk-R42D2BAO.js.map → chunk-CA7LNX3J.js.map} +1 -1
  79. package/dist/{chunk-AYHC62OZ.js → chunk-CMETHPKR.js} +26 -29
  80. package/dist/chunk-CMETHPKR.js.map +1 -0
  81. package/dist/{chunk-Y43B6DS3.js → chunk-CVD5IZGB.js} +2 -2
  82. package/dist/{chunk-Y43B6DS3.js.map → chunk-CVD5IZGB.js.map} +1 -1
  83. package/dist/{chunk-6STQXRT4.js → chunk-CXZXHZTR.js} +121 -12
  84. package/dist/chunk-CXZXHZTR.js.map +1 -0
  85. package/dist/{chunk-GZE2XVKC.js → chunk-DZJ4GBSP.js} +3 -3
  86. package/dist/{chunk-GZE2XVKC.js.map → chunk-DZJ4GBSP.js.map} +1 -1
  87. package/dist/chunk-ELCMSQNJ.js +49 -0
  88. package/dist/chunk-ELCMSQNJ.js.map +1 -0
  89. package/dist/{chunk-P74VXMB6.js → chunk-EOO6PYGF.js} +7 -14
  90. package/dist/chunk-EOO6PYGF.js.map +1 -0
  91. package/dist/{chunk-PRG4CL3Q.js → chunk-FR76ZJQC.js} +21 -19
  92. package/dist/chunk-FR76ZJQC.js.map +1 -0
  93. package/dist/{chunk-QQ3TTWFH.js → chunk-G3J7JDP6.js} +1 -1
  94. package/dist/{chunk-QQ3TTWFH.js.map → chunk-G3J7JDP6.js.map} +1 -1
  95. package/dist/{chunk-IGTWXPTN.js → chunk-HGV7J4T6.js} +3 -3
  96. package/dist/{chunk-IGTWXPTN.js.map → chunk-HGV7J4T6.js.map} +1 -1
  97. package/dist/{chunk-YWQGSUF2.js → chunk-HLDRH23D.js} +3 -3
  98. package/dist/{chunk-YWQGSUF2.js.map → chunk-HLDRH23D.js.map} +1 -1
  99. package/dist/{chunk-PBT3CPPY.js → chunk-HQZYKJ56.js} +24 -4
  100. package/dist/chunk-HQZYKJ56.js.map +1 -0
  101. package/dist/{chunk-V6W56V3N.js → chunk-HZPHCSCS.js} +16 -3
  102. package/dist/chunk-HZPHCSCS.js.map +1 -0
  103. package/dist/{chunk-GJYQ27C2.js → chunk-INI4WDWC.js} +8 -1
  104. package/dist/chunk-INI4WDWC.js.map +1 -0
  105. package/dist/{chunk-U657IHFB.js → chunk-IQSRHXZR.js} +10 -4
  106. package/dist/chunk-IQSRHXZR.js.map +1 -0
  107. package/dist/{chunk-KQDU7Z7P.js → chunk-K4AT3V3J.js} +2 -1
  108. package/dist/chunk-K4AT3V3J.js.map +1 -0
  109. package/dist/{chunk-QIAPOWHH.js → chunk-KCS6NJ3O.js} +2 -2
  110. package/dist/{chunk-QIAPOWHH.js.map → chunk-KCS6NJ3O.js.map} +1 -1
  111. package/dist/{chunk-UKKRPP56.js → chunk-LOSRXZAH.js} +2 -2
  112. package/dist/{chunk-UKKRPP56.js.map → chunk-LOSRXZAH.js.map} +1 -1
  113. package/dist/{chunk-ROZLJULF.js → chunk-MF6ZUUJZ.js} +8 -3
  114. package/dist/chunk-MF6ZUUJZ.js.map +1 -0
  115. package/dist/{chunk-6IVNMC6R.js → chunk-MHMWQ7RB.js} +2 -2
  116. package/dist/{chunk-6IVNMC6R.js.map → chunk-MHMWQ7RB.js.map} +1 -1
  117. package/dist/{chunk-JYD7G4RG.js → chunk-PIVY32AE.js} +15 -5
  118. package/dist/chunk-PIVY32AE.js.map +1 -0
  119. package/dist/{chunk-BU23Z5HZ.js → chunk-PYBBYSZ3.js} +13 -18
  120. package/dist/chunk-PYBBYSZ3.js.map +1 -0
  121. package/dist/chunk-QDSZTQXL.js +28 -0
  122. package/dist/chunk-QDSZTQXL.js.map +1 -0
  123. package/dist/{chunk-YXOMI7ES.js → chunk-QIG4IGUP.js} +2 -2
  124. package/dist/{chunk-YXOMI7ES.js.map → chunk-QIG4IGUP.js.map} +1 -1
  125. package/dist/{chunk-6BRRUPJY.js → chunk-QROYYGWU.js} +3 -3
  126. package/dist/chunk-QROYYGWU.js.map +1 -0
  127. package/dist/{chunk-FOQK4F3L.js → chunk-RHD7RKGI.js} +28 -7
  128. package/dist/chunk-RHD7RKGI.js.map +1 -0
  129. package/dist/{chunk-BBU2SPRE.js → chunk-T4WJ5VGP.js} +4 -4
  130. package/dist/{chunk-BBU2SPRE.js.map → chunk-T4WJ5VGP.js.map} +1 -1
  131. package/dist/{chunk-6NOLL5KG.js → chunk-TGLUDQP2.js} +3 -3
  132. package/dist/{chunk-6NOLL5KG.js.map → chunk-TGLUDQP2.js.map} +1 -1
  133. package/dist/{chunk-HO6FU3G7.js → chunk-TLMMFQWM.js} +4 -4
  134. package/dist/{chunk-HO6FU3G7.js.map → chunk-TLMMFQWM.js.map} +1 -1
  135. package/dist/chunk-UMKV7YVQ.js +20 -0
  136. package/dist/chunk-UMKV7YVQ.js.map +1 -0
  137. package/dist/{chunk-M2NRCFRO.js → chunk-VFYO3UGZ.js} +2 -2
  138. package/dist/{chunk-M2NRCFRO.js.map → chunk-VFYO3UGZ.js.map} +1 -1
  139. package/dist/{chunk-YOKEL75M.js → chunk-WVS6CUCT.js} +2 -2
  140. package/dist/{chunk-YOKEL75M.js.map → chunk-WVS6CUCT.js.map} +1 -1
  141. package/dist/{chunk-MN3POJFN.js → chunk-YMUIQRWK.js} +2 -2
  142. package/dist/{chunk-MN3POJFN.js.map → chunk-YMUIQRWK.js.map} +1 -1
  143. package/dist/{chunk-Y3LC7JQO.js → chunk-YOA5W5ZH.js} +2 -2
  144. package/dist/{chunk-Y3LC7JQO.js.map → chunk-YOA5W5ZH.js.map} +1 -1
  145. package/dist/{chunk-44A6LP54.js → chunk-YQ2AC4E2.js} +2 -2
  146. package/dist/{chunk-44A6LP54.js.map → chunk-YQ2AC4E2.js.map} +1 -1
  147. package/dist/chunk-ZNROKDSZ.js +54 -0
  148. package/dist/chunk-ZNROKDSZ.js.map +1 -0
  149. package/dist/collapsible-panel.d.ts +3 -2
  150. package/dist/collapsible-panel.js +4 -3
  151. package/dist/collapsible-panel.js.map +1 -1
  152. package/dist/composer.d.ts +62 -0
  153. package/dist/composer.js +5 -0
  154. package/dist/composer.js.map +1 -0
  155. package/dist/corner-status.d.ts +1 -1
  156. package/dist/corner-status.js +1 -1
  157. package/dist/css-values.d.ts +9 -1
  158. package/dist/css-values.js +1 -1
  159. package/dist/disclosure-arrow.js +1 -1
  160. package/dist/empty-state.js +1 -1
  161. package/dist/grid.js +1 -1
  162. package/dist/icon-button.js +1 -1
  163. package/dist/icon-toggle-button.js +1 -1
  164. package/dist/index.d.ts +10 -5
  165. package/dist/index.js +40 -37
  166. package/dist/list-action-row.d.ts +10 -2
  167. package/dist/list-action-row.js +1 -1
  168. package/dist/list-header.js +1 -1
  169. package/dist/list-icon-tile-B8dr97OW.d.ts +18 -0
  170. package/dist/list-item.d.ts +10 -3
  171. package/dist/list-item.js +1 -1
  172. package/dist/list.d.ts +36 -4
  173. package/dist/list.js +1 -1
  174. package/dist/loading-spinner.js +1 -1
  175. package/dist/lucide-icon.js +1 -1
  176. package/dist/{nav-stack-BvSKY448.d.ts → nav-stack-VXTumzKa.d.ts} +53 -4
  177. package/dist/nav-stack.d.ts +3 -2
  178. package/dist/nav-stack.js +2 -1
  179. package/dist/pane.d.ts +40 -3
  180. package/dist/pane.js +1 -1
  181. package/dist/{panel-toolbar-CL9tYOkh.d.ts → panel-toolbar-vg6Ir5Dy.d.ts} +6 -1
  182. package/dist/popup-menu-register.d.ts +2 -1
  183. package/dist/popup-menu-register.js +97 -22
  184. package/dist/popup-menu-register.js.map +1 -1
  185. package/dist/popup-menu.js +1 -1
  186. package/dist/row.js +1 -1
  187. package/dist/scroll-clearance-BikHljTc.d.ts +14 -0
  188. package/dist/select-register.js +11 -0
  189. package/dist/select-register.js.map +1 -1
  190. package/dist/select.js +1 -1
  191. package/dist/skeleton.js +1 -1
  192. package/dist/spacer.js +1 -1
  193. package/dist/split-button.js +1 -1
  194. package/dist/split-view.d.ts +7 -5
  195. package/dist/split-view.js +6 -3
  196. package/dist/split-view.js.map +1 -1
  197. package/dist/state-banner.d.ts +6 -1
  198. package/dist/state-banner.js +1 -1
  199. package/dist/styles/anchored-surface.css +27 -1
  200. package/dist/styles/catalog/components/catalog-example-stack.css +5 -0
  201. package/dist/styles/catalog/components/catalog-example.css +33 -0
  202. package/dist/styles/catalog/components/catalog-stage.css +7 -0
  203. package/dist/styles/chip.css +70 -0
  204. package/dist/styles/components/actions/popup-menu/popup-menu.css +21 -4
  205. package/dist/styles/components/actions/toolbar/toolbar.css +19 -0
  206. package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +75 -1
  207. package/dist/styles/components/actions/toolbar-text/toolbar-text.css +75 -0
  208. package/dist/styles/components/collections/list/list.css +77 -7
  209. package/dist/styles/components/collections/list-action-row/list-action-row.css +177 -22
  210. package/dist/styles/components/collections/list-item/list-item.css +158 -11
  211. package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +27 -1
  212. package/dist/styles/components/feedback/chip/chip.css +70 -0
  213. package/dist/styles/components/feedback/state-banner/state-banner.css +24 -13
  214. package/dist/styles/components/forms/composer/composer.css +100 -0
  215. package/dist/styles/components/forms/select/select.css +1 -0
  216. package/dist/styles/components/forms/token-search-field/token-search-field.css +13 -2
  217. package/dist/styles/components/layout/pane/pane.css +81 -2
  218. package/dist/styles/components/navigation/nav-stack/nav-stack.css +84 -0
  219. package/dist/styles/components/navigation/tab-bar/tab-bar.css +15 -4
  220. package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +79 -4
  221. package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +195 -0
  222. package/dist/styles/composer.css +100 -0
  223. package/dist/styles/foundation.css +66 -0
  224. package/dist/styles/list-action-row.css +177 -22
  225. package/dist/styles/list-item.css +158 -11
  226. package/dist/styles/list.css +77 -7
  227. package/dist/styles/nav-stack.css +84 -0
  228. package/dist/styles/pane.css +81 -2
  229. package/dist/styles/popup-menu.css +21 -4
  230. package/dist/styles/select.css +1 -0
  231. package/dist/styles/state-banner.css +24 -13
  232. package/dist/styles/styles.css +1 -0
  233. package/dist/styles/surface-scaffold.css +195 -0
  234. package/dist/styles/tab-bar.css +15 -4
  235. package/dist/styles/tab-navigator.css +79 -4
  236. package/dist/styles/token-search-field.css +13 -2
  237. package/dist/styles/toolbar-control-group.css +75 -1
  238. package/dist/styles/toolbar-text.css +75 -0
  239. package/dist/styles/toolbar.css +19 -0
  240. package/dist/styles/webawesome.css +18 -0
  241. package/dist/surface-scaffold-register.d.ts +6 -1
  242. package/dist/surface-scaffold-register.js +89 -3
  243. package/dist/surface-scaffold-register.js.map +1 -1
  244. package/dist/surface-scaffold.d.ts +66 -7
  245. package/dist/surface-scaffold.js +1 -1
  246. package/dist/tab-navigator.d.ts +23 -2
  247. package/dist/tab-navigator.js +2 -0
  248. package/dist/tab-navigator.js.map +1 -1
  249. package/dist/token-search-field.d.ts +1 -1
  250. package/dist/token-search-field.js +1 -1
  251. package/dist/{token-search-model-DTcTXWMS.d.ts → token-search-model-aYR0YAei.d.ts} +32 -2
  252. package/dist/token-search-model.d.ts +1 -1
  253. package/dist/toolbar-control-group.js +1 -1
  254. package/dist/toolbar-text.js +1 -1
  255. package/dist/toolbar.d.ts +1 -0
  256. package/dist/value-table.js +1 -1
  257. package/dist/wire-catalog.d.ts +3 -2
  258. package/dist/wire-catalog.js +2 -2
  259. package/dist/wire-composers.d.ts +16 -0
  260. package/dist/wire-composers.js +3 -0
  261. package/dist/wire-composers.js.map +1 -0
  262. package/dist/wire-list-focus-scroll.d.ts +16 -0
  263. package/dist/wire-list-focus-scroll.js +3 -0
  264. package/dist/wire-list-focus-scroll.js.map +1 -0
  265. package/dist/wire-nav-stack.js +135 -0
  266. package/dist/wire-nav-stack.js.map +1 -1
  267. package/dist/wire-scroll-dividers.d.ts +9 -0
  268. package/dist/wire-scroll-dividers.js +1 -1
  269. package/dist/wire-tab-bars.js +1 -1
  270. package/dist/wire-tab-navigator.d.ts +3 -1
  271. package/dist/wire-tab-navigator.js +133 -1
  272. package/dist/wire-tab-navigator.js.map +1 -1
  273. package/dist/wire-token-search-fields.d.ts +1 -1
  274. package/dist/wire-token-search-fields.js +1 -1
  275. package/dist/workbench.d.ts +8 -3
  276. package/dist/workbench.js +2 -1
  277. package/docs/accessibility.md +132 -3
  278. package/docs/anchored-surface.md +46 -4
  279. package/docs/app-layouts.md +23 -2
  280. package/docs/apple-hig.md +1 -0
  281. package/docs/catalog.md +61 -9
  282. package/docs/collapsible-panel.md +9 -6
  283. package/docs/component-contract.md +46 -13
  284. package/docs/component-selection.md +147 -20
  285. package/docs/composer.md +143 -0
  286. package/docs/design/templates/choice-chips/multiple-dark.svg +1 -0
  287. package/docs/design/templates/choice-chips/multiple.svg +1 -0
  288. package/docs/design/templates/choice-chips-dark.svg +1 -1
  289. package/docs/design/templates/choice-chips.svg +1 -1
  290. package/docs/design/templates/composer/chat-dark.svg +1 -0
  291. package/docs/design/templates/composer/chat.svg +1 -0
  292. package/docs/design/templates/composer/prompt-dark.svg +1 -0
  293. package/docs/design/templates/composer/prompt.svg +1 -0
  294. package/docs/design/templates/composer-dark.svg +1 -0
  295. package/docs/design/templates/composer.svg +1 -0
  296. package/docs/design/templates/empty-state/filtered-dark.svg +1 -1
  297. package/docs/design/templates/empty-state/filtered.svg +1 -1
  298. package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
  299. package/docs/design/templates/empty-state/with-action.svg +1 -1
  300. package/docs/design/templates/empty-state-dark.svg +1 -1
  301. package/docs/design/templates/empty-state.svg +1 -1
  302. package/docs/design/templates/list/compact-dark.svg +1 -1
  303. package/docs/design/templates/list/compact.svg +1 -1
  304. package/docs/design/templates/list-action-row/default-dark.svg +1 -1
  305. package/docs/design/templates/list-action-row/default.svg +1 -1
  306. package/docs/design/templates/list-action-row/icon-tile-dark.svg +1 -0
  307. package/docs/design/templates/list-action-row/icon-tile.svg +1 -0
  308. package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
  309. package/docs/design/templates/list-action-row/selected.svg +1 -1
  310. package/docs/design/templates/list-action-row/trailing-menu-dark.svg +1 -1
  311. package/docs/design/templates/list-action-row/trailing-menu.svg +1 -1
  312. package/docs/design/templates/list-action-row-dark.svg +1 -1
  313. package/docs/design/templates/list-action-row.svg +1 -1
  314. package/docs/design/templates/list-dark.svg +1 -1
  315. package/docs/design/templates/list-header/size-l-dark.svg +1 -1
  316. package/docs/design/templates/list-header/size-l.svg +1 -1
  317. package/docs/design/templates/list-header/size-m-dark.svg +1 -1
  318. package/docs/design/templates/list-header/size-m.svg +1 -1
  319. package/docs/design/templates/list-header/size-xl-dark.svg +1 -1
  320. package/docs/design/templates/list-header/size-xl.svg +1 -1
  321. package/docs/design/templates/list-header-dark.svg +1 -1
  322. package/docs/design/templates/list-header.svg +1 -1
  323. package/docs/design/templates/list-item/icon-tile-dark.svg +1 -0
  324. package/docs/design/templates/list-item/icon-tile.svg +1 -0
  325. package/docs/design/templates/list-item/selected-dark.svg +1 -1
  326. package/docs/design/templates/list-item/selected.svg +1 -1
  327. package/docs/design/templates/list-item-dark.svg +1 -1
  328. package/docs/design/templates/list-item.svg +1 -1
  329. package/docs/design/templates/list.svg +1 -1
  330. package/docs/design/templates/segmented-control-dark.svg +1 -1
  331. package/docs/design/templates/segmented-control.svg +1 -1
  332. package/docs/design/templates/skeleton-dark.svg +1 -1
  333. package/docs/design/templates/skeleton.svg +1 -1
  334. package/docs/design/templates/state-banner/danger-dark.svg +1 -1
  335. package/docs/design/templates/state-banner/danger.svg +1 -1
  336. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
  337. package/docs/design/templates/state-banner/info-badge.svg +1 -1
  338. package/docs/design/templates/state-banner/info-dark.svg +1 -1
  339. package/docs/design/templates/state-banner/info.svg +1 -1
  340. package/docs/design/templates/state-banner/strip-dark.svg +1 -1
  341. package/docs/design/templates/state-banner/strip.svg +1 -1
  342. package/docs/design/templates/state-banner/success-dark.svg +1 -1
  343. package/docs/design/templates/state-banner/success.svg +1 -1
  344. package/docs/design/templates/state-banner/warning-dark.svg +1 -1
  345. package/docs/design/templates/state-banner/warning.svg +1 -1
  346. package/docs/design/templates/state-banner-dark.svg +1 -1
  347. package/docs/design/templates/state-banner.svg +1 -1
  348. package/docs/design/templates/tab-bar-dark.svg +1 -1
  349. package/docs/design/templates/tab-bar.svg +1 -1
  350. package/docs/design/templates/token-search-field-dark.svg +1 -1
  351. package/docs/design/templates/token-search-field.svg +1 -1
  352. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  353. package/docs/design/templates/toolbar-control-group.svg +1 -1
  354. package/docs/design/templates/toolbar-dark.svg +1 -1
  355. package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
  356. package/docs/design/templates/toolbar-text/default.svg +1 -1
  357. package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
  358. package/docs/design/templates/toolbar-text/large.svg +1 -1
  359. package/docs/design/templates/toolbar-text-dark.svg +1 -1
  360. package/docs/design/templates/toolbar-text.svg +1 -1
  361. package/docs/design/templates/toolbar.svg +1 -1
  362. package/docs/design/templates/value-table/details-dark.svg +1 -1
  363. package/docs/design/templates/value-table/details.svg +1 -1
  364. package/docs/design/templates/value-table-dark.svg +1 -1
  365. package/docs/design/templates/value-table.svg +1 -1
  366. package/docs/design-philosophy.md +9 -1
  367. package/docs/examples/command-palette-adapter.tsx +3 -3
  368. package/docs/layout.md +147 -14
  369. package/docs/nav-stack.md +132 -8
  370. package/docs/recipes.md +9 -9
  371. package/docs/split-view.md +3 -3
  372. package/docs/sunken-panel.md +2 -2
  373. package/docs/surface-scaffold.md +138 -7
  374. package/docs/tab-navigator.md +38 -1
  375. package/docs/type-contracts.md +16 -16
  376. package/docs/ui-analyzer.md +3 -1
  377. package/docs/ui-doctor.md +71 -0
  378. package/docs/ux-demo.md +6 -6
  379. package/docs/webawesome-theme.md +7 -0
  380. package/docs/workbench.md +10 -6
  381. package/docs/writing.md +152 -0
  382. package/doctor/copy-capitalization-checks.mjs +591 -0
  383. package/doctor/index.mjs +17 -8
  384. package/llms.txt +105 -11
  385. package/package.json +15 -1
  386. package/ux-demo/recipes/app-shell.tsx +11 -11
  387. package/ux-demo/recipes/collapsible-sidebar.tsx +12 -11
  388. package/ux-demo/recipes/compact-toolbar.tsx +10 -10
  389. package/ux-demo/recipes/composer-form.tsx +8 -8
  390. package/ux-demo/recipes/list-detail-dialog.tsx +13 -13
  391. package/ux-demo/recipes/list-workspace-states.tsx +13 -13
  392. package/ux-demo/recipes/loading-inspector.tsx +10 -10
  393. package/ux-demo/recipes/navigation-sidebar.tsx +7 -7
  394. package/ux-demo/recipes/navigation-stack.tsx +5 -5
  395. package/ux-demo/recipes/workspace-header.tsx +5 -5
  396. package/dist/chunk-36TL6BCR.js.map +0 -1
  397. package/dist/chunk-4LOK4634.js +0 -21
  398. package/dist/chunk-4LOK4634.js.map +0 -1
  399. package/dist/chunk-6BRRUPJY.js.map +0 -1
  400. package/dist/chunk-6STQXRT4.js.map +0 -1
  401. package/dist/chunk-76PWEN5Y.js +0 -64
  402. package/dist/chunk-76PWEN5Y.js.map +0 -1
  403. package/dist/chunk-AYHC62OZ.js.map +0 -1
  404. package/dist/chunk-BU23Z5HZ.js.map +0 -1
  405. package/dist/chunk-DQDTZE4W.js +0 -70
  406. package/dist/chunk-DQDTZE4W.js.map +0 -1
  407. package/dist/chunk-FOQK4F3L.js.map +0 -1
  408. package/dist/chunk-GJYQ27C2.js.map +0 -1
  409. package/dist/chunk-JYD7G4RG.js.map +0 -1
  410. package/dist/chunk-KQDU7Z7P.js.map +0 -1
  411. package/dist/chunk-P74VXMB6.js.map +0 -1
  412. package/dist/chunk-PBT3CPPY.js.map +0 -1
  413. package/dist/chunk-PRG4CL3Q.js.map +0 -1
  414. package/dist/chunk-ROZLJULF.js.map +0 -1
  415. package/dist/chunk-U657IHFB.js.map +0 -1
  416. package/dist/chunk-V6W56V3N.js.map +0 -1
package/llms.txt CHANGED
@@ -28,7 +28,11 @@ For arbitrary interactive help or popup content, use `openAnchoredSurface` or
28
28
  `openAnchoredSurfaceAt` from `@kerfjs/ui/anchored-surface`; see
29
29
  [AnchoredSurface](./docs/anchored-surface.md). The app owns content and actions.
30
30
  Inside a native `<dialog>`, `wa-dialog`, or `wa-drawer`, the surface stays
31
- interactive and closes with the dialog.
31
+ interactive and closes with the dialog. It keeps an 8px viewport gutter
32
+ (`viewportMargin`) and fits its height to the room beside the anchor, scrolling
33
+ its content, so do not add media queries or `max-height` overrides.
34
+ `tone: 'dark'` gives a popup anchored to dark media chrome the inverse tone of a
35
+ `ToolbarControlGroup tone="dark"`; its content inherits the matching colors.
32
36
  For a public subpath containing several visual components, use a folder with one
33
37
  component per source file and one owned stylesheet per visual component. Keep its
34
38
  compatibility entrypoints implementation-free: JavaScript/TypeScript only re-exports
@@ -141,7 +145,9 @@ explains when to reuse, compose, extend, or use native HTML.
141
145
  Use its component/composition/recipe taxonomy and functional-group,
142
146
  importance, alphabetical-tie, and recipes-last ordering policy. Focused
143
147
  demos and recipes use public components plus `CatalogExample.viewport`,
144
- never local stylesheets, inline styles, or styling-only classes.
148
+ never local stylesheets, inline styles, or styling-only classes. A frame
149
+ that a top-layer specimen anchors to takes `viewport.anchorName`, and a
150
+ centered column takes `viewport.contentWidth` + `contentAlign`.
145
151
  5. **Apply the rules below and in** [skill.md](./ai/skill.md).
146
152
  6. **Before calling an integration complete**, run
147
153
  [`kerf-ui-doctor`](./docs/ui-doctor.md). Its portable JSON report merges
@@ -182,6 +188,23 @@ the cursor; the cursor alone cannot explain an interaction. See
182
188
  [accessibility contracts](./docs/accessibility.md) and
183
189
  [interaction principles](./docs/apple-hig.md#interaction).
184
190
 
191
+ ## Interface copy capitalization
192
+
193
+ Follow Apple's split, the package default: **names are title style;
194
+ descriptions are sentence style.** Title style: buttons, menu items and menu
195
+ headings, popup-menu triggers, segments, `Select` choices, tabs,
196
+ page/view/pane/dialog/toolbar titles, back labels, `ListHeader` and column
197
+ headings, field labels, icon-only control names, and fragment
198
+ `EmptyState`/`StateBanner` titles ("Save Draft", "Back to Inbox"). Sentence
199
+ style: body copy, descriptions, details, messages, tooltips, placeholders,
200
+ checkbox/switch/radio option text, and status lines ("Saved just now"). Title
201
+ style capitalizes the first and last word and phrasal-verb particles ("Log
202
+ In"); it lowercases articles, coordinating conjunctions, `to` in infinitives,
203
+ `as`, and prepositions of four letters or fewer elsewhere. Keep brand spellings
204
+ and user data as written. `kerf-ui-doctor` reports violations in known
205
+ title-style labels as `KUI-D040`. See
206
+ [Writing interface copy](./docs/writing.md#capitalization).
207
+
185
208
  ## Core rules (do the right thing)
186
209
 
187
210
  - **Prefer a Kerf UI component over a Web Awesome component over a raw HTML
@@ -245,6 +268,10 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
245
268
  main, and bottom toolbars, CollapsiblePanel, NavStack, SplitView) and size
246
269
  the groups they compose (panel toggles, the back button) to match; pass
247
270
  `density` to `CollapsiblePanelRelocated` when its toolbar is compact.
271
+ - **Name panel toggles in title style.** A panel toolbar's
272
+ `toggle: { action, name }` uses `name` as the toggle group's accessible name
273
+ and in the default "Show …" / "Hide …" label, so pass a title-style panel
274
+ name (`name: "Navigator"` gives "Show Navigator"); it is never recapitalized.
248
275
  - **Position region restore controls declaratively.** A ResizableRegion has a
249
276
  background token, `contentOverflow="visible"` for an anchored popup,
250
277
  top/bottom `restorePosition`, typed `restoreInset`, and
@@ -263,7 +290,11 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
263
290
  the before/after insertion line, supports Alt+Shift+ArrowUp/ArrowDown, and
264
291
  reports `{ listId, sourceIds, target: { id, position }, source }` for
265
292
  `reorderListItems`. The `dragging` / `dropPosition` props render the same
266
- states from app-owned drag state. Public ListItem tokens set row, icon, label, and trailing geometry;
293
+ states from app-owned drag state. For keyboard row navigation in a
294
+ `scrollable` List, call `wireListFocusScroll` (from
295
+ `@kerfjs/ui/wire-list-focus-scroll`) once: it scrolls a keyboard-focused row
296
+ whose focus ring crosses the clip into view in Firefox and Safari too.
297
+ Public ListItem tokens set row, icon, label, and trailing geometry;
267
298
  ListHeader `size="xs|sm|m|l|xl"` sets label scale independently of
268
299
  `headingLevel` (`sm` keeps the current FieldLabel-sized uppercase style;
269
300
  `m`/`l`/`xl` match themed h5/h4/h3). Its height, title padding, and border
@@ -275,6 +306,12 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
275
306
  `--kui-list-item-hover-background`, `--kui-list-item-hover-border`,
276
307
  `--kui-list-item-selected-background`, `--kui-list-item-selected-color`,
277
308
  and `--kui-list-item-selected-border` at the list or row boundary.
309
+ To align `ListItem` and `ListActionRow` rows in one list on a custom
310
+ leading inset, set `--kui-list-row-padding-inline` on the `List` (never
311
+ `--kui-list-item-padding-inline`, which moves only `ListItem` rows). Both
312
+ row kinds draw an 18px untiled leading icon by default, so their labels
313
+ align; resize both with `--kui-list-row-icon-size` on the `List` (never
314
+ `--kui-list-item-icon-size`, which resizes only `ListItem` icons).
278
315
  - **Use `Row` for horizontal and `List` for vertical layout.** Both expose
279
316
  physical `hAlign`/`vAlign` props and typed gaps. Row defaults to left/full,
280
317
  an `xs` gap, and no wrapping; List retains full/top and zero-gap defaults.
@@ -291,7 +328,9 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
291
328
  for List flex, typed lengths for Skeleton geometry, and
292
329
  foreground colors for choice icons and `LucideIcon.color`: `uiColor()` with an `*-on-*`/text token,
293
330
  `foregroundColorVar()`, or `foregroundColor()` for a custom CSS color; fill tokens such as `uiColor('success-fill-quiet')` do not
294
- type-check. These brands are intentionally
331
+ type-check. Fills (a `ChoiceChips` swatch, a row `iconTile` color) take a
332
+ plain `CssColor`: `fillColor('#3b82f6')` for a custom CSS color, `uiColor()`,
333
+ or `colorVar()`; never `foregroundColor()` for a fill. These brands are intentionally
295
334
  not interchangeable; rows use classes/tokens/props rather than raw `style`.
296
335
  Set `LucideIcon.inline` for an icon in `Text` or prose; it changes the SVG
297
336
  from block to inline-block without changing its inherited 1em size.
@@ -437,6 +476,7 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
437
476
  Text supports `controlMargins` with canonical physical Sides for an exterior 8px control gutter; it preserves border/padding and avoids an isolated Row wrapper. Avoid stacking it with parent insets. Inline spans retain native inline margin behavior.
438
477
 
439
478
  `ToolbarControlGroup.tileTone` selects `neutral`, `brand`, `success`, `warning`, or `danger` quiet fill, matching quiet border, and on-quiet foreground for a non-interactive icon tile. Compose a contained `single` group whose only child is a direct decorative LucideIcon (`aria-hidden="true"`). Omission preserves the normal group palette. The prop does not recolor interactive, mixed, multi-control, or borderless groups; keep real actions in controls with their existing focus/hover/pressed treatments.
479
+ `ListItem`, `ListItemLink`, and `ListActionRow` take `iconTile` to present their leading `icon` in a filled 32px rounded tile holding an 18px glyph, for settings or command rows that identify each entry by color. Pass a tone (`neutral`, `brand`, `success`, `warning`, or `danger`) for the same quiet fill, matching quiet border, and on-quiet foreground as `ToolbarControlGroup.tileTone`, or `{ color, foreground? }` for an application color: `color` is a `CssColor` (`uiColor()`, `colorVar()`, or `fillColor('#e5484d')`) and `foreground` a `CssForegroundColor`; without one, the glyph is black or white, whichever contrasts with the fill. The tile sits 6px clear of a standard 44px row (a compact row is at least 42px tall), moves the label to the tile's edge, and keeps its colors when the row is hovered or selected. Never wrap the icon in your own tile or resize the row's icon slot.
440
480
  Toolbar zones accept `KerfUiContent` directly. Use `ToolbarText` for identity and `ToolbarControlGroup` for related controls that need its shared chrome; a bare control keeps its own styling and accessibility responsibilities.
441
481
  Expanded search groups accept `expandedOverflow="visible"` to anchor app-owned suggestion/date/help surfaces below their relative root; collapsed/default groups retain existing clipping. Apps own surface placement/dismissal and use `data-token-search-keep-open` or `collapsible.keepOpenOn` for focus.
442
482
 
@@ -485,6 +525,26 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
485
525
  bar's top edge beside a sunken active scene or nested Pane even when content
486
526
  fits; hidden suppresses that edge, including scroll dividers.
487
527
  Never add a border, or a permanent `dividerSides`, to fake one.
528
+ For a header that floats over its scrolling content, set
529
+ `headerPresentation="overlay"` on the `Pane` (or in `PaneConfig`). It paints
530
+ the translucent chrome material (`--kui-chrome-material`, opaque under
531
+ reduced transparency, increased contrast, and forced colors). The same
532
+ `wireScrollDividers` call measures the header and publishes its height as
533
+ block-start scroll clearance, which the content clears automatically. Keep
534
+ overlay-header text in the normal text color.
535
+ For a translucent stack bar laid over the active view, set
536
+ `chromePresentation="overlay"` on the `NavStack`. `wireNavStack` measures
537
+ the top chrome and hands its height (top safe area included) to the views
538
+ as their block-start inset, so a plain view or a sole `Pane` clears it
539
+ automatically. Keep its text in the normal text color. For a translucent
540
+ bottom toolbar, set `bottomToolbarPresentation="overlay"` (separate from
541
+ `chromePresentation`); `wireNavStack` hands its height (bottom safe area
542
+ included) to the views as their block-end inset, so the last item rests
543
+ just above it. Its `ToolbarText` and default-tone `ToolbarControlGroup`
544
+ controls lift themselves to AA. For a translucent tab bar, set
545
+ `barPresentation="overlay"` on the `TabNavigator`; `wireTabNavigator`
546
+ measures the bar and the scenes hand its height to their content as the
547
+ block-end inset. Its tab labels lift themselves to AA.
488
548
  While a side overlay is open (a Workbench rail overlay, a `wireSidebar` compact
489
549
  overlay, an overlay `CollapsiblePanel` or horizontal `ResizableRegion`), the
490
550
  layout hides the covered work area's `FloatingToolbar`s and restore corners
@@ -567,8 +627,10 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
567
627
  inherits the banner tone and is announced with the banner, so keep its text
568
628
  meaningful and never rely on color alone.
569
629
  - **Configure StateBanner copy and action placement.** `copyLayout="stacked"`
570
- puts detail below the title and badge; `actionPlacement="below"` gives the
571
- action a separate trailing row. The inline defaults remain compact.
630
+ puts detail below the title and badge; `actionPlacement="below"` always
631
+ gives the action a separate trailing row. The default trailing action stays
632
+ beside the copy while it has room and wraps onto its own trailing row on a
633
+ narrow banner, so do not switch placement per breakpoint.
572
634
  `presentation="strip"` renders a flush, square, full-width band for a
573
635
  layout edge with tone-colored `dividerSides` (bottom by default) instead of
574
636
  the inset rounded card; never restyle a card into a strip.
@@ -598,6 +660,13 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
598
660
  one rounded frame with dividers between them (settings, account cards,
599
661
  shortcut lists), compose `FramedListRow`s; `ValueTable` is the same frame for
600
662
  label/value facts. Never hand-roll a `ul` with borders and row padding.
663
+ - **Bound a result picker with a framed `List`.** For a bordered, scrolling
664
+ list of `ListItem` rows (search results, duplicate candidates), use
665
+ `<List scrollable frame maxHeight={rem(14)} minHeight={rem(4.25)}>`:
666
+ `frame` draws a 1px neutral border, the control radius, and a 4px inset
667
+ that keeps the first and last rows' focus rings inside the clip, and the
668
+ typed `CssLength` bounds need no flex-column ancestor. Never wrap a List in
669
+ an app div with `max-height`, `overflow`, and a border.
601
670
  - **Square an icon-only button with `IconButton`.** Web Awesome squares a
602
671
  `wa-button` only for a lone `wa-icon`; with a `LucideIcon` use
603
672
  `IconButton({ icon, iconName, label })` (label required) instead of
@@ -667,20 +736,44 @@ created, input }`. Never use a hand-styled `<input list>` + `<datalist>`.
667
736
  `removeAction` and a specific `removeLabel`; the application updates the tag
668
737
  list. `itemId` identifies the chip to the action handler. `size="compact"`
669
738
  gives a 20px chip and 16px remove button; `disabled` disables removal.
739
+ - **Use a link `Chip` for downloadable files and short links.** `href` renders a
740
+ native `<a>` with chip geometry; `download` (`true` or a file name) and
741
+ `external` (new tab, safe rel, announced) are optional, and a link chip has
742
+ no remove button. Inside a ContentItem bubble set `tone="inherit"` so it
743
+ follows the bubble's foreground instead of styling an anchor yourself.
670
744
  - **Use `ChoiceChips` for a wrapping exclusive choice.** When one value comes
671
745
  from a visible set too large for a `SegmentedControl` track (a nine-color
672
746
  palette in a 560px dialog), render separate pills backed by native radios:
673
747
  one Tab stop, arrow-key selection, and a real form value under `name`. Name
674
748
  the group with `label` or `labelledBy`, lead choices with a `swatch`
675
- (`CssColor`, or `'transparent'` for the struck-through no-color dot) or an
749
+ (`CssColor` such as `fillColor('#3b82f6')`, or `'transparent'` for the struck-through no-color dot) or an
676
750
  `icon`, and handle the delegated `action` on `change`. Never wrap a
677
- `SegmentedControl` or button-appearance `wa-radio` set instead.
751
+ `SegmentedControl` or button-appearance `wa-radio` set instead. Set
752
+ `multiple` (with a `readonly Value[]` `value`) when any number may be on:
753
+ the same pills become native checkboxes in a `role="group"`, each a Tab stop
754
+ that Space toggles, and every checked value submits under `name`. On dark
755
+ inverse chrome, place the chips in a dark appearance scope (`.wa-dark`)
756
+ instead of restyling them.
757
+
758
+ - **Use `Composer` for a chat or prompt composer.** `@kerfjs/ui/composer`
759
+ renders a raised frame (standard border, large radius, `shadow-l`) whose
760
+ border turns brand while focus is within, a borderless textarea that grows
761
+ between `minRows` (default 1) and `maxRows` (default 8) lines and then
762
+ scrolls, and an action row with an optional quiet `hint` and the trailing
763
+ `actions` (a brand Send button). Name it with a title-style `label`; give it
764
+ an `id` so the hint describes the field. Slot it into a `DialogSurface`
765
+ floating footer (`footerInset="none"`) over a transcript. Never rebuild
766
+ composer chrome around a `wa-textarea` with app CSS. For Enter-to-send, set
767
+ `submitOnEnter` and call `wireComposers(root)` from
768
+ `@kerfjs/ui/wire-composers` once: Enter or Cmd/Ctrl+Enter clicks the first
769
+ trailing action, Shift+Enter inserts a newline, and an IME composition
770
+ commit or an empty message never sends. Never hand-roll that handler.
678
771
 
679
772
  ## Public exports
680
773
 
681
774
  `Badge`, `LucideIcon`, `DisclosureArrow`, `Toolbar`, `ToolbarControlGroup`, `ToolbarActionLink`, `FloatingToolbar`, `ToolbarText`, `Text`,
682
- `ListItem`, `ListActionRow` (+ `wireListReorder`, `reorderListItems`), `ListHeader`, `ListInsetControl`,
683
- `ListInsetText`, `SegmentedControl`, `ChoiceChips`,
775
+ `ListItem`, `ListActionRow` (+ `wireListReorder`, `reorderListItems`), `wireListFocusScroll`, `ListHeader`, `ListInsetControl`,
776
+ `ListInsetText`, `SegmentedControl`, `ChoiceChips`, `Composer`,
684
777
  `Select`, `TokenSearchField` (+ `readTokenSearchField`, `placeTokenSearchCaret`,
685
778
  `wireTokenSearchFields`), `AppTab`, `TabBar` (+ `wireTabBars`, `reorderTabs`),
686
779
  `wireScrollDividers` (scroll-state dividers for every `Pane`, `NavStack`,
@@ -716,6 +809,7 @@ shell, `@kerfjs/ui/collapsible-panel` (`CollapsiblePanel`, `CollapsiblePanelTogg
716
809
 
717
810
  - [skill.md](./ai/skill.md): the full rule set + a common-mistakes table (the shipped AI skill).
718
811
  - [design-philosophy.md](./docs/design-philosophy.md): why kerf UI is shaped this way — the decision principles behind the rules above.
812
+ - [writing.md](./docs/writing.md): interface copy — the title-style vs. sentence-style capitalization rule per element, from Apple's guidance.
719
813
  - [layout.md](./docs/layout.md): the pane/content-item geometry, public roles/tokens, and the spacing scale.
720
814
  - [component-contract.md](./docs/component-contract.md): what a component owns vs. what the app owns.
721
815
  - [accessibility.md](./docs/accessibility.md): keyboard and ARIA contracts per component.
@@ -750,7 +844,7 @@ details.
750
844
 
751
845
  Select menus use native viewport shifting with 10px gutters: default menus grow to content (up to 20rem), while `fitMenu` matches trigger width and long labels wrap. Keep popup sizing and position repairs out of consumer CSS.
752
846
 
753
- Key sibling `DialogSurface`s that render conditionally with `rootAttributes={{ "data-key": "…" }}` so a re-render morphs each dialog into itself; never wrap one in a keyed app `div`. Modal `DialogSurface` bounds use typed `viewportGutter` and `maxHeight` (`CssLength` or `"viewport"`); explicit caps stay within the dynamic viewport minus both gutters. Omitted props preserve Web Awesome defaults. Application render state chooses responsive values, and preview layout remains application-owned. `height` sets a definite bounded modal size and `bodyLayout="fill"` lets one child fill the body. `PopoverDialogSurface` is the non-modal, light-dismiss counterpart on the native Popover API, with the same size props and an `embedded` in-flow presentation.
847
+ Key sibling `DialogSurface`s that render conditionally with `rootAttributes={{ "data-key": "…" }}` so a re-render morphs each dialog into itself; never wrap one in a keyed app `div`. Modal `DialogSurface` bounds use typed `viewportGutter` and `maxHeight` (`CssLength` or `"viewport"`); explicit caps stay within the dynamic viewport minus both gutters. Omitted props preserve Web Awesome defaults. Application render state chooses responsive values, and preview layout remains application-owned. `height` sets a definite bounded modal size and `bodyLayout="fill"` lets one child fill the body; `bodyLayout="fill-last"` keeps leading rows at their height above one filling last child, and `footerPresentation="floating"` (with a typed `footerOffset`) floats a chat composer over the body while the body reserves its measured height; give the transcript `scrollClearance="block-end"` (a `scrollable` `List` or a `Pane`) to let it scroll beneath the composer, with the last message still resting above it, through the published `--kui-scroll-clearance-block-end` context (overlaying chrome publishes `--kui-scroll-clearance-block-start` / `-end`; one scroller per covered edge opts in; a composed Pane opts in through `PaneConfig.scrollClearance` on a Workbench `mainPane` or panel `pane`, a `CollapsiblePanel` `pane`, or a `NavStack` view's `pane`). `headerPresentation="floating"` lays the title bar over the body on the translucent `--kui-chrome-material-raised` (opaque under reduced transparency, increased contrast, and forced colors); the register measures it and the body reserves it, and a `fill` body's only child with `scrollClearance="both"` scrolls beneath both the title bar and a floating composer through the published `--kui-scroll-clearance-block-start`. `PopoverDialogSurface` is the non-modal, light-dismiss counterpart on the native Popover API, with the same size props and an `embedded` in-flow presentation; `describedBy` sets its `aria-describedby` (usually a title `ToolbarText`'s `descriptionId`).
754
848
 
755
849
  Import `@kerfjs/ui/surface-scaffold/register` once when using `DialogSurface`: it registers `wa-dialog` and repairs Web Awesome 3.12 native dialog names before the first modal opening. Headed dialogs reference the rendered title, including live slotted labels; headerless dialogs use the public `label`. Explicit host `aria-label` and pre-existing native names are preserved. A host that is itself an explicit dialog (`role="dialog"` or `"alertdialog"` with its own name) keeps that name alone: the native panel is left unnamed, so one dialog carries the name. Geometry-only markup stays CSS-free and does not register Web Awesome implicitly.
756
850
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerfjs/ui",
3
- "version": "5.0.0-beta.97",
3
+ "version": "5.0.0-beta.99",
4
4
  "description": "Accessible, composable UI primitives for kerf applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -228,6 +228,10 @@
228
228
  "types": "./dist/wire-list-reorder.d.ts",
229
229
  "import": "./dist/wire-list-reorder.js"
230
230
  },
231
+ "./wire-list-focus-scroll": {
232
+ "types": "./dist/wire-list-focus-scroll.d.ts",
233
+ "import": "./dist/wire-list-focus-scroll.js"
234
+ },
231
235
  "./wire-toolbar-visibility": {
232
236
  "types": "./dist/wire-toolbar-visibility.d.ts",
233
237
  "import": "./dist/wire-toolbar-visibility.js"
@@ -312,6 +316,15 @@
312
316
  "browser": "./dist/browser/choice-chips.js",
313
317
  "import": "./dist/choice-chips.js"
314
318
  },
319
+ "./composer": {
320
+ "types": "./dist/composer.d.ts",
321
+ "browser": "./dist/browser/composer.js",
322
+ "import": "./dist/composer.js"
323
+ },
324
+ "./wire-composers": {
325
+ "types": "./dist/wire-composers.d.ts",
326
+ "import": "./dist/wire-composers.js"
327
+ },
315
328
  "./token-search-field": {
316
329
  "types": "./dist/token-search-field.d.ts",
317
330
  "browser": "./dist/browser/token-search-field.js",
@@ -480,6 +493,7 @@
480
493
  "./sunken-panel.css": "./dist/styles/sunken-panel.css",
481
494
  "./segmented-control.css": "./dist/styles/segmented-control.css",
482
495
  "./choice-chips.css": "./dist/styles/choice-chips.css",
496
+ "./composer.css": "./dist/styles/composer.css",
483
497
  "./token-search-field.css": "./dist/styles/token-search-field.css",
484
498
  "./state-banner.css": "./dist/styles/state-banner.css",
485
499
  "./corner-status.css": "./dist/styles/corner-status.css",
@@ -57,7 +57,7 @@ export const createRecipe: RecipeFactory = (announce) => {
57
57
  <ListItem
58
58
  action="recipe-action"
59
59
  itemId="projects"
60
- label="Projects with a deliberately wrapping title"
60
+ label="Projects with a Deliberately Wrapping Title"
61
61
  icon={<LucideIcon icon={Folder} name="folder" />}
62
62
  selected={selected.value === 'projects'}
63
63
  multiline
@@ -69,14 +69,14 @@ export const createRecipe: RecipeFactory = (announce) => {
69
69
  const render = () => (
70
70
  <List fill rootAttributes={{ 'data-recipe': 'recipe-app-shell' }}>
71
71
  <Toolbar
72
- label="Atlas workspace"
72
+ label="Atlas Workspace"
73
73
  safeAreaEdges={['block-start', 'inline-start', 'inline-end']}
74
74
  dividerSides="b"
75
75
  leading={<ToolbarText text="Atlas" />}
76
76
  trailing={
77
77
  <ToolbarControlGroup
78
78
  appearance="borderless"
79
- label="Workspace controls"
79
+ label="Workspace Controls"
80
80
  >
81
81
  <button
82
82
  type="button"
@@ -100,7 +100,7 @@ export const createRecipe: RecipeFactory = (announce) => {
100
100
  <List flex>
101
101
  <Workbench
102
102
  id="recipe-shell"
103
- label="Atlas workspace panes"
103
+ label="Atlas Workspace Panes"
104
104
  leftRail={{
105
105
  label: 'Navigation',
106
106
  size: navigationSize.value,
@@ -109,18 +109,18 @@ export const createRecipe: RecipeFactory = (announce) => {
109
109
  toolbar: {
110
110
  label: 'Navigation',
111
111
  title: <ToolbarText text="Workspace" />,
112
- toggle: { action: NAVIGATION_ACTION, name: 'navigation' },
112
+ toggle: { action: NAVIGATION_ACTION, name: 'Navigation' },
113
113
  },
114
114
  content: navigation(),
115
115
  }}
116
116
  mainToolbar={{
117
- label: 'Current workspace view',
117
+ label: 'Current Workspace View',
118
118
  title: (
119
119
  <ToolbarText
120
120
  text={
121
121
  selected.value === 'inbox'
122
- ? 'Inbox triage'
123
- : 'Active projects'
122
+ ? 'Inbox Triage'
123
+ : 'Active Projects'
124
124
  }
125
125
  size="xlarge"
126
126
  id="recipe-shell-main-title"
@@ -138,7 +138,7 @@ export const createRecipe: RecipeFactory = (announce) => {
138
138
  data-action="recipe-action"
139
139
  data-recipe-command="new"
140
140
  >
141
- New task
141
+ New Task
142
142
  </button>
143
143
  </ToolbarControlGroup>
144
144
  ),
@@ -164,10 +164,10 @@ export const createRecipe: RecipeFactory = (announce) => {
164
164
  toolbar: {
165
165
  label: 'Inspector',
166
166
  title: <ToolbarText text="Inspector" />,
167
- toggle: { action: INSPECTOR_ACTION, name: 'inspector' },
167
+ toggle: { action: INSPECTOR_ACTION, name: 'Inspector' },
168
168
  },
169
169
  content: (
170
- <ValueTable label="Selected task">
170
+ <ValueTable label="Selected Task">
171
171
  <ValueTableRow label="Status" value="In review" />
172
172
  <ValueTableRow label="Owner" value="Mara Chen" />
173
173
  <ValueTableRow label="Priority" value="High" />
@@ -79,14 +79,14 @@ export const createRecipe: RecipeFactory = (announce) => {
79
79
  // rail's leads the main toolbar, the drawer's floats in its container's
80
80
  // bottom-end corner. wireSidebar hands focus between the two positions.
81
81
  const railToolbar: CollapsiblePanelToolbar = {
82
- label: 'Workspace navigation',
82
+ label: 'Workspace Navigation',
83
83
  title: <ToolbarText text="Atlas" />,
84
- toggle: { action: RAIL_ACTION, name: 'navigation' },
84
+ toggle: { action: RAIL_ACTION, name: 'Navigation' },
85
85
  };
86
86
  const drawerToolbar: CollapsiblePanelToolbar = {
87
87
  label: 'Activity',
88
88
  title: <ToolbarText text="Activity" size="small" />,
89
- toggle: { action: DRAWER_ACTION, name: 'activity' },
89
+ toggle: { action: DRAWER_ACTION, name: 'Activity' },
90
90
  };
91
91
 
92
92
  const rail = () => (
@@ -95,7 +95,7 @@ export const createRecipe: RecipeFactory = (announce) => {
95
95
  side="left"
96
96
  size={232}
97
97
  collapsed={railCollapsed.value}
98
- label="Workspace navigation"
98
+ label="Workspace Navigation"
99
99
  toolbar={railToolbar}
100
100
  >
101
101
  <nav aria-label="Workspace">
@@ -119,7 +119,7 @@ export const createRecipe: RecipeFactory = (announce) => {
119
119
  <ListItem
120
120
  action="recipe-action"
121
121
  itemId="shared"
122
- label="Shared with me"
122
+ label="Shared with Me"
123
123
  icon={<LucideIcon icon={Users} name="users" />}
124
124
  selected={selected.value === 'shared'}
125
125
  />
@@ -133,7 +133,7 @@ export const createRecipe: RecipeFactory = (announce) => {
133
133
  element="main"
134
134
  header={
135
135
  <Toolbar
136
- label="Current navigation view"
136
+ label="Current Navigation View"
137
137
  leading={
138
138
  <>
139
139
  <CollapsiblePanelRelocated
@@ -155,11 +155,12 @@ export const createRecipe: RecipeFactory = (announce) => {
155
155
  <ContentItem>
156
156
  <List gap="2xs">
157
157
  <Text variant="span">
158
- <strong>Narrow the window to a compact width</strong>
158
+ <strong>Compact Width</strong>
159
159
  </Text>
160
160
  <Text variant="span" tone="quiet" size="compact">
161
- The rail and drawer start closed and open as a dismissable overlay:
162
- a backdrop, Escape, and a trapped Tab ring, all managed by the wire.
161
+ Narrow the window to a compact width: the rail and drawer start
162
+ closed and open as a dismissable overlay, with a backdrop, Escape,
163
+ and a trapped Tab ring, all managed by the wire.
163
164
  </Text>
164
165
  </List>
165
166
  </ContentItem>
@@ -175,7 +176,7 @@ export const createRecipe: RecipeFactory = (announce) => {
175
176
  label="Activity"
176
177
  toolbar={drawerToolbar}
177
178
  restoreControl={
178
- <FloatingToolbar label="Activity drawer">
179
+ <FloatingToolbar label="Activity Drawer">
179
180
  <CollapsiblePanelRelocated
180
181
  panelId="sidebar-console"
181
182
  side="bottom"
@@ -185,7 +186,7 @@ export const createRecipe: RecipeFactory = (announce) => {
185
186
  </FloatingToolbar>
186
187
  }
187
188
  >
188
- <ValueTable label="Recent activity">
189
+ <ValueTable label="Recent Activity">
189
190
  <ValueTableRow
190
191
  label="Deploy finished"
191
192
  value="2m ago"
@@ -41,10 +41,10 @@ export const createRecipe: RecipeFactory = (announce) => {
41
41
  const filtered = signal(false);
42
42
  const device = deviceClass();
43
43
  const render = () => (
44
- <section data-recipe="recipe-compact-toolbar" aria-label="Task workspace">
44
+ <section data-recipe="recipe-compact-toolbar" aria-label="Task Workspace">
45
45
  <List gap="l" controlInsets="b">
46
46
  <Toolbar
47
- label="Task workspace controls"
47
+ label="Task Workspace Controls"
48
48
  responsive="stack"
49
49
  responsiveAt="compact"
50
50
  leading={<ToolbarText text="Tasks" size="small" />}
@@ -72,23 +72,23 @@ export const createRecipe: RecipeFactory = (announce) => {
72
72
  <Select<string>
73
73
  name="recipe-sort"
74
74
  value={sort.value}
75
- ariaLabel="Sort tasks"
75
+ ariaLabel="Sort Tasks"
76
76
  fitMenu
77
77
  presentation="toolbar-borderless"
78
78
  size="compact"
79
79
  focusRingOwner="group"
80
80
  choices={[
81
- { value: 'updated', label: 'Recently updated' },
81
+ { value: 'updated', label: 'Recently Updated' },
82
82
  { value: 'priority', label: 'Priority' },
83
83
  { value: 'owner', label: 'Owner' },
84
- { value: 'created', label: 'Created date' },
84
+ { value: 'created', label: 'Created Date' },
85
85
  ]}
86
86
  />
87
87
  </ToolbarControlGroup>
88
- <ToolbarControlGroup label="Task actions" buttonAppearance="push">
88
+ <ToolbarControlGroup label="Task Actions" buttonAppearance="push">
89
89
  <button
90
90
  type="button"
91
- aria-label="Toggle filters"
91
+ aria-label="Toggle Filters"
92
92
  aria-pressed={String(filtered.value)}
93
93
  data-action="recipe-action"
94
94
  data-recipe-command="filter"
@@ -97,17 +97,17 @@ export const createRecipe: RecipeFactory = (announce) => {
97
97
  </button>
98
98
  <button
99
99
  type="button"
100
- aria-label="Refresh tasks"
100
+ aria-label="Refresh Tasks"
101
101
  data-action="recipe-action"
102
102
  data-recipe-command="refresh"
103
103
  >
104
104
  <LucideIcon icon={RefreshCw} name="refresh-cw" />
105
105
  </button>
106
106
  </ToolbarControlGroup>
107
- <ToolbarControlGroup label="More task actions" single>
107
+ <ToolbarControlGroup label="More Task Actions" single>
108
108
  <button
109
109
  type="button"
110
- aria-label="More task actions"
110
+ aria-label="More Task Actions"
111
111
  data-action="recipe-action"
112
112
  data-recipe-command="more"
113
113
  >
@@ -63,10 +63,10 @@ export const createRecipe: RecipeFactory = (announce) => {
63
63
  {/* The title and its supporting line are one heading unit. */}
64
64
  <List>
65
65
  <Toolbar
66
- label="Publish workspace update"
66
+ label="Publish Workspace Update"
67
67
  leading={
68
68
  <ToolbarText
69
- text="Publish workspace update"
69
+ text="Publish Workspace Update"
70
70
  size="xlarge"
71
71
  id="recipe-composer-title"
72
72
  />
@@ -80,7 +80,7 @@ export const createRecipe: RecipeFactory = (announce) => {
80
80
  </List>
81
81
  {status.value === 'error' && (
82
82
  <StateBanner
83
- title="Add a title before publishing"
83
+ title="Add a Title Before Publishing"
84
84
  detail="The update body and audience are preserved."
85
85
  tone="danger"
86
86
  urgency="alert"
@@ -88,7 +88,7 @@ export const createRecipe: RecipeFactory = (announce) => {
88
88
  )}
89
89
  {status.value === 'saved' && (
90
90
  <StateBanner
91
- title="Update published"
91
+ title="Update Published"
92
92
  detail="The team audience can now read it."
93
93
  tone="success"
94
94
  />
@@ -96,7 +96,7 @@ export const createRecipe: RecipeFactory = (announce) => {
96
96
  <List gap="xs" controlInsets="rl">
97
97
  <wa-input
98
98
  name="recipe-title"
99
- label="Update title"
99
+ label="Update Title"
100
100
  hint="Summarize the outcome in one line."
101
101
  required
102
102
  value={title.value}
@@ -115,9 +115,9 @@ export const createRecipe: RecipeFactory = (announce) => {
115
115
  value={audience.value}
116
116
  label="Audience"
117
117
  choices={[
118
- { value: 'team', label: 'Workspace team' },
118
+ { value: 'team', label: 'Workspace Team' },
119
119
  { value: 'reviewers', label: 'Reviewers' },
120
- { value: 'organization', label: 'Entire organization' },
120
+ { value: 'organization', label: 'Entire Organization' },
121
121
  ]}
122
122
  />
123
123
  </List>
@@ -136,7 +136,7 @@ export const createRecipe: RecipeFactory = (announce) => {
136
136
  data-action="recipe-action"
137
137
  data-recipe-command="submit"
138
138
  >
139
- Publish update
139
+ Publish Update
140
140
  </wa-button>
141
141
  </Row>
142
142
  </List>
@@ -102,7 +102,7 @@ export const createRecipe: RecipeFactory = (announce) => {
102
102
  data-action="recipe-action"
103
103
  data-recipe-command="open-record"
104
104
  >
105
- Open project
105
+ Open Project
106
106
  </wa-button>
107
107
  </Row>
108
108
  );
@@ -112,11 +112,11 @@ export const createRecipe: RecipeFactory = (announce) => {
112
112
  // control, and the record actions, and runs the full dialog height too.
113
113
  const sidebar = () => (
114
114
  <Pane
115
- label="Recent projects"
115
+ label="Recent Projects"
116
116
  header={
117
117
  <Toolbar
118
- label="Recent projects"
119
- leading={<ToolbarText text="Recent projects" />}
118
+ label="Recent Projects"
119
+ leading={<ToolbarText text="Recent Projects" />}
120
120
  />
121
121
  }
122
122
  >
@@ -131,7 +131,7 @@ export const createRecipe: RecipeFactory = (announce) => {
131
131
  label={record.name}
132
132
  header={
133
133
  <Toolbar
134
- label="Project details"
134
+ label="Project Details"
135
135
  leading={
136
136
  <ToolbarText
137
137
  text={record.name}
@@ -152,7 +152,7 @@ export const createRecipe: RecipeFactory = (announce) => {
152
152
  footer={recordActions()}
153
153
  >
154
154
  <List controlInsets="tb">
155
- <ValueTable label="Project details">
155
+ <ValueTable label="Project Details">
156
156
  <ValueTableRow label="Owner" value={record.owner} />
157
157
  <ValueTableRow label="Status" value={record.state} />
158
158
  <ValueTableRow label="Updated" value={record.updated} />
@@ -166,7 +166,7 @@ export const createRecipe: RecipeFactory = (announce) => {
166
166
  const record = records[selected.value];
167
167
  return (
168
168
  <List>
169
- <ValueTable label="Project details">
169
+ <ValueTable label="Project Details">
170
170
  <ValueTableRow label="Owner" value={record.owner} />
171
171
  <ValueTableRow label="Status" value={record.state} />
172
172
  <ValueTableRow label="Updated" value={record.updated} />
@@ -180,7 +180,7 @@ export const createRecipe: RecipeFactory = (announce) => {
180
180
  return (
181
181
  <section
182
182
  data-recipe="recipe-list-detail-dialog"
183
- aria-label="Project details dialog"
183
+ aria-label="Project Details Dialog"
184
184
  >
185
185
  <Row hAlign="center">
186
186
  <DialogSurface
@@ -197,10 +197,10 @@ export const createRecipe: RecipeFactory = (announce) => {
197
197
  data-action="recipe-action"
198
198
  data-recipe-command="open"
199
199
  >
200
- Open project details
200
+ Open Project Details
201
201
  </wa-button>
202
202
  {compact ? (
203
- <wa-dialog label="Project details" open={open.value} with-footer>
203
+ <wa-dialog label="Project Details" open={open.value} with-footer>
204
204
  <SplitView
205
205
  id="recipe-projects"
206
206
  label="Projects"
@@ -208,7 +208,7 @@ export const createRecipe: RecipeFactory = (announce) => {
208
208
  detailActive={detailOpen.value}
209
209
  listTitle="Recent projects"
210
210
  detailTitle={records[selected.value].name}
211
- backLabel="Back to projects"
211
+ backLabel="Back to Projects"
212
212
  list={<List>{projectRows(true)}</List>}
213
213
  detail={compactDetail()}
214
214
  />
@@ -227,12 +227,12 @@ export const createRecipe: RecipeFactory = (announce) => {
227
227
  data-action="recipe-action"
228
228
  data-recipe-command="open-record"
229
229
  >
230
- Open project
230
+ Open Project
231
231
  </wa-button>
232
232
  </wa-dialog>
233
233
  ) : (
234
234
  <wa-dialog
235
- label="Project details"
235
+ label="Project Details"
236
236
  open={open.value}
237
237
  without-header
238
238
  >