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

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 (714) hide show
  1. package/README.md +494 -138
  2. package/ai/application-ui-diagnostic-ids-v1.json +109 -0
  3. package/ai/application-ui-profile-sync.cjs +529 -0
  4. package/ai/application-ui-profile.d.mts +1 -0
  5. package/ai/application-ui-profile.d.ts +102 -0
  6. package/ai/application-ui-profile.defaults.json +96 -0
  7. package/ai/application-ui-profile.mjs +626 -0
  8. package/ai/application-ui-profile.schema.json +130 -0
  9. package/ai/catalog-authoring.json +36 -0
  10. package/ai/catalog-authoring.schema.json +109 -0
  11. package/ai/compile-time-contracts-v1.json +149 -0
  12. package/ai/compile-time-contracts-v1.schema.json +52 -0
  13. package/ai/component-catalog-extension.schema.json +79 -0
  14. package/ai/component-catalog.json +4041 -591
  15. package/ai/component-catalog.schema.json +78 -1
  16. package/ai/component-class-owners.cjs +83 -0
  17. package/ai/component-composition-extension.d.ts +7 -0
  18. package/ai/component-composition-extension.schema.json +7 -0
  19. package/ai/component-composition-rules.json +524 -0
  20. package/ai/component-composition.d.ts +125 -0
  21. package/ai/component-composition.json +7074 -0
  22. package/ai/component-composition.schema.json +375 -0
  23. package/ai/components/README.md +171 -0
  24. package/ai/components/application-tabs.md +65 -0
  25. package/ai/components/badge.md +72 -0
  26. package/ai/components/chip.md +72 -0
  27. package/ai/components/collapsible-panel.md +102 -0
  28. package/ai/components/content-item.md +90 -0
  29. package/ai/components/disclosure-arrow.md +77 -0
  30. package/ai/components/document-baseline.md +75 -0
  31. package/ai/components/empty-state.md +80 -0
  32. package/ai/components/feedback.md +71 -0
  33. package/ai/components/floating-toolbar.md +82 -0
  34. package/ai/components/foundation.md +65 -0
  35. package/ai/components/grid.md +96 -0
  36. package/ai/components/headers.md +63 -0
  37. package/ai/components/layout.md +75 -0
  38. package/ai/components/list-action-row.md +91 -0
  39. package/ai/components/list-header.md +89 -0
  40. package/ai/components/list-inset-control.md +74 -0
  41. package/ai/components/list-inset-text.md +72 -0
  42. package/ai/components/list-item.md +94 -0
  43. package/ai/components/list.md +103 -0
  44. package/ai/components/loading-spinner.md +72 -0
  45. package/ai/components/lucide-icon.md +66 -0
  46. package/ai/components/nav-stack.md +103 -0
  47. package/ai/components/pane.md +100 -0
  48. package/ai/components/popup-menu.md +171 -0
  49. package/ai/components/recipe-app-shell.md +57 -0
  50. package/ai/components/recipe-collapsible-sidebar.md +57 -0
  51. package/ai/components/recipe-compact-toolbar.md +57 -0
  52. package/ai/components/recipe-composer-form.md +57 -0
  53. package/ai/components/recipe-list-detail-dialog.md +57 -0
  54. package/ai/components/recipe-list-workspace-states.md +57 -0
  55. package/ai/components/recipe-loading-inspector.md +57 -0
  56. package/ai/components/recipe-navigation-sidebar.md +57 -0
  57. package/ai/components/recipe-navigation-stack.md +57 -0
  58. package/ai/components/recipe-workspace-header.md +57 -0
  59. package/ai/components/resize.md +106 -0
  60. package/ai/components/row.md +96 -0
  61. package/ai/components/segmented-control.md +82 -0
  62. package/ai/components/select.md +112 -0
  63. package/ai/components/skeleton.md +84 -0
  64. package/ai/components/spacer.md +86 -0
  65. package/ai/components/split-view.md +102 -0
  66. package/ai/components/state-banner.md +94 -0
  67. package/ai/components/sunken-panel.md +79 -0
  68. package/ai/components/surface-scaffold.md +78 -0
  69. package/ai/components/tab-bar.md +112 -0
  70. package/ai/components/tab-scaffold.md +94 -0
  71. package/ai/components/tabs.md +95 -0
  72. package/ai/components/text.md +83 -0
  73. package/ai/components/token-search-field.md +103 -0
  74. package/ai/components/toolbar-control-group.md +102 -0
  75. package/ai/components/toolbar-text.md +78 -0
  76. package/ai/components/toolbar.md +97 -0
  77. package/ai/components/value-table.md +73 -0
  78. package/ai/components/wa-accordion-item.md +55 -0
  79. package/ai/components/wa-accordion.md +65 -0
  80. package/ai/components/wa-animated-image.md +55 -0
  81. package/ai/components/wa-animation.md +55 -0
  82. package/ai/components/wa-avatar.md +55 -0
  83. package/ai/components/wa-badge.md +55 -0
  84. package/ai/components/wa-breadcrumb-item.md +53 -0
  85. package/ai/components/wa-breadcrumb.md +55 -0
  86. package/ai/components/wa-button-group.md +59 -0
  87. package/ai/components/wa-button.md +55 -0
  88. package/ai/components/wa-callout.md +58 -0
  89. package/ai/components/wa-card.md +59 -0
  90. package/ai/components/wa-carousel-item.md +53 -0
  91. package/ai/components/wa-carousel.md +55 -0
  92. package/ai/components/wa-checkbox-group.md +55 -0
  93. package/ai/components/wa-checkbox.md +55 -0
  94. package/ai/components/wa-color-picker.md +55 -0
  95. package/ai/components/wa-comparison.md +55 -0
  96. package/ai/components/wa-copy-button.md +55 -0
  97. package/ai/components/wa-details.md +61 -0
  98. package/ai/components/wa-dialog.md +59 -0
  99. package/ai/components/wa-divider.md +53 -0
  100. package/ai/components/wa-drawer.md +55 -0
  101. package/ai/components/wa-dropdown-item.md +60 -0
  102. package/ai/components/wa-dropdown.md +60 -0
  103. package/ai/components/wa-format-bytes.md +53 -0
  104. package/ai/components/wa-format-date.md +53 -0
  105. package/ai/components/wa-format-number.md +53 -0
  106. package/ai/components/wa-icon.md +58 -0
  107. package/ai/components/wa-include.md +53 -0
  108. package/ai/components/wa-input.md +59 -0
  109. package/ai/components/wa-intersection-observer.md +53 -0
  110. package/ai/components/wa-known-date.md +53 -0
  111. package/ai/components/wa-markdown.md +53 -0
  112. package/ai/components/wa-mutation-observer.md +53 -0
  113. package/ai/components/wa-number-input.md +55 -0
  114. package/ai/components/wa-option.md +59 -0
  115. package/ai/components/wa-otp-input.md +53 -0
  116. package/ai/components/wa-page.md +53 -0
  117. package/ai/components/wa-pagination.md +55 -0
  118. package/ai/components/wa-popover.md +55 -0
  119. package/ai/components/wa-popup.md +53 -0
  120. package/ai/components/wa-progress-bar.md +53 -0
  121. package/ai/components/wa-progress-ring.md +53 -0
  122. package/ai/components/wa-qr-code.md +53 -0
  123. package/ai/components/wa-radio-group.md +59 -0
  124. package/ai/components/wa-radio.md +55 -0
  125. package/ai/components/wa-random-content.md +53 -0
  126. package/ai/components/wa-rating.md +55 -0
  127. package/ai/components/wa-relative-time.md +53 -0
  128. package/ai/components/wa-resize-observer.md +53 -0
  129. package/ai/components/wa-scroller.md +53 -0
  130. package/ai/components/wa-select.md +63 -0
  131. package/ai/components/wa-skeleton.md +53 -0
  132. package/ai/components/wa-slider.md +55 -0
  133. package/ai/components/wa-spinner.md +58 -0
  134. package/ai/components/wa-split-panel.md +58 -0
  135. package/ai/components/wa-switch.md +53 -0
  136. package/ai/components/wa-tab-group.md +69 -0
  137. package/ai/components/wa-tab-panel.md +60 -0
  138. package/ai/components/wa-tab.md +59 -0
  139. package/ai/components/wa-tag.md +59 -0
  140. package/ai/components/wa-textarea.md +53 -0
  141. package/ai/components/wa-time-input.md +55 -0
  142. package/ai/components/wa-toast-item.md +60 -0
  143. package/ai/components/wa-toast.md +60 -0
  144. package/ai/components/wa-tooltip.md +55 -0
  145. package/ai/components/wa-tree-item.md +55 -0
  146. package/ai/components/wa-tree.md +55 -0
  147. package/ai/components/wa-zoomable-frame.md +55 -0
  148. package/ai/components/workbench.md +114 -0
  149. package/ai/public-api-signatures-v1.md +2597 -190
  150. package/ai/skill.md +259 -65
  151. package/ai/webawesome-jsx-signatures-v1.md +9 -2
  152. package/analyzer/cli.mjs +49 -0
  153. package/analyzer/component-ownership.d.mts +56 -0
  154. package/analyzer/component-ownership.mjs +184 -0
  155. package/analyzer/index.d.mts +49 -0
  156. package/analyzer/index.mjs +1458 -0
  157. package/analyzer/loud-pairs.d.mts +49 -0
  158. package/analyzer/loud-pairs.mjs +357 -0
  159. package/analyzer/report.schema.json +66 -0
  160. package/analyzer/selectors.d.mts +21 -0
  161. package/analyzer/selectors.mjs +212 -0
  162. package/dist/app-tab.d.ts +41 -4
  163. package/dist/app-tab.js +3 -1
  164. package/dist/badge.d.ts +52 -0
  165. package/dist/badge.js +3 -0
  166. package/dist/badge.js.map +1 -0
  167. package/dist/browser/app-tab.js +1 -0
  168. package/dist/browser/badge.js +3 -0
  169. package/dist/browser/catalog.js +25 -0
  170. package/dist/browser/chip.js +3 -0
  171. package/dist/browser/collapsible-panel.js +7 -0
  172. package/dist/browser/content-item.js +3 -0
  173. package/dist/browser/floating-toolbar.js +3 -0
  174. package/dist/browser/grid.js +3 -0
  175. package/dist/browser/list-action-row.js +5 -0
  176. package/dist/browser/list-header.js +8 -0
  177. package/dist/browser/list-inset-control.js +3 -0
  178. package/dist/browser/list-inset-text.js +3 -0
  179. package/dist/browser/list-item.js +5 -0
  180. package/dist/browser/list.js +3 -0
  181. package/dist/browser/{panel-header.js → nav-stack.js} +4 -2
  182. package/dist/browser/pane.js +3 -0
  183. package/dist/browser/popup-menu.js +3 -0
  184. package/dist/browser/row.js +3 -0
  185. package/dist/browser/select.js +2 -0
  186. package/dist/browser/skeleton.js +3 -0
  187. package/dist/browser/spacer.js +3 -0
  188. package/dist/browser/split-view.js +10 -0
  189. package/dist/browser/state-banner.js +2 -0
  190. package/dist/browser/sunken-panel.js +3 -0
  191. package/dist/browser/surface-scaffold.js +3 -0
  192. package/dist/browser/tab-scaffold.js +4 -0
  193. package/dist/browser/text.js +3 -0
  194. package/dist/browser/toolbar-text.js +1 -0
  195. package/dist/browser/value-table.js +1 -0
  196. package/dist/browser/workbench.js +10 -0
  197. package/dist/catalog-resources.d.ts +46 -0
  198. package/dist/catalog-resources.js +22 -0
  199. package/dist/catalog-resources.js.map +1 -0
  200. package/dist/catalog.d.ts +90 -0
  201. package/dist/catalog.js +501 -0
  202. package/dist/catalog.js.map +1 -0
  203. package/dist/chip.d.ts +29 -0
  204. package/dist/chip.js +3 -0
  205. package/dist/chip.js.map +1 -0
  206. package/dist/chunk-2G2ZJQYK.js +152 -0
  207. package/dist/chunk-2G2ZJQYK.js.map +1 -0
  208. package/dist/chunk-3SXMEK3B.js +43 -0
  209. package/dist/chunk-3SXMEK3B.js.map +1 -0
  210. package/dist/{chunk-W2FK7ZNY.js → chunk-4R777DUZ.js} +10 -2
  211. package/dist/chunk-4R777DUZ.js.map +1 -0
  212. package/dist/chunk-4SQK27MR.js +202 -0
  213. package/dist/chunk-4SQK27MR.js.map +1 -0
  214. package/dist/chunk-5I7QGQVT.js +24 -0
  215. package/dist/chunk-5I7QGQVT.js.map +1 -0
  216. package/dist/chunk-5PESG2VP.js +115 -0
  217. package/dist/chunk-5PESG2VP.js.map +1 -0
  218. package/dist/chunk-5Q3N6EV7.js +156 -0
  219. package/dist/chunk-5Q3N6EV7.js.map +1 -0
  220. package/dist/chunk-6ZXOQEAN.js +27 -0
  221. package/dist/chunk-6ZXOQEAN.js.map +1 -0
  222. package/dist/chunk-7JUBXOIV.js +103 -0
  223. package/dist/chunk-7JUBXOIV.js.map +1 -0
  224. package/dist/chunk-A3CKOW2R.js +622 -0
  225. package/dist/chunk-A3CKOW2R.js.map +1 -0
  226. package/dist/chunk-AHEG27M7.js +66 -0
  227. package/dist/chunk-AHEG27M7.js.map +1 -0
  228. package/dist/chunk-AUKT3KMG.js +37 -0
  229. package/dist/chunk-AUKT3KMG.js.map +1 -0
  230. package/dist/chunk-AYUHMAI6.js +48 -0
  231. package/dist/chunk-AYUHMAI6.js.map +1 -0
  232. package/dist/chunk-BFPBS5Y4.js +250 -0
  233. package/dist/chunk-BFPBS5Y4.js.map +1 -0
  234. package/dist/{chunk-MQIZAD4A.js → chunk-C4OYOKWX.js} +138 -22
  235. package/dist/chunk-C4OYOKWX.js.map +1 -0
  236. package/dist/chunk-DG6G5EPF.js +35 -0
  237. package/dist/chunk-DG6G5EPF.js.map +1 -0
  238. package/dist/chunk-E36EVCKG.js +34 -0
  239. package/dist/chunk-E36EVCKG.js.map +1 -0
  240. package/dist/chunk-EAC4CDY7.js +57 -0
  241. package/dist/chunk-EAC4CDY7.js.map +1 -0
  242. package/dist/chunk-EZVNC4J6.js +104 -0
  243. package/dist/chunk-EZVNC4J6.js.map +1 -0
  244. package/dist/chunk-F4QMLOXJ.js +135 -0
  245. package/dist/chunk-F4QMLOXJ.js.map +1 -0
  246. package/dist/chunk-GLDWRCAM.js +49 -0
  247. package/dist/chunk-GLDWRCAM.js.map +1 -0
  248. package/dist/chunk-I3Q7KKYM.js +33 -0
  249. package/dist/chunk-I3Q7KKYM.js.map +1 -0
  250. package/dist/chunk-IA7TMD4Y.js +288 -0
  251. package/dist/chunk-IA7TMD4Y.js.map +1 -0
  252. package/dist/chunk-JCV4GXWB.js +249 -0
  253. package/dist/chunk-JCV4GXWB.js.map +1 -0
  254. package/dist/chunk-K47P7QZC.js +17 -0
  255. package/dist/chunk-K47P7QZC.js.map +1 -0
  256. package/dist/chunk-KLJKU6ZL.js +109 -0
  257. package/dist/chunk-KLJKU6ZL.js.map +1 -0
  258. package/dist/{chunk-56RJXPHG.js → chunk-KROD7KZK.js} +7 -4
  259. package/dist/chunk-KROD7KZK.js.map +1 -0
  260. package/dist/chunk-MA73TA6D.js +42 -0
  261. package/dist/chunk-MA73TA6D.js.map +1 -0
  262. package/dist/chunk-MERWRTBG.js +45 -0
  263. package/dist/chunk-MERWRTBG.js.map +1 -0
  264. package/dist/chunk-MEY33VWB.js +44 -0
  265. package/dist/chunk-MEY33VWB.js.map +1 -0
  266. package/dist/chunk-MP2GDBQZ.js +50 -0
  267. package/dist/chunk-MP2GDBQZ.js.map +1 -0
  268. package/dist/chunk-N3GKMPDW.js +51 -0
  269. package/dist/chunk-N3GKMPDW.js.map +1 -0
  270. package/dist/chunk-NAPE7WUS.js +75 -0
  271. package/dist/chunk-NAPE7WUS.js.map +1 -0
  272. package/dist/chunk-NN6GYOA2.js +47 -0
  273. package/dist/chunk-NN6GYOA2.js.map +1 -0
  274. package/dist/chunk-NNMLEDMD.js +399 -0
  275. package/dist/chunk-NNMLEDMD.js.map +1 -0
  276. package/dist/chunk-NYL63PSD.js +95 -0
  277. package/dist/chunk-NYL63PSD.js.map +1 -0
  278. package/dist/chunk-P7D3EMXM.js +52 -0
  279. package/dist/chunk-P7D3EMXM.js.map +1 -0
  280. package/dist/chunk-PSHUSWQH.js +18 -0
  281. package/dist/chunk-PSHUSWQH.js.map +1 -0
  282. package/dist/chunk-Q6YU3BT7.js +138 -0
  283. package/dist/chunk-Q6YU3BT7.js.map +1 -0
  284. package/dist/chunk-RRB26JRJ.js +247 -0
  285. package/dist/chunk-RRB26JRJ.js.map +1 -0
  286. package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
  287. package/dist/chunk-SRSJO5QE.js.map +1 -0
  288. package/dist/chunk-SWVW56CV.js +188 -0
  289. package/dist/chunk-SWVW56CV.js.map +1 -0
  290. package/dist/chunk-TEYARRAK.js +216 -0
  291. package/dist/chunk-TEYARRAK.js.map +1 -0
  292. package/dist/chunk-UCKKT6KO.js +71 -0
  293. package/dist/chunk-UCKKT6KO.js.map +1 -0
  294. package/dist/chunk-UIDKY4UB.js +14 -0
  295. package/dist/chunk-UIDKY4UB.js.map +1 -0
  296. package/dist/chunk-VCBTX62E.js +135 -0
  297. package/dist/chunk-VCBTX62E.js.map +1 -0
  298. package/dist/chunk-VZOFZPSO.js +71 -0
  299. package/dist/chunk-VZOFZPSO.js.map +1 -0
  300. package/dist/{chunk-TRNBHPNW.js → chunk-X5QSICJ3.js} +13 -7
  301. package/dist/chunk-X5QSICJ3.js.map +1 -0
  302. package/dist/chunk-Y74NWGQW.js +418 -0
  303. package/dist/chunk-Y74NWGQW.js.map +1 -0
  304. package/dist/chunk-ZLWQZUGF.js +27 -0
  305. package/dist/chunk-ZLWQZUGF.js.map +1 -0
  306. package/dist/collapsible-panel.d.ts +122 -0
  307. package/dist/collapsible-panel.js +115 -0
  308. package/dist/collapsible-panel.js.map +1 -0
  309. package/dist/content-item.d.ts +46 -0
  310. package/dist/content-item.js +4 -0
  311. package/dist/content-item.js.map +1 -0
  312. package/dist/css-values.d.ts +107 -0
  313. package/dist/css-values.js +3 -0
  314. package/dist/css-values.js.map +1 -0
  315. package/dist/device-class.js +33 -5
  316. package/dist/device-class.js.map +1 -1
  317. package/dist/disclosure-arrow.d.ts +6 -1
  318. package/dist/disclosure-arrow.js +1 -1
  319. package/dist/empty-state.d.ts +5 -2
  320. package/dist/empty-state.js +1 -1
  321. package/dist/flex-alignment-4ms8ZbV8.d.ts +5 -0
  322. package/dist/floating-toolbar.d.ts +51 -0
  323. package/dist/floating-toolbar.js +3 -0
  324. package/dist/floating-toolbar.js.map +1 -0
  325. package/dist/grid.d.ts +42 -0
  326. package/dist/grid.js +5 -0
  327. package/dist/grid.js.map +1 -0
  328. package/dist/help-tags-register.d.ts +13 -0
  329. package/dist/help-tags-register.js +9 -0
  330. package/dist/help-tags-register.js.map +1 -0
  331. package/dist/index.d.ts +35 -14
  332. package/dist/index.js +43 -22
  333. package/dist/list-action-row.d.ts +64 -0
  334. package/dist/list-action-row.js +8 -0
  335. package/dist/list-action-row.js.map +1 -0
  336. package/dist/list-header.d.ts +99 -0
  337. package/dist/list-header.js +10 -0
  338. package/dist/list-header.js.map +1 -0
  339. package/dist/list-inset-control.d.ts +22 -0
  340. package/dist/list-inset-control.js +3 -0
  341. package/dist/list-inset-control.js.map +1 -0
  342. package/dist/list-inset-text.d.ts +30 -0
  343. package/dist/list-inset-text.js +4 -0
  344. package/dist/list-inset-text.js.map +1 -0
  345. package/dist/list-item.d.ts +62 -0
  346. package/dist/list-item.js +8 -0
  347. package/dist/list-item.js.map +1 -0
  348. package/dist/list.d.ts +59 -0
  349. package/dist/list.js +6 -0
  350. package/dist/list.js.map +1 -0
  351. package/dist/loading-spinner.d.ts +3 -1
  352. package/dist/loading-spinner.js +1 -1
  353. package/dist/lucide-icon.d.ts +3 -1
  354. package/dist/lucide-icon.js +1 -1
  355. package/dist/nav-stack.d.ts +60 -10
  356. package/dist/nav-stack.js +6 -1
  357. package/dist/pane.d.ts +102 -0
  358. package/dist/pane.js +4 -0
  359. package/dist/pane.js.map +1 -0
  360. package/dist/panel-toolbar-DqwtBxQL.d.ts +63 -0
  361. package/dist/popup-menu-register.d.ts +9 -0
  362. package/dist/popup-menu-register.js +31 -0
  363. package/dist/popup-menu-register.js.map +1 -0
  364. package/dist/popup-menu.d.ts +99 -0
  365. package/dist/popup-menu.js +3 -0
  366. package/dist/popup-menu.js.map +1 -0
  367. package/dist/resizable-region.d.ts +32 -3
  368. package/dist/resizable-region.js +1 -1
  369. package/dist/row.d.ts +48 -0
  370. package/dist/row.js +6 -0
  371. package/dist/row.js.map +1 -0
  372. package/dist/segmented-control.d.ts +13 -8
  373. package/dist/segmented-control.js +1 -1
  374. package/dist/select-register.d.ts +5 -1
  375. package/dist/select-register.js +243 -2
  376. package/dist/select-register.js.map +1 -1
  377. package/dist/select.d.ts +101 -8
  378. package/dist/select.js +4 -1
  379. package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
  380. package/dist/sides-BPSWde0A.d.ts +4 -0
  381. package/dist/skeleton.d.ts +31 -0
  382. package/dist/skeleton.js +4 -0
  383. package/dist/skeleton.js.map +1 -0
  384. package/dist/spacer.d.ts +18 -0
  385. package/dist/spacer.js +4 -0
  386. package/dist/spacer.js.map +1 -0
  387. package/dist/split-view.d.ts +39 -7
  388. package/dist/split-view.js +120 -10
  389. package/dist/split-view.js.map +1 -1
  390. package/dist/state-banner.d.ts +19 -5
  391. package/dist/state-banner.js +4 -1
  392. package/dist/styles/app-tab.css +104 -0
  393. package/dist/styles/badge.css +118 -0
  394. package/dist/styles/catalog/components/catalog-example-stack.css +9 -0
  395. package/dist/styles/catalog/components/catalog-example.css +164 -0
  396. package/dist/styles/catalog/components/catalog-stage.css +55 -0
  397. package/dist/styles/catalog/components/catalog.css +12 -0
  398. package/dist/styles/catalog.css +5 -0
  399. package/dist/styles/chip.css +118 -0
  400. package/dist/styles/collapsible-panel.css +575 -0
  401. package/dist/styles/content-item.css +30 -0
  402. package/dist/styles/document.css +23 -0
  403. package/dist/styles/floating-toolbar.css +119 -0
  404. package/dist/styles/foundation.css +178 -40
  405. package/dist/styles/grid.css +28 -0
  406. package/dist/styles/layout.css +3 -41
  407. package/dist/styles/list-action-row.css +257 -0
  408. package/dist/styles/list-header.css +228 -0
  409. package/dist/styles/list-inset-control.css +30 -0
  410. package/dist/styles/list-inset-text.css +29 -0
  411. package/dist/styles/list-item.css +245 -0
  412. package/dist/styles/list.css +133 -0
  413. package/dist/styles/nav-stack.css +158 -50
  414. package/dist/styles/pane.css +229 -0
  415. package/dist/styles/popup-menu.css +271 -0
  416. package/dist/styles/resizable-region.css +429 -4
  417. package/dist/styles/row.css +111 -0
  418. package/dist/styles/segmented-control.css +33 -9
  419. package/dist/styles/select.css +262 -9
  420. package/dist/styles/skeleton.css +92 -0
  421. package/dist/styles/spacer.css +11 -0
  422. package/dist/styles/split-view.css +108 -3
  423. package/dist/styles/state-banner.css +94 -19
  424. package/dist/styles/styles.css +14 -4
  425. package/dist/styles/sunken-panel.css +22 -0
  426. package/dist/styles/surface-scaffold.css +56 -0
  427. package/dist/styles/tab-bar.css +132 -9
  428. package/dist/styles/tab-scaffold.css +113 -3
  429. package/dist/styles/text.css +65 -0
  430. package/dist/styles/token-search-field.css +49 -43
  431. package/dist/styles/toolbar-control-group.css +465 -28
  432. package/dist/styles/toolbar-text.css +88 -4
  433. package/dist/styles/toolbar.css +322 -11
  434. package/dist/styles/value-table.css +34 -4
  435. package/dist/styles/webawesome.css +348 -10
  436. package/dist/styles/workbench.css +828 -7
  437. package/dist/sunken-panel.d.ts +22 -0
  438. package/dist/sunken-panel.js +3 -0
  439. package/dist/sunken-panel.js.map +1 -0
  440. package/dist/surface-scaffold.d.ts +30 -0
  441. package/dist/surface-scaffold.js +3 -0
  442. package/dist/surface-scaffold.js.map +1 -0
  443. package/dist/tab-bar.d.ts +31 -6
  444. package/dist/tab-bar.js +1 -1
  445. package/dist/tab-scaffold.d.ts +54 -7
  446. package/dist/tab-scaffold.js +82 -9
  447. package/dist/tab-scaffold.js.map +1 -1
  448. package/dist/text.d.ts +38 -0
  449. package/dist/text.js +3 -0
  450. package/dist/text.js.map +1 -0
  451. package/dist/token-search-field-DvgFLhLL.d.ts +130 -0
  452. package/dist/token-search-field.d.ts +3 -43
  453. package/dist/token-search-field.js +1 -1
  454. package/dist/token-search-model.d.ts +3 -0
  455. package/dist/token-search-model.js +3 -0
  456. package/dist/token-search-model.js.map +1 -0
  457. package/dist/toolbar-control-group.d.ts +65 -7
  458. package/dist/toolbar-control-group.js +1 -1
  459. package/dist/toolbar-text.d.ts +37 -4
  460. package/dist/toolbar-text.js +3 -1
  461. package/dist/toolbar.d.ts +56 -7
  462. package/dist/toolbar.js +1 -1
  463. package/dist/types-BuHELdXR.d.ts +117 -0
  464. package/dist/value-table.d.ts +13 -3
  465. package/dist/value-table.js +3 -1
  466. package/dist/webawesome.d.ts +8 -1
  467. package/dist/wire-catalog.d.ts +82 -0
  468. package/dist/wire-catalog.js +327 -0
  469. package/dist/wire-catalog.js.map +1 -0
  470. package/dist/wire-nav-stack.d.ts +4 -1
  471. package/dist/wire-nav-stack.js +147 -7
  472. package/dist/wire-nav-stack.js.map +1 -1
  473. package/dist/wire-resizable-regions.d.ts +2 -1
  474. package/dist/wire-resizable-regions.js +2 -1
  475. package/dist/wire-scroll-dividers.d.ts +69 -0
  476. package/dist/wire-scroll-dividers.js +3 -0
  477. package/dist/wire-scroll-dividers.js.map +1 -0
  478. package/dist/wire-sidebar.d.ts +63 -0
  479. package/dist/wire-sidebar.js +231 -0
  480. package/dist/wire-sidebar.js.map +1 -0
  481. package/dist/wire-tab-bars.d.ts +19 -2
  482. package/dist/wire-tab-bars.js +1 -1
  483. package/dist/wire-tab-scaffold.js +9 -4
  484. package/dist/wire-tab-scaffold.js.map +1 -1
  485. package/dist/wire-token-search-fields.d.ts +65 -3
  486. package/dist/wire-token-search-fields.js +1 -1
  487. package/dist/wire-workbench.d.ts +109 -0
  488. package/dist/wire-workbench.js +6 -0
  489. package/dist/wire-workbench.js.map +1 -0
  490. package/dist/workbench.d.ts +218 -7
  491. package/dist/workbench.js +13 -15
  492. package/dist/workbench.js.map +1 -1
  493. package/docs/accessibility.md +413 -23
  494. package/docs/app-layouts.md +106 -8
  495. package/docs/catalog.md +440 -0
  496. package/docs/collapsible-panel.md +363 -0
  497. package/docs/component-contract.md +560 -52
  498. package/docs/component-integration.md +75 -0
  499. package/docs/component-selection.md +421 -89
  500. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  501. package/docs/design/templates/empty-state/filtered.svg +1 -0
  502. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  503. package/docs/design/templates/empty-state/with-action.svg +1 -0
  504. package/docs/design/templates/empty-state-dark.svg +1 -0
  505. package/docs/design/templates/empty-state.svg +1 -0
  506. package/docs/design/templates/list/compact-dark.svg +1 -0
  507. package/docs/design/templates/list/compact.svg +1 -0
  508. package/docs/design/templates/list/stack-dark.svg +1 -0
  509. package/docs/design/templates/list/stack.svg +1 -0
  510. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  511. package/docs/design/templates/list-action-row/default.svg +1 -0
  512. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  513. package/docs/design/templates/list-action-row/selected.svg +1 -0
  514. package/docs/design/templates/list-action-row-dark.svg +1 -0
  515. package/docs/design/templates/list-action-row.svg +1 -0
  516. package/docs/design/templates/list-dark.svg +1 -0
  517. package/docs/design/templates/list-header/action-dark.svg +1 -0
  518. package/docs/design/templates/list-header/action.svg +1 -0
  519. package/docs/design/templates/list-header/count-dark.svg +1 -0
  520. package/docs/design/templates/list-header/count.svg +1 -0
  521. package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
  522. package/docs/design/templates/list-header/inline-action.svg +1 -0
  523. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  524. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  525. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  526. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  527. package/docs/design/templates/list-header-dark.svg +1 -0
  528. package/docs/design/templates/list-header.svg +1 -0
  529. package/docs/design/templates/list-item/default-dark.svg +1 -0
  530. package/docs/design/templates/list-item/default.svg +1 -0
  531. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  532. package/docs/design/templates/list-item/multiline.svg +1 -0
  533. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  534. package/docs/design/templates/list-item/selected.svg +1 -0
  535. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  536. package/docs/design/templates/list-item/trailing.svg +1 -0
  537. package/docs/design/templates/list-item-dark.svg +1 -0
  538. package/docs/design/templates/list-item.svg +1 -0
  539. package/docs/design/templates/list.svg +1 -0
  540. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  541. package/docs/design/templates/segmented-control/equal.svg +1 -0
  542. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  543. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  544. package/docs/design/templates/segmented-control-dark.svg +1 -0
  545. package/docs/design/templates/segmented-control.svg +1 -0
  546. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  547. package/docs/design/templates/skeleton/avatar.svg +1 -0
  548. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  549. package/docs/design/templates/skeleton/block.svg +1 -0
  550. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  551. package/docs/design/templates/skeleton/lines.svg +1 -0
  552. package/docs/design/templates/skeleton-dark.svg +1 -0
  553. package/docs/design/templates/skeleton.svg +1 -0
  554. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  555. package/docs/design/templates/state-banner/danger.svg +1 -0
  556. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  557. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  558. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  559. package/docs/design/templates/state-banner/info.svg +1 -0
  560. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  561. package/docs/design/templates/state-banner/success.svg +1 -0
  562. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  563. package/docs/design/templates/state-banner/warning.svg +1 -0
  564. package/docs/design/templates/state-banner-dark.svg +1 -0
  565. package/docs/design/templates/state-banner.svg +1 -0
  566. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  567. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  568. package/docs/design/templates/tab-bar-dark.svg +1 -0
  569. package/docs/design/templates/tab-bar.svg +1 -0
  570. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  571. package/docs/design/templates/token-search-field/empty.svg +1 -0
  572. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  573. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  574. package/docs/design/templates/token-search-field-dark.svg +1 -0
  575. package/docs/design/templates/token-search-field.svg +1 -0
  576. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  577. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  578. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  579. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  580. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  581. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  582. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  583. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  584. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  585. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  586. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  587. package/docs/design/templates/toolbar-control-group.svg +1 -0
  588. package/docs/design/templates/toolbar-dark.svg +1 -0
  589. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  590. package/docs/design/templates/toolbar-text/default.svg +1 -0
  591. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  592. package/docs/design/templates/toolbar-text/large.svg +1 -0
  593. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  594. package/docs/design/templates/toolbar-text/small.svg +1 -0
  595. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  596. package/docs/design/templates/toolbar-text.svg +1 -0
  597. package/docs/design/templates/toolbar.svg +1 -0
  598. package/docs/design/templates/value-table/details-dark.svg +1 -0
  599. package/docs/design/templates/value-table/details.svg +1 -0
  600. package/docs/design/templates/value-table-dark.svg +1 -0
  601. package/docs/design/templates/value-table.svg +1 -0
  602. package/docs/design/templates.md +138 -0
  603. package/docs/design-philosophy.md +48 -20
  604. package/docs/device-class.md +10 -9
  605. package/docs/document-baseline.md +38 -0
  606. package/docs/examples/application-ui-profile.json +60 -0
  607. package/docs/examples/command-palette-adapter.tsx +69 -17
  608. package/docs/examples/component-catalog-extension.json +45 -0
  609. package/docs/examples/component-composition-extension.json +80 -0
  610. package/docs/layout.md +409 -59
  611. package/docs/nav-stack.md +126 -14
  612. package/docs/recipes.md +137 -54
  613. package/docs/split-view.md +69 -6
  614. package/docs/sunken-panel.md +58 -0
  615. package/docs/surface-scaffold.md +25 -0
  616. package/docs/tab-scaffold.md +94 -10
  617. package/docs/type-contracts.md +134 -0
  618. package/docs/ui-analyzer.md +306 -0
  619. package/docs/ui-doctor.md +107 -0
  620. package/docs/ui-evaluator.md +139 -0
  621. package/docs/ux-demo.md +40 -16
  622. package/docs/webawesome-theme.md +175 -16
  623. package/docs/workbench.md +534 -12
  624. package/doctor/cli.mjs +115 -0
  625. package/doctor/config.schema.json +68 -0
  626. package/doctor/eslint-diagnostics.d.mts +24 -0
  627. package/doctor/eslint-diagnostics.mjs +24 -0
  628. package/doctor/index.d.mts +116 -0
  629. package/doctor/index.mjs +1277 -0
  630. package/doctor/report.schema.json +118 -0
  631. package/evaluator/cli.mjs +89 -0
  632. package/evaluator/index.d.mts +90 -0
  633. package/evaluator/index.mjs +1203 -0
  634. package/evaluator/report.schema.json +152 -0
  635. package/llms.txt +423 -42
  636. package/package.json +241 -37
  637. package/traversal-exclusions.mjs +27 -0
  638. package/ux-demo/recipes/app-shell.tsx +205 -17
  639. package/ux-demo/recipes/collapsible-sidebar.tsx +260 -0
  640. package/ux-demo/recipes/compact-toolbar.tsx +135 -14
  641. package/ux-demo/recipes/composer-form.tsx +133 -21
  642. package/ux-demo/recipes/list-detail-dialog.tsx +282 -0
  643. package/ux-demo/recipes/list-workspace-states.tsx +183 -11
  644. package/ux-demo/recipes/loaders.ts +15 -5
  645. package/ux-demo/recipes/loading-inspector.tsx +204 -0
  646. package/ux-demo/recipes/mount-recipe.ts +28 -10
  647. package/ux-demo/recipes/navigation-sidebar.tsx +139 -13
  648. package/ux-demo/recipes/navigation-stack.tsx +79 -24
  649. package/ux-demo/recipes/types.ts +23 -2
  650. package/ux-demo/recipes/workspace-header.tsx +86 -5
  651. package/dist/browser/menu-action-row.js +0 -3
  652. package/dist/browser/menu-header.js +0 -5
  653. package/dist/browser/menu-item.js +0 -3
  654. package/dist/chunk-24Z2XE6C.js +0 -10
  655. package/dist/chunk-24Z2XE6C.js.map +0 -1
  656. package/dist/chunk-2RJBFNB6.js +0 -172
  657. package/dist/chunk-2RJBFNB6.js.map +0 -1
  658. package/dist/chunk-55DATV3A.js +0 -10
  659. package/dist/chunk-55DATV3A.js.map +0 -1
  660. package/dist/chunk-56RJXPHG.js.map +0 -1
  661. package/dist/chunk-7BR3DO2A.js.map +0 -1
  662. package/dist/chunk-7LDCTU7D.js +0 -134
  663. package/dist/chunk-7LDCTU7D.js.map +0 -1
  664. package/dist/chunk-AQNKJRFH.js +0 -79
  665. package/dist/chunk-AQNKJRFH.js.map +0 -1
  666. package/dist/chunk-CTQKI2XL.js +0 -28
  667. package/dist/chunk-CTQKI2XL.js.map +0 -1
  668. package/dist/chunk-DAFFDO33.js +0 -14
  669. package/dist/chunk-DAFFDO33.js.map +0 -1
  670. package/dist/chunk-EGHL5S4P.js +0 -33
  671. package/dist/chunk-EGHL5S4P.js.map +0 -1
  672. package/dist/chunk-KZKSBUKC.js +0 -19
  673. package/dist/chunk-KZKSBUKC.js.map +0 -1
  674. package/dist/chunk-M5JRI6MN.js +0 -27
  675. package/dist/chunk-M5JRI6MN.js.map +0 -1
  676. package/dist/chunk-MBR3TYNL.js +0 -14
  677. package/dist/chunk-MBR3TYNL.js.map +0 -1
  678. package/dist/chunk-MFKMHK5W.js +0 -10
  679. package/dist/chunk-MFKMHK5W.js.map +0 -1
  680. package/dist/chunk-MQIZAD4A.js.map +0 -1
  681. package/dist/chunk-NPPD2A6O.js +0 -40
  682. package/dist/chunk-NPPD2A6O.js.map +0 -1
  683. package/dist/chunk-R6JHHBKQ.js +0 -19
  684. package/dist/chunk-R6JHHBKQ.js.map +0 -1
  685. package/dist/chunk-RBTVBGRD.js +0 -23
  686. package/dist/chunk-RBTVBGRD.js.map +0 -1
  687. package/dist/chunk-S3TPM2LP.js +0 -23
  688. package/dist/chunk-S3TPM2LP.js.map +0 -1
  689. package/dist/chunk-TRNBHPNW.js.map +0 -1
  690. package/dist/chunk-VHW5YNIB.js +0 -17
  691. package/dist/chunk-VHW5YNIB.js.map +0 -1
  692. package/dist/chunk-W2FK7ZNY.js.map +0 -1
  693. package/dist/chunk-WVPYVDXY.js +0 -15
  694. package/dist/chunk-WVPYVDXY.js.map +0 -1
  695. package/dist/chunk-XZXRLY5Q.js +0 -33
  696. package/dist/chunk-XZXRLY5Q.js.map +0 -1
  697. package/dist/menu-action-row.d.ts +0 -50
  698. package/dist/menu-action-row.js +0 -4
  699. package/dist/menu-action-row.js.map +0 -1
  700. package/dist/menu-header.d.ts +0 -41
  701. package/dist/menu-header.js +0 -6
  702. package/dist/menu-header.js.map +0 -1
  703. package/dist/menu-item.d.ts +0 -31
  704. package/dist/menu-item.js +0 -4
  705. package/dist/menu-item.js.map +0 -1
  706. package/dist/panel-header.d.ts +0 -26
  707. package/dist/panel-header.js +0 -6
  708. package/dist/panel-header.js.map +0 -1
  709. package/dist/styles/menu-action-row.css +0 -160
  710. package/dist/styles/menu-header.css +0 -160
  711. package/dist/styles/menu-item.css +0 -123
  712. package/dist/styles/panel-header.css +0 -59
  713. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
  714. package/ux-demo/recipes/recipes.css +0 -355
