@kerfjs/ui 5.0.0-beta.7 → 5.0.0-beta.70

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 (723) hide show
  1. package/README.md +501 -139
  2. package/ai/application-ui-diagnostic-ids-v1.json +109 -0
  3. package/ai/application-ui-profile-sync.cjs +548 -0
  4. package/ai/application-ui-profile.d.mts +1 -0
  5. package/ai/application-ui-profile.d.ts +106 -0
  6. package/ai/application-ui-profile.defaults.json +96 -0
  7. package/ai/application-ui-profile.mjs +653 -0
  8. package/ai/application-ui-profile.schema.json +140 -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 +104 -0
  14. package/ai/component-catalog.json +3910 -464
  15. package/ai/component-catalog.schema.json +92 -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 +127 -0
  21. package/ai/component-composition.json +7128 -0
  22. package/ai/component-composition.schema.json +399 -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 +93 -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 +98 -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 +105 -0
  44. package/ai/components/loading-spinner.md +73 -0
  45. package/ai/components/lucide-icon.md +78 -0
  46. package/ai/components/nav-stack.md +104 -0
  47. package/ai/components/pane.md +101 -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 +113 -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 +113 -0
  70. package/ai/components/tab-scaffold.md +94 -0
  71. package/ai/components/tabs.md +95 -0
  72. package/ai/components/text.md +85 -0
  73. package/ai/components/token-search-field.md +106 -0
  74. package/ai/components/toolbar-control-group.md +102 -0
  75. package/ai/components/toolbar-text.md +79 -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 +118 -0
  149. package/ai/public-api-signatures-v1.md +2748 -210
  150. package/ai/skill.md +286 -66
  151. package/ai/webawesome-jsx-signatures-v1.md +9 -2
  152. package/analyzer/cli.mjs +49 -0
  153. package/analyzer/component-ownership.d.mts +72 -0
  154. package/analyzer/component-ownership.mjs +286 -0
  155. package/analyzer/index.d.mts +52 -0
  156. package/analyzer/index.mjs +2038 -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/catalog-projection-cli.mjs +96 -0
  163. package/catalog-projection.d.mts +55 -0
  164. package/catalog-projection.mjs +62 -0
  165. package/dist/app-tab.d.ts +39 -4
  166. package/dist/app-tab.js +2 -1
  167. package/dist/badge.d.ts +52 -0
  168. package/dist/badge.js +3 -0
  169. package/dist/badge.js.map +1 -0
  170. package/dist/browser/badge.js +3 -0
  171. package/dist/browser/catalog.js +26 -0
  172. package/dist/browser/chip.js +3 -0
  173. package/dist/browser/collapsible-panel.js +9 -0
  174. package/dist/browser/content-item.js +3 -0
  175. package/dist/browser/floating-toolbar.js +3 -0
  176. package/dist/browser/grid.js +3 -0
  177. package/dist/browser/list-action-row.js +5 -0
  178. package/dist/browser/{menu-header.js → list-header.js} +4 -2
  179. package/dist/browser/list-inset-control.js +3 -0
  180. package/dist/browser/list-inset-text.js +3 -0
  181. package/dist/browser/list-item.js +5 -0
  182. package/dist/browser/list.js +3 -0
  183. package/dist/browser/{panel-header.js → nav-stack.js} +5 -3
  184. package/dist/browser/pane.js +3 -0
  185. package/dist/browser/popup-menu.js +3 -0
  186. package/dist/browser/row.js +3 -0
  187. package/dist/browser/segmented-control.js +0 -1
  188. package/dist/browser/select.js +1 -0
  189. package/dist/browser/spacer.js +3 -0
  190. package/dist/browser/split-view.js +11 -0
  191. package/dist/browser/state-banner.js +1 -0
  192. package/dist/browser/sunken-panel.js +3 -0
  193. package/dist/browser/surface-scaffold.js +3 -0
  194. package/dist/browser/tab-scaffold.js +4 -0
  195. package/dist/browser/text.js +3 -0
  196. package/dist/browser/toolbar-control-group.js +1 -0
  197. package/dist/browser/workbench.js +14 -0
  198. package/dist/catalog-resources.d.ts +48 -0
  199. package/dist/catalog-resources.js +22 -0
  200. package/dist/catalog-resources.js.map +1 -0
  201. package/dist/catalog.d.ts +92 -0
  202. package/dist/catalog.js +503 -0
  203. package/dist/catalog.js.map +1 -0
  204. package/dist/chip.d.ts +35 -0
  205. package/dist/chip.js +3 -0
  206. package/dist/chip.js.map +1 -0
  207. package/dist/chunk-2G2ZJQYK.js +152 -0
  208. package/dist/chunk-2G2ZJQYK.js.map +1 -0
  209. package/dist/chunk-3BYFWTP3.js +108 -0
  210. package/dist/chunk-3BYFWTP3.js.map +1 -0
  211. package/dist/chunk-47CKKLBE.js +34 -0
  212. package/dist/chunk-47CKKLBE.js.map +1 -0
  213. package/dist/chunk-4SQK27MR.js +202 -0
  214. package/dist/chunk-4SQK27MR.js.map +1 -0
  215. package/dist/chunk-5I7QGQVT.js +24 -0
  216. package/dist/chunk-5I7QGQVT.js.map +1 -0
  217. package/dist/chunk-6DFDTWGG.js +288 -0
  218. package/dist/chunk-6DFDTWGG.js.map +1 -0
  219. package/dist/chunk-7OKX3EF6.js +135 -0
  220. package/dist/chunk-7OKX3EF6.js.map +1 -0
  221. package/dist/chunk-AHEG27M7.js +66 -0
  222. package/dist/chunk-AHEG27M7.js.map +1 -0
  223. package/dist/chunk-AYUHMAI6.js +48 -0
  224. package/dist/chunk-AYUHMAI6.js.map +1 -0
  225. package/dist/chunk-B2ZMR7NY.js +85 -0
  226. package/dist/chunk-B2ZMR7NY.js.map +1 -0
  227. package/dist/chunk-BFPBS5Y4.js +250 -0
  228. package/dist/chunk-BFPBS5Y4.js.map +1 -0
  229. package/dist/chunk-DB5ADJKF.js +775 -0
  230. package/dist/chunk-DB5ADJKF.js.map +1 -0
  231. package/dist/chunk-DC6CN2DS.js +123 -0
  232. package/dist/chunk-DC6CN2DS.js.map +1 -0
  233. package/dist/chunk-DG6G5EPF.js +35 -0
  234. package/dist/chunk-DG6G5EPF.js.map +1 -0
  235. package/dist/{chunk-56RJXPHG.js → chunk-G4S2QU6V.js} +7 -4
  236. package/dist/chunk-G4S2QU6V.js.map +1 -0
  237. package/dist/chunk-GLDWRCAM.js +49 -0
  238. package/dist/chunk-GLDWRCAM.js.map +1 -0
  239. package/dist/chunk-I3Q7KKYM.js +33 -0
  240. package/dist/chunk-I3Q7KKYM.js.map +1 -0
  241. package/dist/chunk-IA7TMD4Y.js +288 -0
  242. package/dist/chunk-IA7TMD4Y.js.map +1 -0
  243. package/dist/chunk-J3KGJ6P6.js +135 -0
  244. package/dist/chunk-J3KGJ6P6.js.map +1 -0
  245. package/dist/chunk-K47P7QZC.js +17 -0
  246. package/dist/chunk-K47P7QZC.js.map +1 -0
  247. package/dist/chunk-K67Y2JNF.js +222 -0
  248. package/dist/chunk-K67Y2JNF.js.map +1 -0
  249. package/dist/chunk-KCCEH7RF.js +492 -0
  250. package/dist/chunk-KCCEH7RF.js.map +1 -0
  251. package/dist/chunk-KLJKU6ZL.js +109 -0
  252. package/dist/chunk-KLJKU6ZL.js.map +1 -0
  253. package/dist/chunk-KVBRMEPM.js +44 -0
  254. package/dist/chunk-KVBRMEPM.js.map +1 -0
  255. package/dist/chunk-M6DEEBEF.js +156 -0
  256. package/dist/chunk-M6DEEBEF.js.map +1 -0
  257. package/dist/chunk-MEY33VWB.js +44 -0
  258. package/dist/chunk-MEY33VWB.js.map +1 -0
  259. package/dist/chunk-MP2GDBQZ.js +50 -0
  260. package/dist/chunk-MP2GDBQZ.js.map +1 -0
  261. package/dist/chunk-NC4CFOID.js +188 -0
  262. package/dist/chunk-NC4CFOID.js.map +1 -0
  263. package/dist/chunk-NCSWXG7G.js +80 -0
  264. package/dist/chunk-NCSWXG7G.js.map +1 -0
  265. package/dist/chunk-NN6GYOA2.js +47 -0
  266. package/dist/chunk-NN6GYOA2.js.map +1 -0
  267. package/dist/chunk-OXO2QJWV.js +55 -0
  268. package/dist/chunk-OXO2QJWV.js.map +1 -0
  269. package/dist/chunk-P7D3EMXM.js +52 -0
  270. package/dist/chunk-P7D3EMXM.js.map +1 -0
  271. package/dist/chunk-PSHUSWQH.js +18 -0
  272. package/dist/chunk-PSHUSWQH.js.map +1 -0
  273. package/dist/chunk-Q6YU3BT7.js +138 -0
  274. package/dist/chunk-Q6YU3BT7.js.map +1 -0
  275. package/dist/chunk-QJ5VFCW3.js +131 -0
  276. package/dist/chunk-QJ5VFCW3.js.map +1 -0
  277. package/dist/chunk-RGS4WX2M.js +44 -0
  278. package/dist/chunk-RGS4WX2M.js.map +1 -0
  279. package/dist/chunk-RNQNEENY.js +146 -0
  280. package/dist/chunk-RNQNEENY.js.map +1 -0
  281. package/dist/chunk-RVERAFFY.js +291 -0
  282. package/dist/chunk-RVERAFFY.js.map +1 -0
  283. package/dist/chunk-SMZGAPCU.js +70 -0
  284. package/dist/chunk-SMZGAPCU.js.map +1 -0
  285. package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
  286. package/dist/chunk-SRSJO5QE.js.map +1 -0
  287. package/dist/chunk-T2E5WBCZ.js +482 -0
  288. package/dist/chunk-T2E5WBCZ.js.map +1 -0
  289. package/dist/chunk-TJK44RQC.js +452 -0
  290. package/dist/chunk-TJK44RQC.js.map +1 -0
  291. package/dist/chunk-UIDKY4UB.js +14 -0
  292. package/dist/chunk-UIDKY4UB.js.map +1 -0
  293. package/dist/chunk-V3DX3K65.js +43 -0
  294. package/dist/chunk-V3DX3K65.js.map +1 -0
  295. package/dist/chunk-VZOFZPSO.js +71 -0
  296. package/dist/chunk-VZOFZPSO.js.map +1 -0
  297. package/dist/chunk-W2AC6Q2A.js +83 -0
  298. package/dist/chunk-W2AC6Q2A.js.map +1 -0
  299. package/dist/chunk-WF75U3V4.js +33 -0
  300. package/dist/chunk-WF75U3V4.js.map +1 -0
  301. package/dist/chunk-WTSNV5RG.js +52 -0
  302. package/dist/chunk-WTSNV5RG.js.map +1 -0
  303. package/dist/{chunk-YGN7OMLT.js → chunk-X5QSICJ3.js} +8 -5
  304. package/dist/chunk-X5QSICJ3.js.map +1 -0
  305. package/dist/chunk-YDGNU2OD.js +61 -0
  306. package/dist/chunk-YDGNU2OD.js.map +1 -0
  307. package/dist/collapsible-panel.d.ts +133 -0
  308. package/dist/collapsible-panel.js +131 -0
  309. package/dist/collapsible-panel.js.map +1 -0
  310. package/dist/content-item.d.ts +81 -0
  311. package/dist/content-item.js +4 -0
  312. package/dist/content-item.js.map +1 -0
  313. package/dist/css-values.d.ts +107 -0
  314. package/dist/css-values.js +3 -0
  315. package/dist/css-values.js.map +1 -0
  316. package/dist/device-class.js +33 -5
  317. package/dist/device-class.js.map +1 -1
  318. package/dist/disclosure-arrow.d.ts +6 -1
  319. package/dist/disclosure-arrow.js +2 -1
  320. package/dist/empty-state.d.ts +5 -2
  321. package/dist/empty-state.js +2 -1
  322. package/dist/flex-alignment-4ms8ZbV8.d.ts +5 -0
  323. package/dist/floating-toolbar.d.ts +51 -0
  324. package/dist/floating-toolbar.js +3 -0
  325. package/dist/floating-toolbar.js.map +1 -0
  326. package/dist/grid.d.ts +50 -0
  327. package/dist/grid.js +5 -0
  328. package/dist/grid.js.map +1 -0
  329. package/dist/help-tags-register.d.ts +13 -0
  330. package/dist/help-tags-register.js +9 -0
  331. package/dist/help-tags-register.js.map +1 -0
  332. package/dist/index.d.ts +34 -14
  333. package/dist/index.js +43 -23
  334. package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +21 -9
  335. package/dist/list-action-row.js +8 -0
  336. package/dist/list-action-row.js.map +1 -0
  337. package/dist/list-header.d.ts +99 -0
  338. package/dist/list-header.js +10 -0
  339. package/dist/list-header.js.map +1 -0
  340. package/dist/list-inset-control.d.ts +22 -0
  341. package/dist/list-inset-control.js +3 -0
  342. package/dist/list-inset-control.js.map +1 -0
  343. package/dist/list-inset-text.d.ts +30 -0
  344. package/dist/list-inset-text.js +4 -0
  345. package/dist/list-inset-text.js.map +1 -0
  346. package/dist/list-item.d.ts +62 -0
  347. package/dist/list-item.js +8 -0
  348. package/dist/list-item.js.map +1 -0
  349. package/dist/list.d.ts +63 -0
  350. package/dist/list.js +6 -0
  351. package/dist/list.js.map +1 -0
  352. package/dist/loading-spinner.d.ts +9 -2
  353. package/dist/loading-spinner.js +2 -1
  354. package/dist/lucide-icon.d.ts +9 -2
  355. package/dist/lucide-icon.js +2 -1
  356. package/dist/nav-stack.d.ts +66 -10
  357. package/dist/nav-stack.js +6 -1
  358. package/dist/pane.d.ts +108 -0
  359. package/dist/pane.js +4 -0
  360. package/dist/pane.js.map +1 -0
  361. package/dist/panel-toolbar-DDKh4uKd.d.ts +72 -0
  362. package/dist/popup-menu-register.d.ts +9 -0
  363. package/dist/popup-menu-register.js +244 -0
  364. package/dist/popup-menu-register.js.map +1 -0
  365. package/dist/popup-menu.d.ts +100 -0
  366. package/dist/popup-menu.js +3 -0
  367. package/dist/popup-menu.js.map +1 -0
  368. package/dist/resizable-region.d.ts +32 -3
  369. package/dist/resizable-region.js +1 -1
  370. package/dist/row.d.ts +48 -0
  371. package/dist/row.js +6 -0
  372. package/dist/row.js.map +1 -0
  373. package/dist/segmented-control.d.ts +12 -9
  374. package/dist/segmented-control.js +1 -2
  375. package/dist/select-register.d.ts +5 -1
  376. package/dist/select-register.js +243 -2
  377. package/dist/select-register.js.map +1 -1
  378. package/dist/select.d.ts +102 -8
  379. package/dist/select.js +3 -1
  380. package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
  381. package/dist/sides-BPSWde0A.d.ts +4 -0
  382. package/dist/skeleton.d.ts +14 -7
  383. package/dist/skeleton.js +2 -1
  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 +18 -6
  391. package/dist/state-banner.js +3 -1
  392. package/dist/styles/app-tab.css +221 -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 +168 -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 +132 -0
  400. package/dist/styles/collapsible-panel.css +575 -0
  401. package/dist/styles/content-item.css +75 -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 +35 -0
  406. package/dist/styles/layout.css +3 -41
  407. package/dist/styles/list-action-row.css +258 -0
  408. package/dist/styles/list-header.css +229 -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 +255 -0
  412. package/dist/styles/list.css +133 -0
  413. package/dist/styles/loading-spinner.css +4 -0
  414. package/dist/styles/lucide-icon.css +5 -0
  415. package/dist/styles/nav-stack.css +172 -47
  416. package/dist/styles/pane.css +263 -0
  417. package/dist/styles/popup-menu.css +285 -0
  418. package/dist/styles/resizable-region.css +438 -4
  419. package/dist/styles/row.css +111 -0
  420. package/dist/styles/segmented-control.css +36 -9
  421. package/dist/styles/select.css +337 -21
  422. package/dist/styles/skeleton.css +64 -3
  423. package/dist/styles/spacer.css +11 -0
  424. package/dist/styles/split-view.css +114 -3
  425. package/dist/styles/state-banner.css +94 -19
  426. package/dist/styles/styles.css +13 -4
  427. package/dist/styles/sunken-panel.css +29 -0
  428. package/dist/styles/surface-scaffold.css +56 -0
  429. package/dist/styles/tab-bar.css +147 -9
  430. package/dist/styles/tab-scaffold.css +126 -3
  431. package/dist/styles/text.css +112 -0
  432. package/dist/styles/token-search-field.css +155 -55
  433. package/dist/styles/toolbar-control-group.css +608 -29
  434. package/dist/styles/toolbar-text.css +101 -8
  435. package/dist/styles/toolbar.css +334 -12
  436. package/dist/styles/value-table.css +34 -4
  437. package/dist/styles/webawesome.css +365 -10
  438. package/dist/styles/workbench.css +980 -7
  439. package/dist/sunken-panel.d.ts +27 -0
  440. package/dist/sunken-panel.js +3 -0
  441. package/dist/sunken-panel.js.map +1 -0
  442. package/dist/surface-scaffold.d.ts +30 -0
  443. package/dist/surface-scaffold.js +3 -0
  444. package/dist/surface-scaffold.js.map +1 -0
  445. package/dist/tab-bar.d.ts +39 -6
  446. package/dist/tab-bar.js +1 -1
  447. package/dist/tab-scaffold.d.ts +57 -7
  448. package/dist/tab-scaffold.js +83 -9
  449. package/dist/tab-scaffold.js.map +1 -1
  450. package/dist/text.d.ts +56 -0
  451. package/dist/text.js +3 -0
  452. package/dist/text.js.map +1 -0
  453. package/dist/token-search-field-CVGwDn2y.d.ts +154 -0
  454. package/dist/token-search-field.d.ts +3 -43
  455. package/dist/token-search-field.js +2 -1
  456. package/dist/token-search-model.d.ts +3 -0
  457. package/dist/token-search-model.js +3 -0
  458. package/dist/token-search-model.js.map +1 -0
  459. package/dist/toolbar-control-group.d.ts +77 -7
  460. package/dist/toolbar-control-group.js +3 -1
  461. package/dist/toolbar-text.d.ts +35 -4
  462. package/dist/toolbar-text.js +2 -1
  463. package/dist/toolbar.d.ts +59 -7
  464. package/dist/toolbar.js +1 -1
  465. package/dist/types-BuHELdXR.d.ts +117 -0
  466. package/dist/value-table.d.ts +11 -3
  467. package/dist/value-table.js +2 -1
  468. package/dist/webawesome.d.ts +8 -1
  469. package/dist/wire-catalog.d.ts +82 -0
  470. package/dist/wire-catalog.js +327 -0
  471. package/dist/wire-catalog.js.map +1 -0
  472. package/dist/wire-content-items.d.ts +11 -0
  473. package/dist/wire-content-items.js +114 -0
  474. package/dist/wire-content-items.js.map +1 -0
  475. package/dist/wire-nav-stack.d.ts +4 -1
  476. package/dist/wire-nav-stack.js +147 -7
  477. package/dist/wire-nav-stack.js.map +1 -1
  478. package/dist/wire-resizable-regions.d.ts +2 -1
  479. package/dist/wire-resizable-regions.js +2 -1
  480. package/dist/wire-scroll-dividers.d.ts +72 -0
  481. package/dist/wire-scroll-dividers.js +3 -0
  482. package/dist/wire-scroll-dividers.js.map +1 -0
  483. package/dist/wire-sidebar.d.ts +63 -0
  484. package/dist/wire-sidebar.js +231 -0
  485. package/dist/wire-sidebar.js.map +1 -0
  486. package/dist/wire-tab-bars.d.ts +19 -2
  487. package/dist/wire-tab-bars.js +1 -1
  488. package/dist/wire-tab-scaffold.js +9 -4
  489. package/dist/wire-tab-scaffold.js.map +1 -1
  490. package/dist/wire-token-search-fields.d.ts +65 -3
  491. package/dist/wire-token-search-fields.js +2 -1
  492. package/dist/wire-workbench.d.ts +116 -0
  493. package/dist/wire-workbench.js +6 -0
  494. package/dist/wire-workbench.js.map +1 -0
  495. package/dist/workbench.d.ts +244 -9
  496. package/dist/workbench.js +17 -15
  497. package/dist/workbench.js.map +1 -1
  498. package/docs/accessibility.md +476 -24
  499. package/docs/app-layouts.md +106 -8
  500. package/docs/apple-hig.md +1 -0
  501. package/docs/catalog.md +483 -0
  502. package/docs/collapsible-panel.md +371 -0
  503. package/docs/component-contract.md +658 -56
  504. package/docs/component-integration.md +75 -0
  505. package/docs/component-selection.md +529 -90
  506. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  507. package/docs/design/templates/empty-state/filtered.svg +1 -0
  508. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  509. package/docs/design/templates/empty-state/with-action.svg +1 -0
  510. package/docs/design/templates/empty-state-dark.svg +1 -0
  511. package/docs/design/templates/empty-state.svg +1 -0
  512. package/docs/design/templates/list/compact-dark.svg +1 -0
  513. package/docs/design/templates/list/compact.svg +1 -0
  514. package/docs/design/templates/list/stack-dark.svg +1 -0
  515. package/docs/design/templates/list/stack.svg +1 -0
  516. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  517. package/docs/design/templates/list-action-row/default.svg +1 -0
  518. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  519. package/docs/design/templates/list-action-row/selected.svg +1 -0
  520. package/docs/design/templates/list-action-row-dark.svg +1 -0
  521. package/docs/design/templates/list-action-row.svg +1 -0
  522. package/docs/design/templates/list-dark.svg +1 -0
  523. package/docs/design/templates/list-header/action-dark.svg +1 -0
  524. package/docs/design/templates/list-header/action.svg +1 -0
  525. package/docs/design/templates/list-header/count-dark.svg +1 -0
  526. package/docs/design/templates/list-header/count.svg +1 -0
  527. package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
  528. package/docs/design/templates/list-header/inline-action.svg +1 -0
  529. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  530. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  531. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  532. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  533. package/docs/design/templates/list-header-dark.svg +1 -0
  534. package/docs/design/templates/list-header.svg +1 -0
  535. package/docs/design/templates/list-item/default-dark.svg +1 -0
  536. package/docs/design/templates/list-item/default.svg +1 -0
  537. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  538. package/docs/design/templates/list-item/multiline.svg +1 -0
  539. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  540. package/docs/design/templates/list-item/selected.svg +1 -0
  541. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  542. package/docs/design/templates/list-item/trailing.svg +1 -0
  543. package/docs/design/templates/list-item-dark.svg +1 -0
  544. package/docs/design/templates/list-item.svg +1 -0
  545. package/docs/design/templates/list.svg +1 -0
  546. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  547. package/docs/design/templates/segmented-control/equal.svg +1 -0
  548. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  549. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  550. package/docs/design/templates/segmented-control-dark.svg +1 -0
  551. package/docs/design/templates/segmented-control.svg +1 -0
  552. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  553. package/docs/design/templates/skeleton/avatar.svg +1 -0
  554. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  555. package/docs/design/templates/skeleton/block.svg +1 -0
  556. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  557. package/docs/design/templates/skeleton/lines.svg +1 -0
  558. package/docs/design/templates/skeleton-dark.svg +1 -0
  559. package/docs/design/templates/skeleton.svg +1 -0
  560. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  561. package/docs/design/templates/state-banner/danger.svg +1 -0
  562. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  563. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  564. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  565. package/docs/design/templates/state-banner/info.svg +1 -0
  566. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  567. package/docs/design/templates/state-banner/success.svg +1 -0
  568. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  569. package/docs/design/templates/state-banner/warning.svg +1 -0
  570. package/docs/design/templates/state-banner-dark.svg +1 -0
  571. package/docs/design/templates/state-banner.svg +1 -0
  572. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  573. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  574. package/docs/design/templates/tab-bar-dark.svg +1 -0
  575. package/docs/design/templates/tab-bar.svg +1 -0
  576. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  577. package/docs/design/templates/token-search-field/empty.svg +1 -0
  578. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  579. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  580. package/docs/design/templates/token-search-field-dark.svg +1 -0
  581. package/docs/design/templates/token-search-field.svg +1 -0
  582. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  583. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  584. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  585. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  586. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  587. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  588. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  589. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  590. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  591. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  592. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  593. package/docs/design/templates/toolbar-control-group.svg +1 -0
  594. package/docs/design/templates/toolbar-dark.svg +1 -0
  595. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  596. package/docs/design/templates/toolbar-text/default.svg +1 -0
  597. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  598. package/docs/design/templates/toolbar-text/large.svg +1 -0
  599. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  600. package/docs/design/templates/toolbar-text/small.svg +1 -0
  601. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  602. package/docs/design/templates/toolbar-text.svg +1 -0
  603. package/docs/design/templates/toolbar.svg +1 -0
  604. package/docs/design/templates/value-table/details-dark.svg +1 -0
  605. package/docs/design/templates/value-table/details.svg +1 -0
  606. package/docs/design/templates/value-table-dark.svg +1 -0
  607. package/docs/design/templates/value-table.svg +1 -0
  608. package/docs/design/templates.md +138 -0
  609. package/docs/design-philosophy.md +48 -20
  610. package/docs/device-class.md +10 -9
  611. package/docs/document-baseline.md +38 -0
  612. package/docs/examples/application-ui-profile.json +60 -0
  613. package/docs/examples/command-palette-adapter.tsx +69 -17
  614. package/docs/examples/component-catalog-extension.json +52 -0
  615. package/docs/examples/component-composition-extension.json +80 -0
  616. package/docs/layout.md +534 -59
  617. package/docs/nav-stack.md +138 -14
  618. package/docs/recipes.md +137 -54
  619. package/docs/split-view.md +74 -6
  620. package/docs/sunken-panel.md +87 -0
  621. package/docs/surface-scaffold.md +28 -0
  622. package/docs/tab-scaffold.md +99 -10
  623. package/docs/type-contracts.md +134 -0
  624. package/docs/ui-analyzer.md +347 -0
  625. package/docs/ui-doctor.md +128 -0
  626. package/docs/ui-evaluator.md +139 -0
  627. package/docs/ux-demo.md +41 -16
  628. package/docs/webawesome-theme.md +218 -16
  629. package/docs/workbench.md +608 -12
  630. package/doctor/cli.mjs +120 -0
  631. package/doctor/config.schema.json +72 -0
  632. package/doctor/eslint-diagnostics.d.mts +24 -0
  633. package/doctor/eslint-diagnostics.mjs +24 -0
  634. package/doctor/index.d.mts +121 -0
  635. package/doctor/index.mjs +1324 -0
  636. package/doctor/report.schema.json +118 -0
  637. package/evaluator/cli.mjs +89 -0
  638. package/evaluator/index.d.mts +90 -0
  639. package/evaluator/index.mjs +1203 -0
  640. package/evaluator/report.schema.json +152 -0
  641. package/llms.txt +445 -48
  642. package/package.json +249 -37
  643. package/scripts/component-catalog-extension-validation.mjs +28 -0
  644. package/traversal-exclusions.mjs +27 -0
  645. package/ux-demo/recipes/app-shell.tsx +205 -17
  646. package/ux-demo/recipes/collapsible-sidebar.tsx +260 -0
  647. package/ux-demo/recipes/compact-toolbar.tsx +135 -14
  648. package/ux-demo/recipes/composer-form.tsx +133 -21
  649. package/ux-demo/recipes/list-detail-dialog.tsx +282 -0
  650. package/ux-demo/recipes/list-workspace-states.tsx +183 -11
  651. package/ux-demo/recipes/loaders.ts +15 -5
  652. package/ux-demo/recipes/loading-inspector.tsx +204 -0
  653. package/ux-demo/recipes/mount-recipe.ts +28 -10
  654. package/ux-demo/recipes/navigation-sidebar.tsx +139 -13
  655. package/ux-demo/recipes/navigation-stack.tsx +79 -24
  656. package/ux-demo/recipes/types.ts +23 -2
  657. package/ux-demo/recipes/workspace-header.tsx +86 -5
  658. package/dist/browser/menu-action-row.js +0 -4
  659. package/dist/browser/menu-item.js +0 -4
  660. package/dist/chunk-2RJBFNB6.js +0 -172
  661. package/dist/chunk-2RJBFNB6.js.map +0 -1
  662. package/dist/chunk-2Z5TWHKQ.js +0 -34
  663. package/dist/chunk-2Z5TWHKQ.js.map +0 -1
  664. package/dist/chunk-3YD6ZNRA.js +0 -34
  665. package/dist/chunk-3YD6ZNRA.js.map +0 -1
  666. package/dist/chunk-55DATV3A.js +0 -10
  667. package/dist/chunk-55DATV3A.js.map +0 -1
  668. package/dist/chunk-56RJXPHG.js.map +0 -1
  669. package/dist/chunk-7BR3DO2A.js.map +0 -1
  670. package/dist/chunk-7LDCTU7D.js +0 -134
  671. package/dist/chunk-7LDCTU7D.js.map +0 -1
  672. package/dist/chunk-AQNKJRFH.js +0 -79
  673. package/dist/chunk-AQNKJRFH.js.map +0 -1
  674. package/dist/chunk-B24C4A6T.js +0 -22
  675. package/dist/chunk-B24C4A6T.js.map +0 -1
  676. package/dist/chunk-DAFFDO33.js +0 -14
  677. package/dist/chunk-DAFFDO33.js.map +0 -1
  678. package/dist/chunk-H6URQGJI.js +0 -42
  679. package/dist/chunk-H6URQGJI.js.map +0 -1
  680. package/dist/chunk-I7INRJO4.js +0 -17
  681. package/dist/chunk-I7INRJO4.js.map +0 -1
  682. package/dist/chunk-KJYXOTG3.js +0 -10
  683. package/dist/chunk-KJYXOTG3.js.map +0 -1
  684. package/dist/chunk-LC3YSAZC.js +0 -39
  685. package/dist/chunk-LC3YSAZC.js.map +0 -1
  686. package/dist/chunk-M5JRI6MN.js +0 -27
  687. package/dist/chunk-M5JRI6MN.js.map +0 -1
  688. package/dist/chunk-MBR3TYNL.js +0 -14
  689. package/dist/chunk-MBR3TYNL.js.map +0 -1
  690. package/dist/chunk-MFKMHK5W.js +0 -10
  691. package/dist/chunk-MFKMHK5W.js.map +0 -1
  692. package/dist/chunk-MQIZAD4A.js +0 -227
  693. package/dist/chunk-MQIZAD4A.js.map +0 -1
  694. package/dist/chunk-N3SUXLLS.js +0 -23
  695. package/dist/chunk-N3SUXLLS.js.map +0 -1
  696. package/dist/chunk-PONXLV5U.js +0 -19
  697. package/dist/chunk-PONXLV5U.js.map +0 -1
  698. package/dist/chunk-TZNCCM5H.js +0 -20
  699. package/dist/chunk-TZNCCM5H.js.map +0 -1
  700. package/dist/chunk-W2FK7ZNY.js +0 -26
  701. package/dist/chunk-W2FK7ZNY.js.map +0 -1
  702. package/dist/chunk-WVPYVDXY.js +0 -15
  703. package/dist/chunk-WVPYVDXY.js.map +0 -1
  704. package/dist/chunk-YGN7OMLT.js.map +0 -1
  705. package/dist/chunk-YKUUZPED.js +0 -24
  706. package/dist/chunk-YKUUZPED.js.map +0 -1
  707. package/dist/menu-action-row.js +0 -5
  708. package/dist/menu-action-row.js.map +0 -1
  709. package/dist/menu-header.d.ts +0 -43
  710. package/dist/menu-header.js +0 -7
  711. package/dist/menu-header.js.map +0 -1
  712. package/dist/menu-item.d.ts +0 -33
  713. package/dist/menu-item.js +0 -5
  714. package/dist/menu-item.js.map +0 -1
  715. package/dist/panel-header.d.ts +0 -28
  716. package/dist/panel-header.js +0 -7
  717. package/dist/panel-header.js.map +0 -1
  718. package/dist/styles/menu-action-row.css +0 -160
  719. package/dist/styles/menu-header.css +0 -160
  720. package/dist/styles/menu-item.css +0 -123
  721. package/dist/styles/panel-header.css +0 -64
  722. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
  723. package/ux-demo/recipes/recipes.css +0 -355
