@kerfjs/ui 5.0.0-beta.98 → 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 (407) 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 +487 -77
  47. package/ai/skill.md +40 -13
  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 +22 -5
  59. package/dist/catalog.js +50 -31
  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-AZWE2FB5.js → chunk-CMETHPKR.js} +12 -15
  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-527IAEWG.js → chunk-PYBBYSZ3.js} +10 -17
  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-Cp3Y8kJ9.d.ts → nav-stack-VXTumzKa.d.ts} +48 -3
  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.js +1 -1
  189. package/dist/skeleton.js +1 -1
  190. package/dist/spacer.js +1 -1
  191. package/dist/split-button.js +1 -1
  192. package/dist/split-view.d.ts +7 -5
  193. package/dist/split-view.js +6 -3
  194. package/dist/split-view.js.map +1 -1
  195. package/dist/state-banner.d.ts +6 -1
  196. package/dist/state-banner.js +1 -1
  197. package/dist/styles/anchored-surface.css +27 -1
  198. package/dist/styles/catalog/components/catalog-example.css +33 -0
  199. package/dist/styles/catalog/components/catalog-stage.css +7 -0
  200. package/dist/styles/chip.css +70 -0
  201. package/dist/styles/components/actions/popup-menu/popup-menu.css +14 -4
  202. package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +75 -1
  203. package/dist/styles/components/actions/toolbar-text/toolbar-text.css +66 -0
  204. package/dist/styles/components/collections/list/list.css +77 -7
  205. package/dist/styles/components/collections/list-action-row/list-action-row.css +177 -22
  206. package/dist/styles/components/collections/list-item/list-item.css +158 -11
  207. package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +27 -1
  208. package/dist/styles/components/feedback/chip/chip.css +70 -0
  209. package/dist/styles/components/feedback/state-banner/state-banner.css +24 -13
  210. package/dist/styles/components/forms/composer/composer.css +100 -0
  211. package/dist/styles/components/forms/token-search-field/token-search-field.css +13 -2
  212. package/dist/styles/components/layout/pane/pane.css +81 -2
  213. package/dist/styles/components/navigation/nav-stack/nav-stack.css +84 -0
  214. package/dist/styles/components/navigation/tab-bar/tab-bar.css +15 -4
  215. package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +79 -4
  216. package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +195 -0
  217. package/dist/styles/composer.css +100 -0
  218. package/dist/styles/foundation.css +40 -0
  219. package/dist/styles/list-action-row.css +177 -22
  220. package/dist/styles/list-item.css +158 -11
  221. package/dist/styles/list.css +77 -7
  222. package/dist/styles/nav-stack.css +84 -0
  223. package/dist/styles/pane.css +81 -2
  224. package/dist/styles/popup-menu.css +14 -4
  225. package/dist/styles/state-banner.css +24 -13
  226. package/dist/styles/styles.css +1 -0
  227. package/dist/styles/surface-scaffold.css +195 -0
  228. package/dist/styles/tab-bar.css +15 -4
  229. package/dist/styles/tab-navigator.css +79 -4
  230. package/dist/styles/token-search-field.css +13 -2
  231. package/dist/styles/toolbar-control-group.css +75 -1
  232. package/dist/styles/toolbar-text.css +66 -0
  233. package/dist/surface-scaffold-register.d.ts +6 -1
  234. package/dist/surface-scaffold-register.js +89 -3
  235. package/dist/surface-scaffold-register.js.map +1 -1
  236. package/dist/surface-scaffold.d.ts +66 -7
  237. package/dist/surface-scaffold.js +1 -1
  238. package/dist/tab-navigator.d.ts +23 -2
  239. package/dist/tab-navigator.js +2 -0
  240. package/dist/tab-navigator.js.map +1 -1
  241. package/dist/token-search-field.d.ts +1 -1
  242. package/dist/token-search-field.js +1 -1
  243. package/dist/{token-search-model-DTcTXWMS.d.ts → token-search-model-aYR0YAei.d.ts} +32 -2
  244. package/dist/token-search-model.d.ts +1 -1
  245. package/dist/toolbar-control-group.js +1 -1
  246. package/dist/toolbar-text.js +1 -1
  247. package/dist/toolbar.d.ts +1 -0
  248. package/dist/value-table.js +1 -1
  249. package/dist/wire-catalog.d.ts +3 -2
  250. package/dist/wire-catalog.js +2 -2
  251. package/dist/wire-composers.d.ts +16 -0
  252. package/dist/wire-composers.js +3 -0
  253. package/dist/wire-composers.js.map +1 -0
  254. package/dist/wire-list-focus-scroll.d.ts +16 -0
  255. package/dist/wire-list-focus-scroll.js +3 -0
  256. package/dist/wire-list-focus-scroll.js.map +1 -0
  257. package/dist/wire-nav-stack.js +135 -0
  258. package/dist/wire-nav-stack.js.map +1 -1
  259. package/dist/wire-scroll-dividers.d.ts +9 -0
  260. package/dist/wire-scroll-dividers.js +1 -1
  261. package/dist/wire-tab-bars.js +1 -1
  262. package/dist/wire-tab-navigator.d.ts +3 -1
  263. package/dist/wire-tab-navigator.js +133 -1
  264. package/dist/wire-tab-navigator.js.map +1 -1
  265. package/dist/wire-token-search-fields.d.ts +1 -1
  266. package/dist/wire-token-search-fields.js +1 -1
  267. package/dist/workbench.d.ts +8 -3
  268. package/dist/workbench.js +2 -1
  269. package/docs/accessibility.md +132 -3
  270. package/docs/anchored-surface.md +46 -4
  271. package/docs/app-layouts.md +23 -2
  272. package/docs/apple-hig.md +1 -0
  273. package/docs/catalog.md +54 -8
  274. package/docs/collapsible-panel.md +9 -6
  275. package/docs/component-contract.md +41 -12
  276. package/docs/component-selection.md +143 -19
  277. package/docs/composer.md +143 -0
  278. package/docs/design/templates/choice-chips/multiple-dark.svg +1 -0
  279. package/docs/design/templates/choice-chips/multiple.svg +1 -0
  280. package/docs/design/templates/choice-chips-dark.svg +1 -1
  281. package/docs/design/templates/choice-chips.svg +1 -1
  282. package/docs/design/templates/composer/chat-dark.svg +1 -0
  283. package/docs/design/templates/composer/chat.svg +1 -0
  284. package/docs/design/templates/composer/prompt-dark.svg +1 -0
  285. package/docs/design/templates/composer/prompt.svg +1 -0
  286. package/docs/design/templates/composer-dark.svg +1 -0
  287. package/docs/design/templates/composer.svg +1 -0
  288. package/docs/design/templates/empty-state/filtered-dark.svg +1 -1
  289. package/docs/design/templates/empty-state/filtered.svg +1 -1
  290. package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
  291. package/docs/design/templates/empty-state/with-action.svg +1 -1
  292. package/docs/design/templates/empty-state-dark.svg +1 -1
  293. package/docs/design/templates/empty-state.svg +1 -1
  294. package/docs/design/templates/list/compact-dark.svg +1 -1
  295. package/docs/design/templates/list/compact.svg +1 -1
  296. package/docs/design/templates/list-action-row/default-dark.svg +1 -1
  297. package/docs/design/templates/list-action-row/default.svg +1 -1
  298. package/docs/design/templates/list-action-row/icon-tile-dark.svg +1 -0
  299. package/docs/design/templates/list-action-row/icon-tile.svg +1 -0
  300. package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
  301. package/docs/design/templates/list-action-row/selected.svg +1 -1
  302. package/docs/design/templates/list-action-row/trailing-menu-dark.svg +1 -1
  303. package/docs/design/templates/list-action-row/trailing-menu.svg +1 -1
  304. package/docs/design/templates/list-action-row-dark.svg +1 -1
  305. package/docs/design/templates/list-action-row.svg +1 -1
  306. package/docs/design/templates/list-dark.svg +1 -1
  307. package/docs/design/templates/list-header/size-l-dark.svg +1 -1
  308. package/docs/design/templates/list-header/size-l.svg +1 -1
  309. package/docs/design/templates/list-header/size-m-dark.svg +1 -1
  310. package/docs/design/templates/list-header/size-m.svg +1 -1
  311. package/docs/design/templates/list-header/size-xl-dark.svg +1 -1
  312. package/docs/design/templates/list-header/size-xl.svg +1 -1
  313. package/docs/design/templates/list-header-dark.svg +1 -1
  314. package/docs/design/templates/list-header.svg +1 -1
  315. package/docs/design/templates/list-item/icon-tile-dark.svg +1 -0
  316. package/docs/design/templates/list-item/icon-tile.svg +1 -0
  317. package/docs/design/templates/list-item/selected-dark.svg +1 -1
  318. package/docs/design/templates/list-item/selected.svg +1 -1
  319. package/docs/design/templates/list-item-dark.svg +1 -1
  320. package/docs/design/templates/list-item.svg +1 -1
  321. package/docs/design/templates/list.svg +1 -1
  322. package/docs/design/templates/segmented-control-dark.svg +1 -1
  323. package/docs/design/templates/segmented-control.svg +1 -1
  324. package/docs/design/templates/skeleton-dark.svg +1 -1
  325. package/docs/design/templates/skeleton.svg +1 -1
  326. package/docs/design/templates/state-banner/danger-dark.svg +1 -1
  327. package/docs/design/templates/state-banner/danger.svg +1 -1
  328. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
  329. package/docs/design/templates/state-banner/info-badge.svg +1 -1
  330. package/docs/design/templates/state-banner/info-dark.svg +1 -1
  331. package/docs/design/templates/state-banner/info.svg +1 -1
  332. package/docs/design/templates/state-banner/strip-dark.svg +1 -1
  333. package/docs/design/templates/state-banner/strip.svg +1 -1
  334. package/docs/design/templates/state-banner/success-dark.svg +1 -1
  335. package/docs/design/templates/state-banner/success.svg +1 -1
  336. package/docs/design/templates/state-banner/warning-dark.svg +1 -1
  337. package/docs/design/templates/state-banner/warning.svg +1 -1
  338. package/docs/design/templates/state-banner-dark.svg +1 -1
  339. package/docs/design/templates/state-banner.svg +1 -1
  340. package/docs/design/templates/tab-bar-dark.svg +1 -1
  341. package/docs/design/templates/tab-bar.svg +1 -1
  342. package/docs/design/templates/token-search-field-dark.svg +1 -1
  343. package/docs/design/templates/token-search-field.svg +1 -1
  344. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  345. package/docs/design/templates/toolbar-control-group.svg +1 -1
  346. package/docs/design/templates/toolbar-dark.svg +1 -1
  347. package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
  348. package/docs/design/templates/toolbar-text/default.svg +1 -1
  349. package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
  350. package/docs/design/templates/toolbar-text/large.svg +1 -1
  351. package/docs/design/templates/toolbar-text-dark.svg +1 -1
  352. package/docs/design/templates/toolbar-text.svg +1 -1
  353. package/docs/design/templates/toolbar.svg +1 -1
  354. package/docs/design/templates/value-table/details-dark.svg +1 -1
  355. package/docs/design/templates/value-table/details.svg +1 -1
  356. package/docs/design/templates/value-table-dark.svg +1 -1
  357. package/docs/design/templates/value-table.svg +1 -1
  358. package/docs/design-philosophy.md +9 -1
  359. package/docs/examples/command-palette-adapter.tsx +3 -3
  360. package/docs/layout.md +147 -14
  361. package/docs/nav-stack.md +112 -2
  362. package/docs/recipes.md +9 -9
  363. package/docs/split-view.md +3 -3
  364. package/docs/sunken-panel.md +2 -2
  365. package/docs/surface-scaffold.md +138 -7
  366. package/docs/tab-navigator.md +38 -1
  367. package/docs/type-contracts.md +16 -16
  368. package/docs/ui-analyzer.md +3 -1
  369. package/docs/ui-doctor.md +71 -0
  370. package/docs/ux-demo.md +6 -6
  371. package/docs/workbench.md +10 -6
  372. package/docs/writing.md +152 -0
  373. package/doctor/copy-capitalization-checks.mjs +591 -0
  374. package/doctor/index.mjs +17 -8
  375. package/llms.txt +105 -11
  376. package/package.json +15 -1
  377. package/ux-demo/recipes/app-shell.tsx +11 -11
  378. package/ux-demo/recipes/collapsible-sidebar.tsx +12 -11
  379. package/ux-demo/recipes/compact-toolbar.tsx +10 -10
  380. package/ux-demo/recipes/composer-form.tsx +8 -8
  381. package/ux-demo/recipes/list-detail-dialog.tsx +13 -13
  382. package/ux-demo/recipes/list-workspace-states.tsx +13 -13
  383. package/ux-demo/recipes/loading-inspector.tsx +10 -10
  384. package/ux-demo/recipes/navigation-sidebar.tsx +7 -7
  385. package/ux-demo/recipes/navigation-stack.tsx +5 -5
  386. package/ux-demo/recipes/workspace-header.tsx +5 -5
  387. package/dist/chunk-36TL6BCR.js.map +0 -1
  388. package/dist/chunk-4LOK4634.js +0 -21
  389. package/dist/chunk-4LOK4634.js.map +0 -1
  390. package/dist/chunk-527IAEWG.js.map +0 -1
  391. package/dist/chunk-6BRRUPJY.js.map +0 -1
  392. package/dist/chunk-6STQXRT4.js.map +0 -1
  393. package/dist/chunk-76PWEN5Y.js +0 -64
  394. package/dist/chunk-76PWEN5Y.js.map +0 -1
  395. package/dist/chunk-AZWE2FB5.js.map +0 -1
  396. package/dist/chunk-DQDTZE4W.js +0 -70
  397. package/dist/chunk-DQDTZE4W.js.map +0 -1
  398. package/dist/chunk-FOQK4F3L.js.map +0 -1
  399. package/dist/chunk-GJYQ27C2.js.map +0 -1
  400. package/dist/chunk-JYD7G4RG.js.map +0 -1
  401. package/dist/chunk-KQDU7Z7P.js.map +0 -1
  402. package/dist/chunk-P74VXMB6.js.map +0 -1
  403. package/dist/chunk-PBT3CPPY.js.map +0 -1
  404. package/dist/chunk-PRG4CL3Q.js.map +0 -1
  405. package/dist/chunk-ROZLJULF.js.map +0 -1
  406. package/dist/chunk-U657IHFB.js.map +0 -1
  407. package/dist/chunk-V6W56V3N.js.map +0 -1