@@ -5,9 +5,9 @@ Use the shipped [`component-catalog.json`](../ai/component-catalog.json) when a
5
5
  tool needs exhaustive structured facts; this page remains the concise human
6
6
  decision procedure.
7
7
 
8
- 1. Search the [Kerf UX catalog](./ux-demo.md) and the supported Web Awesome set.
8
+ 1. Search the [Kerf UX catalog](./ux-demo.md) and the supported Web Awesome set. Prefer a Kerf UI component over a Web Awesome component over a raw HTML tag, using each naturally for what it is good at.
9
9
  2. Reuse a primitive when its purpose, anatomy, state, and interaction match.
10
- 3. Compose primitives for recurring layout. Prefer public props and tokens; when a composition needs a selector, target only classes listed in the catalog's `publicClasses` contract.
10
+ 3. Compose primitives for recurring layout through public props and tokens. Do not select one component from another component's stylesheet.
11
11
  4. Add a thin application adapter for product copy, domain mapping, actions, routing, persistence, permissions, and transport.
12
12
  5. Use custom markup only when the semantic contract differs. If the pattern recurs across products, open an upstream component or recipe request.
13
13
 
@@ -15,34 +15,110 @@ The application adapter is usually a plain function that maps domain state to
15
15
  component props plus stable `data-action` values. It is not a fork of package