@@ -18,25 +18,271 @@ package delivery paths, Web Awesome's installed manifest, AI guidance, and
18
18
  documentation links. Nuanced prose in this contract and the selection guide
19
19
  remains authored rather than generated.
20
20
 
21
+ ### Markdown component reference (AI entry point)
22
+
23
+ [`ai/components/README.md`](../ai/components/README.md) and one
24
+ `ai/components/<id>.md` page per catalog entry are the assistant-facing view of
25
+ both catalogs. An assistant reads the index, then only the pages it uses,
26
+ instead of loading the full JSON. Each page merges the entry's selection facts
27
+ (purpose, use/avoid, alternatives), delivery (JavaScript subpath, CSS,
28
+ registration, wiring helpers), a link into `public-api-signatures-v1.md` for
29
+ exact props, composition (parents, contexts, zones with their JSX prop and
30
+ cardinality, children), state and wiring ownership including wiring-owned
31
+ state attributes, geometry, accessibility obligations, public class and token
32
+ hooks framed as a styling boundary (components own their styles; configure
33
+ through props rather than overriding internals), and `KUI-C###` diagnostics
34
+ with their conditions. `scripts/sync-component-docs.mjs` renders them only from
35
+ `component-catalog.json` and `component-composition.json` (plus the signature
36
+ file's module headings for anchors), sorted and Prettier-formatted;
37
+ `check:catalog` rejects a stale, missing, or orphaned page. The JSON files stay
38
+ the canonical tool-facing sources.
39
+
40
+ ### Composition catalog
41
+
42
+ [`component-composition.json`](../ai/component-composition.json) adds a formal,
43
+ machine-evaluable composition layer beside `component-catalog.json` without
44
+ changing its selection and delivery contract; it is a generated companion, not
45
+ a newer version of that catalog. Every component-catalog entry projects once,
46
+ in order, under the stable qualified key `package:id`. Each composition entry
47
+ explicitly covers parents/contexts,
48
+ named zones and cardinality, optional explicit JSX prop bindings, child concepts, state ownership, required wiring,
49
+ responsive ownership, layout and geometry, accessibility obligations, public
50
+ CSS boundaries, diagnostics, and provenance. A wrapper entry may add
51
+ `rendersAs`: the cataloged roots it renders (any one of them, or nothing), so
52
+ composition checks treat its elements as each root rather than as an unknown
53
+ child. A placement with listed parent constraints produces one `KUI-L201`
54
+ diagnostic even when both the wrapper and a rendered root reject that parent.
55
+ When one catalog entry exports another JSX component with different placement,
56
+ `jsxExports` gives that export its own parent contract. The extra export does
57
+ not stand for the entry's root when a parent zone is checked.
58
+ Generated `boundaries.rootClass` prefers the public `kui-<entry-id>` class when
59
+ present, so a sibling export's class cannot become the entry's root class.
60
+
61
+ `boundaries.placeableClasses` names the subset of `publicClasses` an
62
+ application may write onto its own elements (layout utilities, the document
63
+ root, item geometry on another carrier); absent means none, and every other
64
+ public class is the component's rendered anatomy. `boundaries.rootElement`
65
+ names the element a component renders around its own placeable classes
66
+ (`content-item`: `div`), so a plain element of that tag carrying them is still
67
+ the component recreated. `eslint-plugin-kerfjs`'s `ui-public-boundaries`
68
+ reports both as `KUI-L103` with the export to render — for `@kerfjs/ui` and for
69
+ every consumer catalog a `.kerf-ui-profile.json` declares, whatever the class
70
+ prefix. `create-kerf-component` packages declare the same fields in
71
+ `kerf.components.json`.
72
+
73
+ An entry's optional `cssValueProps` makes CSS-adjacent prop grammar equally
74
+ machine-evaluable. Each path records the property grammar, finite shorthands,
75
+ canonical versus exceptional spacing choices, standalone and expression-only
76
+ helpers, raw/unsafe policy, and serialization examples. Nested paths use the
77
+ bounded `items[].property` form. Consumer catalogs can publish the same facts;
78
+ ESLint, `kerf-ui-analyze`, and `kerf-ui-doctor` then issue the same stable
79
+ `KUI-L013`–`KUI-L017` feedback for application and third-party components.
80
+
81
+ `wiring.stateAttributes` lists the **wiring-owned state attributes**: the
82
+ `data-*` attributes a wiring helper writes at runtime, such as
83
+ `data-handle-inset` and `data-resizing`, which `wireResizableRegions` puts on a
84
+ `ResizableRegion`. Each entry names the attribute, the element that carries it
85
+ (`on`), the `helper` that writes it, and what it means. An application must not
86
+ render, remove, or treat these attributes as its own state; tools use the list
87
+ to tell them apart from app-authored attributes. They are declared once, on the
88
+ helper's wiring item in the component catalog, and the composition catalog
89
+ flattens them. `check:catalog`
90
+ reads each `wire*` helper's source and fails when its declarations differ from
91
+ the `data-*` attributes it actually writes. First-party entries always carry
92
+ the list, empty when their helpers write none; it is optional in extension
93
+ catalogs.
94
+
95
+ An extension catalog may add `wiring.sources` entries with `export` and
96
+ `source` for each application-owned helper. The source path is relative to its
97
+ package root; `ui-wiring` resolves helper imports through this file, including
98
+ relative re-export barrels. Every named export must also appear in
99
+ `wiring.helpers`.
100
+
101
+ The selection catalog's `wiringProviders` lists public helpers that install
102
+ other wiring helpers. The `ui-wiring` rule accepts a provider call for each
103
+ helper it declares; `wireCatalog` provides `wireScrollDividers`.
104
+
105
+ The generator starts with permissive defaults. An `any` mode records that the
106
+ catalog has no defensible prohibition; it does not claim every composition is
107
+ recommended. Objective rules live in
108
+ `component-composition-rules.json` and may carry stable `KUI-C###`
109
+ diagnostics. A tool reports a diagnostic only after proving its exact `when`
110
+ condition. Subjective choice, product policy, and visual taste remain prose.
111
+
112
+ Kerf UI calls component content positions **named `SafeHtml` prop slots**.
113
+ Use `children` for one homogeneous primary region; use explicit named
114
+ `SafeHtml` props for semantic positions or replacement content such as
115
+ `header`, `footer`, `leading`, `trailing`, `icon`, and `action`. These are typed
116
+ function-component props, not native web-component `<slot>` elements and not a
117
+ generic `slots={{...}}` object. A composition zone declares `jsx.prop` only when tooling
118
+ can soundly bind that zone to one public JSX prop; `children` is spelled
119
+ explicitly. Zones without that binding remain guidance and are never guessed
120
+ from their ids. Static tools inspect only visible JSX structure and leave
121
+ dynamic `SafeHtml` expressions to runtime and accessibility evaluation.
122
+
123
+ ### Native named-slot assignment
124
+
125
+ Every public visual component with one stable conceptual root accepts an
126
+ explicit `slot?: string` prop and forwards it to that root on every render
127
+ branch. Use it when a Kerf component is a light-DOM child of a custom element,
128
+ for example `StateBanner({ title: 'Ready', slot: 'status' })`. Omitting the prop
129
+ omits the attribute.
130
+
131
+ This contract does not open a generic native-attribute bag: components still
132
+ accept only their documented props and narrow extension-attribute objects.
133
+ `ResizableRegion` and `CollapsiblePanel` are deliberately excluded because each
134
+ can return a region plus a sibling restore control, leaving no single root that
135
+ can truthfully own native slot assignment. Their single-root controls and all
136
+ alternate-root branches of supported components retain the contract.
137
+
138
+ Component-catalog consumers continue unchanged. Composition-aware consumers
139
+ read `component-catalog.json` for selection/delivery and
140
+ `component-composition.json` for composition. `npm run catalog:sync` projects every new component,
141
+ recipe, and supported Web Awesome entry, and the completeness gate prevents
142
+ silent omissions. Downstream catalogs use the composition extension schema and types,
143
+ retain their own package identity, and qualify cross-catalog references. See
144
+ the checked
145
+ [`component-composition-extension.json`](./examples/component-composition-extension.json)
146
+ application-owned example.
147
+
148
+ ### Compile-time contract boundary
149
+
150
+ The versioned
151
+ [`compile-time-contracts-v1.json`](../ai/compile-time-contracts-v1.json)
152
+ artifact maps stable `KUI-T###` ids to public imports, emitted symbols, and
153
+ catalog identities. Its positive/negative fixture compiles against both source
154
+ and a freshly packed package, so source declarations and shipped declarations
155
+ cannot silently diverge. See [Compile-time contracts](./type-contracts.md) for
156
+ the full audit, migration guidance, and the relationships deliberately left to
157
+ runtime/catalog checks because TypeScript cannot prove them.
158
+
159
+ ### Application UI profile
160
+
161
+ The component catalogs describe what packages provide; an application profile
162
+ describes which supported choices a project has approved. A workspace may
163
+ check in `.kerf-ui-profile.json` conforming to
164
+ [`application-ui-profile.schema.json`](../ai/application-ui-profile.schema.json).
165
+ Keep package-qualified catalog locations and recurring-concept preferences,
166
+ allowed color schemes and density, public semantic-token overrides,
167
+ layout/responsive conventions, application wiring entry paths, and narrow rule exceptions there. Product
168
+ records, copy, permissions, user preferences, and transport state do not belong
169
+ in this policy file.
170
+
171
+ Every catalog location names a composition artifact. `selection` is optional
172
+ for consumer packages whose generated metadata declares component-catalog
173
+ selection guidance not applicable; `@kerfjs/ui` retains its required
174
+ `component-catalog.json` selection artifact. This
175
+ lets a generated `component-composition.json` participate directly without a
176
+ fabricated compatibility file.
177
+
178
+ Discovery and precedence are deterministic:
179
+
180
+ 1. Load `application-ui-profile.defaults.json` from `@kerfjs/ui`.
181
+ 2. Load `.kerf-ui-profile.json` at the workspace root when present.
182
+ 3. Walk from the workspace root toward the target directory and load each
183
+ directory-local profile in parent-to-child order.
184
+
185
+ Later scalar and object-map values win. Catalogs merge by package; preferences
186
+ and token overrides merge by key; exceptions merge by stable id. Lists such as
187
+ allowed themes/densities replace the earlier list rather than accumulating.
188
+ `wiring.entries` similarly names workspace-relative application entry modules;
189
+ later profile layers replace the entry list. The `ui-wiring` ESLint rule checks
190
+ application-scoped helper calls and registration imports once at each entry
191
+ that reaches a rendering module, while a composition entry with
192
+ `wiring.scope: "module"` keeps a local obligation. See the
193
+ [`ui-wiring` rule](../../eslint-plugin/docs/rules/ui-wiring.md).
194
+ Every resolved field retains its source file. The shipped
195
+ `application-ui-profile.mjs` API implements discovery, merge, loading, and
196
+ validation and reports actionable originating file + JSON-path diagnostics for stale
197
+ catalogs, unknown component/rule/token references, preference conflicts, and
198
+ broad exceptions. Each raw layer is validated against the catalogs effective at
199
+ that exact precedence point before merge, so a broken parent catalog, stale
200
+ parent reference, invalid value, or unknown field cannot disappear merely
201
+ because a child profile replaces it. The shipped
202
+ `application-ui-profile-sync.cjs` projects the same merge and validation contract
203
+ for synchronous hosts such as ESLint rules; it deliberately performs no async
204
+ I/O.
205
+ See the
206
+ [`application-ui-profile.json`](./examples/application-ui-profile.json) example.
207
+
208
+ The package defaults avoid every `Discouraged` Web Awesome entry — each
209
+ `webawesome` catalog entry whose `recommendation` is not `supported`, except the
210
+ encouraged `wa-popup` primitive — under a concept whose `preferred` value is the
211
+ Kerf component that wraps or replaces it (`command-menu` → `PopupMenu`,
212
+ `value-selection` → `Select`, `application-tabs` → `TabBar`, `icon` →
213
+ `LucideIcon`, `resizable-pane` → `ResizableRegion`, `exclusive-choice` →
214
+ `SegmentedControl`, `hierarchy-navigation` → `List`, `embedded-media` →
215
+ `ContentItem`). `eslint-plugin-kerfjs` `ui-preferences` (`KUI-L301`, also run by
216
+ `kerf-ui-doctor`) therefore reports a Discouraged element written directly as a
217
+ JSX tag (`<wa-dropdown>`) as well as through an imported component. A declared
218
+ component's own source file is exempt, because that wrapper is the sanctioned
219
+ owner of the element it renders. A unit test holds the avoid set and the
220
+ catalog's Discouraged set equal in both directions, so neither can drift. A
221
+ product that genuinely needs one of these elements records a narrow `KUI-L301`
222
+ profile exception for the file that renders it.
223
+
224
+ Catalog detail footers use one standard resource vocabulary and order. Build
225
+ them with `catalogResources()` from `@kerfjs/ui/catalog-resources`: `Demo source`
226
+ first, optional `Component source` and `Design template`, then `Guidance`.
227
+ Third-party entries use the typed `integrationGuidance` kind, which renders
228
+ `Integration guidance`, and omit `Component source` when the implementation is
229
+ not owned by the catalog's project. Prefer these semantic kinds over local labels
230
+ such as “View source” or “Read UI guidance”; the helper makes that convention a
231
+ compile-time choice while still returning ordinary `CatalogResource[]` values.
232
+
21
233
  ## Ownership boundaries
22
234
 
235
+ ### Geometry ownership metadata
236
+
237
+ The canonical [`component-catalog.json`](../ai/component-catalog.json) records
238
+ the margin, border, and padding owner for every component and composition under
239
+ `geometry`. Read it before adding a wrapper or local spacing rule:
240
+
241
+ - `self` means the entry's public visual contract supplies that geometry.
242
+ - `parent` means the embedding layout must supply it.
243
+ - `child` means composed descendants supply it; the entry's outer shell does not.
244
+ - `none` means that dimension is intentionally absent from the standard contract.
245
+ - `conditional` means a variant, part, or configuration changes ownership; the
246
+ accompanying `notes` explain the boundary.
247
+
248
+ The values describe public ownership, including themed shadow parts, rather
249
+ than merely asking whether a CSS declaration appears on the host element.
250
+ Recipes are exempt because they arrange entries whose individual ownership is
251
+ already cataloged.
252
+
253
+ Downstream packages and applications should publish the same contract for their
254
+ reusable visual components. Use
255
+ [`component-catalog-extension.schema.json`](../ai/component-catalog-extension.schema.json)
256
+ and copy the structure of the checked
257
+ [`component-catalog-extension.json`](./examples/component-catalog-extension.json)
258
+ example. Keep app and Kerf entries as package-qualified inputs, then let people
259
+ or AI tools search their combined entries and compare `geometry` before adding
260
+ wrappers or insets. Do not add app-owned entries to Kerf's canonical catalog or
261
+ claim Kerf delivery paths for them.
262
+ Extension entries may add `uses` for direct composition dependencies and
263
+ repository-relative `source` and `demoSource` files. Resolve each dependency
264
+ against the combined Kerf and consumer catalogs; do not duplicate a Kerf id.
265
+ For Doctor's opt-in component ownership mode, add `styleSources` with the CSS
266
+ paths each entry owns, relative to the consumer package root.
267
+
23
268
  - Components own semantic markup, stable anatomy, documented variants, ARIA projection, and package CSS hooks.
24
269
  - Applications own signals/stores, product copy, domain-state mapping, persistence, routing, permissions, and transport.
25
270
  - Actions are `data-action` strings. Wire them at a stable root with `delegate()` or `delegateActions()` and retain the disposer.
26
271
  - A reusable component never owns per-instance mutable module state.
27
- - Consumers style through `--kui-*` semantic tokens and public component classes. Foundation tokens provide opinionated neutral, brand/info, success, warning, and danger fill/border/foreground roles. Stateful components expose local override variables; prefer an equivalent prop or token before writing a selector.
272
+ - Consumers configure presentation through component props and documented `--kui-*` semantic tokens. Foundation tokens provide opinionated neutral, brand/info, non-status pop, success, warning, and danger fill/border/foreground roles. Application stylesheets must not select package component classes or descendants. If genuinely new structure needs CSS, encapsulate that structure and its stylesheet in an application-owned component; configure any nested Kerf components through their public APIs.
28
273
 
29
- `MenuItem.rootAttributes`, `MenuActionRow.rootAttributes`,
30
- `MenuHeader.rootAttributes`, and `AppTab.rootAttributes` accept
274
+ `ListItem.rootAttributes`, `ListActionRow.rootAttributes`,
275
+ `ListHeader.rootAttributes`, `AppTab.rootAttributes`,
276
+ `CatalogExample.rootAttributes`, and `CatalogExampleStack.rootAttributes` accept
31
277
  application-owned `data-*` metadata without
32
- adding product fields to the shared API. A `MenuActionRow` trailing action uses
33
- `trailingActionAttributes`, and a `MenuHeader` action/disclosure uses
278
+ adding product fields to the shared API. A `ListActionRow` trailing action uses
279
+ `trailingActionAttributes`, and a `ListHeader` action/disclosure uses
34
280
  `triggerAttributes`, for `data-*`, native popover target/action attributes, and
35
281
  the corresponding `aria-controls`/`aria-haspopup` relationship. These slots do
36
282
  not accept roles or component-owned action, selection, disclosure, accessible
37
283
  name, disabled, or icon fields. Extension objects are filtered at runtime,
38
284
  including case-insensitive rejection of protected `data-*` names, and the
39
- component writes its protected attributes after the accepted metadata. Typed,
285
+ component or helper writes its protected attributes after the accepted metadata. Typed,
40
286
  structurally widened, and JavaScript callers therefore cannot replace its
41
287
  contract.
42
288
 
@@ -46,10 +292,10 @@ component continues to own the named close button or focusable separator, and
46
292
  `wireTabBars()` / `wireResizableRegions()` continue to own transient behavior
47
293
  and disposal.
48
294
 
49
- `MenuItem.trailing`, `MenuActionRow.label`, `MenuActionRow.icon`, and
50
- `MenuActionRow.trailingActionIcon` are dormant SafeHtml slots. They must not
295
+ `ListItem.trailing`, `ListActionRow.label`, `ListActionRow.icon`, and
296
+ `ListActionRow.trailingActionIcon` are dormant SafeHtml slots. They must not
51
297
  contain controls. When a row needs an independently interactive trailing
52
- region, `MenuActionRow` owns the noninteractive root and the two sibling native
298
+ region, `ListActionRow` owns the noninteractive root and the two sibling native
53
299
  buttons; the application owns their delegated behavior and controlled state.
54
300
  Both row components align a leading icon with the first label line when
55
301
  `multiline` allows the label to wrap; additional lines extend below that fixed
@@ -58,77 +304,317 @@ visual anchor.
58
304
  ### Public CSS anatomy
59
305
 
60
306
  The `publicClasses` array on each entry in
61
- [`component-catalog.json`](../ai/component-catalog.json) is the exact supported
62
- anatomy boundary. A scoped selector may join documented public classes, such as
63
- `.workspace .kui-toolbar .kui-toolbar__trailing`, when composition-specific
64
- layout cannot be expressed by a prop or token. A class being public does not
65
- make copied component markup an invocation or transfer state and accessibility
66
- ownership to the application.
67
-
68
- Do not select a component's descendant by element name, id, attribute alone, or
69
- an unlisted implementation class. Selectors such as `.kui-state-banner span`,
70
- `.kui-menu-item [data-state]`, and `.kui-menu-item .local-label` depend on
71
- private structure. If no prop, token, or cataloged class expresses a recurring
72
- need, request a supported hook instead of inferring one from rendered markup.
73
-
74
- `DisclosureArrow` uses an 18px root-scaled visual by default. Override
75
- `--kui-disclosure-arrow-size` on the component or its containing scope when a
76
- consumer needs another size. Kerf `Select` uses a separate Web Awesome expand
307
+ [`component-catalog.json`](../ai/component-catalog.json) identifies stable
308
+ anatomy for diagnostics, tooling, browser assertions, and package-owned
309
+ composition. It is not an application customization API. Applications choose
310
+ documented props and tokens; package components may compose other components
311
+ only through those public contracts, never by styling a child's classes from a
312
+ parent stylesheet.
313
+
314
+ Inside the package, a component never styles another component or writes
315
+ another component's variables. When a child must adapt to a composing parent,
316
+ the child styles itself in that context from its own stylesheet: the parent's
317
+ class and state appear only in an ancestor compound, the styled compound is
318
+ the child's own class (or one of the child's own unclassed internals below
319
+ it), and the child reads context values the parent provides under the
320
+ parent's name. `ResizableRegion` widens its edge extent by the unsafe inset a
321
+ split `SplitView` provides, and a `Skeleton` derives its fill from a solid
322
+ `Badge`'s on-solid color, both in their own stylesheets; an `AppTab` rounds its
323
+ outer corners in a segmented `TabBar` and shares a fill-allocated strip from
324
+ `app-tab.css`. A `PopupMenu` sizes
325
+ its trigger and applies a group's `menuInset` in its own stylesheet, and an
326
+ icon-only `Select` reads the control slot a `ToolbarControlGroup` provides
327
+ (`--_kui-toolbar-control-group-slot-*`). A parent may still style its own
328
+ elements keyed on a child's state, naming the child by class inside `:has()`
329
+ (`:has(> .kui-select[open])`, or `TabBar` insetting a segmented strip that
330
+ holds compact tabs), and it keeps styling raw native and raw Web
331
+ Awesome children no kerf component owns, excluding kerf children by class
332
+ (`wa-dropdown:not(.kui-popup-menu)`). `npm run check:css-ownership` enforces
333
+ this in every component stylesheet (a `src/*.css` with a same-basename
334
+ component), deriving the component names, the Web Awesome tags each component
335
+ renders, and its composed children from `src/*.tsx`:
336
+
337
+ - **Foreign classes.** Another component's class or `[data-component]` may
338
+ appear only in an ancestor compound or inside `:has()` / `:not()`; the
339
+ styled compound is the stylesheet's own class.
340
+ - **Owned Web Awesome tags.** Kerf components are named by class, never by a
341
+ `wa-*` tag they render. A tag another component renders (`wa-dropdown`,
342
+ `wa-select`, `wa-option`, a `PopupMenu`'s trigger `wa-button`) — as the
343
+ subject, or as an ancestor whose internals the rule reaches — must be scoped
344
+ to that component by its class or excluded with `:not()`. A direct child
345
+ that cannot be the owner's internal
346
+ (`.kui-toolbar-control-group > wa-button`) needs no exclusion.
347
+ - **Variables.** A stylesheet writes only its own `--_kui-<self>-*` private
348
+ variables, and never overrides a public `--kui-<other>-*` token that another
349
+ component reads. Shared foundation tokens (`--kui-layout-*`,
350
+ `--kui-color-*`, …) are not any one component's. Context a parent provides
351
+ is named after the parent.
352
+ - **Context on any child.** Custom properties set on a subject that can be any
353
+ element (`.kui-x > *`, `> :not(…)`) also land on composed components' roots.
354
+ - **Hook classes.** A class the component places on a composed kerf child's
355
+ root (`<ToolbarText className="kui-nav-stack__title">`, a class NavStack once styled) may not be styled;
356
+ the child styles itself in that context instead.
357
+
358
+ The documented exceptions live with their reasons in
359
+ `scripts/lib/css-ownership.mjs`, and an exception that stops matching fails the
360
+ check, so the list only shrinks. The layout regions' inherited edge-inset
361
+ context (`--kui-edge-inset-*` handed to a sole Pane or layout child and cleared
362
+ for any other content, and a Toolbar clearing it for its zones) is the one
363
+ design exception; the others name the open fix ticket.
364
+
365
+ Applications and component packages built on the package follow the same
366
+ rules, and the downstream tools enforce them against the composition catalog
367
+ instead of package source: `kerf-ui-analyze` / `kerf-ui-doctor` report a rule
368
+ whose subject is another package's component (`KUI-L019`), another
369
+ component's private `--_*` variable (`KUI-L020`), an override of a token a
370
+ typed prop sets (`KUI-L021`), and a hook class on a component's root
371
+ (`KUI-L022`), plus borrowed public classes in source markup (`KUI-L023`);
372
+ `eslint-plugin-kerfjs`'s `ui-component-ownership` reports
373
+ `KUI-L020` / `KUI-L021` in script. Both share the selector parsing in
374
+ `analyzer/selectors.mjs` with `check:css-ownership`, and
375
+ `check:css-ownership` fails when a component renders a Web Awesome tag the
376
+ catalog does not list, so `KUI-L019` keeps covering every tag a kerf component
377
+ renders. Each finding names the configuration to use and routes a missing
378
+ prop to a component-gap report (see [the analyzer guide](./ui-analyzer.md#component-ownership-kui-l019kui-l023)).
379
+
380
+ The composition catalog additionally requires `boundaries.rootClass` to be
381
+ either one exact member of `publicClasses` or `null` when the entry has no
382
+ rendered class root. Runtime geometry tooling uses this explicit field; array
383
+ order never implies root ownership.
384
+
385
+ Do not select a component root or descendant by class, element name, id, or
386
+ attribute. Selectors such as `.workspace .kui-toolbar`,
387
+ `.kui-state-banner span`, and `.kui-list-item [data-state]` cross the ownership
388
+ boundary. If no prop or token expresses a recurring need, add the configuration
389
+ to the owning component instead of inferring it from rendered markup.
390
+
391
+ `Badge` owns the complete visual treatment for compact status, count, category,
392
+ and metadata labels. Configure its semantic `tone`, `appearance`, `shape`, and
393
+ `size`; do not recreate or override badge anatomy in an application or consuming
394
+ component. Use `label` when visible content is abbreviated and `ariaHidden` only
395
+ when a surrounding component already includes the same value in its accessible name.
396
+ Every tone's text and solid fill use that tone's `--kui-color-{tone}-on-fill`
397
+ token (`neutral` uses the text colors), the foreground tuned to clear WCAG AA
398
+ (4.5:1) on the tone's fills in light and dark mode.
399
+
400
+ `ListItem` renders its leading icon and nested SVG at a root-scaled 18px by
401
+ default while retaining the row's 44px minimum interactive target. Multiline
402
+ rows align that 18px visual with the first inherited text line. The row clips
403
+ descendant painting to its rounded root so labels, status, and dormant trailing
404
+ content cannot escape its visible boundary. Rows with descriptions grow enough
405
+ to keep their owned two-line label stack readable; use `multiline` when the
406
+ primary label should wrap rather than truncate. A single-line row ends its
407
+ primary label and description with an ellipsis before the trailing status
408
+ slot, even when the label is one long word; a `multiline` row wraps instead,
409
+ breaking a word wider than the label column (a long component or file name)
410
+ rather than letting it run under the trailing slot. A string label in a
411
+ `multiline` row gets line-break opportunities at its camelCase and acronym
412
+ boundaries, so `QuickTicketComposer` wraps as `QuickTicket` / `Composer`
413
+ rather than mid-word; breaking anywhere is only the last resort for a single
414
+ word that is still too wide. The text and accessible name are unchanged. `ListActionRow` labels
415
+ follow the same single-line and `multiline` rules before the trailing action.
416
+
417
+ `DisclosureArrow` uses an 18px root-scaled visual by default. Pass its typed
418
+ `size` prop when a consumer needs another size. Kerf `Select` uses a separate Web Awesome expand
77
419
  glyph contract, `--kui-disclosure-icon-scale: .5`; changing one contract does
78
420
  not implicitly change the other. Direction changes take the shortest rotation
79
421
  path; a 180-degree closed-to-open tie uses counterclockwise rotation.
80
422
 
81
- In `MenuHeader` toggle mode, omitting `actionIcon` composes the production
423
+ In `ListHeader` toggle mode, omitting `actionIcon` composes the production
82
424
  `DisclosureArrow` and derives its visual direction from `expanded`. The app
83
425
  must update that controlled state and reveal or hide real content. Passing a
84
426
  custom `actionIcon` replaces the default rather than layering or rotating both.
85
- The root fills the available inline width after its standard margins. A
86
- separate action stays at the logical end in its 44px target while the visible
87
- glyph defaults to 18px through `--kui-menu-header-action-icon-size`.
427
+ The root fills the available inline width after its standard margins by
428
+ default. Set `inline` when it must shrink-wrap beside surrounding content; that
429
+ presentation removes the root margin, border, and padding while retaining the
430
+ title and optional action's own control geometry. A separate action stays at
431
+ the logical end while the visible glyph defaults to 18px through
432
+ `--kui-list-header-action-icon-size`.
88
433
 
89
434
  `StateBanner` exposes instance-level `--kui-state-banner-background`,
90
435
  `--kui-state-banner-border`, `--kui-state-banner-foreground`,
91
- `--kui-state-banner-detail`, and action background variables. Its five built-in
436
+ `--kui-state-banner-detail` and action background variables. Its optional
437
+ badge composes `Badge` beside the title and follows the banner tone by default. The
438
+ title, badge, and detail flow as inline text: a wrapping title keeps the badge one item
439
+ gap after its last word, and a detail that does not fit whole beside them moves to its
440
+ own line and truncates at the full copy width (below 576px it always takes its own
441
+ line). The title and detail both use the tone's `--kui-color-{tone}-on-fill`
442
+ foreground (`info` uses `brand`) at full strength, clearing WCAG AA over the tone's fill in light and dark
443
+ mode; the detail reads as secondary through its smaller, regular-weight type, and
444
+ only the neutral tone quiets it, to `--kui-color-neutral-on-quiet`. Its five built-in
92
445
  tones can be rethemed globally with
93
446
  `--kui-state-banner-{tone}-{background|border|foreground}`. Toolbar control,
94
447
  segmented-control, app-tab, and tab-bar colors likewise use their public
95
448
  `--kui-*-*` variables. `SegmentedControl` exposes surface, border, foreground,
96
449
  hover, and selected-state variables, so rounded, pill, and toolbar presentations
97
- remain opinionated but locally overridable.
450
+ remain opinionated but locally overridable. Its label color switches with the
451
+ theme without Web Awesome's native button color transition, so a dark filled
452
+ track never briefly pairs with an interpolated low-contrast label.
453
+
454
+ Inset hover and selection surfaces follow the outer control shape instead of
455
+ choosing an independent radius. `--kui-layout-highlight-inset` is the full
456
+ distance between those edges; the inner radius is the outer radius minus that
457
+ inset. `ToolbarControlGroup` publishes its derived radius to nested toolbar
458
+ `SegmentedControl` choices, keeping both direct buttons and exclusive-choice
459
+ highlights concentric when the group switches between pill and rounded shapes.
98
460
 
99
461
  `ResizableRegion` owns the Hot Sheet 2 split treatment: a persistent 1px
100
- separator with a compact grip that appears on hover or keyboard focus. Override
462
+ separator with a compact grip that appears on hover or keyboard focus. The grip
463
+ is drawn centered on the separator line even though the pointer target is wider
464
+ than the line, so the handle never reads as offset into a pane. Override
101
465
  `--kui-resizable-region-separator-color`,
102
466
  `--kui-resizable-region-handle-color`, or
103
467
  `--kui-resizable-region-handle-active-color` on a region when its containing
104
468
  surface needs a different contrast level.
105
469
 
106
- `@kerfjs/ui/layout.css` defines one structural model for sidebars, main areas,
107
- inspectors, and dialogs. `.kui-pane` is unpadded and contains an optional
108
- toolbar, one scrolling `.kui-pane__content`, and an optional footer.
470
+ `Pane` defines one structural model for sidebars, main areas, inspectors, and
471
+ dialogs. Its `.kui-pane` root is unpadded and contains an optional vertical
472
+ header, one scrolling vertical `.kui-pane__content`, and an optional footer.
473
+ Logical-edge separator lines are independently opt-in and default off.
474
+ `@kerfjs/ui/layout.css` retains the pane roles and supplies the related content
475
+ geometry classes.
476
+ `List` is the corresponding layout-only vertical stack: its children stretch by
477
+ default, while `gap`, `flex`, and `scrollable` opt into typed spacing,
478
+ flex growth, and vertical scroll ownership. `Row.flex` uses the same typed flex
479
+ participation contract on the horizontal counterpart. `dividerSides` accepts canonical
480
+ physical top/right/bottom/left combinations such as `tr` and `trbl`; the stack
481
+ adds no list semantics, margin, or padding of its own. Use a direct finite
482
+ spacing shorthand such as `gap="2xs"`, or pass a `CssLength` created by the
483
+ CSS-free `@kerfjs/ui/css-values` helpers: `space('m')`, `rem(0.25)`,
484
+ `lengthVar('--app-gap')`, or `calc(plus(rem(0.25), pct(10)))`. `plus` returns a
485
+ non-standalone `CssLengthExpression`; only `calc` turns it into a complete
486
+ length. `remify(18)` is the runtime twin of source CSS's `remify(18px)`: it
487
+ takes a pixel count and returns a `CssLength` in rem (`1.125rem`) against the
488
+ same fixed 16px baseline. Raw CSS strings, and the source-only `remify(4px)`
489
+ string form, are not valid runtime props. The `CssLength` name intentionally includes percentages for UI dimension
490
+ props. Its opaque string brand improves authoring correctness; it is not a CSS
491
+ sanitizer or security boundary.
492
+ `Row.flex` and `List.flex` likewise accept their boolean default, finite keywords, or `CssFlex`
493
+ from `flex(grow, shrink, basis)`. `Skeleton` width/height use `CssSize`, radius
494
+ uses `CssLength`, and `SelectChoice.color` uses `CssForegroundColor` from
495
+ `uiColor()` with a foreground token such as `success-on-quiet`, or from
496
+ `foregroundColorVar()`. Fill tokens such as `success` are pale background tints
497
+ and do not type-check there. Never exchange these property grammars. List rows expose
498
+ `className`, public tokens, and semantic props instead of unrestricted `style`
499
+ declarations.
109
500
  `.kui-content` gives major children 24px vertical separation.
110
- `.kui-content-item` gives one child 8px inline margin, a real 1px border,
111
- 8px padding, and 12px corners; border and background may be transparent without
112
- changing geometry. `.kui-content-item--pill` selects the 22px radius.
501
+ `ContentItem` (`@kerfjs/ui/content-item`, rendering `.kui-content-item`) gives
502
+ one child 8px inline margin, a real 1px border, 8px padding, and 12px corners;
503
+ the border stays transparent unless `frame="framed"`, so framing never changes
504
+ geometry. `shape="pill"` selects the 22px radius.
505
+ `flush` removes block padding and borders while preserving inline geometry;
506
+ `title` passes through as the native tooltip. Interactive hover uses the normal
507
+ neutral fill so it remains visible on a lowered surface. For a card with
508
+ multiple actions, put sibling buttons in a static ContentItem (or use
509
+ `ListActionRow` for a simple two-action row).
510
+ For an interactive ContentItem, `wireContentItems` activates the card on Enter
511
+ or Space only when the key event originated from the card itself. A nested
512
+ native control, including one inside a custom element's shadow root, keeps its
513
+ own keyboard activation.
113
514
 
114
515
  `ToolbarControlGroup` is the unit of toolbar organization, even for dormant
115
516
  text. Each group reserves `calc(2px + remify(42px))`, or 44px, with 8px between
116
- groups and inside items. `MenuHeader` similarly separates its dormant title and
517
+ groups and inside items. For an ordinary icon/action control inside a group, use
518
+ a plain `<button>` — the group styles `> button` fully, and it keeps the group
519
+ free of a Web Awesome dependency and shadow DOM. Use `ToolbarActionLink` when
520
+ the action must retain native anchor navigation; the group owns its geometry,
521
+ hover, and focus treatment. Set `overflow="scroll"` when a row of controls must
522
+ stay inside the available toolbar width. A disabled native button keeps the
523
+ group's geometry, dims to half opacity, uses the `not-allowed` cursor, and
524
+ paints neither its own nor a lone group's hover surface.
525
+ Set `visibility="compact-only"` for a
526
+ group that replaces wider toolbar controls below the Toolbar's compact container
527
+ breakpoint; do not hide the group with an application class.
528
+ Use `sizing="grow"` on an expanded search group in a wrapping zone: it grows from
529
+ `growBasis` (19rem by default) into available row space, then takes a whole
530
+ row at 480px container width or less. The basis remains its intrinsic minimum
531
+ in a wider, non-stacked Toolbar, so sibling trailing groups can share the
532
+ row. The collapsed search stays icon-sized.
533
+ Use `sizing="fill"` for a group, including a non-search segmented control,
534
+ that spans its wrapping row. Set `visibility="hide-collapsed-tiny"` on a
535
+ collapsible group to omit its closed icon at 224px or less; expanded content
536
+ remains visible. These sizes use the enclosing Toolbar's container width.
537
+ In a stacked Toolbar, set `placement="end"` on a collapsed group that should
538
+ sit at the trailing edge of its flex row. An expanded search with
539
+ `sizing="fill"` takes a whole row and enters from the row above; reduced-motion
540
+ preferences remove that entrance animation.
541
+ Use `visibility="yield-to-expanded-sibling"` on trailing groups that should
542
+ leave a 480px-or-narrower toolbar while a sibling trailing group is expanded.
543
+ They return when it collapses or the toolbar widens; leading and center zones
544
+ remain visible.
545
+ Reach for `wa-button` only when you need a Web Awesome feature; a popup menu is a `PopupMenu`, which renders its own
546
+ trigger. By default a group stays on one row: each icon sits beside its label,
547
+ and the enclosing `Toolbar`'s responsive policy relocates whole groups. Set
548
+ `overflow="wrap"` only for independent links that should move as whole actions
549
+ to another row within the group; use `overflow="scroll"` for a bounded one-row
550
+ action strip. A wrapping group centers in the toolbar band on one row and
551
+ starts at its top when it grows to multiple rows. For a compact mixed-content
552
+ group, set `nestedDropdown`; a
553
+ text-and-caret trigger grows to its intrinsic width while an icon-only trigger
554
+ can remain square. A compact icon `<button>` is a 32px circle, so a selected
555
+ compact segment never becomes an oval. An icon-only `Select` joins a group as
556
+ one more segment with its siblings' inset, radius, hover, and focus ring; only a
557
+ lone Select grows the group to its icon-and-caret pill.
558
+
559
+ For a rail view heading, use `Select presentation="title"`: its intrinsic
560
+ 36px borderless trigger has a bold large label flush with its leading edge and
561
+ a caret beside that label. Set `focusRingInset` when a NavStack or panel clips
562
+ outside painting; the Select keeps its focus outline 3px inside the trigger
563
+ and drops it while the listbox is open. Both settings belong to Select, not an
564
+ application `::part()` override.
565
+
566
+ Set `menuInset` to configure the dropdown menu surface
567
+ without application `::part(menu)` CSS; the default `standard` keeps each menu's
568
+ own inset (8px for a raw `wa-dropdown`, the Select listbox's 4px for a
569
+ `PopupMenu`), while `compact` and `list-zero` apply to every nested menu. A
570
+ `PopupMenu` and a `Select` draw one popup: the same caret that turns to face the
571
+ open popup, fade-in surface, surface inset, row height and padding, current-row
572
+ fill, and group titles, all read from one private popup-choice contract in
573
+ `foundation.css`. Checkbox items reserve a check column; plain commands start
574
+ at the checkmark's leading edge, beside the group title rather than the
575
+ farther-in Select option label. Checked submenu items keep their checkmark and
576
+ label inside the submenu surface. Compact mixed controls retain the standard
577
+ item padding, omit an internal separator, and let the raised selected item paint
578
+ over the outer border instead of shrinking to an inset highlight. The Web
579
+ Awesome trigger's shadow base owns max-content sizing, so icons, localized text,
580
+ and the shadow caret determine the width without a fixed host measurement.
581
+ With `popup-menu/register` loaded, PopupMenu and a raw dropdown inside
582
+ `PopupSurface` preserve item navigation made during the Web Awesome show
583
+ animation across its final initial-focus handoff.
584
+ At viewport widths of 480px or less, `PopupMenu` places an open submenu above
585
+ or below its parent menu, choosing the side with room and constraining the
586
+ submenu to the viewport. It never paints over the parent list. The submenu
587
+ remains a native Web Awesome `menu`: the reverse horizontal arrow (ArrowLeft in
588
+ LTR, ArrowRight in RTL) returns focus to its parent item, Escape dismisses the
589
+ menu, and choosing a child dispatches the normal
590
+ `data-action` and `wa-select` events. Wider viewports retain the side cascade.
591
+ The enclosing `Toolbar` owns zone alignment and responsive topology: use
592
+ `centerAlign="stretch"` when the center group should consume its track, and
593
+ choose `responsive="stack"` with `responsiveAt="compact" | "narrow"`,
594
+ `responsive="wrap"` for a heading whose title must stay whole, or
595
+ `responsive="center-priority"` instead of selecting its zone classes from a
596
+ parent stylesheet. No policy clips an action: the trailing zone, and a stacked
597
+ center zone, wrap whole groups onto another row. The center zone never
598
+ compresses below its content width, so when the leading identity, center, and
599
+ trailing zones share a row that is too narrow, the leading title is what gives
600
+ way: it truncates with an ellipsis (as a platform toolbar's title does) instead
601
+ of the center group painting over it. When everything fits, the center group
602
+ keeps its place, centered in the track between the leading and trailing zones.
603
+ `ListHeader` similarly separates its dormant title and
117
604
  optional count or badge from its optional 44px action. Use the mutually
118
605
  exclusive `count`/`countLabel` pair for non-negative safe-integer section
119
606
  quantities; reserve `badge` for non-count `SafeHtml`. Do not concatenate counts
120
- into the section label. Do not add padding to pane shells,
607
+ into the section label. Set `width="content"` to shrink-wrap while retaining
608
+ normal header geometry; `inline` is the separate zero-outer-geometry mode. Do
609
+ not add padding to pane shells,
121
610
  double child-owned geometry with wrapper insets, or create competing scroll
122
611
  owners. The [layout contract](./layout.md) lists the public roles and tokens.
123
612
 
124
- `PanelHeader` is a plain top `Toolbar` used as a panel, dialog, or page heading;
125
- it overrides no Toolbar styles. The leading zone holds an optional icon (a normal
126
- bordered `ToolbarControlGroup` given a brand fill with a matching border) and the
127
- title as extra-large `ToolbarText`, and the app's `actions` go straight into the
128
- trailing zone (typically as a `ToolbarControlGroup`). The icon group is omitted
129
- when no icon is passed. The optional summary is a separate row aligned below the
130
- title, so it cannot pull the icon group out of vertical alignment with the
131
- title and action row.
613
+ Panel, dialog, and page headings are direct `Toolbar` compositions. The leading
614
+ zone holds an optional icon `ToolbarControlGroup` and a direct extra-large
615
+ `ToolbarText`; app actions belong in a trailing `ToolbarControlGroup`. Omit the
616
+ icon group when it has no content. Supporting copy is app-owned content below
617
+ the toolbar so it cannot pull the icon or actions out of alignment.
132
618
 
133
619
  `ValueTable` composes typed `ValueTableRow` entries. A row owns its `dt`/`dd`
134
620
  semantics and may receive a leading `SafeHtml` icon. Every row keeps 8px of
@@ -139,6 +625,16 @@ padding + 24px icon + 8px gap) and 8px from the right edge for an icon-bearing
139
625
  row. Applications own the values, formatting, and whether an icon is decorative
140
626
  or meaningfully labeled.
141
627
 
628
+ A row follows the settings-style key/value priority: the value keeps its
629
+ content and the label gives way. The label and value are two content-sized
630
+ columns that share the row's free space equally, each stopping once its content
631
+ fits. A short value therefore stays whole on one line beside any label, and a
632
+ long label takes the remaining width, wrapping at its spaces and ending a word
633
+ wider than its column (a long component or file name) in an ellipsis before the
634
+ value rather than running under it. A short label keeps its width beside a
635
+ long value, which wraps; when both are long they split the row, so neither is
636
+ starved.
637
+
142
638
  ## Imports and side effects
143
639
 
144
640
  Every component has an explicit JS and CSS subpath. In CSS-aware browser builds,
@@ -159,25 +655,56 @@ by default it also owns the collapsible field's transient expand/collapse/focus
159
655
  state in a signal it exposes on the returned handle. An app reads that signal in
160
656
  render, hands in its own via `collapsible.signals`, drives it through
161
657
  `handle.open`/`handle.close`, or disables any individual behavior — so transient
162
- UI is consistent by default without every app reinventing it.
658
+ UI is consistent by default without every app reinventing it. A collapsed
659
+ field may reveal its editor after the current microtask; the helper keeps a
660
+ bounded render-frame focus handoff for that reveal and cancels it on disposal.
661
+ `TokenSearchField.trailingAction` supplies an icon, accessible `label`, delegated
662
+ `action`, and optional `id`; the field renders a 24px wide button that fills its
663
+ 34px line slot, including the hit target, disabled state, hover, and focus ring.
664
+ Use `trailing` for passive content. The two props are mutually exclusive.
665
+ For a collapsible search in a stretched Toolbar center, `fill` lets the expanded
666
+ field and its enclosing search `ToolbarControlGroup` consume the available width;
667
+ the collapsed action retains its fixed icon width.
668
+ Grammar suggestion rows align their labels to one leading edge inside the
669
+ anchored popover, including when the labels have different lengths.
670
+
671
+ A public subpath that exposes several visual components is a folder-backed
672
+ surface. Put each component in its own source file and give each visual
673
+ component exactly one owned stylesheet alongside it. Keep the public
674
+ compatibility entrypoints implementation-free: the JavaScript/TypeScript
675
+ entrypoint only re-exports the folder modules, and the CSS entrypoint only
676
+ imports their owner stylesheets. A shared public name is not permission to put
677
+ several component implementations or visual contracts back into one file.
163
678
 
164
679
  `wireTokenSearchFields` is a deliberate exception, not the rule for `wire…`
165
- helpers. Its collapse behavior was *rich and error-prone* — reveal, focus
680
+ helpers. Its collapse behavior was _rich and error-prone_ — reveal, focus
166
681
  transfer, Escape, empty-blur collapse, focus return — the kind of transient chrome
167
682
  apps kept reimplementing inconsistently, so the helper owns it. Everywhere else the
168
683
  app's state is **domain or persisted, not transient chrome, and stays app-owned**: a
169
684
  `NavStack`'s view stack is navigation history, a `TabBar`/`TabScaffold`'s selection
170
685
  and tab order are data, a `ResizableRegion`'s committed size and a
171
686
  `Workbench`/`SplitView` rail's `collapsed` flag are persisted layout preferences.
172
- Each helper already owns only the *ephemeral mechanics* around that state —
687
+ Each helper already owns only the _ephemeral mechanics_ around that state —
173
688
  `wireNavStack` the push/pop animation, `wireTabBars` the overflow autoscroll and
174
689
  drag preview, `wireResizableRegions` the live drag preview — and reports committed
175
- changes through callbacks. A `MenuHeader` `toggle` disclosure's `expanded` is
690
+ changes through callbacks. A `ListHeader` `toggle` disclosure's `expanded` is
176
691
  likewise app-owned: it is a one-line boolean the app already tracks and must read to
177
692
  render the section body, so a managed helper would remove no real complexity. Reach
178
693
  for a managed default only when the transient behavior is substantial enough that
179
694
  hand-rolling it produces genuine, inconsistent variation.
180
695
 
696
+ TabBar itself owns the inline geometry around those mechanics. Its tab list is
697
+ the only shrinking, horizontally scrolling region; `leading`, `trailing`, and
698
+ `end` action regions remain fixed. Set `trailingPlacement="adjacent"` when the
699
+ trailing action belongs immediately after the final tab and use `end` for a
700
+ separate action pinned to the far edge. Applications own the action content,
701
+ but must not restyle TabBar descendants to recreate that allocation.
702
+ When a leading AppTab is pinned, the leading scroll divider appears at that
703
+ tab's trailing edge while peer tabs scroll beneath it, including in RTL.
704
+ With `snapTabs`, `wireTabBars` measures the pinned tab and settles peers at
705
+ whole-tab starts after scroll and selection changes, reserving end room so the
706
+ last peer can align there. The application still owns selection and tab order.
707
+
181
708
  CSS, the generated wrappers that make it
182
709
  reachable, and the registration module are the package's only declared side
183
710
  effects.
@@ -186,14 +713,71 @@ A value-bearing component's `placeholder` prop is a first-class loading mode: th
186
713
  component renders its own real chrome (labels, icon and action affordances,
187
714
  container geometry) while replacing each **value** slot with a subtle, deliberately
188
715
  unanimated `Skeleton` block and disabling its own interactive controls
189
- (`aria-busy`, dropped `data-action`, disabled buttons). Sizes and shapes stay
716
+ (`aria-busy`, dropped `data-action`, disabled buttons). Only unknown values become
717
+ skeletons; nothing else may change. Sizes, shapes, typography, color, and opacity stay
190
718
  identical to the populated component, so a parent composes a faithful loading view —
191
719
  an inspector, a detail pane — from placeholder children without hand-rebuilding
192
720
  markup. The application still owns the loading lifecycle (when to pass `placeholder`)
193
- and which slots are unknown; the standalone `Skeleton` covers custom slots. It is a
721
+ and which slots are unknown; the standalone `Skeleton` covers custom slots. A
722
+ `Skeleton` is `inline-block` so it sits in a text line; pass `block` to lay it out
723
+ as `display: block` where it stands in for a box, such as an icon, so it takes no
724
+ line of its own and centers in its slot. It is a
194
725
  placeholder, not progress: use `LoadingSpinner` for known busy activity, and never
195
726
  animate the skeleton.
196
727
 
728
+ `LucideIcon` defaults to `appearance="outline"`. Pass
729
+ `appearance="solid"` for a filled state glyph such as a selected star or
730
+ triangle. The SVG uses `fill="currentColor"`, so fill follows the icon's text
731
+ color without styling its internal SVG from the application. Choose a Lucide
732
+ node whose geometry supports a solid fill; the prop does not replace an icon
733
+ node's own fill attributes.
734
+
735
+ `LoadingSpinner` accepts the same `size` steps as `LucideIcon`: `xs`, `s`, `m`,
736
+ `l`, `xl` (12/16/20/24/32px at a 16px root) or a positive finite pixel
737
+ number converted to rem. Omit `size` to keep its inherited 1em box. A supplied
738
+ size is exposed as `data-size`; callers configure the spinner through this prop
739
+ instead of sizing its SVG from outside.
740
+
741
+ A placeholder is loading, not unavailable, so it keeps the `disabled` attribute (its
742
+ controls stay inert and out of the keyboard order) but never shows disabled chrome.
743
+ `skeleton.css`, which every component that renders a skeleton loads, neutralizes generic
744
+ disabled styling under `[data-placeholder="true"]` — including Web Awesome's native
745
+ `button:disabled { opacity: 0.5; cursor: not-allowed }` when `webawesome.css` is
746
+ loaded — to full opacity and the non-interactive `default` cursor. That opacity reset
747
+ carries zero specificity, so a component's own opacity (a control hidden until
748
+ interaction) still wins, and each component excludes placeholders from its own
749
+ disabled tone and hover feedback — including hover reveals: a `ListActionRow` with
750
+ `trailingActionVisibility="interaction"` keeps its trailing action hidden when its
751
+ placeholder is hovered, as the resting live row does. The cursor is the one intentional difference from the
752
+ live state: a placeholder is not clickable, so it never advertises a pointer.
753
+
754
+ A control the author disabled is different: it will be unavailable once loaded, so its
755
+ placeholder keeps the live disabled tone. Each such control carries the component-owned
756
+ `data-kui-disabled="true"` marker whenever its own disabled prop is set, independent of
757
+ `placeholder` — `ListItem` and `ListActionRow` `disabled`, `trailingActionDisabled`,
758
+ a `SegmentedControl` choice's `disabled`, and `ListHeader` `actionDisabled` (on the
759
+ action or disclosure toggle). The placeholder reset skips the marker and components key
760
+ their disabled tone on it, so only the `placeholder`-only `disabled` attribute is
761
+ neutralized. That tone is each component's own, never Web Awesome's native
762
+ `button:disabled`, so an author-disabled control is dimmed with or without
763
+ `webawesome.css`. The marker is reserved: the extension-attribute channels
764
+ (`rootAttributes`, `trailingActionAttributes`, `triggerAttributes`) cannot set it. A
765
+ disabled placeholder still shows the `default` cursor, never `not-allowed`.
766
+ Chrome a component draws itself from a known prop stays live, even inside a slot that
767
+ also holds values. `ListItem`'s trailing slot has three inputs: the `busy` spinner is
768
+ component-owned chrome and stays live, while `status` text and author-supplied
769
+ `trailing` content are values and each become a skeleton (the component cannot tell an
770
+ author's chevron from a count, so all `trailing` content is treated as a value).
771
+ `ListActionRow`'s status line follows the same split: its `busy` spinner stays live and
772
+ only the `status` text becomes a skeleton.
773
+ `SegmentedControl` is the one placeholder without a skeleton: its choices are known
774
+ chrome and only the selection is unknown, so it renders every choice's live label or
775
+ icon at its live geometry, selects none, and neutralizes its own disabled chrome.
776
+ `tests/browser/placeholder-fixture.spec.ts` enforces the rule: it renders every
777
+ placeholder component live and as a placeholder from identical props and diffs each
778
+ element's geometry and computed chrome, excluding only the value slots, and checks that
779
+ every author-disabled control is dimmed in both renders.
780
+
197
781
  Package source styles express root-scaled geometry with `remify(<px>)`; the
198
782
  build converts it against the 16px authoring baseline and exposes only ordinary
199
783
  `rem` CSS. Literal pixels remain for intentional hairlines, and `em` remains
@@ -209,7 +793,25 @@ Kerf JSX declaration boundary for every catalog-supported `wa-*` tag.
209
793
  Consumers register only the individual Web Awesome modules they render and can
210
794
  override `--wa-*` values after the theme import. The shared `--wa-*` values feed
211
795
  both Web Awesome controls and Kerf's `--kui-*` foundation aliases, preventing a
212
- second application palette.
796
+ second application palette. Without the theme, every foundation color token,
797
+ every elevation token (`--kui-shadow-*`), and the focus-ring color fall back to
798
+ the same value the theme assigns, including its `light-dark()` pair, so
799
+ surfaces (including the dark `surface-raised`), links (`text-link` is the brand
800
+ on-quiet text color, not the brand fill), tone text, fills, and raised-surface
801
+ shadows keep their light and dark appearance and contrast either way. The
802
+ theme's elevation shadows are scheme-aware: light keeps the soft Hot Sheet 2
803
+ alphas, and dark raises them (`--wa-shadow-s` 14% to 50% black, `-m` 11% to
804
+ 40%, `-l` 9% to 32%) so a raised surface still separates from a near-black
805
+ page.
806
+ Kerf owns the light `brand`, `success`, and `warning` `on-quiet` values in that
807
+ theme layer: each keeps its hue but is dark enough to clear WCAG AA (4.5:1) on
808
+ the lowered page background (`--kui-color-surface-lowered`, `#f2f2f7`), not
809
+ only on the white surface, so toned text reads on every background a page
810
+ paints. It likewise owns every tone's loud pair: each `on-loud` text color
811
+ clears AA on its `fill-loud` in both schemes. Light brand and danger keep white
812
+ text on a slightly deeper fill of the same hue (`#0075db`, `#eb0005`); success
813
+ keeps its bright green with dark text, as warning does; the dark-mode brand and
814
+ danger fills keep their color with dark text.
213
815
 
214
816
  `Select` owns its custom-element reconciliation seam. It gives each slotted
215
817
  option icon a stable key and leaves the upgraded Web Awesome-owned slot subtree
@@ -228,4 +830,4 @@ Keep product adapters outside the package: connection-state maps, ticket empty-s
228
830
 
229
831
  ## Testing contract
230
832
 
231
- Each behavior has focused unit coverage and a real-browser flow through the production-backed catalog. Consumer bundle tests enforce subpath CSS reachability, transitive component styles, root/SSR isolation, optional registration boundaries, and the CSS-only Web Awesome theme boundary. Visual evidence supplements—never replaces—keyboard, focus, state, and event assertions.
833
+ Each behavior has focused unit coverage and a real-browser flow through the production-backed catalog. Consumer bundle tests enforce subpath CSS reachability, transitive component styles, root/SSR isolation, optional registration boundaries, and the CSS-only Web Awesome theme boundary. `npm run check:css-ownership` rejects application selectors into Kerf or Web Awesome components, a non-minimal catalog compatibility stylesheet, or a return of the shared recipe stylesheet. Visual evidence supplements—never replaces—keyboard, focus, state, and event assertions.