@@ -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)).
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>
@@ -0,0 +1,152 @@
1
+ # Writing interface copy
2
+
3
+ Kerf UI follows Apple's capitalization conventions for interface text.
4
+ Apple's _Writing_ guidance asks for one style per UI element type, applied
5
+ consistently, and its element guidance assigns those styles. This page is the
6
+ package default, so every Kerf application, demo, and recipe uses the same
7
+ rule. An application may replace it only through a written, product-wide
8
+ style. It should never vary from one screen to the next.
9
+
10
+ ## Capitalization
11
+
12
+ **Names are title style. Descriptions are sentence style.** Text that names a
13
+ command, control, view, or field uses title style ("Save Draft", "Workspace
14
+ Settings", "Release Date"). Text that describes, explains, reports, or asks
15
+ uses sentence style ("Changes sync when you reconnect").
16
+
17
+ | Element | Style | Examples |
18
+ | --------------------------------------------------------------------------------------------------- | ---------------------------- | ------------------------------------------------------ |
19
+ | Buttons, split-button and toolbar actions, link-style commands | Title | Save Draft, Add to Favorites, Don’t Save, Log In |
20
+ | Menu items, menu group headings, popup-menu triggers | Title | Copy Reference, Sort By, Recently Updated, New Folder… |
21
+ | Segmented-control segments, `Select` and pop-up choices, tabs | Title | List View, Last 30 Days, In Review |
22
+ | Page, view, pane, dialog, and toolbar titles (`ToolbarText` titles), back labels | Title | Workspace Settings, Back to Inbox |
23
+ | Group and list headers (`ListHeader`), table column headings | Title | Saved Projects, Needs Attention, Last Modified |
24
+ | Catalog example and stack labels (`CatalogExample`, `CatalogExampleStack`) | Title | Heading Identity Stays Whole, Icon Trigger in a Group |
25
+ | Content item titles: the name an item or card shows | Title | Build Output, Work Area, Unsaved Changes |
26
+ | Field labels (`FieldLabel`, a field's `label`) | Title | Project Name, Release Date |
27
+ | Region and landmark names (accessible names of panes, toolbars, groups, lists, tab bars) | Title | Ticket Workbench, View Actions, Open Documents |
28
+ | Accessible names of icon-only controls | Title | More Actions, Clear Search |
29
+ | `EmptyState`, `StateBanner`, alert, and notification titles that are fragments | Title, no ending punctuation | No Release Tasks, Migration Needs Review |
30
+ | The same titles written as complete sentences | Sentence, with punctuation | Release tasks could not be refreshed. |
31
+ | Checkbox, switch, and radio option text | Sentence | Show hidden files, Notify me about replies |
32
+ | Body copy, `description`, `detail`, hints, empty-state and banner detail, dialog and alert messages | Sentence, with punctuation | Changes sync when you reconnect. |
33
+ | Tooltips and help text | Sentence | Copy the link to this ticket |
34
+ | Placeholders | Sentence | Search tickets |
35
+ | Status lines, counts, and timestamps (`ToolbarText` status, `Badge`, `CornerStatus`) | Sentence | Saved just now, 3 of 12 updated |
36
+ | Progress labels (`LoadingSpinner` and `Skeleton` `label`): the operation under way | Sentence, trailing ellipsis | Loading preview…, Refreshing release tasks… |
37
+
38
+ A name that a component composes into a longer label is a name too, so pass
39
+ it in title style. A panel toolbar's `toggle: { action, name }` labels the
40
+ toggle group with `name` and completes "Show …" / "Hide …" with it:
41
+ `name: "Navigator"` gives "Show Navigator". Kerf UI never recapitalizes it.
42
+
43
+ Data is never recapitalized. Names that a person or a system supplies keep
44
+ their own spelling: file names, ticket titles, user names, and tags. So do
45
+ code identifiers shown as text, such as a prop value a specimen is labeled
46
+ with (`danger`, `transparent`). A content
47
+ item that shows such data (a ticket subject, a message) keeps it as written;
48
+ only a title the interface itself names is title style. `ContentItem`'s own
49
+ `title` prop is a native tooltip, so it is sentence style. Keep a content
50
+ item's `ariaLabel` equal to its visible title.
51
+
52
+ ### Title-style rules
53
+
54
+ From the [Apple Style Guide](https://support.apple.com/guide/applestyleguide/c-apsgb744e4a3/web)
55
+ (_capitalization_):
56
+
57
+ - **Capitalize** the first and last word, whatever their part of speech.
58
+ - **Capitalize** nouns, pronouns, verbs, adjectives, and adverbs, however
59
+ short: It, This, Your, Is, Are, Be.
60
+ - **Capitalize** subordinating conjunctions such as If, and prepositions of
61
+ five letters or more: About, Between, Through.
62
+ - **Capitalize** a preposition of any length that belongs to a phrasal verb
63
+ or acts as another part of speech: Start Up the Computer, Turn On, Log In to
64
+ the Server.
65
+ - **Capitalize** the second word of a hyphenated compound: High-Level Events,
66
+ Pop-Up Menu. Built-in and Plug-in are the exceptions.
67
+ - **Keep lowercase**:
68
+ - articles (a, an, the), unless one is the first word or follows a colon;
69
+ - coordinating conjunctions (and, but, or, nor, for, yet, so);
70
+ - _to_ in an infinitive (How to Start Your Computer);
71
+ - _as_ (Export a Document as a PDF);
72
+ - prepositions of four letters or fewer (at, by, for, from, in, into, of,
73
+ off, on, onto, out, over, to, up, with).
74
+ - **Keep the brand's own spelling** for words that always start lowercase,
75
+ such as iPad, iOS, and macOS.
76
+
77
+ ### Sentence-style rules
78
+
79
+ Capitalize the first word and proper nouns or adjectives only. End a complete
80
+ sentence with punctuation. Leave a fragment without ending punctuation.
81
+ Tooltips may omit the final period.
82
+
83
+ ### Related conventions
84
+
85
+ - An ellipsis (…) ends a command or menu item that needs more input before it
86
+ acts: New Ticket…, Save As…. The dialog it opens repeats the command as its
87
+ title, without the ellipsis.
88
+ - Label an action with its result. Prefer a verb: Publish Update, not OK.
89
+ - Keep a control's accessible name equal to its visible label, or starting
90
+ with it.
91
+
92
+ ## Diagnostics
93
+
94
+ `kerf-ui-doctor` reports `KUI-D040` (warning) when a string or template
95
+ literal passed to a known title-style prop leaves a word lowercase that title
96
+ style capitalizes. In a template literal it judges only the static words:
97
+ `` `Edit ${name} shortcut` `` should be `` `Edit ${name} Shortcut` ``, and the
98
+ interpolated value keeps its own spelling. The covered props are:
99
+
100
+ - `ToolbarText` `text` when the text is a title: it has `headingLevel`, a
101
+ large size, or sits in a toolbar or panel `title`, `header`, or (unless small) `leading` slot;
102
+ - `ListHeader`, `ListActionRow`, `SplitButton`, and `Combobox` `label`;
103
+ - `CatalogExample` and `CatalogExampleStack` `label`;
104
+ - `EmptyState` and `StateBanner` `title`;
105
+ - `PopupMenu` `text` and item and heading labels, including submenus;
106
+ - `SegmentedControl`, `Select`, and `TabNavigator` choice and tab labels;
107
+ - `NavStack` view titles and `backText`, and `SplitView` `backLabel`;
108
+ - `FieldLabel` text and the text of a `button` or `wa-button`;
109
+ - the `name` of a panel toolbar's `toggle: { action, name }`;
110
+ - region and landmark names: the `label` of `Pane`, `Workbench`, `Toolbar`,
111
+ `ToolbarControlGroup`, `TabBar`, `NavStack`, `CollapsiblePanel`,
112
+ `FloatingToolbar`, and `ResizableRegion`, the `ariaLabel` of `SunkenPanel`
113
+ and `List`, a `Pane`'s `contentLabel`, the panel, toolbar, and pane labels
114
+ inside `Workbench`, `CollapsiblePanel`, and `NavStack` configuration, and
115
+ the `aria-label` of a landmark element (`section`, `nav`, `aside`, `main`,
116
+ `header`, `footer`, `form`, `search`) or landmark role;
117
+ - the `label` of a raw `wa-input`, `wa-select`, `wa-textarea`, or
118
+ `wa-radio-group`;
119
+ - the accessible names of icon-only controls: `IconButton` and
120
+ `IconToggleButton` `label`, `PopupMenu` `label`, `SplitButton`
121
+ `optionsLabel`, `TokenSearchField` `clearLabel`, and the `aria-label` of a
122
+ `button` or `wa-button` whose only content is an icon.
123
+
124
+ The check never judges sentence-style surfaces (including `wa-checkbox`,
125
+ `wa-switch`, and `wa-radio` option text and the `LoadingSpinner` and
126
+ `Skeleton` status labels), interpolated or other dynamic values, or text
127
+ ending with `.`, `!`, or `?`. It skips a word joined to an interpolation, tokens containing digits, code, or paths, and
128
+ lowercase-led brands such as iOS. It does not report capitalized short words,
129
+ because Apple capitalizes a phrasal verb's particle. Suppress `KUI-D040` for a
130
+ lowercase brand or product name. See [the doctor](./ui-doctor.md).
131
+
132
+ ## Sources
133
+
134
+ - Apple Human Interface Guidelines,
135
+ [Writing](https://developer.apple.com/design/human-interface-guidelines/writing):
136
+ choose a capitalization style for each element type and use it consistently.
137
+ - [Menus](https://developer.apple.com/design/human-interface-guidelines/menus)
138
+ and [The menu bar](https://developer.apple.com/design/human-interface-guidelines/the-menu-bar):
139
+ menu items and multi-word menu titles use title-style capitalization.
140
+ - [Alerts](https://developer.apple.com/design/human-interface-guidelines/alerts)
141
+ and [Notifications](https://developer.apple.com/design/human-interface-guidelines/notifications):
142
+ a fragment title uses title style. A complete-sentence title and the
143
+ message use sentence style with punctuation.
144
+ - [Offering help](https://developer.apple.com/design/human-interface-guidelines/offering-help):
145
+ tooltips use sentence case.
146
+ - The archived OS X Human Interface Guidelines, _Use the Right Capitalization
147
+ Style in Labels and Text_. Title style covers menu titles and items, push
148
+ buttons, toolbar item labels, and labels that are not sentences, such as
149
+ group box and list headings. Sentence style covers checkbox and radio option
150
+ text and dialog messages.
151
+ - [Apple Style Guide](https://support.apple.com/guide/applestyleguide/c-apsgb744e4a3/web),
152
+ _capitalization_: the title-style word rules above.