16
16
  markup or CSS.
17
17
 
18
- **Don't fight the components.** The package is built to look right unstyled, so
19
- custom CSS is the exception. Before adding `padding`, `margin`, `width`, `height`,
20
- `border`, `background`, a wrapper card, or a decoration, check whether the
21
- component, the pane, or the content-item already owns it — it almost always does,
22
- and adding more usually double-insets or fights it. Trust component defaults and
23
- fix the surrounding layout instead of overriding a control. See
18
+ **Don't fight the components.** The package is built to look right without an
19
+ application customization layer. Before adding `padding`, `margin`, `width`,
20
+ `height`, `border`, `background`, a wrapper card, or a decoration, check whether
21
+ the component, the pane, or the content-item already owns it — it almost always
22
+ does. Express presentation through props and tokens. Encapsulate genuinely new
23
+ structure in a self-styled application component, and never reach into a nested
24
+ Kerf or Web Awesome component from that stylesheet. See
24
25
  [`design-philosophy.md`](./design-philosophy.md) "Reach for the primitive, not for
25
26
  CSS".
26
27
 
28
+ ## Foundation tokens
29
+
30
+ Import the opt-in `@kerfjs/ui/document.css` baseline when the application wants
31
+ Kerf UI to own global box sizing, body typography/colors, plain-link color, and
32
+ the definite full-height chain. Put `.kui-app-root` on the one direct mount
33
+ container only for a full-height shell; ordinary flowing pages can omit it. See
34
+ [Document baseline](document-baseline.md).
35
+
36
+ Import `@kerfjs/ui/foundation.css` when application-owned composition CSS needs
37
+ the same semantic typography, spacing, device breakpoints, geometry, radii,
38
+ colors, focus ring, or shadows as Kerf components. Its complete supported
39
+ `--kui-*` surface is cataloged by the `foundation` entry; tools may treat that
40
+ entry as the public allowlist rather than inferring stability from CSS text.
41
+ Override these tokens globally for an application theme or on the narrowest
42
+ useful subtree. Prefer a component prop or component-specific token when the
43
+ change belongs to one component or instance, and preserve the documented
44
+ foreground/background contrast relationships and visible focus ring.
45
+
46
+ ### Typed runtime dimensions
47
+
48
+ Use `Row`, `Grid`, or `List` spacing shorthands for repeated sibling spacing: `gap="xs"`,
49
+ `gap="m"`, and the other `UiSpaceName` values resolve to their matching
50
+ `--kui-space-*` token. Use `Spacer` for one deliberate empty width or height, or
51
+ pass `flex` to consume the remaining space along a flex parent's main axis.
52
+ The canonical rhythm is `none`, `2xs`, `xs`, `m`, and `l`; use `s` and `xl`
53
+ only as deliberate exceptions.
54
+
55
+ When a value cannot be expressed by one spacing step, import the CSS-free
56
+ `@kerfjs/ui/css-values` subpath. `px`, `rem`, `em`, and `pct` create typed
57
+ `CssLength` values; `space` and `lengthVar` reference spacing or
58
+ application-owned length tokens. `plus` creates a non-standalone
59
+ `CssLengthExpression`, so wrap it with `calc` before passing it to a gap prop:
60
+
61
+ ```tsx
62
+ import { calc, pct, plus, rem, space } from "@kerfjs/ui/css-values";
63
+ import { Grid } from "@kerfjs/ui/grid";
64
+ import { List } from "@kerfjs/ui/list";
65
+ import { Row } from "@kerfjs/ui/row";
66
+
67
+ <List gap="xs">...</List>;
68
+ <Grid columns={3} gap="m">...</Grid>;
69
+ <Row hAlign="full" vAlign="middle" gap="m">...</Row>;
70
+ <List gap={space("m")}>...</List>;
71
+ <List gap={calc(plus(rem(0.25), pct(10)))}>...</List>;
72
+ ```
73
+
74
+ `CssValue` is the common branded base, not an arbitrary-string constructor.
75
+ For a responsive equal-width form, pass `Grid minColumnWidth={px(376)}` instead
76
+ of `columns`; the Grid's container width decides when the tracks collapse.
77
+ Raw strings such as `"0.25rem"` do not satisfy `Row.gap`, `Grid.gap`, or `List.gap`; this
78
+ contract lands before the 5.0 stable release so misspelled tokens and incomplete
79
+ expressions fail during typechecking rather than in the browser.
80
+
81
+ Other CSS-valued props use distinct contracts: `Spacer.width` and
82
+ `Spacer.height` take `UiSpaceName | CssLength`; `flex()` creates `CssFlex` for
83
+ `Row.flex`, `Grid.flex`, and `List.flex`; `Skeleton` dimensions take typed lengths (plus finite intrinsic
84
+ keywords for width/height); and `SelectChoice.color` takes a
85
+ `CssForegroundColor` from `uiColor()` with a foreground token (`*-on-*`, a text
86
+ role, or a `*-text` alias) or from `foregroundColorVar()`. The bare `success`,
87
+ `warning`, `danger`, `pop`, and `accent` tokens are quiet fills, not
88
+ foregrounds, and are rejected there; `colorVar()` returns a plain `CssColor`.
89
+ Do not substitute one brand for another. Row components
90
+ have no declaration-string `style` prop; use `className`, documented public
91
+ tokens, and semantic component props. Catalog media queries remain strings and
92
+ semantic pixel measurements remain numbers because those are different APIs.
93
+ The corresponding public builders are `flex`, `uiColor`, `colorVar`, and
94
+ `foregroundColorVar`.
95
+
27
96
  ## Production recipes
