@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
@@ -54,7 +54,7 @@ import { remify } from '@kerfjs/ui/css-values';
54
54
  import { DialogSurface } from '@kerfjs/ui/surface-scaffold';
55
55
 
56
56
  <DialogSurface preferredWidth={remify(832)} viewportGutter={remify(8)} maxHeight="viewport" bodyInset="none">
57
- <wa-dialog label="Close workspace">{/* Application-owned content and footer */}</wa-dialog>
57
+ <wa-dialog label="Close Workspace">{/* Application-owned content and footer */}</wa-dialog>
58
58
  </DialogSurface>;
59
59
  ```
60
60
 
@@ -86,12 +86,129 @@ itself stops scrolling. Combine it with `bodyInset="none"`.
86
86
 
87
87
  ```tsx
88
88
  <DialogSurface size="large" height={remify(640)} viewportGutter={remify(8)} bodyInset="none" bodyLayout="fill">
89
- <wa-dialog label="Repository status">
89
+ <wa-dialog label="Repository Status">
90
90
  <SplitView id="repo-split" label="Changes" listWidth={{ size: 432, maxFraction: 0.5 }} list={list} detail={detail} />
91
91
  </wa-dialog>
92
92
  </DialogSurface>;
93
93
  ```
94
94
 
95
+ ### Chat dialogs: a floating footer over a filling transcript
96
+
97
+ A chat-style dialog keeps a heading or notices above one scrolling transcript
98
+ and floats its composer over the body's block end. Two props compose it:
99
+
100
+ - `bodyLayout="fill-last"` stacks the body's children: the leading children
101
+ keep their natural height, and the last one takes the remaining height and
102
+ owns its scrolling (a `scrollable` `List` transcript). The body itself stops
103
+ scrolling. `PopoverDialogSurface` accepts the same layout.
104
+ - `footerPresentation="floating"` lays the footer over the body's block end,
105
+ `footerOffset` (a branded `CssLength`, default the 16px container inset)
106
+ from the panel's inline and block-end edges, on a transparent background.
107
+ The modal panel is its positioning context. The default `docked` footer
108
+ sits below the body as before.
109
+
110
+ The register measures the footer (it follows a composer that grows, wraps, or
111
+ hides) and the body reserves that height plus the offset after its own inset,
112
+ with a matching `scroll-padding`. Flowing body content scrolls beneath the
113
+ floating footer and its end stays reachable above it; with `fill` or
114
+ `fill-last` the filling child's scrollport ends at the footer's top. Content
115
+ is never hidden beneath the footer.
116
+
117
+ To let a filling transcript scroll visibly beneath the floating footer, opt
118
+ its scroller in with `scrollClearance="block-end"` (a `scrollable` `List`, or a
119
+ `Pane`, as the `fill` or last `fill-last` child). The body then stops
120
+ reserving the footer below that child, so the child reaches the panel's block
121
+ end, and publishes the footer's clearance (measured height plus offset) as the
122
+ inherited `--kui-scroll-clearance-block-end` context. The child adds it to its
123
+ own block-end padding and `scroll-padding`: messages pass beneath the
124
+ transparent footer mid-scroll, the last message rests above the composer at
125
+ the end, and focus or `scrollIntoView` never leaves an item hidden beneath it.
126
+ The context is general: chrome that overlays a region's block-start or
127
+ block-end edge publishes `--kui-scroll-clearance-block-start` / `-end`, and a
128
+ scroll owner opts in per edge (see `docs/layout.md` "Scroll clearance"). A
129
+ dialog body clears any clearance inherited from the page.
130
+
131
+ Choose the offset like any other responsive value: the scaffold adds no
132
+ breakpoint. An 8px offset lines a composer up with the transcript's
133
+ content-item edges when the body has `bodyInset="none"` and list geometry;
134
+ `viewportGutter` and `footerOffset` can follow `deviceClass()` when a phone
135
+ needs tighter edges. Put a [`Composer`](./composer.md) in the floating footer
136
+ with `footerInset="none"`: its raised frame and shadow lift it over the
137
+ transcript, and its field grows the measured footer. With `submitOnEnter` and
138
+ `wireComposers(root)`, Enter sends through the same action as the Send button.
139
+
140
+ ```tsx
141
+ <DialogSurface height={remify(560)} viewportGutter={remify(8)} bodyInset="none" bodyLayout="fill-last" footerPresentation="floating" footerOffset={remify(8)} footerInset="none">
142
+ <wa-dialog label="Assistant">
143
+ <StateBanner title="Answers use the latest nightly build." tone="neutral" />
144
+ <List gap="xs" controlInsets="tb" scrollable scrollClearance="block-end">
145
+ {messages.map((message) => (
146
+ <ContentItem bubble={message.mine ? 'end' : 'start'} appearance={message.mine ? 'brand-loud' : 'neutral'}>
147
+ <Text variant="span">{message.text}</Text>
148
+ </ContentItem>
149
+ ))}
150
+ </List>
151
+ <Composer submitOnEnter slot="footer" id="assistant-message" name="message" label="Message" placeholder="Ask about this build" actions={<wa-button variant="brand" data-action="send-message">Send</wa-button>} />
152
+ </wa-dialog>
153
+ </DialogSurface>;
154
+ ```
155
+
156
+ ### A floating title bar
157
+
158
+ `headerPresentation="floating"` lays the dialog's header (its title and close
159
+ control) over the body's block-start edge on the translucent chrome material,
160
+ `--kui-chrome-material-raised` (the raised panel surface at 80% opacity)
161
+ behind `--kui-chrome-material-filter` (a blur and saturation backdrop filter).
162
+ Content passing beneath the title bar reads as softened color, not as
163
+ competing text. The default `docked` header sits above the body as before.
164
+
165
+ The register measures the header the same way it measures the footer (it
166
+ follows a title that wraps, text that grows, or a `without-header` toggle).
167
+ The body reserves the measured height before its own inset, with a matching
168
+ `scroll-padding`, so flowing content starts below the title bar and scrolls
169
+ beneath it. A dialog without the register has no measurement, so its header
170
+ stays docked rather than covering content.
171
+
172
+ To let a filling transcript scroll visibly beneath the title bar, make it the
173
+ body's only child (`bodyLayout="fill"`, or `fill-last` with no leading rows)
174
+ and opt its scroller in with block-start `scrollClearance`. The body then
175
+ stops reserving the header, so the child reaches the panel's block-start
176
+ edge, and publishes the header's height as `--kui-scroll-clearance-block-start`.
177
+ Leading `fill-last` rows (a notice above the transcript) keep the body's
178
+ reservation: they never sit beneath the title bar. Combined with a floating
179
+ composer and `scrollClearance="both"`, the transcript scrolls beneath both:
180
+
181
+ ```tsx
182
+ <DialogSurface height={remify(560)} viewportGutter={remify(8)} bodyInset="none" bodyLayout="fill" headerPresentation="floating" footerPresentation="floating" footerOffset={remify(8)} footerInset="none">
183
+ <wa-dialog label="Release Thread">
184
+ <ToolbarText slot="label" text="Release Thread" size="xlarge" description="Engineering · 5.2 candidate" />
185
+ <List gap="xs" controlInsets="tb" scrollable scrollClearance="both">
186
+ {bubbles}
187
+ </List>
188
+ <Composer submitOnEnter slot="footer" id="thread-message" name="message" label="Message" placeholder="Reply to the thread" actions={<wa-button variant="brand" data-action="send-message">Send</wa-button>} />
189
+ </wa-dialog>
190
+ </DialogSurface>;
191
+ ```
192
+
193
+ Like a `Pane`'s overlay header, the title bar draws its divider as scroll
194
+ state: a 1px line along its bottom edge only while the body, or a filling
195
+ child that reaches beneath it, is scrolled away from its top, and none at
196
+ rest. The register follows those scrollers (a nested scroller that starts
197
+ below the title bar never counts), and the line is an inset shadow, so it
198
+ never moves the header or the content.
199
+
200
+ The material turns opaque under `prefers-reduced-transparency: reduce` or
201
+ `prefers-contrast: more`, and where the browser lacks `backdrop-filter`; in
202
+ forced colors the header paints `Canvas` with a `CanvasText` bottom edge.
203
+
204
+ For a title with a short supporting line, slot a `ToolbarText` as the label
205
+ (`<ToolbarText slot="label" text="Release Thread" size="xlarge" description="Engineering · 5.2 candidate" />`).
206
+ On the floating title bar an untoned `ToolbarText` keeps AA by itself: its
207
+ title takes the normal text color and its description lifts halfway toward
208
+ it. Any other quiet copy you place there can fall below AA over the content
209
+ passing beneath it, so keep it in the body (see
210
+ [Accessibility › Chrome material](accessibility.md#chrome-material)).
211
+
95
212
  Treat a dialog body as a `List` by default. When its rows or sections own their
96
213
  standard list geometry, set `bodyInset="none"` so the dialog does not add a
97
214
  second inset. Bare prose is still a list child: wrap it in `ListInsetText` so its
@@ -109,8 +226,22 @@ hand-built popover with application chrome CSS. It is a native
109
226
  `popover="auto"` section with `role="dialog"` that owns Kerf's dialog chrome
110
227
  (raised surface, border, large radius and shadow, and a backdrop) and the
111
228
  same geometry props as a modal `DialogSurface`: `size`, `preferredWidth`,
112
- `viewportGutter` (16px by default), `height` / `maxHeight`, and `bodyLayout`.
113
- Name it with `label` or `labelledBy`.
229
+ `viewportGutter` (16px by default), `height` / `maxHeight`, and `bodyLayout`
230
+ (`flow`, `fill`, or `fill-last`).
231
+ Name it with `label` or `labelledBy`. To give it an accessible description,
232
+ pass `describedBy`, the id of the element that holds the summary. It renders as
233
+ `aria-describedby` on the `role="dialog"` root and usually names a title
234
+ `ToolbarText`'s `descriptionId`:
235
+
236
+ ```tsx
237
+ <PopoverDialogSurface id="connection-details" labelledBy="connection-title" describedBy="connection-summary" bodyLayout="fill-last">
238
+ <Toolbar label="Connection Details Heading" leading={<ToolbarText text="Connection Details" size="xlarge" id="connection-title" description="Connected to the build server." descriptionId="connection-summary" />} />
239
+ {details}
240
+ </PopoverDialogSurface>;
241
+ ```
242
+
243
+ `DialogSurface` has no `describedBy`. It wraps a `wa-dialog` that the app
244
+ authors, so the app names and describes that dialog on its own markup.
114
245
 
115
246
  Open it with a control's `popoverTarget` (or `showPopover()`); the browser
116
247
  places it centered in the top layer, light-dismisses it, and restores nothing
@@ -121,8 +252,8 @@ layer and never wider than its container, for a catalog specimen or an inline
121
252
  preview.
122
253
 
123
254
  ```tsx
124
- <button type="button" popoverTarget="repository-status">Repository status</button>
125
- <PopoverDialogSurface id="repository-status" label="Repository status" preferredWidth={remify(1024)} height={remify(640)} viewportGutter={remify(8)} bodyLayout="fill">
255
+ <button type="button" popoverTarget="repository-status">Repository Status</button>
256
+ <PopoverDialogSurface id="repository-status" label="Repository Status" preferredWidth={remify(1024)} height={remify(640)} viewportGutter={remify(8)} bodyLayout="fill">
126
257
  <SplitView id="repo-split" label="Changes" list={list} detail={detail} />
127
258
  </PopoverDialogSurface>;
128
259
  ```
@@ -136,4 +267,4 @@ preserve `data-morph-skip-children` when its upgraded light-DOM items must retai
136
267
  identity across Kerf rerenders. Import `@kerfjs/ui/popup-menu/register` once for
137
268
  the focus handoff fix when wrapping a raw `wa-dropdown`.
138
269
 
139
- 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.
270
+ Import `@kerfjs/ui/surface-scaffold/register` once when using `DialogSurface`: it registers `wa-dialog`, measures each surface's footer for a floating footer's body clearance, draws a floating header's divider while content is scrolled beneath it, 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.
@@ -136,7 +136,7 @@ The app owns each stack's view array and the navigator's active tab. Call
136
136
  `wireTabNavigator` for tab selection and `wireNavStack` for back navigation
137
137
  and transitions, each on its own component root. Push a detail by appending
138
138
  a view with its own `toolbar` and `pane`; switching tabs keeps that view
139
- mounted. The catalog's **NavStack inside a tab** example demonstrates the
139
+ mounted. The catalog's **NavStack Inside a Tab** example demonstrates the
140
140
  push, tab switch, and return flow.
141
141
 
142
142
  Set a tab's `appearance: "sunken"` when its scene owns scrolling and should
@@ -169,6 +169,43 @@ so it never moves the bar or a tab.
169
169
  the wiring reports content below. It does not suppress the sunken-surface
170
170
  separator; use `separator="hidden"` for that.
171
171
 
172
+ ## Translucent bar
173
+
174
+ `barPresentation="overlay"` lays the tab bar over the active scene's bottom
175
+ edge on the translucent chrome material (`--kui-chrome-material` behind
176
+ `--kui-chrome-material-filter`), so the scene's content scrolls visibly beneath
177
+ it, as under an iOS tab bar. The default, `docked`, gives the bar its own band.
178
+
179
+ ```tsx
180
+ <TabNavigator
181
+ id="mail"
182
+ label="Mail Sections"
183
+ barPresentation="overlay"
184
+ active={active.value}
185
+ tabs={tabs}
186
+ />;
187
+ ```
188
+
189
+ `wireTabNavigator` measures the live bar's border box (bottom safe area and its
190
+ 1px top edge included), publishes it on the root, and marks the root
191
+ `data-bar-overlay="measured"`. The scenes then hand that height to their
192
+ content as the block-end `--kui-edge-inset-*` context: a plain scene pads and
193
+ scroll-pads by it, and a sole `Pane` or `NavStack` clears it itself, so the
194
+ last item, and anything focused or scrolled into view, rests just above the
195
+ bar. A `NavStack` with its own bottom toolbar pads that toolbar for the bar as
196
+ it would for a bottom safe area: the toolbar's controls sit just above the tab
197
+ bar while its material runs on beneath it. Until the wiring runs, the bar stays
198
+ docked.
199
+
200
+ Over the material, unselected labels lift halfway toward the normal text color
201
+ and the selected label mixes 40% of it into the brand color, so both keep AA
202
+ over any content beneath. The material turns opaque under reduced
203
+ transparency, increased contrast, or without `backdrop-filter`, and paints
204
+ `Canvas` with a `CanvasText` top edge in forced colors. The scroll divider still
205
+ shows only while more content lies below. Like the NavStack overlay bars, the
206
+ tab bar ends where a classic (space-taking) scrollbar's track begins, so the
207
+ active scene's thumb and buttons stay visible and usable.
208
+
172
209
  ## Tab badges
173
210
 
174
211
  Give a tab a `badge` (a count or short string such as `3`, `"99+"`, or
@@ -11,22 +11,22 @@ public-signature artifact.
11
11
 
12
12
  ## Strengthened contracts
13
13
 
14
- | ID | Compile-time guarantee |
15
- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
16
- | `KUI-T001` | `ListHeader` is a passive heading, a fully named/icon-bearing trailing action, or a controlled disclosure with `action` + `expanded`. Props ignored by the selected mode are rejected. |
17
- | `KUI-T002` | `Select` has a visible `label` or an `ariaLabel`. |
18
- | `KUI-T003` | A literal `Select.value` belongs to its literal `choices`. |
19
- | `KUI-T004` | A literal `SegmentedControl.value` belongs to its literal `choices`. |
20
- | `KUI-T005` | `expanded`, `expandAction`, and `expandLabel` exist only on `TokenSearchField({ collapsible: true })`. |
21
- | `KUI-T006` | `TokenSearchEditorAttributes` accepts application `data-*` metadata but rejects component-owned identity, morph, count, and placeholder attributes. |
22
- | `KUI-T007` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code. |
23
- | `KUI-T008` | A literal `TabNavigator.active` id belongs to its literal `tabs`. |
24
- | `KUI-T009` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback. |
25
- | `KUI-T010` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all AppTab, TabBar, ToolbarControlGroup, and Select presentation axes; the convenience root barrel re-exports them. |
26
- | `KUI-T011` | Semantic component zones use the recursive `KerfUiContent` type: `SafeHtml`, runtime-empty booleans/nullish values, and readonly nested arrays are valid; arbitrary strings, numbers, and signals are rejected. Explicit text positions such as `ListInsetText` retain their text exception. |
27
- | `KUI-T012` | `List.gap` accepts finite `UiSpaceName` shorthands or a complete branded `CssLength`; raw strings and non-standalone `CssLengthExpression` arithmetic are rejected. `calc(plus(...))` promotes a typed expression to a complete value. |
28
- | `KUI-T013` | CSS-valued props preserve property grammar: `List.flex` uses `CssFlex`, `Skeleton` dimensions use `CssSize`/`CssLength`, and choice icons and `LucideIcon.color` use `CssForegroundColor` (foreground tokens via `uiColor()`, `foregroundColorVar()`, or custom values via `foregroundColor()`; fill tokens such as `uiColor('success-fill-quiet')` are rejected); row declaration strings are absent. |
29
- | `KUI-T014` | Stable single-root visual components accept the explicit native `slot` attribute. Multi-root `ResizableRegion` and `CollapsiblePanel` remain excluded because no single returned element can own the assignment, and other arbitrary native attributes remain rejected. |
14
+ | ID | Compile-time guarantee |
15
+ | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
16
+ | `KUI-T001` | `ListHeader` is a passive heading, a fully named/icon-bearing trailing action, or a controlled disclosure with `action` + `expanded`. Props ignored by the selected mode are rejected. |
17
+ | `KUI-T002` | `Select` has a visible `label` or an `ariaLabel`. |
18
+ | `KUI-T003` | A literal `Select.value` belongs to its literal `choices`. |
19
+ | `KUI-T004` | A literal `SegmentedControl.value` belongs to its literal `choices`. |
20
+ | `KUI-T005` | `expanded`, `expandAction`, and `expandLabel` exist only on `TokenSearchField({ collapsible: true })`. |
21
+ | `KUI-T006` | `TokenSearchEditorAttributes` accepts application `data-*` metadata but rejects component-owned identity, morph, count, and placeholder attributes. |
22
+ | `KUI-T007` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code. |
23
+ | `KUI-T008` | A literal `TabNavigator.active` id belongs to its literal `tabs`. |
24
+ | `KUI-T009` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback. |
25
+ | `KUI-T010` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all AppTab, TabBar, ToolbarControlGroup, and Select presentation axes; the convenience root barrel re-exports them. |
26
+ | `KUI-T011` | Semantic component zones use the recursive `KerfUiContent` type: `SafeHtml`, runtime-empty booleans/nullish values, and readonly nested arrays are valid; arbitrary strings, numbers, and signals are rejected. Explicit text positions such as `ListInsetText` retain their text exception. |
27
+ | `KUI-T012` | `List.gap` accepts finite `UiSpaceName` shorthands or a complete branded `CssLength`; raw strings and non-standalone `CssLengthExpression` arithmetic are rejected. `calc(plus(...))` promotes a typed expression to a complete value. |
28
+ | `KUI-T013` | CSS-valued props preserve property grammar: `List.flex` uses `CssFlex`, `Skeleton` dimensions use `CssSize`/`CssLength`, and choice icons and `LucideIcon.color` use `CssForegroundColor` (foreground tokens via `uiColor()`, `foregroundColorVar()`, or custom values via `foregroundColor()`; fill tokens such as `uiColor('success-fill-quiet')` are rejected); a row `iconTile` is a finite tone or a `CssColor` fill (custom values via `fillColor()`) with an optional `CssForegroundColor` glyph, and a `ChoiceChips` swatch is a `CssColor` fill (`fillColor()`, `uiColor()`, `colorVar()`) or `'transparent'`; a `fillColor()` value is a plain `CssColor` and is rejected where a `CssForegroundColor` is required; row declaration strings are absent. |
29
+ | `KUI-T014` | Stable single-root visual components accept the explicit native `slot` attribute. Multi-root `ResizableRegion` and `CollapsiblePanel` remain excluded because no single returned element can own the assignment, and other arbitrary native attributes remain rejected. |
30
30
 
31
31
  `KerfUiContent` lets conditionals and mapped component collections be direct
32
32
  siblings without an otherwise-unnecessary `Fragment`:
@@ -110,7 +110,9 @@ Catalog entries may also publish `cssValueProps`. Each path names its grammar,
110
110
  finite shorthands, canonical and exceptional scale steps, accepted typed
111
111
  helpers, expression-only helpers, raw escape policy, and examples. The analyzer
112
112
  uses that same metadata for direct component calls and JSX, including nested
113
- paths such as `choices[].color`; consumer catalogs receive identical checks.
113
+ paths such as `choices[].color` (each array item's member) and
114
+ `iconTile.color` (an object-valued prop's member, skipped when the prop is a
115
+ tone string); consumer catalogs receive identical checks.
114
116
  Dynamic values remain a type-system responsibility rather than being guessed.
115
117
 
116
118
  ## Component ownership (`KUI-L019`–`KUI-L023`)
package/docs/ui-doctor.md CHANGED
@@ -139,6 +139,77 @@ The browser evaluator is different: it runs the application and is disabled by d
139
139
 
140
140
  An unavailable or failed stage does not prevent independent stages from reporting. Its final exit is still a configuration failure, so a partial run cannot appear clean.
141
141
 
142
+ ## Interface copy capitalization
143
+
144
+ The analyzer stage also checks visible label copy against the package
145
+ capitalization rule in [Writing interface copy](./writing.md#capitalization).
146
+ Names of commands, controls, views, and fields use title style, following
147
+ Apple's guidance. `KUI-D040` (warning) reports a string or template literal
148
+ passed to a known title-style prop that leaves a word lowercase where title style
149
+ capitalizes it. For example, `<wa-button>Save draft</wa-button>` should be
150
+ "Save Draft", and `<ListHeader label="Needs attention" />` should be "Needs
151
+ Attention". The action names the repaired label.
152
+
153
+ The check reads `.tsx` and `.jsx` source text only and skips test modules.
154
+ Fixture components that tests render (for example
155
+ `tests/browser/fixtures/*.tsx`) are not test modules and are checked: they
156
+ stand in for application screens, so their copy follows the same rule. It
157
+ covers these string literals:
158
+
159
+ - `ToolbarText` titles: text with `headingLevel`, a large size, or in a
160
+ toolbar or panel `title`, `header`, or (unless small) `leading` slot;
161
+ - `ListHeader`, `ListActionRow`, `SplitButton`, and `Combobox` labels;
162
+ - `CatalogExample` and `CatalogExampleStack` labels, which render as a
163
+ `ListHeader` heading (their `note` stays sentence style and is not read);
164
+ - `EmptyState` and `StateBanner` titles;
165
+ - `PopupMenu` trigger text and item and heading labels, including submenus
166
+ declared in the same file;
167
+ - `SegmentedControl`, `Select`, and `TabNavigator` choice and tab labels;
168
+ - `NavStack` view titles and `backText`, and `SplitView` `backLabel`;
169
+ - `FieldLabel` text and the static text of a `button` or `wa-button`;
170
+ - the `name` of a panel toolbar's `toggle: { action, name }` object literal
171
+ (Workbench panels, `CollapsiblePanel`), which names the toggle group and
172
+ completes its "Show …" / "Hide …" label;
173
+ - region and landmark names: the `label` of `Pane`, `Workbench`, `Toolbar`,
174
+ `ToolbarControlGroup`, `TabBar`, `NavStack`, `CollapsiblePanel`,
175
+ `FloatingToolbar`, and `ResizableRegion`; the `ariaLabel` of `SunkenPanel`
176
+ and `List`; a `Pane`'s `contentLabel`; the panel, toolbar, and pane labels
177
+ in `Workbench` (`leftRail`, `rightRail`, `bottomDrawer`, `mainToolbar`,
178
+ `mainBottomToolbar`, `mainPane`), `CollapsiblePanel` (`toolbar`,
179
+ `bottomToolbar`, `pane`), and `NavStack` (`views`, `bottomToolbar`)
180
+ configuration declared in the same file; and the `aria-label` of a
181
+ `section`, `nav`, `aside`, `main`, `header`, `footer`, `form`, or `search`
182
+ element or an element with a landmark `role`;
183
+ - the `label` of a raw `wa-input`, `wa-select`, `wa-textarea`, or
184
+ `wa-radio-group`, which is a field or group label;
185
+ - the accessible names of icon-only controls, which the rule also gives title
186
+ style: `IconButton` and `IconToggleButton` `label`, `PopupMenu` `label`,
187
+ `SplitButton` `optionsLabel`, `TokenSearchField` `clearLabel`, and the
188
+ `aria-label` of a `button` or `wa-button` whose only content is an icon. A
189
+ button with visible text is judged by that text, never by its `aria-label`.
190
+
191
+ The check is deliberately one-directional, to keep false positives rare:
192
+
193
+ - It reports only lowercase words. It never reports a capitalized short word,
194
+ because Apple capitalizes a phrasal verb's particle ("Log In to the
195
+ Server").
196
+ - It leaves text ending in `.`, `!`, or `?` to sentence style.
197
+ - It judges only a template literal's static words, so
198
+ `` `Edit ${name} shortcut` `` is reported and the interpolated value never
199
+ is. A word joined to an interpolation (`` `${count}s` ``) is skipped.
200
+ - It ignores other dynamic values and every sentence-style surface: descriptions,
201
+ details, placeholders, tooltips, status text, and option text, including
202
+ `wa-checkbox`, `wa-switch`, and `wa-radio` labels. It does not read
203
+ `LoadingSpinner` or `Skeleton` labels, which announce a status.
204
+ - It skips tokens containing digits, code, paths, or URLs, and lowercase-led
205
+ brands such as iOS and macOS.
206
+ - It keeps a minor word inside a longer hyphenated compound lowercase, as
207
+ between separate words ("Edge-to-Edge", "Up-to-Date").
208
+ - It does not read `ContentItem` `title`, which is a native tooltip.
209
+
210
+ Suppress `KUI-D040` for a file whose copy is a lowercase brand or product
211
+ name.
212
+
142
213
  ## Full and changed modes
143
214
 
144
215
  `--full` is the default and analyzes the selected package. `--changed` reads tracked and untracked paths from Git unless one or more `--path` values are supplied. An empty changed set is a configuration error instead of a false-clean success. With `--package`, workspace-relative Git paths are converted to package-relative paths before TypeScript, ESLint, and analyzer selection; paths outside the selected package are ignored.
package/docs/ux-demo.md CHANGED
@@ -21,13 +21,13 @@ Each `?component=recipe-*` route uses public package primitives, real wiring,
21
21
  deterministic state, and semantic layout owners from the [recipe guide](./recipes.md).
22
22
 
23
23
  - It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
24
- - The `ListActionRow` route's Run tests row composes a trailing `PopupMenu` (`trailingMenu`) with Edit and Delete commands, revealed on interaction, that also opens at the pointer when the row's primary button is right-clicked.
24
+ - The `ListActionRow` route's Run Tests row composes a trailing `PopupMenu` (`trailingMenu`) with Edit and Delete commands, revealed on interaction, that also opens at the pointer when the row's primary button is right-clicked.
25
25
  - The `ListActionRow` route demonstrates sibling primary/trailing native controls, independent disabled states, controlled selection, first-line leading-icon alignment for wrapped labels, and an application-owned native popover relationship without nesting controls. The `ListItem` route demonstrates the same multiline leading-icon anchor alongside its single-control navigation contract.
26
- - The `PopupMenu` route's context example opens one app-owned menu either at the row's right-click point or below its adjacent, keyboard-operable Demand actions button. The Nested decisions example shows the automatic phone-width submenu placement above or below its parent list, with native ArrowLeft return, Escape dismissal, and child selection.
26
+ - The `PopupMenu` route's context example opens one app-owned menu either at the row's right-click point or below its adjacent, keyboard-operable Demand Actions button. The Nested Decisions example shows the automatic phone-width submenu placement above or below its parent list, with native ArrowLeft return, Escape dismissal, and child selection.
27
27
  - The `AppTab` and `TabBar` routes are focused component specimens: AppTab shows its selected, compact, segmented, icon-only, truncation, placeholder, pending, metadata, and close-icon axes inside the required tablist context; TabBar shows its rail, segmented, inspector, allocation, placement, and accessory-zone axes, including an adjacent action plus a separately pinned end action whose tabs overflow at narrow widths, without application readouts. The pinned leading-tab specimen uses native strip scrolling so momentum and the final tab's end position remain natural; `snapTabs` stays an opt-in for applications that want whole-tab settling. The separate `Application tabs` composition route demonstrates controlled selection, matching panel content, overflow, adding (a new tab opens `pending` with a spinner and stays selectable while its panel shows a `Skeleton` heading until a simulated load completes), closing, keyboard/pointer reordering, and application-owned ordering. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
28
28
  - Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, and feedback scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component. The former aggregate Web Awesome theme specimen is intentionally omitted because its component families are covered by those focused routes.
29
29
  - `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, recommendation, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Functional groups follow their authored importance order; components precede compositions inside each group, equal-importance entries use alphabetical order, and the Recipes group comes last. Composition names stay concise and project a visible `Composition` sidebar tag. Non-supported Web Awesome recommendations project to a visible `Discouraged` tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<public-subpath>.tsx` from their canonical `delivery.browserImport` or opt-in `delivery.moduleImport`.
30
- - Every detail footer groups compact `Demo source` and `Guidance` actions; first-party component details also expose `Component source`. Web Awesome details say `Integration guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
30
+ - Every detail footer groups compact `Demo Source` and `Guidance` actions; first-party component details also expose `Component Source`. Web Awesome details say `Integration Guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
31
31
  - The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. Catalog entries can supply short reusable trailing tags; the Web Awesome projection uses them to label exactly the 15 discouraged choices. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
32
32
  - The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with supporting copy in its own aligned row; the current-view heading is also a toolbar. The shared Catalog sidebar includes a filter for entry and heading names that works across first-party and ecosystem sections, including collapsed sections. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The preview stage sits directly in the sunken main Pane, with its canvas padded by the medium spacing token. The stage fills the preview scroller for short specimens and grows with taller ones; every background choice follows that same shared behavior in this demo and in consumers. The checkerboard belongs to that stage, rather than a viewport-height wrapper or nested card, and one explicitly repeated SVG tile keeps the pattern continuous through below-fold content. A lightly separated footer keeps live status above compact source, guidance, and relationship groups. The focused `ResizableRegion` specimen stretches its surface through the available stage height and reports committed width in that shared status footer; narrow layouts retain local horizontal access when the pane is resized wider than the stage, and the split remains readable at 200% root scaling. Geometry coverage checks wide, intermediate, narrow, and 200% zoom layouts.
33
33
  - The composer-form recipe demonstrates one coherent visible form surface with toolbar title/supporting-copy hierarchy, fields and actions on the shared 8px control gutter, and 24px major rhythm. Its conditional error or success `StateBanner` is the only nested semantic surface. Reset synchronizes the upgraded Web Awesome fields' live value properties with their empty controlled attributes and announces `Draft reset`; browser coverage preserves behavior and verifies wide, narrow, dark, validation, success, reset, 200% zoom, and forced-colors states.
@@ -35,7 +35,7 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
35
35
  - Catalog-only brand marks, preview dimensions, checkerboard tiles, and the compound display-settings control retain local geometry because they are specimen chrome or intrinsic control anatomy rather than reusable application spacing roles.
36
36
  - The menu composition route demonstrates toolbar/content/footer anatomy, 24px major gaps, 8/1/8 child geometry, the full-width `ListHeader` count-or-badge/action split, logical-end 44px action targets with 18px visuals, and 44px row/header/toolbar targets. Its one chevron is a production 18px `DisclosureArrow` inside a controlled Tools header that actually reveals and hides content; ordinary Projects navigation has no false disclosure affordance. Browser assertions cover stable naming, expanded/content transitions, visual rotation, node identity, reduced motion, RTL alignment and containment, and wide, narrow, dark, and 200% layouts. The focused ListHeader route separately shows the shared neutral count pill for Attachments, Notes (including zero), and Duplicates plus a non-count legacy badge, full-width logical-end actions, and no disclosure without content.
37
37
  - The `DisclosureArrow` route renders the production 18px root-scaled default without a catalog-only size override. Its two native buttons independently control the default right-to-down chevron and a recognizable right-facing replacement arrow with left-to-up custom directions; both use shortest-path rotation and their names stay stable while `aria-expanded` communicates state. Browser coverage verifies the custom clockwise path, pointer and native keyboard activation, focus, narrow and 200%-zoom stacking with contained labels, consumer sizing, and the independent `.5` Web Awesome glyph scale retained by Kerf `Select`.
38
- - A detail with relationships renders one content-fitted `Components` footer selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Its popup owns the standard surface inset so headings and even long component names stay clear of the menu border. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
38
+ - A detail with relationships renders one content-fitted `Components` footer selector: `Uses` links to direct dependencies and `Used By` links to reverse dependents. Its popup owns the standard surface inset so headings and even long component names stay clear of the menu border. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
39
39
  - Focused demos ship no local stylesheets, inline styles, or styling-only class names. They compose Kerf components first, Web Awesome surfaces where Kerf has no equivalent, and ordinary semantic HTML for the remainder. The catalog-owned `CatalogExample.viewport` contract supplies the few specimen constraints needed to demonstrate layout behavior. Recipes follow the same rule: they ship no stylesheets, inline styles, or styling-only classes, and the catalog frames each one in the `CatalogExample` viewport its exported `presentation` declares (full-width application arrangements span the canvas measure; narrower recipes sit in the left-aligned example column). Demo-only CSS is limited to the document-shell `style.css`, which carries no recipe rules. Decorative chrome and Web Awesome controls share the same semantic system palette instead of carrying a separate demo identity.
40
40
  - Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. Individual Web Awesome action, form, layout, navigation, feedback, media, and helper routes provide focused theme specimens without a redundant aggregate route; ListItem and ListHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; the Headers composition demonstrates direct toolbar headings with an extra-large title, optional supporting copy, and grouped icon/actions; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone with tone-tinted badges, a scoped override, and a placeholder with and without a badge, the badged placeholder in every tone so captures cover the solid badge skeleton; the Application tabs composition uses a Web Awesome card with semantic header/footer slots and Kerf `Text` while demonstrating overflow, edge autoscroll, and controlled pointer/keyboard reordering.
41
41
  - The ToolbarControlGroup route also demonstrates the public compact/tight,
@@ -53,11 +53,11 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
53
53
  - The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
54
54
  - The focused Toast route calls Web Awesome's documented programmatic stack API and notes that Hot Sheet 2 still uses its own app-level toast implementation; the catalog demonstrates ecosystem support without implying current product adoption.
55
55
  - Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, validates offline source/guidance hrefs and accessible wide/narrow/200%-zoom wrapping, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup and responsive find behavior, single/multiline search alignment, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
56
- - The production build keeps the catalog shell and Kerf routes in the entry chunk and moves the third-party code loaded at startup (the Web Awesome select and popup-menu registrations with their Lit and Floating UI runtime) into an eagerly preloaded `vendor` chunk, so neither nears the largest-chunk limit in `demo-bundle-budget.json`; only modules statically reachable from the entry qualify, so lazy-only dependencies stay in their lazy chunks. It then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route or the ToolbarControlGroup popup specimen is selected. Directly addressed routes pass through the same loader.
56
+ - The production build splits the code loaded at startup into three eagerly preloaded chunks behind a tiny entry, so none nears the largest-chunk limit in `demo-bundle-budget.json`: `vendor` (the Web Awesome select and popup-menu registrations with their Lit and Floating UI runtime), `kerf` (the built `@kerfjs/ui` modules and the kerf core, plus every startup stylesheet), and `catalog` (the catalog shell and per-component demo modules). A chunk carries the CSS its modules import, so all startup CSS modules share the `kerf` chunk: the stylesheet stays one file with the cascade order a single-chunk build emits, and the chunk graph stays acyclic (`catalog` imports `kerf`, never the reverse). Only modules statically reachable from the entry qualify, so lazy-only dependencies stay in their lazy chunks. It then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route or the ToolbarControlGroup popup specimen is selected. Directly addressed routes pass through the same loader.
57
57
 
58
58
  The focused ListHeader route shows `xs`, `sm`, `m`, `l`, and `xl` labels at one
59
59
  semantic heading level. Its default `sm` style still matches `FieldLabel`.
60
60
  It also shows simultaneous badge and status indicators, with a conditional
61
61
  badge expression that may return `undefined`.
62
62
 
63
- Before handoff, inspect the actual captures for readability, context, alignment, clipping, spacing, responsiveness, focus, and obvious defects. Fix and recapture rather than treating the screenshot as proof by itself.
63
+ Before handoff, inspect the actual captures for readability, context, alignment, clipping, spacing, responsiveness, focus, and obvious defects. Fix and recapture rather than treating the screenshot as proof by itself. Firefox and WebKit screenshots leave out `backdrop-filter`, so translucent chrome looks unblurred in them. Check the blur on a `recordVideo` frame instead (see [Accessibility › Chrome material](accessibility.md#chrome-material)).
@@ -190,6 +190,13 @@ a panel, and a Web Awesome card containing details, using public components and
190
190
  no demo stylesheet. Browser tests cover repeated layers, actual compositing,
191
191
  text contrast, light/dark switching, narrow/wide layouts, and forced colors.
192
192
 
193
+ The built-in close control of a `wa-dialog` or `wa-drawer` matches a borderless
194
+ single `ToolbarControlGroup` button, the close a toolbar-composed dialog uses
195
+ (see the list-detail dialog recipe): a 40px circle with no border and the
196
+ neutral hover fill, in place of Web Awesome's 35px rounded square. The theme
197
+ sizes it through the directly exported `close-button__base` part, so the
198
+ default header and a toolbar-composed header present one close affordance.
199
+
193
200
  Add `class="hide-actions"` to a `wa-dialog` when the dialog supplies its own
194
201
  dismissal affordance and should omit Web Awesome's header action region. The
195
202
  theme sets `display: none` on the directly exported `header-actions` part; it
package/docs/workbench.md CHANGED
@@ -198,7 +198,7 @@ and calls `wireNavStack` for animated push/pop and focus movement. See
198
198
  rightRail={{
199
199
  label: "Tickets",
200
200
  collapsed: ticketsCollapsed.value,
201
- toolbar: { toggle: { action: "toggle-tickets", name: "tickets" } },
201
+ toolbar: { toggle: { action: "toggle-tickets", name: "Tickets" } },
202
202
  navStack: { id: "ticket-stack", label: "Tickets", views: ticketViews.value },
203
203
  }}
204
204
  />
@@ -221,7 +221,10 @@ and close. A panel's `toolbar` has these parts:
221
221
  toggle, which the Workbench renders: the per-side panel glyph,
222
222
  `aria-controls` naming the panel, `aria-expanded`, a "Show …"/"Hide …"
223
223
  label (or your localized `showLabel` / `hideLabel`), and the `data-action`
224
- the app handles. It is always the last group.
224
+ the app handles. It is always the last group. `name` is the panel's
225
+ title-style name ("Navigator", "Build Output"): it becomes the toggle
226
+ group's accessible name and completes the "Show Navigator" / "Hide
227
+ Navigator" label, so it follows [the capitalization rule](./writing.md#capitalization).
225
228
 
226
229
  While a panel is closed, marked groups follow their open-panel zone order
227
230
  (`leading`, `center`, `trailing`), followed by its toggle:
@@ -249,8 +252,8 @@ toggle, so it never depends on a control it covers. An app's own
249
252
  label: "Navigator",
250
253
  title: <ToolbarText text="Navigator" />,
251
254
  leading: <NewFileGroup />,
252
- trailing: <ToolbarControlGroup relocateOnCollapse label="Search files"><SearchButton /></ToolbarControlGroup>,
253
- toggle: { action: "toggle-navigator", name: "navigator" },
255
+ trailing: <ToolbarControlGroup relocateOnCollapse label="Search Files"><SearchButton /></ToolbarControlGroup>,
256
+ toggle: { action: "toggle-navigator", name: "Navigator" },
254
257
  },
255
258
  content: <Files />,
256
259
  collapsed: navCollapsed.value,
@@ -297,7 +300,8 @@ The `Pane`s and `List`s the Workbench composes around your content are
297
300
  configurable the same way:
298
301
 
299
302
  - `mainPane` (`PaneConfig`: `contentElement`, `contentLabel`, `separators`,
300
- `safeAreaEdges`, `chromeDividers`, `appearance`) configures the work area's
303
+ `safeAreaEdges`, `chromeDividers`, `appearance`, `deepInset`,
304
+ `scrollClearance`, `headerPresentation`, `tabIndex`, `outlined`) configures the work area's
301
305
  `Pane`, which exists whenever the work area has a toolbar, `mainHeader`, or
302
306
  `mainFooter`. Without that chrome, `appearance: "sunken"` paints the main
303
307
  region's own scroll surface;
@@ -500,7 +504,7 @@ presents as one, mirroring `wireSidebar`'s compact overlay:
500
504
  data-action="toggle-navigator"
501
505
  aria-controls="editor-left-rail"
502
506
  aria-expanded={String(!navCollapsed.value)}
503
- aria-label={navCollapsed.value ? "Show navigator" : "Hide navigator"}
507
+ aria-label={navCollapsed.value ? "Show Navigator" : "Hide Navigator"}
504
508
  >
505
509
  <LucideIcon {...collapsiblePanelToggleIcon("left", navCollapsed.value)} />
506
510
  </button>