28
97
 
29
98
  Use the [complete recipe guide](./recipes.md) when several primitives form one
30
99
  application boundary:
31
100
 
32
- | Task | Stable catalog route |
33
- | --- | --- |
34
- | Desktop application shell | [Catalog](../ux-demo/) · `?component=recipe-app-shell` |
35
- | Navigation sidebar | [Catalog](../ux-demo/) · `?component=recipe-navigation-sidebar` |
36
- | Workspace header | [Catalog](../ux-demo/) · `?component=recipe-workspace-header` |
37
- | Master-detail dialog | [Catalog](../ux-demo/) · `?component=recipe-master-detail-dialog` |
38
- | Composer form | [Catalog](../ux-demo/) · `?component=recipe-composer-form` |
39
- | List workspace states | [Catalog](../ux-demo/) · `?component=recipe-list-workspace-states` |
40
- | Compact toolbar choices and actions | [Catalog](../ux-demo/) · `?component=recipe-compact-toolbar` |
41
- | Navigation stack | [Catalog](../ux-demo/) · `?component=recipe-navigation-stack` |
101
+ | Task | Stable catalog route |
102
+ | ----------------------------------- | ------------------------------------------------------------------ |
103
+ | Desktop application shell | [Catalog](../ux-demo/) · `?component=recipe-app-shell` |
104
+ | Navigation sidebar | [Catalog](../ux-demo/) · `?component=recipe-navigation-sidebar` |
105
+ | Workspace header | [Catalog](../ux-demo/) · `?component=recipe-workspace-header` |
106
+ | List-detail dialog | [Catalog](../ux-demo/) · `?component=recipe-list-detail-dialog` |
107
+ | Composer form | [Catalog](../ux-demo/) · `?component=recipe-composer-form` |
108
+ | List workspace states | [Catalog](../ux-demo/) · `?component=recipe-list-workspace-states` |
109
+ | Compact toolbar choices and actions | [Catalog](../ux-demo/) · `?component=recipe-compact-toolbar` |
110
+ | Navigation stack | [Catalog](../ux-demo/) · `?component=recipe-navigation-stack` |
111
+ | Loading inspector | [Catalog](../ux-demo/) · `?component=recipe-loading-inspector` |
112
+ | Collapsible sidebar | [Catalog](../ux-demo/) · `?component=recipe-collapsible-sidebar` |
42
113
 
43
114
  Recipes use public production exports and show ownership boundaries; they are
44
115
  copyable reference compositions, not new monolithic components.
45
116
 
117
+ When presenting a component or recipe in a Catalog, use the authoritative
118
+ [Catalog demo authoring contract](./catalog.md#catalog-demo-authoring-contract)
119
+ rather than deriving preview structure or geometry-overlay behavior from these
120
+ selection entries.
121
+
46
122
  ## Missing recurring concepts
47
123
 
48
124
  Kerf UI does not export a command-palette component. Do not invent a package
@@ -56,38 +132,183 @@ an upstream component or recipe request.
56
132
 
57
133
  ## Problem-to-component matrix
58
134
 
59
- | Interface need | Use when | Do not use when; nearest alternative | Required wiring | Application owns | Import | Recipe |
60
- | --- | --- | --- | --- | --- | --- | --- |
61
- | Icon — `LucideIcon` | A decorative or explicitly labeled Lucide-compatible icon belongs in app UI. | Do not use an icon as the only name of an unfamiliar action; add visible or accessible text. Prefer it over Web Awesome `wa-icon`. | None. | Icon choice and meaningful label. | `@kerfjs/ui/lucide-icon` | [Accessibility](./accessibility.md#shared-rules) |
62
- | Disclosure indicator — `DisclosureArrow` | A control needs one animated 18px root-scaled visual for open and closed state, including configurable directions or a replacement icon. | Do not use it as the interactive control or accessible name; place it inside the button or control that exposes expanded state. | Pass the controlled `open` state, render it inside the owning control, author replacement icon content facing right before transforms, and override `--kui-disclosure-arrow-size` only when another visual size is required. Direction changes use the shortest rotation path, with counterclockwise chosen for a 180-degree closed-to-open tie. | Open state, interaction, accessible name, size override, replacement glyph, direction choices, and shortest-path rotation. | `@kerfjs/ui/disclosure-arrow` | [Component ownership](./component-contract.md#ownership-boundaries) |
63
- | Application toolbar — `Toolbar` | Leading identity, optional centered content, and trailing controls form one horizontal app bar. | Do not use it for a page, panel, or dialog heading; use `PanelHeader`. | Compose `ToolbarText` and `ToolbarControlGroup` where their contracts fit. | Actions, command availability, responsive relocation, and state. | `@kerfjs/ui/toolbar` | [Toolbar composition](../README.md#component-subpaths) |
64
- | Toolbar control cluster — `ToolbarControlGroup` | Related toolbar controls need contained, borderless, pressed, or single-control treatment. | Do not use it merely to align unrelated buttons; use toolbar slots or ordinary layout. Web Awesome `wa-button-group` is only for an exceptional grouped-action contract. | Delegate child actions; use `SegmentedControl` for an exclusive choice. | Actions, pressed/expanded state, and policy. | `@kerfjs/ui/toolbar-control-group` | [Component ownership](./component-contract.md#ownership-boundaries) |
65
- | Toolbar identity text — `ToolbarText` | A toolbar needs extra-large (page/panel title), large, default, or compact textual identity. | Do not substitute it for document heading semantics; use `PanelHeader` or native headings. | None. | Text and responsive priority. | `@kerfjs/ui/toolbar-text` | [Toolbar composition](../README.md#component-subpaths) |
66
- | Navigation row — `MenuItem` | A pane or navigation area needs a selectable, disabled, dormant-trailing, or multiline action row. | Do not put a control in `trailing`; use `MenuActionRow` when the trailing region must be independently interactive. Use an `<a>` for navigation that must retain link behavior, a native `<button>` for an ordinary action, or implement the complete ARIA menu widget. | Delegate its `data-action`; compose inside a `.kui-content` section. Put domain event/drop metadata in `rootAttributes` rather than adding wrapper markup. | Routing, selection, permissions, copy, action handling, and domain `data-*` values. | `@kerfjs/ui/menu-item` | [Pane geometry](../README.md#pane-and-content-geometry) |
67
- | Navigation row with trailing action — `MenuActionRow` | A full-width row needs a selectable primary action and an independently focusable trailing action. | Use `MenuItem` when trailing content is dormant metadata. Do not put controls inside the row's `label`, `icon`, or `trailingActionIcon` SafeHtml slots. Do not use `AppTab` outside tablist semantics or `ToolbarControlGroup` outside a toolbar. | Delegate both action strings; update controlled selection and any popover/context-menu state in the app. | Routing, selection, both action policies, domain metadata, and popover/context-menu behavior. | `@kerfjs/ui/menu-action-row` | [Accessibility](./accessibility.md#menuactionrow) |
68
- | Navigation section heading — `MenuHeader` | A menu section needs a full-width label, semantic count, non-count badge, logical-end action, or real disclosure state. | Do not concatenate counts into `label` or put numeric content in `badge`; use `count` with the localized full phrase in `countLabel`. Do not add a disclosure arrow to navigation that reveals nothing. Do not shrink its 44px action target to the 18px visual. Do not use it as a page, panel, or dialog title; use `PanelHeader`. | Delegate its optional action; the app controls expanded state and revealed content. Toggle mode supplies `DisclosureArrow` unless `actionIcon` replaces it. Use `triggerAttributes` only for domain `data-*` or a native popover relationship. | Section organization, valid count and localized count label, disclosure state and content, non-count badge content, popover target behavior, and policy. | `@kerfjs/ui/menu-header` | [Pane geometry](../README.md#pane-and-content-geometry) |
69
- | Application layout composition | A sidebar, main area, inspector, or dialog needs shared toolbar/content/footer and child geometry. | Do not pad the pane shell, wrap child-owned geometry in competing insets, invent unrelated centered measures, or leave an icon-only rail for a hidden pane. | Use `.kui-pane` and one `.kui-pane__content`; add `.kui-content` and `.kui-content-item` as needed. A visible pane owns collapse in its toolbar; move a hidden inline-start pane's restore control to the main toolbar leading edge and an inline-end pane's restore control to its trailing edge. | Layout hierarchy, reading width, scroll ownership, responsive relocation, and pane visibility state. | `@kerfjs/ui/layout.css` | [Pane anatomy](./layout.md#anatomy) |
70
- | Menu composition | Navigation sections need full-size rows and the same content-item geometry as every other pane. | Do not add sidebar-specific wrapper padding, shrink targets to icon size, nest an interactive trailing control in `MenuItem`, or use a chevron on a row that does not disclose content. Use ordinary links for a different navigation contract. | Compose `MenuHeader`, `MenuItem`, and `MenuActionRow` in `.kui-content`; use `MenuHeader` toggle mode with real controlled content, and use `.kui-content-item` for other surfaces plus a pane footer for toolbar actions. | Information architecture, disclosure content and state, responsive drawer/shell behavior, and token overrides. | `@kerfjs/ui/layout.css` | [Pane geometry](../README.md#pane-and-content-geometry) |
71
- | Resizable application pane — `ResizableRegion`, `clampRegionSize`, `resizeRegionFromPointer` | A controlled split pane needs the Kerf separator, collapse state, pointer plus keyboard resizing, or a product-specific decorative grip. | Do not use it for a static two-column layout; use CSS grid. Prefer it over Web Awesome `wa-split-panel` unless that component's distinct API is required. Keep `handleIcon` noninteractive. | Call `wireResizableRegions` from `@kerfjs/ui/wire-resizable-regions` once and retain its disposer. | Size signal, min/max policy, collapse policy, persistence, and optional decorative handle icon. | `@kerfjs/ui/resizable-region` | [ResizableRegion contract](./accessibility.md#resizableregion) |
72
- | One application tab — `AppTab` | A controlled app tab needs selection, close, drag, leading/trailing anatomy, safe domain metadata, or a product-specific close glyph. | Do not render it alone or use it for a small settings choice; compose in `TabBar`, or use `SegmentedControl`. Keep `closeIcon` noninteractive. | Compose in `TabBar`; let `wireTabBars` manage interaction. Put only domain `data-*` values in `rootAttributes`. | Tab identity, order, selection, close policy, content, and domain metadata values. | `@kerfjs/ui/app-tab` | [Tabs contract](./accessibility.md#tabs) |
73
- | Application tab strip — `TabBar`, `wireTabBars`, `reorderTabs` | Tabs switch page regions and may overflow, close, or reorder. | Do not use it for a compact local view toggle; use `SegmentedControl`. Do not use it for a long choice list; use `Select`. Prefer it over Web Awesome `wa-tab-group`, `wa-tab`, and `wa-tab-panel` for Kerf app tabs. | Call `wireTabBars` once, retain the disposer, and apply `onReorder` synchronously; `reorderTabs` is the default array helper. | Ordered tabs, selection, panels, routing, closing, and persistence. | `@kerfjs/ui/tab-bar` plus `@kerfjs/ui/wire-tab-bars` | [Tabs contract](./accessibility.md#tabs) |
74
- | Panel, dialog, or page heading — `PanelHeader` | A panel, dialog, or page needs a heading with an extra-large title, an optional icon and subtitle, and trailing actions. | Do not use it as persistent app chrome; use `Toolbar`. It supplies header structure, not modal behavior; use an application overlay or Web Awesome `wa-dialog` for that behavior. | Connect the title id and any provided summary id to the dialog or panel host, pass the trailing controls (typically a `ToolbarControlGroup`), and delegate their actions. | Open state, focus lifecycle, dismissal, the trailing controls, labels, and copy. | `@kerfjs/ui/panel-header` | [Header ownership](./component-contract.md#extracted-versus-application-specific) |
75
- | Key/value facts — `ValueTable`, `ValueTableRow` | Read-only labels and values form a semantic definition list, optionally with a leading icon. | Do not use it for editable form fields or a row/column data grid; use native form or table semantics. | Compose typed `ValueTableRow` entries; pass `icon` when a 24px leading icon adds useful context. | Values, formatting, icon meaning, and empty/loading policy. | `@kerfjs/ui/value-table` | [Component ownership](./component-contract.md#ownership-boundaries) |
76
- | Indeterminate activity — `LoadingSpinner` | A Kerf surface needs compact, labeled or decorative indeterminate progress. | Do not use it for known progress; use Web Awesome `wa-progress-bar` or `wa-progress-ring`. Direct Web Awesome UI may use `wa-spinner`; do not mix spinner systems within one surface. | None; pass a label when the spinner conveys status. | Loading lifecycle and adjacent status copy. | `@kerfjs/ui/loading-spinner` | [Accessibility](./accessibility.md#shared-rules) |
77
- | Value selection — `Select` | A controlled form value comes from a moderate or long choice list, possibly grouped or icon-bearing. | Do not use it for commands; use a real action menu. Do not use it for a small visible choice set; use `SegmentedControl`. Prefer it over direct `wa-select`, `wa-option`, or value-like `wa-dropdown`/`wa-dropdown-item` composition. | Import `@kerfjs/ui/select/register` once; listen for standard input/change events. | Controlled value, validation, choices, and domain mapping. | `@kerfjs/ui/select` | [Web Awesome integration](../README.md#web-awesome-theme) |
78
- | Small exclusive choice — `SegmentedControl` | A few visible choices switch a compact view or setting, with toolbar, rounded, or pill presentation. | Do not use it for tabpanel semantics; use `TabBar`. Do not use it for many choices; use `Select`. Prefer it over `wa-button-group` when the controls select one value. | Delegate its action, read `data-segment-value`, update `value`, and rerender. | Controlled value, labels, action, and persistence. | `@kerfjs/ui/segmented-control` | [SegmentedControl contract](./accessibility.md#segmentedcontrol) |
79
- | Structured search editor — `TokenSearchField`, `readTokenSearchField`, `placeTokenSearchCaret`, `wireTokenSearchFields` | Free text and ordered, editable, removable filter tokens share one searchbox; enable `collapsible` when an empty, unfocused field should reduce to one iconic action, standalone or in a toolbar group. | Do not use it for ordinary text entry; use a native input or Web Awesome `wa-input`. Do not use it when filters belong in separate form controls. | Read DOM-owned text on input, empty `textContent` on clear, and use `placeTokenSearchCaret` after explicit controlled focus changes. Call `wireTokenSearchFields` from `@kerfjs/ui/wire-token-search-fields` once so Enter submits without adding a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. In `collapsible` mode it also manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse); bind the field's `expanded` to the signal on the returned handle (`handle.expanded(id)`) or adopt your own via `collapsible.signals`, and opt out per behavior only when the app must own it. | Parsing, suggestions, tokens, query execution, results, announcements, and — only if overriding the default — the collapsible `expanded` signal. | `@kerfjs/ui/token-search-field` | [TokenSearchField contract](./accessibility.md#tokensearchfield) |
80
- | Persistent inline status — `StateBanner` | A neutral, info, success, warning, or danger message belongs next to the affected work. | Do not use it for a no-content screen; use `EmptyState`. Do not use it for transient confirmation; use a toast. Web Awesome `wa-callout` is the ecosystem alternative for Web Awesome-owned content. | Delegate an optional action; choose alert urgency only for attention-requiring failure. | State mapping, message lifetime, retry/action behavior, and copy. | `@kerfjs/ui/state-banner` | [Feedback accessibility](./accessibility.md#shared-rules) |
81
- | Empty or busy content area — `EmptyState` | A content region has no items, cannot proceed, or is loading and needs explanation plus an optional action. | Do not use it for an inline status update; use `StateBanner`. Do not use it for transient success; use `wa-toast`/`wa-toast-item` or the application's toast system. | Delegate its optional action; it composes `LoadingSpinner` when busy. | Empty/busy policy, recovery action, illustration, and copy. | `@kerfjs/ui/empty-state` | [Feedback ownership](./component-contract.md#extracted-versus-application-specific) |
135
+ | Interface need | Use when | Do not use when; nearest alternative | Required wiring | Application owns | Import | Recipe |
136
+ | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
137
+ | Icon — `LucideIcon` | A decorative or explicitly labeled Lucide-compatible icon belongs in app UI. | Do not use an icon as the only name of an unfamiliar action; add visible or accessible text. Prefer it over Web Awesome `wa-icon`. | None. | Icon choice and meaningful label. | `@kerfjs/ui/lucide-icon` | [Accessibility](./accessibility.md#shared-rules) |
138
+ | Compact metadata — `Badge` | A short status, count, category, or metadata label needs self-owned visual treatment configured by semantic props. | Do not use it as a button, link, or removable chip. Do not restyle consuming-component descendants to create a badge. | Choose `tone`, `appearance`, `shape`, and `size`; supply `label` for abbreviated content or `ariaHidden` when a surrounding component already owns the same accessible text. For new content without a count use the text-free `size="dot"`, which requires `label` or `ariaHidden`. | Metadata text, semantic tone mapping, and abbreviated-content labeling. | `@kerfjs/ui/badge` | [Component ownership](./component-contract.md#ownership-boundaries) |
139
+ | Disclosure indicator — `DisclosureArrow` | A control needs one animated 18px root-scaled visual for open and closed state, including configurable directions or a replacement icon. | Do not use it as the interactive control or accessible name; place it inside the button or control that exposes expanded state. | Pass the controlled `open` state, render it inside the owning control, author replacement icon content facing right before transforms, and override `--kui-disclosure-arrow-size` only when another visual size is required. Direction changes use the shortest rotation path, with counterclockwise chosen for a 180-degree closed-to-open tie. | Open state, interaction, accessible name, size override, replacement glyph, direction choices, and shortest-path rotation. | `@kerfjs/ui/disclosure-arrow` | [Component ownership](./component-contract.md#ownership-boundaries) |
140
+ | Application toolbar or heading — `Toolbar` | Leading identity, optional centered content, and trailing controls form one horizontal app bar or a page/panel/dialog heading. | Do not add bespoke heading geometry or a private wrapper; use the same zone contract everywhere. | Put identity/title text directly in a zone as `ToolbarText`; group controls and icon tiles in `ToolbarControlGroup`. | Actions, command availability, responsive relocation, state, and supporting copy below a heading. | `@kerfjs/ui/toolbar` | [Toolbar composition](../README.md#component-subpaths) |
141
+ | Toolbar control cluster — `ToolbarControlGroup`, `ToolbarActionLink` | Related toolbar buttons, semantic links, compact/tight controls, avatar actions, or a dropdown trigger/menu surface need one configured chrome contract. | Do not use it merely to align unrelated controls. Do not restyle anchors or dropdown parts in application CSS; use `ToolbarActionLink`, `overflow`, and `menuInset`. Web Awesome `wa-button-group` is only for an exceptional grouped-action contract. | Delegate child actions; use `ToolbarActionLink` for native navigation, `overflow="scroll"` for bounded action rows, and `menuInset` for dropdown surface padding. Use `avatarImage` instead of inserting an `img`; use `SegmentedControl` for an exclusive choice. | Actions, destinations, pressed/expanded state, content, and policy. | `@kerfjs/ui/toolbar-control-group` | [Component ownership](./component-contract.md#ownership-boundaries) |
142
+ | Floating controls over content — `FloatingToolbar` | A small cluster of controls (e.g. a drawer restore) must float over scrolling content — distinct and forced-dark — inside a positioned container. | Do not use it for a primary page or panel toolbar (use `Toolbar`), or for anything that must sit above dialogs/overlays — it is not top-layer. | Compose `ToolbarControlGroup`s and delegate their actions; the app owns visibility and position. | Controls, visibility, and position. | `@kerfjs/ui/floating-toolbar` | [Component subpaths](../README.md#component-subpaths) |
143
+ | Toolbar identity text — `ToolbarText` | A toolbar needs extra-large (page/panel title), large, default, or compact textual identity. | Plain text is not a heading by default; when a title needs heading semantics pass `headingLevel`. Sizes below `xlarge` are intentionally quiet identity text with no tone option — do not use them as a normal-color subsection title; use `Text` with an `h2`–`h6` variant, or `ListHeader` for a list section. | Optional `headingLevel` for `role="heading"` + `aria-level`. Defaults to one line, ellipsized when it does not fit; `wrap` flows onto multiple lines, `ellipsis` toggles the trailing …, and `maxLines` caps a wrap. | Text, heading level, overflow behavior, and responsive priority. | `@kerfjs/ui/toolbar-text` | [Toolbar composition](../README.md#component-subpaths) |
144
+ | Semantic block or inline text — `Text` | Ordinary application copy needs native `h1`–`h6` or `p` semantics plus standard geometry, or inline secondary copy needs a native `span` without box geometry. | Do not choose heading levels for appearance alone or recreate muted, error, small, or code utility classes. Use `ToolbarText` for toolbar identity/title content and `ListInsetText` when list-aligned prose needs full margin/border/padding geometry. | Omit `variant` for the default `p`, choose `h1`–`h6` from the document outline, or choose `span` inside a row, label, or table cell. Independently compose `tone="quiet | danger"`, `size="compact"`, and `font="monospace"`; ordinary native global, `data-_`, and `aria-_` attributes pass through. | Copy, localization, semantic variant, presentation roles, and document heading hierarchy. | `@kerfjs/ui/text` | [Text](./layout.md#text) |
145
+ | Vertical stack — `List` | Rows, sections, or arbitrary components need a consistent vertical stretch layout, optional standard/custom gap, flex growth, one vertical scroll owner, or edge dividers. | It is layout-only and does not provide `ul`/`ol` semantics. Do not nest scroll owners or use it when a horizontal layout is required. | Choose `gap`, `flex`, `scrollable`, and canonical `dividerSides` (`t`, `r`, `b`, `l` combinations in that order); bound the block size when scrolling. | Child semantics/content, containing height, and which optional layout behaviors apply. | `@kerfjs/ui/list` | [Pane geometry](../README.md#pane-and-content-geometry) |
146
+ | Equal-width columns — `Grid` | Equal-width sibling columns need a fixed count or should collapse as their container narrows, while preserving equal tracks despite intrinsic child widths. | Do not use it for intrinsic, asymmetric, spanning, or masonry tracks; use application-owned CSS grid. Do not use it for a user-operable separator; use `ResizableRegion`. | Choose fixed `columns` or typed `minColumnWidth`, plus a typed `gap` and optional `flex`; responsive tracks preserve source order. | Child semantics/content, outer width, fixed count or minimum width, and typed gap/flex choices. | `@kerfjs/ui/grid` | [Grid](./layout.md#grid) |
147
+ | Navigation row — `ListItem` | A pane or navigation area needs a selectable, disabled, dormant-trailing, or multiline action row. | Do not put a control in `trailing`; use `ListActionRow` when the trailing region must be independently interactive. Use an `<a>` for navigation that must retain link behavior, a native `<button>` for an ordinary action, or implement the complete ARIA menu widget. | Delegate its `data-action`; compose inside a `.kui-content` section. Put domain event/drop metadata in `rootAttributes` rather than adding wrapper markup. The rounded root clips overflowing descendants; set `multiline` when the primary label should wrap. | Routing, selection, permissions, copy, action handling, and domain `data-*` values. | `@kerfjs/ui/list-item` | [Pane geometry](../README.md#pane-and-content-geometry) |
148
+ | Navigation row with trailing action — `ListActionRow` | A full-width row needs a selectable primary action and an independently focusable trailing action. | Use `ListItem` when trailing content is dormant metadata. Do not put controls inside the row's `label`, `icon`, or `trailingActionIcon` SafeHtml slots. Do not use `AppTab` outside tablist semantics or `ToolbarControlGroup` outside a toolbar. | Delegate both action strings; update controlled selection and any popover/context-menu state in the app. | Routing, selection, both action policies, domain metadata, and popover/context-menu behavior. | `@kerfjs/ui/list-action-row` | [Accessibility](./accessibility.md#listactionrow) |
149
+ | Navigation section heading — `ListHeader` | A menu section needs a full-width label, semantic count, non-count badge, logical-end action, or real disclosure state. | Do not concatenate counts into `label` or put numeric content in `badge`; use `count` with the localized full phrase in `countLabel`. Do not add a disclosure arrow to navigation that reveals nothing. Keep its 44px hit target; do not resize its 36px square. Do not use it as a page, panel, or dialog title; use a heading `Toolbar`. | Delegate its optional action; the app controls expanded state and revealed content. Toggle mode supplies `DisclosureArrow` unless `actionIcon` replaces it. Use `triggerAttributes` only for domain `data-*` or a native popover relationship. | Section organization, valid count and localized count label, disclosure state and content, non-count badge content, popover target behavior, and policy. | `@kerfjs/ui/list-header` | [Pane geometry](../README.md#pane-and-content-geometry) |
150
+ | Inset self-bordered control — `ListInsetControl` | A control that owns its own border and padding but no outer margin (a search input, a `SegmentedControl`) must line up inside a `.kui-content` list with the standard 8px inline margins and stretch to fill the row. | Do not wrap a `.kui-content-item` or a `ListItem`/`ListHeader` that already owns its inline margin — that double-insets it. Do not add ad-hoc `margin`/`padding` around a bare control to align it; use this instead. | Place the self-bordered control(s) as children; they stretch to fill. It applies only the inline margin, flex stretch, and 8px gap — the child owns its own border and padding. | The control(s), their state, and action wiring. | `@kerfjs/ui/list-inset-control` | [Pane geometry](../README.md#component-subpaths) |
151
+ | Inset bare text — `ListInsetText` | A plain string or inline content with no margin, border, or padding of its own must sit in a `.kui-content` list with its text edge aligned to the bordered items around it. | Do not use it to wrap a component that already owns content-item geometry (`ListItem`, `StateBanner`, a `.kui-content-item`) — that double-insets it. Do not hand-roll the 8px margin / 1px border / 8px padding. | Pass the text or inline `SafeHtml` as children; it supplies the 8px inline margin, 1px transparent border, and 8px padding so the text edge lands at the standard 17px inset. Pass `sides="rl"` to keep that horizontal inset but drop the vertical margin, border, and padding for tight text layout. | The text, copy, and localization. | `@kerfjs/ui/list-inset-text` | [Pane geometry](../README.md#component-subpaths) |
152
+ | Self-contained content child — `ContentItem` | An ordinary surface-like child of a `Pane` or `.kui-content` stack needs the shared 8px margin / 1px border / 8px padding geometry; pass `frame="framed"` only when it marks a real distinction and must read as visibly bounded, and `shape="pill"` for the 22px radius. | Do not wrap a component that already owns content-item geometry (`ListItem`, `ListHeader`, `StateBanner`, `Toolbar`, `ValueTable`) or pad around it — that double-insets. Do not frame an item only to make it look contained. Use `SunkenPanel` for a lowered filled surface and `ListInsetText` for bare text that only needs to align. | Pass the item content as children; choose `frame` (`none` default, `framed`) and `shape` (`rounded` default, `pill`) instead of writing `.kui-content-item` modifier classes. Put safe `data-*` metadata in `rootAttributes`; `ariaLabel` names a distinct region and `focusTarget` makes it a programmatic focus target. | The content and whether it marks a distinction worth a visible frame. | `@kerfjs/ui/content-item` | [Content items](./layout.md#content-items) |
153
+ | Application column — `Pane` | A sidebar, main area, inspector, or dialog needs shared vertical header/content/footer organization, one scroll owner, and optional edge separators. | Do not pad the pane shell, wrap child-owned geometry in competing insets, invent unrelated centered measures, or leave an icon-only rail for a hidden pane. | Import `Pane`, use `children` for its homogeneous primary content and the named `SafeHtml` prop slots `header`/`footer` for optional fixed chrome, then opt into only the needed logical-edge separators; add `ContentItem` children as needed. A visible pane owns collapse in its toolbar; move a hidden inline-start pane's restore control to the main toolbar leading edge and an inline-end pane's restore control to its trailing edge. | Layout hierarchy, reading width, responsive relocation, pane visibility state, slot semantics, labels, and separator placement. | `@kerfjs/ui/pane` | [Pane anatomy](./layout.md#anatomy) |
154
+ | Scroll dividers — `wireScrollDividers` | Pinned chrome (a `Pane` header or footer, a `NavStack` top chrome or bottom toolbar, a `TabScaffold` bar, the sides of a `TabBar` strip, or app-owned chrome around a scroller) needs a divider only while content is scrolled away beneath it. | Do not keep a static toolbar divider to fake it or write a border onto another component; each component draws its own line from the wiring's state. | Call `wireScrollDividers` once at the app root and retain the disposer; it covers every `Pane`, `NavStack`, `TabScaffold`, and `TabBar` below it. Name app-owned arrangements in `targets` by id; a `Toolbar` or `List` named as chrome draws its facing side. Configure a pane with `chromeDividers` (`scroll` default, `always`, `none`). | Which panes follow the scroll, app-owned targets, and the disposer. | `@kerfjs/ui/wire-scroll-dividers` | [Scroll dividers](./layout.md#scroll-dividers) |
155
+ | One-pane navigation — `NavStack` | A compact flow pushes into details while keeping earlier views mounted, or a single-pane screen needs the same stable layout contract. | Do not use it when list and detail should remain visible together; use `SplitView`. Do not use it alone for co-equal top-level destinations; use one stack per `TabScaffold` tab. | Keep the ordered view array controlled, put view-specific bottom chrome on each view (or use the component fallback), import its CSS explicitly, and call `wireNavStack` for back delegation, content-slide/chrome-cross-fade transitions, and focus move/restore. Mark a preferred initial target with `data-nav-focus` when DOM order is insufficient. | View keys/content/titles, push and pop actions, preferred initial focus targets, and per-view or persistent bottom toolbar content. | `@kerfjs/ui/nav-stack` | [App-layout decision matrix](./app-layouts.md#decision-matrix) |
156
+ | Responsive list-detail — `SplitView` | A primary list and related detail belong together on roomy devices but become a one-pane drill-down on compact devices. | Do not use it for a single linear flow or a multi-panel editor shell. | Provide controlled selection/detail state and device class; opt into resize wiring only when the separator is user adjustable. | Selection, detail content, compact back action, device policy, and optional persisted width. | `@kerfjs/ui/split-view` | [App-layout decision matrix](./app-layouts.md#decision-matrix) |
157
+ | Compact top-level destinations — `TabScaffold` | A handset or portrait-tablet app has two to five co-equal destinations whose scenes and nested navigation must remain mounted across switches. | Do not use it for document tabs, reorderable work items, or a desktop navigation rail. | Keep the active id controlled, import its CSS explicitly, and call `wireTabScaffold`; commonly place one `NavStack` in each tab scene. | Destination definitions, active id, routing, persistence, and roomy-device replacement. | `@kerfjs/ui/tab-scaffold` | [App-layout decision matrix](./app-layouts.md#decision-matrix) |
158
+ | Desktop multi-panel workspace — `Workbench` | A desktop-class tool needs a central work area plus independently collapsible navigator, inspector, or console regions. | Do not shrink the full shell onto compact devices or use it for one standalone panel. | Render only the required rails/drawer, keep collapse state controlled, and replace peripheral regions with stacks or overlays below desktop sizes. Opt a panel into drag/keyboard resizing with `resizable` and call `wireWorkbench` with its size signal. | Panel presence/content, collapsed states, sizes, persistence, responsive replacement, and controls. | `@kerfjs/ui/workbench` | [Workbench](./workbench.md) |
159
+ | Standalone rail or drawer — `CollapsiblePanel` | One left/right rail or bottom drawer needs controlled collapse without adopting the complete `Workbench` shell. | Do not use it for several coordinated workspace panels or retain an empty icon-only rail while collapsed. | Pair it with `CollapsiblePanelToggle`; call `wireSidebar` for focus transfer, Escape/backdrop dismissal, compact overlay trapping, and persistence integration. | Panel content/size, collapsed state, toggle placement, persistence, and compact policy. | `@kerfjs/ui/collapsible-panel` | [App-layout decision matrix](./app-layouts.md#decision-matrix) |
160
+ | Lowered content surface — `SunkenPanel` | A main work area or nested content group needs one visually lowered surface with a compact inset and vertical stack. | Do not use it merely to add padding, as a replacement for `Pane` anatomy, or around children that already own the same outer surface. | Compose the application-owned content as children, choose the default `rounded` shape or `square` for a flush edge-to-edge area, and pass `ariaLabel` only when the surface is a distinct named region. | Region semantics, accessible name, shape, scroll ownership, child ordering, and content. | `@kerfjs/ui/sunken-panel` | [SunkenPanel](./sunken-panel.md) |
161
+ | Menu composition | Navigation sections need full-size rows and the same content-item geometry as every other pane. | Do not add sidebar-specific wrapper padding, shrink targets to icon size, nest an interactive trailing control in `ListItem`, or use a chevron on a row that does not disclose content. Use ordinary links for a different navigation contract. | Compose `ListHeader`, `ListItem`, and `ListActionRow` in `.kui-content`; use `ListHeader` toggle mode with real controlled content, and use `ContentItem` for other surfaces plus a pane footer for toolbar actions. | Information architecture, disclosure content and state, responsive drawer/shell behavior, and token overrides. | `@kerfjs/ui/layout.css` | [Pane geometry](../README.md#pane-and-content-geometry) |
162
+ | Resizable application pane — `ResizableRegion`, `clampRegionSize`, `resizeRegionFromPointer` | A controlled split pane needs the Kerf separator, collapse state, pointer plus keyboard resizing, or a product-specific decorative grip. | Do not use it for static equal columns; use `Grid`. Use application CSS grid for static asymmetric or intrinsic tracks. Prefer it over Web Awesome `wa-split-panel` unless that component's distinct API is required. Keep `handleIcon` noninteractive. | Call `wireResizableRegions` from `@kerfjs/ui/wire-resizable-regions` once and retain its disposer. | Size signal, min/max policy, collapse policy, persistence, and optional decorative handle icon. | `@kerfjs/ui/resizable-region` | [ResizableRegion contract](./accessibility.md#resizableregion) |
163
+ | One application tab — `AppTab` | A controlled app tab needs selection, close, drag, leading/trailing anatomy, compact/segmented/icon-only presentation, label truncation, safe domain metadata, or a product-specific close glyph. | Do not render it alone or use it for a small settings choice; compose in `TabBar`, or use `SegmentedControl`. Keep `closeIcon` noninteractive. | Compose in `TabBar`; let `wireTabBars` manage interaction. Put only domain `data-*` values in `rootAttributes`; use typed appearance props instead of descendant CSS. | Tab identity, order, selection, close policy, content, domain metadata values, and outer placement. | `@kerfjs/ui/app-tab` | [Tabs contract](./accessibility.md#tabs) |
164
+ | Application tab strip — `TabBar`, `wireTabBars`, `reorderTabs` | Tabs switch page regions and may overflow, close, reorder, fill a segmented inspector strip, sit beside a trailing action, or keep that adjacent action distinct from a far-edge end action. | Do not use it for a compact local view toggle; use `SegmentedControl`. Do not use it for a long choice list; use `Select`. Prefer it over Web Awesome `wa-tab-group`, `wa-tab`, and `wa-tab-panel` for Kerf app tabs. | Call `wireTabBars` once, retain the disposer, and apply `onReorder` synchronously; `reorderTabs` is the default array helper. Combine `trailingPlacement="adjacent"` with `end` for split actions; TabBar owns the shrinking/scrolling tab geometry and keeps both actions visible. Choose typed presentation/allocation props while the parent owns outer placement. | Ordered tabs, selection, panels, routing, closing, persistence, action content, and outer placement. | `@kerfjs/ui/tab-bar` plus `@kerfjs/ui/wire-tab-bars` | [Tabs contract](./accessibility.md#tabs) |
165
+ | Panel, dialog, or page heading — `Toolbar` + `ToolbarText` | A panel, dialog, or page needs an extra-large title, optional icon, grouped trailing actions, and optional supporting copy. | Do not add a private heading wrapper or bespoke layout. The toolbar supplies structure, not modal behavior; use an application overlay or Web Awesome `wa-dialog` for that behavior. In a narrow peripheral rail or drawer, where xlarge would truncate at the panel's minimum size, use the default `ToolbarText` size as pane identity. | Connect title/supporting-copy ids to the host, group controls, and delegate actions. **For a page or view title set `headingLevel` (usually `1`)** on `ToolbarText`; omit it for a dialog title referenced by `aria-labelledby`. | Open state, focus lifecycle, dismissal, controls, ids, copy, and heading level. | `@kerfjs/ui/toolbar` + `@kerfjs/ui/toolbar-text` | [Toolbar headings](./accessibility.md#toolbar-headings) |
166
+ | Dialog or popup geometry — `DialogSurface`, `PopupSurface` | Recurring Web Awesome dialogs need standard sizes/presentations/body/footer insets, or dropdown menus need list-compatible inset ownership. | Do not use these wrappers as generic layout or to replace native dialog/dropdown behavior. Avoid consumer `::part()` overrides and do not stack a dialog body inset around list-owned child geometry. | Wrap one `wa-dialog` or `wa-dropdown`; choose the typed geometry while retaining native labeling, open state, focus, dismissal, and menu semantics. Compose a dialog body as a `List` by default, use `bodyInset="none"` when its children own list geometry, and wrap bare prose in `ListInsetText`. | State, focus lifecycle, dismissal, labels, content, and outer placement. | `@kerfjs/ui/surface-scaffold` | [Surface scaffolds](./surface-scaffold.md) |
167
+ | Key/value facts — `ValueTable`, `ValueTableRow` | Read-only labels and values form a semantic definition list, optionally with a leading icon. | Do not use it for editable form fields or a row/column data grid; use native form or table semantics. | Compose typed `ValueTableRow` entries; pass `icon` when a 24px leading icon adds useful context. | Values, formatting, icon meaning, and empty/loading policy. | `@kerfjs/ui/value-table` | [Component ownership](./component-contract.md#ownership-boundaries) |
168
+ | Indeterminate activity — `LoadingSpinner` | A Kerf surface needs compact, labeled or decorative indeterminate progress. | Do not use it for known progress; use Web Awesome `wa-progress-bar` or `wa-progress-ring`. Direct Web Awesome UI may use `wa-spinner`; do not mix spinner systems within one surface. | None; pass a label when the spinner conveys status. | Loading lifecycle and adjacent status copy. | `@kerfjs/ui/loading-spinner` | [Accessibility](./accessibility.md#shared-rules) |
169
+ | Loading placeholder — `Skeleton` + a component's `placeholder` prop | A value or a whole component is still loading and should hold its space as a subtle, unanimated block, keeping the layout stable — an inspector or detail view rendering its real chrome with per-record values absent. | Do not use it for known progress (use `LoadingSpinner`), do not animate it, and do not hand-rebuild a component's empty state — set `placeholder` on the component instead. Prefer it over `wa-skeleton`, which the pure-Kerf primitives avoid to stay Web-Awesome-free. | Set `placeholder` on a value-bearing component (`Select`, `ListHeader`, `ListItem`, `ValueTableRow`, `SegmentedControl`, `StateBanner`, `AppTab`, `ToolbarText`, `ListActionRow`) to render skeletons in its value slots with interactivity disabled; use the standalone `Skeleton` for a custom slot. | Loading lifecycle, which slots are unknown, and announcing the loading region. | `@kerfjs/ui/skeleton` | [Accessibility](./accessibility.md#shared-rules) |
170
+ | Command menu — `PopupMenu` | A short list of commands sits behind one trigger, such as a toolbar sort or More menu, optionally grouped under headings and dividers with item icons. | Do not use it to choose a persistent value; use `Select`. Do not hand-write `wa-dropdown`/`wa-dropdown-item` markup for an action menu. | Import `@kerfjs/ui/popup-menu/register` once. Place it in a `single` `ToolbarControlGroup` with `nestedDropdown` and a `menuInset`, or wrap it in `PopupSurface` outside a toolbar. Handle item `action`s by delegation. | Commands, enabled state, and reactions to opening or closing. | `@kerfjs/ui/popup-menu` | [Toolbar composition](#toolbar-composition) |
171
+ | Value selection — `Select` | A controlled form, toolbar, or compact navigation value comes from a moderate or long choice list, possibly grouped, icon-bearing, icon-only when closed, or truncating. Set `multiple` with an array `value` when the person may choose any number of them; add `selectedPresentation="icon-only"` and a fixed `triggerIcon` for a toolbar filter menu. | Do not use it for commands; use `PopupMenu`. Do not use it for a small visible choice set; use `SegmentedControl`. Prefer it over direct `wa-select`, `wa-option`, or value-like `wa-dropdown`/`wa-dropdown-item` composition. | Import `@kerfjs/ui/select/register` once for registration and current-request animation ownership; listen for standard input/change events. Do not add consumer popup timing or positioning repairs. Use typed presentation props rather than consumer `::part()` overrides; let the parent own placement. | Controlled value, validation, choices, domain mapping, and outer placement. | `@kerfjs/ui/select` | [Web Awesome integration](../README.md#web-awesome-theme) |
172
+ | Small exclusive choice — `SegmentedControl` | A few visible choices switch a compact view or setting, with toolbar, rounded, or pill presentation. | Do not use it for tabpanel semantics; use `TabBar`. Do not use it for many choices; use `Select`. Prefer it over `wa-button-group` when the controls select one value. | Delegate its action, read `data-segment-value`, update `value`, and rerender. | Controlled value, labels, action, and persistence. | `@kerfjs/ui/segmented-control` | [SegmentedControl contract](./accessibility.md#segmentedcontrol) |
173
+ | Structured search editor — `TokenSearchField`, `readTokenSearchField`, `placeTokenSearchCaret`, `wireTokenSearchFields` | Free text and ordered, editable, removable filter tokens share one searchbox; enable `collapsible` when an empty, unfocused field should reduce to one iconic action, standalone or in a toolbar group. | Do not use it for ordinary text entry; use a native input or Web Awesome `wa-input`. Do not use it when filters belong in separate form controls. | Read DOM-owned text on input, empty `textContent` on clear, and use `placeTokenSearchCaret` after explicit controlled focus changes. Call `wireTokenSearchFields` from `@kerfjs/ui/wire-token-search-fields` once so Enter submits without adding a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. In `collapsible` mode it also manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse); bind the field's `expanded` to the signal on the returned handle (`handle.expanded(id)`) or adopt your own via `collapsible.signals`, and opt out per behavior only when the app must own it. | Parsing, suggestions, tokens, query execution, results, announcements, and — only if overriding the default — the collapsible `expanded` signal. | `@kerfjs/ui/token-search-field` | [TokenSearchField contract](./accessibility.md#tokensearchfield) |
174
+ | Persistent inline status — `StateBanner` | A neutral, info, pop, success, warning, or danger message belongs next to the affected work; pop highlights featured or novel content without implying status. | Do not use pop as a success, warning, or danger substitute. Do not use StateBanner for a no-content screen; use `EmptyState`. Do not use it for transient confirmation; use a toast. Web Awesome `wa-callout` is the ecosystem alternative for Web Awesome-owned content. | Delegate an optional action; choose alert urgency only for attention-requiring failure. Use the optional tone-tinted `badge` for a terse count or status beside the title, not as the only expression of meaning. | State mapping, message lifetime, retry/action behavior, badge content, and copy. | `@kerfjs/ui/state-banner` | [Feedback accessibility](./accessibility.md#shared-rules) |
175
+ | Empty or busy content area — `EmptyState` | A content region has no items, cannot proceed, or is loading and needs explanation plus an optional action. | Do not use it for an inline status update; use `StateBanner`. Do not use it for transient success; use `wa-toast`/`wa-toast-item` or the application's toast system. | Delegate its optional action; it composes `LoadingSpinner` when busy. | Empty/busy policy, recovery action, illustration, and copy. | `@kerfjs/ui/empty-state` | [Feedback ownership](./component-contract.md#extracted-versus-application-specific) |
82
176
 
83
177
  ## Ambiguous choices
84
178
 
85
- - `Toolbar` is persistent app chrome; `PanelHeader` heads a panel, dialog, or page.
179
+ Set `ResizableRegion`'s `--kui-resizable-region-background` for an inline
180
+ surface. Use `contentOverflow="visible"` while an anchored popup must escape
181
+ the region; this also raises the region on its public popup layer. A collapsed
182
+ region can place `restoreControl` in a top or bottom corner with
183
+ `restorePosition`, set its typed `restoreInset`, or use
184
+ `restorePlacement="inline"` for a control in normal flow.
185
+
186
+ Use `FloatingToolbar placement="inline"` when a restore owner supplies the
187
+ position. Floating placement supports per-edge `--kui-floating-toolbar-inset-*`
188
+ tokens; `safeAreaInsets` adds the layout-routed or device safe-area inset to
189
+ the positioned edges.
190
+
191
+ For a larger option row, use `ListItem density="spacious"`; `compact` and
192
+ `standard` remain available. `multiline` wraps its label and aligns its icon
193
+ to the first line; `multilineIconAlign="center"` centers the icon against the
194
+ whole label. `divider` owns a before or after separator. Set
195
+ `state="drag-target"` for the component's drop outline and background. Public
196
+ `--kui-list-item-*` tokens control its row, icon, label, and trailing geometry
197
+ and color. `ListHeader` exposes `--kui-list-header-min-height`, title padding,
198
+ title minimum height, and border-width tokens for section density.
199
+ For a filled command list, set `--kui-list-item-background` and
200
+ `--kui-list-item-border` on the list or row. The matching
201
+ `--kui-list-item-hover-border` and `--kui-list-item-selected-border` tokens
202
+ set state outlines without replacing ListItem CSS or neutralizing global
203
+ semantic border colors.
204
+
205
+ Use `ListItemLink` from `@kerfjs/ui/list-item` for a navigation row with a
206
+ real `href`. It shares `ListItem` content slots, density, selection, and row
207
+ geometry while rendering a native anchor. `external` opens a new tab with
208
+ `noopener noreferrer` and announces that behavior in the default accessible
209
+ name. Disabled and loading links omit `href` and leave the tab order. Use
210
+ `ListItem` for an application action dispatched through `action`.
211
+
212
+ Use `Toolbar responsive="trailing-priority"` for a collapsible search in the
213
+ trailing zone that should take a full second row when expanded. `responsiveAt`
214
+ chooses the compact or narrow container breakpoint. The leading identity stays
215
+ in the first row. `--kui-toolbar-leading-min-width`,
216
+ `--kui-toolbar-trailing-gap`, `--kui-toolbar-trailing-justify`, and the
217
+ `--kui-toolbar-*-padding-inline` zone tokens adjust local geometry without
218
+ selecting toolbar anatomy from application CSS.
219
+ Set `--kui-toolbar-inset: 0px` on an embedded Toolbar that should have no outer
220
+ padding while keeping its `--kui-toolbar-gap` between control zones. The token
221
+ also reduces its minimum height by the removed inset; safe-area compensation
222
+ still adds to the chosen inset on claimed edges.
223
+
224
+ For compact dialog copy, use `Text flush lineHeight="tight"`; block Text still
225
+ resets native margins. `ValueTable density="compact"` reduces row padding and
226
+ gap. Its `--kui-value-table-row-columns`, `--kui-value-table-row-padding-block`,
227
+ and `--kui-value-table-row-gap` tokens support a specific metadata layout.
228
+ Use `ToolbarText size="xsmall"` for a dense rail heading. A
229
+ `TokenSearchField` editor can shrink to zero minimum width inside a narrow
230
+ group; set `--kui-token-search-editor-min-width` only when a wider editor is
231
+ required.
232
+
233
+ - `Toolbar` serves persistent app chrome and headings; the zone contents and accessible naming distinguish the purpose.
86
234
  - `TabBar` changes tabpanels and supports overflow/reorder; `SegmentedControl` chooses among a few compact views; `Select` handles a longer value list.
235
+
236
+ Use `AppTab presentation="icon-only"` with its `name` kept as the accessible
237
+ tab name, and `labelMaxWidth` for an ellipsized visible name. Set `attention`
238
+ to color a tab's name with `--kui-app-tab-attention-color`. Set `dropTarget`
239
+ while an app drag is over a tab's content; the tab keeps its selection semantics
240
+ and uses the `--kui-app-tab-drop-target-*` tokens for its highlight. Set
241
+ `nameOverflow="visible"` when an inline loading treatment needs the full name
242
+ instead of the default ellipsis. A `TabBar` owns its
243
+ scrolling strip; its public `--kui-tab-bar-strip-*` tokens configure strip
244
+ height, spacing, border, radius, background, and scroll inset without styling
245
+ its internal classes. `--kui-tab-bar-trailing-flex` controls the trailing zone.
246
+ Set `pinned` on the first `AppTab` when that tab must remain visible as peers
247
+ scroll. It stays inside the tablist and keyboard order. Set the public
248
+ `--kui-app-tab-pinned-background` token if the surrounding surface differs from
249
+ the default; the pinned tab must cover peers as they scroll beneath it.
250
+
87
251
  - `StateBanner` persists beside affected work; `EmptyState` replaces absent content; `wa-callout` is contextual ecosystem content; `wa-toast` and `wa-toast-item` are transient and must not carry the only copy of important state.
88
- - `ResizableRegion` is an interactive controlled pane. CSS grid is the right answer when columns do not need a user-operable separator.
252
+
253
+ For a banner with multi-line supporting copy, use `copyLayout="stacked"` to put
254
+ the detail below the title and badge. Use `actionPlacement="below"` when its
255
+ action needs a separate trailing row. The default values keep both inline;
256
+ `--kui-state-banner-copy-gap` and `--kui-state-banner-copy-row-gap` adjust the
257
+ stacked copy's spacing at an instance boundary.
258
+
259
+ For a compact `Select`, set `triggerWidth` to `"fit-content"`, `"max-content"`,
260
+ or `"fill"` and cap it with `--kui-select-trigger-max-width` when needed.
261
+ Custom `renderSelected` text inherits the control color by default; the
262
+ `--kui-select-selected-color`, `--kui-select-selected-font-size`, and
263
+ `--kui-select-selected-font-weight` tokens configure its typography. Plain
264
+ selected text stays on one ellipsized line when the trigger narrows.
265
+
266
+ For an unavailable entry, set `SelectChoice.disabled` and optionally
267
+ `disabledReason`; the option stays visible but cannot be chosen. A multiple
268
+ `Select` can opt in to `selectAllLabel` and `clearLabel` footer buttons.
269
+ Select all includes only enabled choices, Clear removes the selection, and
270
+ both report changes through the normal `input` and `change` events. The app
271
+ still owns the controlled `value` and localized action labels.
272
+
273
+ - `ResizableRegion` is an interactive controlled pane. `Grid` is the right answer for static equal-width columns; application CSS grid remains the answer for asymmetric or intrinsic tracks.
274
+ - A `ResizableRegion`'s content spans the whole region, like its separator. Give
275
+ it one child — normally a `Pane` — and that child fills the region, so the
276
+ pane reaches the region's far edge and owns scrolling for long content.
277
+ Several children stack at their natural height. Do not size the child with
278
+ application `height` rules. While expanded, slide-motion content follows the
279
+ region's actual track, so a region its parent clamps (a `max-width`, a narrow
280
+ container) never shows content past its separator; the fixed expanded size
281
+ applies only while the collapsed content slides out.
282
+ - A `ResizableRegion` overlay automatically clamps both its track and fixed-size
283
+ animated content to the responsive overlay maximum. Set the policy and maximum
284
+ on the component instead of adding application descendant width/height fixes.
285
+ It paints `--kui-color-surface` itself (an inline region stays transparent),
286
+ so do not add a background to make an overlay opaque.
287
+ - Give `Workbench` panels a `toolbar` (title, `leading` / `center` / `trailing`
288
+ groups, and a standard `toggle`) and the work area a `mainToolbar` /
289
+ `mainBottomToolbar`: the Workbench moves a closed panel's groups marked
290
+ `relocateOnCollapse`
291
+ and toggle into the work area's toolbar, so never hand-place or duplicate
292
+ panel toggles. See [Workbench panel toolbars](workbench.md#panel-toolbars).
293
+ - Configure application panels through the shared `separator`, `collapseMotion`,
294
+ `contentOverflow`, `presentation`, `restoreControl`, and `restorePosition`
295
+ props on `ResizableRegion`, `Workbench` panels, and `CollapsiblePanel`. These
296
+ cover separator suppression, one-reflow collapse with composited motion,
297
+ bottom-drawer popups, compact overlays or replacements, and safe-area restore
298
+ placement without descendant `.kui-*` overrides. A `Workbench` rail's
299
+ or bottom drawer's `responsiveOverlayAt` (like `ResizableRegion`'s
300
+ `responsiveFillAt`) switches it to an overlay below a container breakpoint
301
+ without a device-class check.
302
+ `wireSidebar` also accepts a
303
+ hidden compact replacement and keeps compact overlays exclusive by default.
89
304
  - `TokenSearchField` is a structured editor. A native input or `wa-input` is the right answer for ordinary text.
90
305
 
306
+ For common structured search, use `createTokenSearchModel` from `@kerfjs/ui/token-search-model`, pass the model to `TokenSearchField`, and register it under the field id in `wireTokenSearchFields(root, { models })`. Rules parse `name:value` expressions and provide suggestions; the model owns chips, selection, removal, clear, and an optional evaluation result. Omit `model` and `models` for full manual control.
307
+ `TokenSearchRule.suggest(input, state)` receives the unfinished value and current committed tokens, so a rule can omit values already selected. Existing one-argument suggestion callbacks continue to work.
308
+ For a sibling date picker or other app helper, call `model.commit(value)` while a `name:` prefix is active. It parses and commits that value for the active rule even when it is absent from suggestions, replacing the unfinished prefix text. Invalid values and calls without an active prefix leave the query unchanged; `choose(value)` remains restricted to suggested tokens.
309
+ To apply a saved search or restore a query programmatically, call `model.replace({ query, tokens })`. It publishes parsed state and rebuilds the DOM-owned editor text even when no chip changed. For a field without a model, change the field's `revision` prop when setting `query` yourself.
310
+ When one root contains both kinds of field, a registered model enables chip keyboard behavior only for its own field. Other fields keep native editing unless the root opts into `keyboard` with an application removal callback.
311
+
91
312
  ## Toolbar composition
92
313
 
93
314
  A `Toolbar` has three zones — `leading`, `center`, and `trailing`. In almost
@@ -97,53 +318,136 @@ controls). Do not drop bare buttons, inputs, links, or arbitrary markup straight
97
318
  into a zone; wrap controls in a `ToolbarControlGroup` so they get the shared
98
319
  toolbar geometry, hover/pressed treatment, and grouping. `SegmentedControl`,
99
320
  `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live
100
- **inside** a `ToolbarControlGroup`, not loose in the zone. `PanelHeader` is the
101
- one wrapper that composes these for you as a panel/dialog/page heading.
321
+ **inside** a `ToolbarControlGroup`, not loose in the zone. For a page, panel, or
322
+ dialog heading, put an extra-large `ToolbarText` directly in the leading zone,
323
+ optionally preceded by a grouped icon, and group trailing actions. Keep supporting
324
+ copy below as app-owned content. Size the title to its track: a narrow
325
+ peripheral rail or drawer (a navigator, inspector, or console beside a work
326
+ area that carries the extra-large title, often with its own close control in
327
+ the header) labels itself with the default size, because extra-large truncates
328
+ there — "Inspector" becomes "Ins…" in a 160px rail. Nothing wraps inside a
329
+ `ToolbarControlGroup`: an icon sits beside its label on one row and the group
330
+ sizes to its content, so a row that does not fit relocates whole groups
331
+ through the toolbar's `responsive` policy rather than breaking a group. A
332
+ toolbar draws no divider by default: pinned over or under scrolling content,
333
+ it gets one from the scroll state instead — the `Pane` it heads draws the line
334
+ only while content is scrolled beneath it, once `wireScrollDividers` is wired
335
+ (see [Scroll dividers](./layout.md#scroll-dividers)). Set `dividerSides` to the
336
+ canonical physical-edge combinations (`t`, `r`, `b`, `l`, in that order—for
337
+ example `b`, `tr`, or `trbl`) only when the toolbar owns a permanent separator
338
+ edge. Dividers earn their place: never fake one with a border or pad below one
339
+ to make it look right.
102
340
 
103
341
  Common toolbar patterns:
104
342
 
105
- | Want | Put in the zone | Notes |
106
- | --- | --- | --- |
107
- | Identity or title text | `<ToolbarText text="…" size="large" />` (or `xlarge` for a page/panel title) | Wrap in a `single` borderless group only when it must align with adjacent control pills |
108
- | One or more icon/text buttons | `<ToolbarControlGroup>{buttons}</ToolbarControlGroup>` | Use `buttonAppearance="push"` for toggle buttons with `aria-pressed`; `single` for a lone control |
109
- | An exclusive view switch | `<ToolbarControlGroup><SegmentedControl … /></ToolbarControlGroup>` | Not `TabBar`, which switches tabpanels |
110
- | A value list | `<ToolbarControlGroup><Select … /></ToolbarControlGroup>` | Register `@kerfjs/ui/select/register` once |
111
- | A collapsible search box | `<ToolbarControlGroup single><TokenSearchField collapsible … /></ToolbarControlGroup>` | The group animates the iconic ↔ expanded states; `wireTokenSearchFields` manages expand/collapse/focus by default |
343
+ | Want | Put in the zone | Notes |
344
+ | ----------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
345
+ | Identity or title text | `<ToolbarText text="…" size="large" />` (or `xlarge` for a page/panel title that fits) | Wrap in a `single` borderless group only when it must align with adjacent control pills |
346
+ | One or more icon/text buttons | `<ToolbarControlGroup>{buttons}</ToolbarControlGroup>` | Use `buttonAppearance="push"` for toggle buttons with `aria-pressed`; `single` for a lone control |
347
+ | An exclusive view switch | `<ToolbarControlGroup><SegmentedControl … /></ToolbarControlGroup>` | Not `TabBar`, which switches tabpanels |
348
+ | A value list | `<ToolbarControlGroup><Select … /></ToolbarControlGroup>` | Register `@kerfjs/ui/select/register` once; use `focusRingOwner="group"` so focus follows the group's geometry |
349
+ | A command menu | `<ToolbarControlGroup single nestedDropdown><PopupMenu … /></ToolbarControlGroup>` | Register `@kerfjs/ui/popup-menu/register` once; set `menuInset` on the group |
350
+ | A collapsible search box | `<ToolbarControlGroup single><TokenSearchField collapsible … /></ToolbarControlGroup>` | The group animates the iconic ↔ expanded states; `wireTokenSearchFields` manages expand/collapse/focus by default |
351
+
352
+ ### Popup menu migration
112
353
 
113
- A **popup menu in a toolbar** is a `single` `ToolbarControlGroup` wrapping a Web
114
- Awesome `wa-dropdown`: its `slot="trigger"` `wa-button` is the toolbar button and
115
- the `wa-dropdown-item`s are the menu. Keep the dropdown's managed light-DOM
116
- children under `data-morph-skip-children` so kerf does not reconcile Web Awesome's
117
- own DOM.
354
+ A **popup menu in a toolbar** is a `PopupMenu` (`@kerfjs/ui/popup-menu`) in a
355
+ `single` `ToolbarControlGroup` with `nestedDropdown`; the group sizes the trigger
356
+ as a toolbar button and `menuInset` sets the menu's inset. `PopupMenu` renders the
357
+ Web Awesome dropdown, its trigger, and its typed items (commands, headings, and
358
+ dividers), and keeps Web Awesome's managed children out of the morph. Register
359
+ `@kerfjs/ui/popup-menu/register` once. Do not hand-write `wa-dropdown` markup for a
360
+ menu.
118
361
 
119
362
  ```tsx
120
- <ToolbarControlGroup single>
121
- <wa-dropdown placement="bottom-start" data-morph-skip-children>
122
- <wa-button slot="trigger" appearance="plain" with-caret aria-label="Sort">
123
- <LucideIcon icon={ArrowDownAZ} name="arrow-down-a-z" />
124
- </wa-button>
125
- <wa-dropdown-item data-action="sort-recent">Recently updated</wa-dropdown-item>
126
- <wa-dropdown-item data-action="sort-priority">Priority</wa-dropdown-item>
127
- </wa-dropdown>
363
+ <ToolbarControlGroup single nestedDropdown menuInset="compact">
364
+ <PopupMenu
365
+ label="Sort"
366
+ icon={<LucideIcon icon={ArrowDownAZ} name="arrow-down-a-z" />}
367
+ items={[
368
+ { label: "Recently updated", action: "sort-recent" },
369
+ { label: "Priority", action: "sort-priority" },
370
+ ]}
371
+ />
128
372
  </ToolbarControlGroup>
129
373
  ```
130
374
 
375
+ Nested decisions use an item with `submenu`. Children keep their own actions,
376
+ `attributes` metadata, icons, `checked` state, disabled state and
377
+ `disabledReason` tooltip. Nested `heading` and `divider` entries group those
378
+ commands without entering keyboard navigation. `tone: 'danger'` maps to the Web Awesome destructive
379
+ variant. A parent can sit among ordinary commands and dividers, so the same
380
+ items shape covers single-line and batch actions; compute each batch child's
381
+ `disabled` state from the current selection.
382
+
383
+ ```tsx
384
+ <PopupMenu
385
+ label="Line actions"
386
+ items={[
387
+ { label: 'Copy reference', action: 'copy-reference', attributes: { 'data-item-id': lineId } },
388
+ { label: 'Decide', submenu: [
389
+ { kind: 'heading', label: 'Review' },
390
+ { label: 'Approve', action: 'decide', checked: current === 'approve', attributes: { 'data-item-id': lineId, 'data-decision': 'approve' } },
391
+ { label: 'Reject', action: 'decide', tone: 'danger', disabled: needsPrice, disabledReason: needsPrice ? 'Add a price first' : undefined, attributes: { 'data-item-id': lineId, 'data-decision': 'reject' } },
392
+ { kind: 'divider' },
393
+ { label: 'Other…', action: 'choose-other' },
394
+ ] },
395
+ { kind: 'divider' },
396
+ { label: 'Remove', action: 'remove', tone: 'danger' },
397
+ ]}
398
+ />
399
+ ```
400
+
401
+ For a flat command menu with persistent sort choices, group commands under
402
+ `heading` entries and set `details` to a safe trailing tick for the chosen
403
+ item. Set `checked` when checkbox menu semantics fit. Commands still dispatch
404
+ their own `data-action` once on pointer and keyboard selection.
405
+
406
+ For a row opened by right-click, render `PopupMenu` with `context`, `label`,
407
+ and a stable root `data-*` marker. Its trigger is an invisible anchor removed
408
+ from the accessibility tree; a separate visible button may stay disabled. In
409
+ the app's `contextmenu` handler, find that root, call `openPopupMenuAt` with
410
+ the menu and `event.clientX` / `event.clientY`, and prevent the browser
411
+ menu. Call `closePopupMenu` for programmatic dismissal. The pointer anchor
412
+ retains Web Awesome's collision placement, focus management, submenu keyboard
413
+ navigation, and outside/Escape dismissal.
414
+
415
+ ## Configuring recurring list rows
416
+
417
+ Configure the List family instead of selecting its descendant classes. Use
418
+ `density="compact"` for result-heavy panes, `description` for the secondary
419
+ label line, `status` for dormant state text, and `busy` for known progress that
420
+ keeps current content visible. `divider` marks group boundaries without an
421
+ app-owned separator rule. `ListHeader.indicatorTone` gives count, badge, or
422
+ status content neutral, accent, pop, or danger attention. Pop marks featured or
423
+ novel content without claiming a status transition.
424
+
425
+ When a row needs a context action, use `ListActionRow`; its two native buttons
426
+ remain siblings. `trailingActionVisibility="interaction"` keeps the action
427
+ available on hover and keyboard focus and automatically leaves it visible on
428
+ non-hover devices. These props own presentation only—the application still
429
+ owns labels, status meaning, action policy, selection, and domain metadata.
430
+
131
431
  ## Correct composition and duplicated-markup trap
132
432
 
133
433
  Correct: let the pane stay unpadded while its children own the shared 8/1/8
134
434
  geometry and 44px targets.
135
435
 
136
436
  ```tsx
137
- <aside class="kui-pane">
138
- <nav class="kui-pane__content kui-content">
437
+ <Pane element="aside" contentElement="nav" label="Workspace" contentLabel="Workspace pages">
139
438
  <section>
140
- <MenuHeader label="Workspace" />
141
- <MenuItem action="open" label="Inbox" icon={inboxIcon} />
142
- <MenuActionRow action="open-file" label="main.ts" trailingAction="file-actions" trailingActionLabel="Actions for main.ts" trailingActionIcon={moreIcon} />
439
+ <ListHeader label="Workspace" />
440
+ <ListItem action="open" label="Inbox" icon={inboxIcon} />
441
+ <ListActionRow
442
+ action="open-file"
443
+ label="main.ts"
444
+ trailingAction="file-actions"
445
+ trailingActionLabel="Actions for main.ts"
446
+ trailingActionIcon={moreIcon}
447
+ />
143
448
  </section>
144
- <div class="kui-content-item">Workspace details</div>
145
- </nav>
146
- </aside>
449
+ <ContentItem>Workspace details</ContentItem>
450
+ </Pane>
147
451
  ```
148
452
 
149
453
  Incorrect: duplicating component-like rows and compensating for nested padding
@@ -151,32 +455,60 @@ forks the package anatomy and spacing contract.
151
455
 
152
456
  ```tsx
153
457
  <aside class="sidebar padded">
154
- <h2 class="menu-header-copy">Workspace</h2>
458
+ <h2 class="list-header-copy">Workspace</h2>
155
459
  <button class="menu-row-copy padded">Inbox</button>
156
460
  <div class="panel indented-with-negative-margin">Workspace details</div>
157
461
  </aside>
158
462
  ```
159
463
 
464
+ ## Removable tags
465
+
466
+ Use `Chip` when a short tag has a remove action. `removeAction` becomes the
467
+ native button's `data-action`; `removeLabel` must name the specific tag for
468
+ assistive technology. Use `itemId` for the application's delegated handler.
469
+ The application updates its tag list after the action. `size="compact"` uses a
470
+ 20px chip with a 16px remove button, and `disabled` disables removal. Tone,
471
+ appearance, and shape use the same semantic choices as `Badge`; use `Badge`
472
+ when no removal is needed and the content is status or count metadata.
473
+
474
+ ```tsx
475
+ <Chip
476
+ size="compact"
477
+ tone="brand"
478
+ itemId={tag.id}
479
+ removeAction="remove-tag"
480
+ removeLabel={`Remove ${tag.name} tag`}
481
+ >
482
+ {tag.name}
483
+ </Chip>
484
+ ```
485
+
160
486
  ## Web Awesome overlap policy
161
487
 
162
488
  Web Awesome catalog coverage means supported and themed, not preferred. Import
163
489
  `@kerfjs/ui/webawesome` for Kerf JSX types, individual component modules for
164
- registration, and the CSS-only `@kerfjs/ui/webawesome.css` theme.
165
-
166
- | Web Awesome choice | Kerf decision |
167
- | --- | --- |
168
- | `wa-button`, `wa-dropdown`, `wa-dropdown-item` | Use buttons and command menus for actions. Use `MenuItem` for a navigation row and `Select` when the user chooses a value. |
169
- | `wa-button-group` | Use only for exceptional grouped actions; use `SegmentedControl` for one-of-many selection. |
170
- | `wa-input`, `wa-tag` | Use for ordinary text and tags; use `TokenSearchField` only when text and ordered filter tokens form one editor. |
171
- | `wa-select`, `wa-option` | Use `Select`, which owns Kerf spacing, controlled rendering, icon stability, and explicit registration. |
172
- | `wa-tab-group`, `wa-tab`, `wa-tab-panel` | Use `TabBar`/`AppTab` for application tabs or `SegmentedControl` for compact local views. |
173
- | `wa-icon` | Use `LucideIcon` in application UI. |
174
- | `wa-split-panel` | Use `ResizableRegion` for Kerf application panes; retain Split Panel only when its distinct API is required. |
490
+ registration, and the CSS-only `@kerfjs/ui/webawesome.css` theme. The UX catalog
491
+ marks the superseded or exceptional choices below with a visible `Discouraged`
492
+ tag; Popup remains untagged because low-level anchored positioning can be the
493
+ right primitive. Never render a `Discouraged` element directly in application,
494
+ demo, or recipe markup: reach it only through the Kerf component that wraps or
495
+ replaces it (`PopupMenu` renders `wa-dropdown`; `Select` renders `wa-select` /
496
+ `wa-option`), and file a component gap rather than dropping to the raw tag.
497
+
498
+ | Web Awesome choice | Kerf decision |
499
+ | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
500
+ | `wa-button`, `wa-dropdown`, `wa-dropdown-item` | Use buttons for actions and `PopupMenu` for a command menu, never raw dropdown markup. Use `ListItem` for a navigation row and `Select` for a value. |
501
+ | `wa-button-group` | Use only for exceptional grouped actions; use `SegmentedControl` for one-of-many selection. |
502
+ | `wa-input`, `wa-tag` | Use `Chip` for removable or disabled tags; use `TokenSearchField` only when text and ordered filter tokens form one editor. |
503
+ | `wa-select`, `wa-option` | Use `Select`, which owns Kerf spacing, controlled rendering, icon stability, and explicit registration. |
504
+ | `wa-tab-group`, `wa-tab`, `wa-tab-panel` | Use `TabBar`/`AppTab` for application tabs or `SegmentedControl` for compact local views. |
505
+ | `wa-icon` | Use `LucideIcon` in application UI. |
506
+ | `wa-split-panel` | Use `ResizableRegion` for Kerf application panes; retain Split Panel only when its distinct API is required. |
175
507
  | `wa-spinner`, `wa-progress-bar`, `wa-progress-ring`, `wa-skeleton` | Use `LoadingSpinner` for compact Kerf indeterminate activity; choose the ecosystem component when its distinct progress or placeholder semantics fit. |
176
- | `wa-callout`, `wa-toast`, `wa-toast-item` | Use `StateBanner` for persistent inline app status, `EmptyState` for absent content, and toasts only for transient feedback. |
177
- | `wa-popup`, `wa-tooltip`, `wa-popover` | Prefer the high-level interaction whose semantics fit. Use Popup only when its low-level anchored positioning removes custom placement code. |
178
- | `wa-tree`, `wa-tree-item`, `wa-animated-image`, `wa-comparison` | Use only for the specialized behavior named by the component. |
179
- | `wa-zoomable-frame` | Avoid for application UI; keep embedded-media behavior application-owned. |
508
+ | `wa-callout`, `wa-toast`, `wa-toast-item` | Use `StateBanner` for persistent inline app status, `EmptyState` for absent content, and toasts only for transient feedback. |
509
+ | `wa-popup`, `wa-tooltip`, `wa-popover` | Prefer the high-level interaction whose semantics fit. Use Popup only when its low-level anchored positioning removes custom placement code. |
510
+ | `wa-tree`, `wa-tree-item`, `wa-animated-image`, `wa-comparison` | Use only for the specialized behavior named by the component. |
511
+ | `wa-zoomable-frame` | Avoid for application UI; keep embedded-media behavior application-owned. |
180
512
 
181
513
  All other entries in the [Web Awesome theme contract](./webawesome-theme.md#coverage)
182
514
  remain supported when their native semantic contract matches the product need.