@kerfjs/ui 5.0.0-beta.4 → 5.0.0-beta.41

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 (498) hide show
  1. package/README.md +342 -108
  2. package/ai/application-ui-diagnostic-ids-v1.json +100 -0
  3. package/ai/application-ui-profile-sync.cjs +529 -0
  4. package/ai/application-ui-profile.d.mts +1 -0
  5. package/ai/application-ui-profile.d.ts +102 -0
  6. package/ai/application-ui-profile.defaults.json +41 -0
  7. package/ai/application-ui-profile.mjs +626 -0
  8. package/ai/application-ui-profile.schema.json +130 -0
  9. package/ai/catalog-authoring.json +25 -0
  10. package/ai/catalog-authoring.schema.json +61 -0
  11. package/ai/compile-time-contracts-v1.json +132 -0
  12. package/ai/compile-time-contracts-v1.schema.json +51 -0
  13. package/ai/component-catalog-extension-v2.d.ts +7 -0
  14. package/ai/component-catalog-extension-v2.schema.json +7 -0
  15. package/ai/component-catalog-extension.schema.json +79 -0
  16. package/ai/component-catalog-v2-overrides.json +496 -0
  17. package/ai/component-catalog-v2.d.ts +90 -0
  18. package/ai/component-catalog-v2.json +5678 -0
  19. package/ai/component-catalog-v2.schema.json +298 -0
  20. package/ai/component-catalog.json +2694 -417
  21. package/ai/component-catalog.schema.json +39 -0
  22. package/ai/public-api-signatures-v1.md +1591 -126
  23. package/ai/skill.md +140 -54
  24. package/ai/webawesome-jsx-signatures-v1.md +2 -1
  25. package/analyzer/cli.mjs +49 -0
  26. package/analyzer/index.d.mts +46 -0
  27. package/analyzer/index.mjs +1086 -0
  28. package/analyzer/report.schema.json +66 -0
  29. package/dist/app-tab.d.ts +15 -4
  30. package/dist/app-tab.js +3 -1
  31. package/dist/browser/app-tab.js +1 -0
  32. package/dist/browser/floating-toolbar.js +3 -0
  33. package/dist/browser/list-action-row.js +5 -0
  34. package/dist/browser/list-header.js +7 -0
  35. package/dist/browser/list-inset-control.js +3 -0
  36. package/dist/browser/list-inset-text.js +3 -0
  37. package/dist/browser/list-item.js +5 -0
  38. package/dist/browser/list.js +3 -0
  39. package/dist/browser/pane.js +3 -0
  40. package/dist/browser/row.js +3 -0
  41. package/dist/browser/segmented-control.js +1 -0
  42. package/dist/browser/select.js +1 -0
  43. package/dist/browser/skeleton.js +3 -0
  44. package/dist/browser/spacer.js +3 -0
  45. package/dist/browser/state-banner.js +1 -0
  46. package/dist/browser/sunken-panel.js +3 -0
  47. package/dist/browser/surface-scaffold.js +3 -0
  48. package/dist/browser/text.js +3 -0
  49. package/dist/browser/toolbar-text.js +1 -0
  50. package/dist/browser/value-table.js +1 -0
  51. package/dist/catalog-resources.d.ts +35 -0
  52. package/dist/catalog-resources.js +22 -0
  53. package/dist/catalog-resources.js.map +1 -0
  54. package/dist/catalog.d.ts +151 -0
  55. package/dist/catalog.js +396 -0
  56. package/dist/catalog.js.map +1 -0
  57. package/dist/chunk-2TYUFZUC.js +22 -0
  58. package/dist/chunk-2TYUFZUC.js.map +1 -0
  59. package/dist/chunk-6NDBE2OK.js +32 -0
  60. package/dist/chunk-6NDBE2OK.js.map +1 -0
  61. package/dist/chunk-6WTETUZ6.js +21 -0
  62. package/dist/chunk-6WTETUZ6.js.map +1 -0
  63. package/dist/chunk-AQFWBG23.js +83 -0
  64. package/dist/chunk-AQFWBG23.js.map +1 -0
  65. package/dist/chunk-B6JYRX3P.js +51 -0
  66. package/dist/chunk-B6JYRX3P.js.map +1 -0
  67. package/dist/chunk-C34NZOLJ.js +53 -0
  68. package/dist/chunk-C34NZOLJ.js.map +1 -0
  69. package/dist/chunk-CAB6NQ2T.js +102 -0
  70. package/dist/chunk-CAB6NQ2T.js.map +1 -0
  71. package/dist/chunk-CK6P5AUK.js +136 -0
  72. package/dist/chunk-CK6P5AUK.js.map +1 -0
  73. package/dist/chunk-DAO2VFTT.js +129 -0
  74. package/dist/chunk-DAO2VFTT.js.map +1 -0
  75. package/dist/chunk-DEYOGDE7.js +141 -0
  76. package/dist/chunk-DEYOGDE7.js.map +1 -0
  77. package/dist/chunk-DYBPI5O2.js +25 -0
  78. package/dist/chunk-DYBPI5O2.js.map +1 -0
  79. package/dist/{chunk-TRNBHPNW.js → chunk-E34TE2ZI.js} +12 -8
  80. package/dist/chunk-E34TE2ZI.js.map +1 -0
  81. package/dist/chunk-EYALJQQD.js +25 -0
  82. package/dist/chunk-EYALJQQD.js.map +1 -0
  83. package/dist/chunk-I45QO2IZ.js +88 -0
  84. package/dist/chunk-I45QO2IZ.js.map +1 -0
  85. package/dist/chunk-I4V3UDSA.js +79 -0
  86. package/dist/chunk-I4V3UDSA.js.map +1 -0
  87. package/dist/chunk-OGTJNHRG.js +42 -0
  88. package/dist/chunk-OGTJNHRG.js.map +1 -0
  89. package/dist/chunk-OL4TC3U2.js +156 -0
  90. package/dist/chunk-OL4TC3U2.js.map +1 -0
  91. package/dist/chunk-OMSRSL3B.js +120 -0
  92. package/dist/chunk-OMSRSL3B.js.map +1 -0
  93. package/dist/chunk-R3EBGEAW.js +46 -0
  94. package/dist/chunk-R3EBGEAW.js.map +1 -0
  95. package/dist/chunk-SOK5EUF7.js +25 -0
  96. package/dist/chunk-SOK5EUF7.js.map +1 -0
  97. package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
  98. package/dist/chunk-SRSJO5QE.js.map +1 -0
  99. package/dist/chunk-T7DOWS6T.js +61 -0
  100. package/dist/chunk-T7DOWS6T.js.map +1 -0
  101. package/dist/chunk-TIGSG6BX.js +218 -0
  102. package/dist/chunk-TIGSG6BX.js.map +1 -0
  103. package/dist/chunk-UDHHKZWO.js +41 -0
  104. package/dist/chunk-UDHHKZWO.js.map +1 -0
  105. package/dist/{chunk-MQIZAD4A.js → chunk-UKKSMZYT.js} +76 -22
  106. package/dist/chunk-UKKSMZYT.js.map +1 -0
  107. package/dist/chunk-V6GQKOX2.js +39 -0
  108. package/dist/chunk-V6GQKOX2.js.map +1 -0
  109. package/dist/chunk-V6HCTCVD.js +46 -0
  110. package/dist/chunk-V6HCTCVD.js.map +1 -0
  111. package/dist/chunk-VDUJZF5G.js +515 -0
  112. package/dist/chunk-VDUJZF5G.js.map +1 -0
  113. package/dist/chunk-VXFGMDA7.js +31 -0
  114. package/dist/chunk-VXFGMDA7.js.map +1 -0
  115. package/dist/chunk-WN52USHV.js +115 -0
  116. package/dist/chunk-WN52USHV.js.map +1 -0
  117. package/dist/chunk-WSXUPSID.js +43 -0
  118. package/dist/chunk-WSXUPSID.js.map +1 -0
  119. package/dist/chunk-X5GRDQ7V.js +50 -0
  120. package/dist/chunk-X5GRDQ7V.js.map +1 -0
  121. package/dist/{chunk-56RJXPHG.js → chunk-Y6LYKFXY.js} +1 -1
  122. package/dist/{chunk-56RJXPHG.js.map → chunk-Y6LYKFXY.js.map} +1 -1
  123. package/dist/chunk-ZASJJMGV.js +17 -0
  124. package/dist/chunk-ZASJJMGV.js.map +1 -0
  125. package/dist/chunk-ZPWZIVI6.js +47 -0
  126. package/dist/chunk-ZPWZIVI6.js.map +1 -0
  127. package/dist/collapsible-panel.d.ts +77 -0
  128. package/dist/collapsible-panel.js +84 -0
  129. package/dist/collapsible-panel.js.map +1 -0
  130. package/dist/css-values.d.ts +75 -0
  131. package/dist/css-values.js +3 -0
  132. package/dist/css-values.js.map +1 -0
  133. package/dist/device-class.d.ts +62 -0
  134. package/dist/device-class.js +103 -0
  135. package/dist/device-class.js.map +1 -0
  136. package/dist/disclosure-arrow.js +1 -1
  137. package/dist/divider-sides-BzB6rphT.d.ts +6 -0
  138. package/dist/empty-state.d.ts +3 -2
  139. package/dist/empty-state.js +1 -1
  140. package/dist/flex-alignment-CF8NLj7i.d.ts +4 -0
  141. package/dist/floating-toolbar.d.ts +29 -0
  142. package/dist/floating-toolbar.js +3 -0
  143. package/dist/floating-toolbar.js.map +1 -0
  144. package/dist/index.d.ts +28 -14
  145. package/dist/index.js +34 -23
  146. package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +19 -8
  147. package/dist/list-action-row.js +7 -0
  148. package/dist/list-action-row.js.map +1 -0
  149. package/dist/list-header.d.ts +80 -0
  150. package/dist/list-header.js +9 -0
  151. package/dist/list-header.js.map +1 -0
  152. package/dist/list-inset-control.d.ts +20 -0
  153. package/dist/list-inset-control.js +3 -0
  154. package/dist/list-inset-control.js.map +1 -0
  155. package/dist/list-inset-text.d.ts +29 -0
  156. package/dist/list-inset-text.js +3 -0
  157. package/dist/list-inset-text.js.map +1 -0
  158. package/dist/list-item.d.ts +45 -0
  159. package/dist/list-item.js +7 -0
  160. package/dist/list-item.js.map +1 -0
  161. package/dist/list.d.ts +30 -0
  162. package/dist/list.js +5 -0
  163. package/dist/list.js.map +1 -0
  164. package/dist/loading-spinner.js +1 -1
  165. package/dist/nav-stack.d.ts +41 -0
  166. package/dist/nav-stack.js +6 -0
  167. package/dist/nav-stack.js.map +1 -0
  168. package/dist/pane.d.ts +50 -0
  169. package/dist/pane.js +4 -0
  170. package/dist/pane.js.map +1 -0
  171. package/dist/resizable-region.d.ts +21 -3
  172. package/dist/resizable-region.js +1 -1
  173. package/dist/row.d.ts +28 -0
  174. package/dist/row.js +5 -0
  175. package/dist/row.js.map +1 -0
  176. package/dist/segmented-control.d.ts +11 -8
  177. package/dist/segmented-control.js +3 -1
  178. package/dist/select-register.d.ts +5 -1
  179. package/dist/select-register.js +129 -1
  180. package/dist/select-register.js.map +1 -1
  181. package/dist/select.d.ts +34 -8
  182. package/dist/select.js +3 -1
  183. package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
  184. package/dist/skeleton.d.ts +25 -0
  185. package/dist/skeleton.js +4 -0
  186. package/dist/skeleton.js.map +1 -0
  187. package/dist/spacer.d.ts +16 -0
  188. package/dist/spacer.js +4 -0
  189. package/dist/spacer.js.map +1 -0
  190. package/dist/split-view.d.ts +43 -0
  191. package/dist/split-view.js +82 -0
  192. package/dist/split-view.js.map +1 -0
  193. package/dist/state-banner.d.ts +11 -5
  194. package/dist/state-banner.js +3 -1
  195. package/dist/styles/app-tab.css +26 -0
  196. package/dist/styles/catalog.css +582 -0
  197. package/dist/styles/collapsible-panel.css +212 -0
  198. package/dist/styles/floating-toolbar.css +38 -0
  199. package/dist/styles/foundation.css +80 -0
  200. package/dist/styles/layout.css +2 -20
  201. package/dist/styles/list-action-row.css +224 -0
  202. package/dist/styles/list-header.css +224 -0
  203. package/dist/styles/list-inset-control.css +23 -0
  204. package/dist/styles/list-inset-text.css +30 -0
  205. package/dist/styles/list-item.css +171 -0
  206. package/dist/styles/list.css +118 -0
  207. package/dist/styles/nav-stack.css +141 -0
  208. package/dist/styles/pane.css +62 -0
  209. package/dist/styles/resizable-region.css +158 -0
  210. package/dist/styles/row.css +92 -0
  211. package/dist/styles/segmented-control.css +10 -7
  212. package/dist/styles/select.css +115 -2
  213. package/dist/styles/skeleton.css +31 -0
  214. package/dist/styles/spacer.css +11 -0
  215. package/dist/styles/split-view.css +35 -0
  216. package/dist/styles/state-banner.css +49 -10
  217. package/dist/styles/styles.css +10 -5
  218. package/dist/styles/sunken-panel.css +22 -0
  219. package/dist/styles/surface-scaffold.css +51 -0
  220. package/dist/styles/tab-bar.css +24 -0
  221. package/dist/styles/tab-scaffold.css +85 -0
  222. package/dist/styles/text.css +6 -0
  223. package/dist/styles/token-search-field.css +14 -2
  224. package/dist/styles/toolbar-control-group.css +235 -8
  225. package/dist/styles/toolbar-text.css +65 -2
  226. package/dist/styles/toolbar.css +47 -2
  227. package/dist/styles/webawesome.css +171 -0
  228. package/dist/styles/workbench.css +187 -0
  229. package/dist/sunken-panel.d.ts +20 -0
  230. package/dist/sunken-panel.js +3 -0
  231. package/dist/sunken-panel.js.map +1 -0
  232. package/dist/surface-scaffold.d.ts +26 -0
  233. package/dist/surface-scaffold.js +3 -0
  234. package/dist/surface-scaffold.js.map +1 -0
  235. package/dist/tab-bar.d.ts +24 -6
  236. package/dist/tab-bar.js +1 -1
  237. package/dist/tab-scaffold.d.ts +31 -0
  238. package/dist/tab-scaffold.js +51 -0
  239. package/dist/tab-scaffold.js.map +1 -0
  240. package/dist/text.d.ts +20 -0
  241. package/dist/text.js +3 -0
  242. package/dist/text.js.map +1 -0
  243. package/dist/token-search-field.d.ts +30 -13
  244. package/dist/token-search-field.js +1 -1
  245. package/dist/toolbar-control-group.d.ts +33 -7
  246. package/dist/toolbar-control-group.js +1 -1
  247. package/dist/toolbar-text.d.ts +31 -4
  248. package/dist/toolbar-text.js +3 -1
  249. package/dist/toolbar.d.ts +10 -7
  250. package/dist/toolbar.js +1 -1
  251. package/dist/value-table.d.ts +7 -3
  252. package/dist/value-table.js +3 -1
  253. package/dist/webawesome.d.ts +1 -0
  254. package/dist/wire-catalog.d.ts +57 -0
  255. package/dist/wire-catalog.js +302 -0
  256. package/dist/wire-catalog.js.map +1 -0
  257. package/dist/wire-nav-stack.d.ts +18 -0
  258. package/dist/wire-nav-stack.js +228 -0
  259. package/dist/wire-nav-stack.js.map +1 -0
  260. package/dist/wire-resizable-regions.d.ts +1 -1
  261. package/dist/wire-resizable-regions.js +1 -1
  262. package/dist/wire-sidebar.d.ts +48 -0
  263. package/dist/wire-sidebar.js +147 -0
  264. package/dist/wire-sidebar.js.map +1 -0
  265. package/dist/wire-tab-bars.d.ts +19 -2
  266. package/dist/wire-tab-bars.js +1 -1
  267. package/dist/wire-tab-scaffold.d.ts +11 -0
  268. package/dist/wire-tab-scaffold.js +21 -0
  269. package/dist/wire-tab-scaffold.js.map +1 -0
  270. package/dist/wire-token-search-fields.d.ts +104 -4
  271. package/dist/wire-token-search-fields.js +1 -1
  272. package/dist/workbench.d.ts +44 -0
  273. package/dist/workbench.js +106 -0
  274. package/dist/workbench.js.map +1 -0
  275. package/docs/accessibility.md +155 -20
  276. package/docs/app-layouts.md +61 -0
  277. package/docs/catalog.md +364 -0
  278. package/docs/collapsible-panel.md +142 -0
  279. package/docs/component-contract.md +258 -28
  280. package/docs/component-integration.md +63 -0
  281. package/docs/component-selection.md +219 -57
  282. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  283. package/docs/design/templates/empty-state/filtered.svg +1 -0
  284. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  285. package/docs/design/templates/empty-state/with-action.svg +1 -0
  286. package/docs/design/templates/empty-state-dark.svg +1 -0
  287. package/docs/design/templates/empty-state.svg +1 -0
  288. package/docs/design/templates/list/compact-dark.svg +1 -0
  289. package/docs/design/templates/list/compact.svg +1 -0
  290. package/docs/design/templates/list/stack-dark.svg +1 -0
  291. package/docs/design/templates/list/stack.svg +1 -0
  292. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  293. package/docs/design/templates/list-action-row/default.svg +1 -0
  294. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  295. package/docs/design/templates/list-action-row/selected.svg +1 -0
  296. package/docs/design/templates/list-action-row-dark.svg +1 -0
  297. package/docs/design/templates/list-action-row.svg +1 -0
  298. package/docs/design/templates/list-dark.svg +1 -0
  299. package/docs/design/templates/list-header/action-dark.svg +1 -0
  300. package/docs/design/templates/list-header/action.svg +1 -0
  301. package/docs/design/templates/list-header/count-dark.svg +1 -0
  302. package/docs/design/templates/list-header/count.svg +1 -0
  303. package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
  304. package/docs/design/templates/list-header/inline-action.svg +1 -0
  305. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  306. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  307. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  308. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  309. package/docs/design/templates/list-header-dark.svg +1 -0
  310. package/docs/design/templates/list-header.svg +1 -0
  311. package/docs/design/templates/list-item/default-dark.svg +1 -0
  312. package/docs/design/templates/list-item/default.svg +1 -0
  313. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  314. package/docs/design/templates/list-item/multiline.svg +1 -0
  315. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  316. package/docs/design/templates/list-item/selected.svg +1 -0
  317. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  318. package/docs/design/templates/list-item/trailing.svg +1 -0
  319. package/docs/design/templates/list-item-dark.svg +1 -0
  320. package/docs/design/templates/list-item.svg +1 -0
  321. package/docs/design/templates/list.svg +1 -0
  322. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  323. package/docs/design/templates/segmented-control/equal.svg +1 -0
  324. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  325. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  326. package/docs/design/templates/segmented-control-dark.svg +1 -0
  327. package/docs/design/templates/segmented-control.svg +1 -0
  328. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  329. package/docs/design/templates/skeleton/avatar.svg +1 -0
  330. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  331. package/docs/design/templates/skeleton/block.svg +1 -0
  332. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  333. package/docs/design/templates/skeleton/lines.svg +1 -0
  334. package/docs/design/templates/skeleton-dark.svg +1 -0
  335. package/docs/design/templates/skeleton.svg +1 -0
  336. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  337. package/docs/design/templates/state-banner/danger.svg +1 -0
  338. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  339. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  340. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  341. package/docs/design/templates/state-banner/info.svg +1 -0
  342. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  343. package/docs/design/templates/state-banner/success.svg +1 -0
  344. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  345. package/docs/design/templates/state-banner/warning.svg +1 -0
  346. package/docs/design/templates/state-banner-dark.svg +1 -0
  347. package/docs/design/templates/state-banner.svg +1 -0
  348. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  349. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  350. package/docs/design/templates/tab-bar-dark.svg +1 -0
  351. package/docs/design/templates/tab-bar.svg +1 -0
  352. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  353. package/docs/design/templates/token-search-field/empty.svg +1 -0
  354. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  355. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  356. package/docs/design/templates/token-search-field-dark.svg +1 -0
  357. package/docs/design/templates/token-search-field.svg +1 -0
  358. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  359. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  360. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  361. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  362. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  363. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  364. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  365. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  366. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  367. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  368. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  369. package/docs/design/templates/toolbar-control-group.svg +1 -0
  370. package/docs/design/templates/toolbar-dark.svg +1 -0
  371. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  372. package/docs/design/templates/toolbar-text/default.svg +1 -0
  373. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  374. package/docs/design/templates/toolbar-text/large.svg +1 -0
  375. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  376. package/docs/design/templates/toolbar-text/small.svg +1 -0
  377. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  378. package/docs/design/templates/toolbar-text.svg +1 -0
  379. package/docs/design/templates/toolbar.svg +1 -0
  380. package/docs/design/templates/value-table/details-dark.svg +1 -0
  381. package/docs/design/templates/value-table/details.svg +1 -0
  382. package/docs/design/templates/value-table-dark.svg +1 -0
  383. package/docs/design/templates/value-table.svg +1 -0
  384. package/docs/design/templates.md +128 -0
  385. package/docs/design-philosophy.md +33 -11
  386. package/docs/device-class.md +54 -0
  387. package/docs/examples/application-ui-profile.json +60 -0
  388. package/docs/examples/command-palette-adapter.tsx +68 -17
  389. package/docs/examples/component-catalog-extension-v2.json +80 -0
  390. package/docs/examples/component-catalog-extension.json +45 -0
  391. package/docs/layout.md +169 -38
  392. package/docs/nav-stack.md +77 -0
  393. package/docs/recipes.md +64 -16
  394. package/docs/split-view.md +55 -0
  395. package/docs/sunken-panel.md +53 -0
  396. package/docs/surface-scaffold.md +25 -0
  397. package/docs/tab-scaffold.md +52 -0
  398. package/docs/type-contracts.md +127 -0
  399. package/docs/ui-analyzer.md +118 -0
  400. package/docs/ui-doctor.md +91 -0
  401. package/docs/ui-evaluator.md +122 -0
  402. package/docs/ux-demo.md +29 -10
  403. package/docs/webawesome-theme.md +82 -8
  404. package/docs/workbench.md +123 -0
  405. package/doctor/cli.mjs +115 -0
  406. package/doctor/config.schema.json +68 -0
  407. package/doctor/eslint-diagnostics.d.mts +24 -0
  408. package/doctor/eslint-diagnostics.mjs +24 -0
  409. package/doctor/index.d.mts +116 -0
  410. package/doctor/index.mjs +1272 -0
  411. package/doctor/report.schema.json +118 -0
  412. package/evaluator/cli.mjs +89 -0
  413. package/evaluator/index.d.mts +90 -0
  414. package/evaluator/index.mjs +1138 -0
  415. package/evaluator/report.schema.json +152 -0
  416. package/llms.txt +260 -32
  417. package/package.json +205 -38
  418. package/traversal-exclusions.mjs +27 -0
  419. package/ux-demo/recipes/app-shell.tsx +223 -13
  420. package/ux-demo/recipes/collapsible-sidebar.tsx +230 -0
  421. package/ux-demo/recipes/compact-toolbar.tsx +116 -12
  422. package/ux-demo/recipes/composer-form.tsx +120 -19
  423. package/ux-demo/recipes/list-detail-dialog.tsx +205 -0
  424. package/ux-demo/recipes/list-workspace-states.tsx +176 -9
  425. package/ux-demo/recipes/loaders.ts +13 -4
  426. package/ux-demo/recipes/loading-inspector.tsx +191 -0
  427. package/ux-demo/recipes/mount-recipe.ts +31 -5
  428. package/ux-demo/recipes/navigation-sidebar.tsx +123 -11
  429. package/ux-demo/recipes/navigation-stack.tsx +121 -0
  430. package/ux-demo/recipes/recipes.css +125 -16
  431. package/ux-demo/recipes/types.ts +9 -1
  432. package/ux-demo/recipes/workspace-header.tsx +72 -3
  433. package/dist/browser/dialog-header.js +0 -5
  434. package/dist/browser/menu-action-row.js +0 -3
  435. package/dist/browser/menu-header.js +0 -5
  436. package/dist/browser/menu-item.js +0 -3
  437. package/dist/browser/page-header.js +0 -3
  438. package/dist/chunk-2PES33HS.js +0 -13
  439. package/dist/chunk-2PES33HS.js.map +0 -1
  440. package/dist/chunk-55DATV3A.js +0 -10
  441. package/dist/chunk-55DATV3A.js.map +0 -1
  442. package/dist/chunk-7BR3DO2A.js.map +0 -1
  443. package/dist/chunk-7LDCTU7D.js +0 -134
  444. package/dist/chunk-7LDCTU7D.js.map +0 -1
  445. package/dist/chunk-AQNKJRFH.js +0 -79
  446. package/dist/chunk-AQNKJRFH.js.map +0 -1
  447. package/dist/chunk-CTQKI2XL.js +0 -28
  448. package/dist/chunk-CTQKI2XL.js.map +0 -1
  449. package/dist/chunk-DAFFDO33.js +0 -14
  450. package/dist/chunk-DAFFDO33.js.map +0 -1
  451. package/dist/chunk-EGHL5S4P.js +0 -33
  452. package/dist/chunk-EGHL5S4P.js.map +0 -1
  453. package/dist/chunk-GY5WH7TO.js +0 -10
  454. package/dist/chunk-GY5WH7TO.js.map +0 -1
  455. package/dist/chunk-H5AGGVU5.js +0 -75
  456. package/dist/chunk-H5AGGVU5.js.map +0 -1
  457. package/dist/chunk-K3G72I6D.js +0 -24
  458. package/dist/chunk-K3G72I6D.js.map +0 -1
  459. package/dist/chunk-M5JRI6MN.js +0 -27
  460. package/dist/chunk-M5JRI6MN.js.map +0 -1
  461. package/dist/chunk-MBR3TYNL.js +0 -14
  462. package/dist/chunk-MBR3TYNL.js.map +0 -1
  463. package/dist/chunk-MFKMHK5W.js +0 -10
  464. package/dist/chunk-MFKMHK5W.js.map +0 -1
  465. package/dist/chunk-MQIZAD4A.js.map +0 -1
  466. package/dist/chunk-NPPD2A6O.js +0 -40
  467. package/dist/chunk-NPPD2A6O.js.map +0 -1
  468. package/dist/chunk-R6JHHBKQ.js +0 -19
  469. package/dist/chunk-R6JHHBKQ.js.map +0 -1
  470. package/dist/chunk-S3TPM2LP.js +0 -23
  471. package/dist/chunk-S3TPM2LP.js.map +0 -1
  472. package/dist/chunk-TRNBHPNW.js.map +0 -1
  473. package/dist/chunk-VHW5YNIB.js +0 -17
  474. package/dist/chunk-VHW5YNIB.js.map +0 -1
  475. package/dist/chunk-WVPYVDXY.js +0 -15
  476. package/dist/chunk-WVPYVDXY.js.map +0 -1
  477. package/dist/chunk-XZXRLY5Q.js +0 -33
  478. package/dist/chunk-XZXRLY5Q.js.map +0 -1
  479. package/dist/dialog-header.d.ts +0 -15
  480. package/dist/dialog-header.js +0 -5
  481. package/dist/dialog-header.js.map +0 -1
  482. package/dist/menu-action-row.js +0 -4
  483. package/dist/menu-action-row.js.map +0 -1
  484. package/dist/menu-header.d.ts +0 -41
  485. package/dist/menu-header.js +0 -6
  486. package/dist/menu-header.js.map +0 -1
  487. package/dist/menu-item.d.ts +0 -31
  488. package/dist/menu-item.js +0 -4
  489. package/dist/menu-item.js.map +0 -1
  490. package/dist/page-header.d.ts +0 -9
  491. package/dist/page-header.js +0 -3
  492. package/dist/page-header.js.map +0 -1
  493. package/dist/styles/dialog-header.css +0 -87
  494. package/dist/styles/menu-action-row.css +0 -160
  495. package/dist/styles/menu-header.css +0 -143
  496. package/dist/styles/menu-item.css +0 -123
  497. package/dist/styles/page-header.css +0 -33
  498. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
package/docs/layout.md CHANGED
@@ -1,29 +1,42 @@
1
1
  # Pane and content layout
2
2
 
3
- Import `@kerfjs/ui/layout.css` for the shared geometry used by sidebars, main
4
- areas, inspectors, and dialogs. The vocabulary deliberately describes structure
5
- rather than location: the same pane can be placed anywhere.
3
+ Import `Pane` from `@kerfjs/ui/pane`. Browser-aware bundlers receive its CSS
4
+ automatically; other consumers can import `@kerfjs/ui/pane.css`. Import
5
+ `@kerfjs/ui/layout.css` as well when using the related content-item and control
6
+ layout classes. The vocabulary deliberately describes structure rather than
7
+ location: the same pane can be placed anywhere.
6
8
 
7
9
  ## Anatomy
8
10
 
9
11
  ```tsx
10
- <aside class="kui-pane">
11
- <div class="kui-pane__toolbar"><Toolbar label="Workspace" ... /></div>
12
- <div class="kui-pane__content kui-content">
12
+ <Pane
13
+ element="aside"
14
+ label="Workspace"
15
+ contentElement="nav"
16
+ contentLabel="Workspace pages"
17
+ separators={["inline-end"]}
18
+ header={<Toolbar label="Workspace" ... />}
19
+ footer={<Toolbar label="Actions" ... />}
20
+ >
13
21
  <section>...</section>
14
22
  <section>...</section>
15
- </div>
16
- <footer class="kui-pane__footer"><Toolbar label="Actions" ... /></footer>
17
- </aside>
23
+ </Pane>
18
24
  ```
19
25
 
20
- `.kui-pane` has no padding. It reserves rows for an optional toolbar, one
21
- scrolling content area, and an optional footer. A main area or dialog often
22
- omits the footer; a navigation pane commonly uses all three. Fixed chrome stays
23
- outside `.kui-pane__content`, which is the pane's scroll owner.
26
+ `Pane` has no padding. It reserves rows for an optional vertical header, one
27
+ scrolling vertical content area, and an optional footer. A header may contain a
28
+ top toolbar followed by secondary toolbar or status rows. A main area or dialog
29
+ often omits the footer; a navigation pane commonly uses all three. Fixed chrome
30
+ stays outside `.kui-pane__content`, which is the pane's only scroll owner.
31
+
32
+ Pass any combination of logical sides to `separators`: `block-start`,
33
+ `block-end`, `inline-start`, and `inline-end`. Every line is off by default and
34
+ each enabled side uses `--kui-pane-separator-width` (1px) and
35
+ `--kui-pane-separator-color` (`--kui-color-border`). Logical sides keep pane
36
+ boundaries correct in both left-to-right and right-to-left layouts.
24
37
 
25
38
  `.kui-content` is a vertical stack with a 24px gap between major children.
26
- Sections may contain adjacent `MenuItem` rows without adding another major gap.
39
+ Sections may contain adjacent `ListItem` rows without adding another major gap.
27
40
  Ordinary surface-like children use `.kui-content-item` and own their complete
28
41
  geometry:
29
42
 
@@ -37,8 +50,15 @@ Use `.kui-content-item--pill` for the 22px pill radius expressed as
37
50
  changing its geometry by setting `--kui-content-item-border` and
38
51
  `--kui-content-item-background`.
39
52
 
53
+ Nested selection and hover highlights stay concentric and match their owning
54
+ control's shape. Their inner radius is the outer radius minus the full inset,
55
+ expressed by the shared `--kui-layout-highlight-inset` token (2px by default).
56
+ For example, `SegmentedControl` and `ToolbarControlGroup` place each highlight
57
+ behind a 1px control border and 1px control padding, so a rounded control's 12px
58
+ outer radius produces a 10px highlight radius; a 22px pill produces 20px.
59
+
40
60
  A visible parent surface does not make every child another visible card. The
41
- composer recipe keeps its form as the single surface, uses `DialogHeader` for
61
+ composer recipe keeps its form as the single surface, uses a heading `Toolbar` for
42
62
  its task hierarchy, and places field and action control edges on the shared 8px
43
63
  inline gutter instead of nesting them inside another padded content item. A
44
64
  conditional `StateBanner` remains visibly distinct because it communicates
@@ -46,19 +66,20 @@ semantic status.
46
66
 
47
67
  ## Public roles and tokens
48
68
 
49
- | Need | Class | Token / default |
50
- | --- | --- | --- |
51
- | Unpadded toolbar/content/footer structure | `.kui-pane` | — |
52
- | Scrolling pane content | `.kui-pane__content` | — |
53
- | Major vertical rhythm | `.kui-content` | `--kui-layout-content-gap: 24px` |
54
- | Self-contained child geometry | `.kui-content-item` | 8px margin + 1px border + 8px padding |
55
- | Pill child | `.kui-content-item--pill` | `--kui-layout-pill-radius: 22px` |
56
- | Related controls | `.kui-control-cluster` | `--kui-layout-control-gap: 8px` |
57
- | Inline metadata | `.kui-inline-metadata` | `--kui-layout-metadata-gap: 4px` |
58
- | Explicit scroll owner outside a pane | `.kui-scroll-owner` | `overflow: auto` |
59
-
60
- The component layer applies the same contract to `Toolbar`, `MenuHeader`,
61
- `MenuItem`, `PageHeader`, `DialogHeader`, `StateBanner`, `ValueTable`,
69
+ | Need | Class | Token / default |
70
+ | ---------------------------------------- | ------------------------- | ------------------------------------- |
71
+ | Unpadded header/content/footer structure | `Pane`, `.kui-pane` | — |
72
+ | Scrolling pane content | `.kui-pane__content` | — |
73
+ | Optional logical-edge separators | `Pane.separators` | `--kui-pane-separator-width: 1px` |
74
+ | Major vertical rhythm | `.kui-content` | `--kui-layout-content-gap: 24px` |
75
+ | Self-contained child geometry | `.kui-content-item` | 8px margin + 1px border + 8px padding |
76
+ | Pill child | `.kui-content-item--pill` | `--kui-layout-pill-radius: 22px` |
77
+ | Related controls | `.kui-control-cluster` | `--kui-layout-control-gap: 8px` |
78
+ | Inline metadata | `.kui-inline-metadata` | `--kui-layout-metadata-gap: 4px` |
79
+ | Explicit scroll owner outside a pane | `.kui-scroll-owner` | `overflow: auto` |
80
+
81
+ The component layer applies the same contract to `Toolbar`, `ListHeader`,
82
+ `ListItem`, `Toolbar`, `StateBanner`, `ValueTable`,
62
83
  `ValueTableRow`, tabs, and form controls. A value-table row separator starts at
63
84
  the row's 8px content inset, or at 40px when the row contains its 24px leading
64
85
  icon and 8px gap, and always ends 8px from the right edge. Each value-table row
@@ -66,12 +87,122 @@ also keeps 8px of root-scaled block padding independently of its semantic inline
66
87
  inset. Most interactive rows and toolbar groups are 44px tall.
67
88
  Toolbar groups reserve a real 1px outer border around a 42px inner area, even
68
89
  when their border and background are transparent.
69
- `PageHeader` keeps its title on the shared inset while its action border aligns
70
- with the logical edge of the following `.kui-content-item` border.
71
- `DialogHeader` applies the same rule internally: its icon/title identity is a
72
- borderless group in a top toolbar, its direct action children are wrapped in a
73
- contained group, and its optional subtitle is a separate row aligned below the
74
- title. The icon visual is 24px inside a 34px circular background.
90
+ Panel, dialog, and page headings are plain `Toolbar` compositions. The leading
91
+ zone holds an optional icon `ToolbarControlGroup` and a direct extra-large
92
+ `ToolbarText`; actions belong in a trailing group. Omit empty groups. Supporting
93
+ copy is app-owned content below the toolbar and aligns with the intended content
94
+ edge.
95
+
96
+ ## Spacing scale
97
+
98
+ Spacing is not a free choice. The official scale is five canonical steps, each
99
+ expressing exactly one relationship — pick the step by **how connected two
100
+ elements are**, not by eye. Every scalable value is `remify`-authored against the
101
+ fixed 16px baseline, so it delivers as `rem`.
102
+
103
+ | Value | Token | Relationship — when to use |
104
+ | ----- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
105
+ | 0px | `--kui-space-none` | No separation. The elements read as a single unit (a control and its own affordance, adjacent `ListItem` rows). |
106
+ | 4px | `--kui-space-2xs` | Very minor. Still one connected cluster, but readability or aesthetics demand a hair of air (inline metadata, an icon beside its label). |
107
+ | 8px | `--kui-space-xs` | Standard. Between elements **within a group** — the content-item padding/gutter, gaps between toolbar controls in a group. |
108
+ | 16px | `--kui-space-m` | Minor. Between **homogeneous groups** — two lists, two sibling sections of the same kind. |
109
+ | 24px | `--kui-space-l` | Major. Between **heterogeneous groups** — the `.kui-content` rhythm between major, differing regions. |
110
+
111
+ The two relationships that get confused most are 8px vs 24px: 8px is _inside_ a
112
+ group, 24px is _between_ major, differing regions. 16px sits between them for
113
+ same-kind groups.
114
+
115
+ For `Row.gap` and `List.gap`, pass these token names directly (`gap="xs"`, `gap="m"`) or use
116
+ `space('xs')` from `@kerfjs/ui/css-values` when composing a value in JavaScript.
117
+ Use `rem`, `em`, `px`, `pct`, `lengthVar`, and `calc(plus(...))` only when a
118
+ named spacing relationship does not express the requirement; do not pass raw
119
+ CSS strings.
120
+
121
+ ## Text
122
+
123
+ Use `Text` from `@kerfjs/ui/text` for ordinary semantic headings and
124
+ paragraphs. Its required `variant` renders the corresponding native `h1`–`h6`
125
+ or `p` element, and ordinary global, `data-*`, and `aria-*` attributes pass
126
+ through. Every variant resets its native margin, owns a 1px transparent border,
127
+ and supplies the standard 8px item padding; native typography remains available
128
+ to the surrounding composition. Choose heading levels from the document
129
+ outline, not for visual size. Toolbar identity and page-heading compositions
130
+ continue to use `ToolbarText`.
131
+
132
+ ```tsx
133
+ import { Text } from "@kerfjs/ui/text";
134
+
135
+ <Text variant="h2" id="details-title">Details</Text>;
136
+ <Text variant="p" aria-describedby="details-title">Supporting copy</Text>;
137
+ ```
138
+
139
+ ## Spacer
140
+
141
+ Use `Spacer` from `@kerfjs/ui/spacer` for one intentional empty dimension that
142
+ is not the repeated relationship owned by a parent's `gap`. Its `width` and
143
+ `height` accept the same finite `UiSpaceName` vocabulary or a complete typed
144
+ `CssLength`. Fixed spacers use `flex: 0 0 auto`, so flex layouts do not compress
145
+ the requested dimension. Pass `flex` to use `1 1 auto` and consume the remaining
146
+ space along a `Row`, `List`, or other flex parent's main axis.
147
+
148
+ ```tsx
149
+ import { Row } from "@kerfjs/ui/row";
150
+ import { Spacer } from "@kerfjs/ui/spacer";
151
+
152
+ <Row gap="none">
153
+ <button>Back</button>
154
+ <Spacer flex />
155
+ <button>Save</button>
156
+ </Row>;
157
+ ```
158
+
159
+ Spacer is always decorative (`aria-hidden="true"`), accepts no children, and
160
+ uses physical width and height. Prefer `Row.gap` or `List.gap` for uniform
161
+ sibling rhythm, and never use Spacer to cancel or duplicate component-owned
162
+ insets.
163
+
164
+ ## Row and List alignment
165
+
166
+ Use `Row` for a horizontal flex layout and `List` for a vertical one. `Row`
167
+ defaults to `hAlign="left"`, `vAlign="full"`, `gap="xs"`, and no wrapping;
168
+ `List` retains its existing full-width, top-aligned defaults and zero gap.
169
+ Both components accept the same physical-axis vocabulary:
170
+
171
+ | Axis | Values and aliases | Flex behavior |
172
+ | ---------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
173
+ | Horizontal | `left` / `l` / `flex-start`; `center` / `c` / `space-around`; `right` / `r` / `flex-end`; `full` / `f` / `space-between` | Distributes a Row's children; aligns a List's children across its width. |
174
+ | Vertical | `top` / `t` / `flex-start`; `middle` / `m` / `c` / `space-around`; `bottom` / `b` / `flex-end`; `full` / `f` / `space-between` | Aligns a Row's children across its height; distributes a List's children. |
175
+
176
+ Cross-axis `space-around` and `space-between` are not valid `align-items`
177
+ values. Kerf therefore maps `middle` to centered items and `full` to stretched
178
+ items, while applying the requested distribution to `align-content` when a
179
+ Row wraps. Prefer the descriptive values in application code; the short and
180
+ CSS-shaped aliases are provided for compact or migrated call sites.
181
+
182
+ `Row.flex` and `List.flex` share one typed participation contract. Omit the prop
183
+ for the CSS initial value, pass `true` for `1 1 auto`, select the finite
184
+ `"none"`, `"auto"`, or `"initial"` keyword, or use
185
+ `flex(grow, shrink, basis)` from `@kerfjs/ui/css-values` for a complete branded
186
+ shorthand. A `CssLength` is not a flex shorthand, and arbitrary strings are
187
+ rejected by the type contract.
188
+
189
+ Both components also accept `textInsets` and `controlInsets` using the shared
190
+ physical `Sides` union. Values follow canonical top/right/bottom/left order
191
+ (`t`, `rb`, `tbl`, `trbl`, and so on). A selected control side adds 8px of
192
+ padding. A selected text side adds the complete content-item alignment inset —
193
+ 8px outer margin + 1px border + 8px inner padding, represented as 17px of
194
+ container padding. When both props select the same side, the text inset wins.
195
+
196
+ `ListInsetText` and `ListInsetControl` use the same `sides` vocabulary and
197
+ default to `trbl`. The text wrapper applies its complete 8/1/8 geometry only on
198
+ selected sides; the control wrapper applies its 8px outer margin only on
199
+ selected sides. `ListInsetText.horizontalOnly` remains a deprecated alias for
200
+ `sides="rl"`; an explicit `sides` value takes precedence.
201
+
202
+ `--kui-space-s` (12px) and `--kui-space-xl` (32px) exist but are **off the
203
+ canonical rhythm** — reach for them only as a deliberate exception, never as a
204
+ default step. Prefer the five canonical tokens so spacing stays legible and
205
+ consistent across every surface.
75
206
 
76
207
  ## Ownership rules
77
208
 
@@ -84,14 +215,14 @@ title. The icon visual is 24px inside a 34px circular background.
84
215
  4. Keep one scrolling content owner per pane. Toolbar and footer siblings stay
85
216
  fixed while the content scrolls.
86
217
  5. A split item keeps dormant and interactive regions separate. For example,
87
- `MenuHeader` renders its title/count-or-badge cluster separately from its optional
218
+ `ListHeader` renders its title/count-or-badge cluster separately from its optional
88
219
  logical-end 44px action. The header fills the available inline width and its
89
220
  action glyph defaults to 18px; disclosure mode makes the title cluster itself
90
221
  the button and supplies the production `DisclosureArrow` unless `actionIcon`
91
222
  replaces it.
92
- `MenuActionRow` uses a noninteractive row root around sibling 44px primary
93
- and trailing buttons. `MenuItem.trailing` remains dormant content.
94
- In multiline `MenuItem` and `MenuActionRow` rows, the leading icon stays
223
+ `ListActionRow` uses a noninteractive row root around sibling 44px primary
224
+ and trailing buttons. `ListItem.trailing` remains dormant content.
225
+ In multiline `ListItem` and `ListActionRow` rows, the leading icon stays
95
226
  centered on the label's first line rather than the full wrapped label.
96
227
  6. Reading width, column placement, and responsive relocation remain application
97
228
  decisions. The shared classes define local geometry, not the whole shell.
@@ -0,0 +1,77 @@
1
+ # Navigation stack
2
+
3
+ `@kerfjs/ui/nav-stack` is an iOS-style push/pop navigation stack: views slide in
4
+ and out over one another while the top chrome settles. A **single-pane layout is
5
+ a `NavStack` with one entry**. It is one of the opt-in app layouts (see
6
+ [`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
7
+
8
+ Import the component and its companion stylesheet:
9
+
10
+ ```ts
11
+ import { NavStack, type NavStackView } from "@kerfjs/ui/nav-stack";
12
+ import { wireNavStack } from "@kerfjs/ui/wire-nav-stack";
13
+ import "@kerfjs/ui/nav-stack.css";
14
+ ```
15
+
16
+ ## State lives in the app
17
+
18
+ Like every `@kerfjs/ui` component, `NavStack` is declarative: the app owns the
19
+ stack as a `signal<NavStackView[]>`, `NavStack({ views })` renders it, and
20
+ `wireNavStack` animates the transitions.
21
+
22
+ ```tsx
23
+ const views = signal<NavStackView[]>([
24
+ {
25
+ key: "inbox",
26
+ title: "Inbox",
27
+ content: <InboxView />,
28
+ bottomToolbar: <InboxStatus />,
29
+ },
30
+ ]);
31
+
32
+ // render inside mount():
33
+ <NavStack id="mail" label="Mail" views={views.value} />;
34
+
35
+ // once, after first render:
36
+ const dispose = wireNavStack(root, {
37
+ onBack: () => {
38
+ views.value = views.value.slice(0, -1);
39
+ },
40
+ });
41
+
42
+ // push / pop by editing the signal:
43
+ views.value = [
44
+ ...views.value,
45
+ { key: id, title: "Message", content: <MessageView id={id} /> },
46
+ ];
47
+ ```
48
+
49
+ `NavStack` renders every entry stacked, the last one active and the rest kept
50
+ mounted (so their DOM state survives) but hidden. Each entry carries a
51
+ `key` (stable identity), `content`, an optional `title`, optional per-view
52
+ `toolbar` actions, and an optional per-view `bottomToolbar`. The component-level
53
+ `bottomToolbar` remains a persistent fallback for views that do not provide one.
54
+ The back control appears automatically once the stack has more than one entry;
55
+ `wireNavStack`'s `onBack` is where the app pops its own signal.
56
+
57
+ Focus follows the controlled stack automatically. On push, `wireNavStack`
58
+ remembers the focused descendant of the departing view and moves focus into the
59
+ new top view. On pop it restores that exact descendant when it still exists. A
60
+ new view prefers a descendant marked `data-nav-focus`, then its first enabled
61
+ focusable descendant, and finally the view container itself. Use
62
+ `data-nav-focus` for a heading or primary control when DOM order is not the best
63
+ initial reading position; it may use `tabindex="-1"` for programmatic focus.
64
+ When a remembered control was removed during the controlled rerender, the same
65
+ fallback order applies. Disposing the helper stops future transitions and
66
+ cleans up any temporary fallback `tabindex` it added.
67
+
68
+ ## Transitions
69
+
70
+ `wireNavStack(root, { onBack, duration? })` observes the rendered stack and
71
+ animates each change: a pushed view slides in from the trailing edge; a popped
72
+ view slides back off it over the revealed view; and snapshots of the previous
73
+ top and bottom chrome cross-fade into the active view's chrome. Focus moves to
74
+ the new top view independently of animation duration. It returns a disposer.
75
+ The animation honors `prefers-reduced-motion` (transitions collapse to instant)
76
+ and `duration: 0` disables it. Applicable at every device size and inside
77
+ dialogs.
package/docs/recipes.md CHANGED
@@ -14,12 +14,12 @@ the public `onCommit` callback, retains every disposer, and returns one
14
14
  idempotent disposer:
15
15
 
16
16
  ```ts
17
- import { createRecipe } from './navigation-sidebar.js';
18
- import { mountRecipe } from './mount-recipe.js';
17
+ import { createRecipe } from "./navigation-sidebar.js";
18
+ import { mountRecipe } from "./mount-recipe.js";
19
19
 
20
- const root = document.querySelector<HTMLElement>('#navigation')!;
20
+ const root = document.querySelector<HTMLElement>("#navigation")!;
21
21
  const stopRecipe = mountRecipe(root, createRecipe(announce));
22
- window.addEventListener('pagehide', stopRecipe, { once: true });
22
+ window.addEventListener("pagehide", stopRecipe, { once: true });
23
23
  ```
24
24
 
25
25
  The adapter is delivered as reference source, not a new package runtime export.
@@ -44,32 +44,32 @@ Adapt only public `--kui-layout-*` and component variables.
44
44
  [Open the recipe](../ux-demo/?component=recipe-navigation-sidebar) · [TSX source](../ux-demo/recipes/navigation-sidebar.tsx)
45
45
 
46
46
  One unpadded `.kui-pane` owns toolbar/content/footer structure. Its
47
- `.kui-content` uses 24px major gaps; `MenuHeader`, `MenuItem`, and other
47
+ `.kui-content` uses 24px major gaps; `ListHeader`, `ListItem`, and other
48
48
  `.kui-content-item` children own their 8px margin, 1px border, and 8px padding.
49
49
  Rows and footer toolbar groups remain 44px tall. The app owns routes,
50
50
  permissions, labels, selection, valid section counts and their localized
51
51
  `countLabel` phrases, non-count badge content, disclosure state, and revealed
52
- content. A toggled `MenuHeader` supplies the production `DisclosureArrow` when
52
+ content. A toggled `ListHeader` supplies the production `DisclosureArrow` when
53
53
  no custom `actionIcon` is needed; ordinary navigation rows stay chevron-free.
54
54
 
55
55
  ## Workspace header
56
56
 
57
57
  [Open the recipe](../ux-demo/?component=recipe-workspace-header) · [TSX source](../ux-demo/recipes/workspace-header.tsx)
58
58
 
59
- `PageHeader` owns the page heading while one control cluster holds secondary,
59
+ The page heading is a direct `Toolbar`/`ToolbarText` composition while one control cluster holds secondary,
60
60
  overflow, and primary actions. The app owns authorization and command policy;
61
61
  controls relocate without changing focus order.
62
62
 
63
- ## Master-detail dialog
63
+ ## List-detail dialog
64
64
 
65
- [Open the recipe](../ux-demo/?component=recipe-master-detail-dialog) · [TSX source](../ux-demo/recipes/master-detail-dialog.tsx)
65
+ [Open the recipe](../ux-demo/?component=recipe-list-detail-dialog) · [TSX source](../ux-demo/recipes/list-detail-dialog.tsx)
66
66
 
67
67
  The production Web Awesome dialog owns modal focus and Escape; the thin recipe
68
68
  adapter restores the invoking control consistently after the hide event.
69
- `DialogHeader`, `MenuHeader`, `MenuItem`, and `ValueTable` own their included
69
+ `Toolbar`, `ListHeader`, `ListItem`, and `ValueTable` own their included
70
70
  anatomy. The application owns open state, selection, dismissal policy, and
71
- record actions. Pass the dialog action children directly; `DialogHeader` places
72
- them in its top toolbar's action group and uses the localized `actionsLabel`.
71
+ record actions. A `ToolbarControlGroup` places the app's trailing controls in
72
+ the top toolbar's trailing zone.
73
73
  The header sits on the dialog edge while retaining its internal control inset;
74
74
  the selected title receives the full content gutter, the metadata table fills
75
75
  the available detail width between the usual outer margins, and the action
@@ -80,8 +80,11 @@ Do not rebuild the dialog or reach into private shadow parts.
80
80
 
81
81
  [Open the recipe](../ux-demo/?component=recipe-composer-form) · [TSX source](../ux-demo/recipes/composer-form.tsx)
82
82
 
83
- `DialogHeader` supplies the task title and summary, with their ids referenced
84
- by the form. Production fields own labels, help, and native focus. The field
83
+ A direct `ToolbarText` supplies the task title, with app-owned supporting copy
84
+ below; their ids are referenced by the form. `ListInsetText` gives that bare
85
+ supporting copy the same content-item text inset as the fields instead of
86
+ duplicating its geometry in recipe CSS. Production fields own labels, help, and
87
+ native focus. The field
85
88
  and footer control edges sit directly on the shared 8px inline gutter rather
86
89
  than acquiring a second content-item padding inset; major children remain 24px
87
90
  apart and related controls use 8px gaps. The app owns
@@ -106,8 +109,53 @@ rows.
106
109
 
107
110
  Use `ToolbarControlGroup` for related commands, `SegmentedControl` for a few
108
111
  visible exclusive choices, `Select` for a longer value list, and an ordinary
109
- button for an independent command. The app owns values, actions, persistence,
110
- and responsive priority.
112
+ button for an independent command. Even that independent button sits in a
113
+ single-control `ToolbarControlGroup`, which keeps its height, pill shape, focus,
114
+ and hover treatment aligned with the adjacent toolbar controls. The app owns
115
+ values, actions, persistence, and responsive priority.
116
+
117
+ ## Navigation stack
118
+
119
+ [Open the recipe](../ux-demo/?component=recipe-navigation-stack) · [TSX source](../ux-demo/recipes/navigation-stack.tsx)
120
+
121
+ Drill from a library list into a detail and back with `NavStack`
122
+ (`@kerfjs/ui/nav-stack`): the app owns the stack as a signal of views and
123
+ pushes/pops it, `NavStack` renders it, and `wireNavStack` slides the content and
124
+ settles the chrome (reduced motion collapses the slide to instant). A live
125
+ `deviceClass()` badge shows the current size/orientation. See the layout guide
126
+ [`app-layouts.md`](app-layouts.md) for choosing among `NavStack`, `SplitView`,
127
+ `Workbench`, and `TabScaffold`.
128
+
129
+ ## Loading inspector
130
+
131
+ [Open the recipe](../ux-demo/?component=recipe-loading-inspector) · [TSX source](../ux-demo/recipes/loading-inspector.tsx)
132
+
133
+ A record inspector whose per-record values load asynchronously. Every
134
+ value-bearing component (`ToolbarText`, `ValueTable`/`ValueTableRow`, `Select`,
135
+ `SegmentedControl`, `ListItem`, `StateBanner`) takes its `placeholder` from one
136
+ loading flag, so the same real chrome renders a faithful loading state and then
137
+ the populated record — no separate skeleton markup. The composition is the point;
138
+ `Skeleton` is the primitive it builds on. The app owns the loading lifecycle and
139
+ which values are still unknown.
140
+
141
+ ## Collapsible sidebar
142
+
143
+ [Open the recipe](../ux-demo/?component=recipe-collapsible-sidebar) · [TSX source](../ux-demo/recipes/collapsible-sidebar.tsx)
144
+
145
+ A mini app frame whose left navigation rail and bottom activity drawer are
146
+ standalone `CollapsiblePanel`s (`@kerfjs/ui/collapsible-panel`) driven by
147
+ `wireSidebar` (`@kerfjs/ui/wire-sidebar`). `CollapsiblePanelToggle` supplies the
148
+ standard per-side glyph: a collapse toggle lives inside each panel and an expand
149
+ toggle lives in the always-visible main header, so a collapsed panel is still
150
+ reachable. `wireSidebar` owns the toggle delegation, moves focus into a panel on
151
+ open and restores it to the trigger on close, and — when a `deviceClass()` reports
152
+ `compact` — switches the open panel to a dismissable **overlay** (backdrop, Escape
153
+ and backdrop-click collapse, and a trapped Tab ring, the ARIA dialog pattern). It
154
+ also persists each panel's collapsed state through a supplied storage hook. The app
155
+ owns each `collapsed` signal, the panel sizes, and the content; the wire owns the
156
+ ephemeral interaction. For a full three-pane shell use `Workbench` instead — see
157
+ [`app-layouts.md`](app-layouts.md). This recipe is covered end-to-end across
158
+ Chromium, Firefox, and WebKit by `tests/browser/collapsible-sidebar-recipe.spec.ts`.
111
159
 
112
160
  ## Rules shared by every recipe
113
161
 
@@ -0,0 +1,55 @@
1
+ # Split view (list-detail)
2
+
3
+ `@kerfjs/ui/split-view` is a list-detail layout: a list and a
4
+ detail side by side on roomy screens, collapsing to a `NavStack` (list → detail)
5
+ on compact ones. One of the opt-in app layouts (see
6
+ [`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
7
+
8
+ ```ts
9
+ import { SplitView } from "@kerfjs/ui/split-view";
10
+ import { deviceClass } from "@kerfjs/ui/device-class";
11
+ import "@kerfjs/ui/split-view.css";
12
+ // plus nav-stack.css when the compact path is reachable, and
13
+ // wireResizableRegions / wireNavStack for the interactive behavior.
14
+ ```
15
+
16
+ ## Responsive by device class
17
+
18
+ `SplitView` is declarative; the app derives `compact` from the device class and
19
+ tracks its own selection:
20
+
21
+ ```tsx
22
+ const device = deviceClass();
23
+ const selected = signal<string | null>(null);
24
+
25
+ <SplitView
26
+ id="mail"
27
+ label="Mail"
28
+ compact={device.value.compact}
29
+ detailActive={selected.value !== null}
30
+ list={<ThreadList />}
31
+ detail={<Message id={selected.value} />}
32
+ listTitle="Threads"
33
+ detailTitle="Message"
34
+ resizable={{ size: 320, min: 220, max: 480 }}
35
+ />;
36
+ ```
37
+
38
+ - **Roomy** (`compact: false`): both panes show. With `resizable`, the list sits
39
+ in a `ResizableRegion` (wire it with `wireResizableRegions`); without it the
40
+ list takes a fixed `--kui-split-view-list-width` (default 320px) and the detail
41
+ fills the rest.
42
+ - **Compact** (`compact: true`): the split collapses to a `NavStack`. The list is
43
+ the root; when `detailActive` is true the detail is pushed over it with an
44
+ automatic back control (wire it with `wireNavStack`, whose `onBack` clears the
45
+ app's selection). This is the portrait-tablet / handset presentation; as a
46
+ dialog the compact form is a full-screen or large partial-cover modal.
47
+
48
+ The catalog's **Interactive compact drill-down** example exercises this exact
49
+ controlled flow: a list action sets the selected message, `detailActive` pushes
50
+ its detail, and the wired Back action clears the selection to reveal the
51
+ preserved list. Use the same structure in application demos so compact examples
52
+ show the interaction rather than rendering a disconnected detail state.
53
+
54
+ Compose the interactive wiring from the existing helpers — `SplitView` adds no
55
+ wire of its own.
@@ -0,0 +1,53 @@
1
+ # SunkenPanel
2
+
3
+ `SunkenPanel` is a visually lowered application surface with one compact inset
4
+ and a vertical content stack. Use it for a main work area or nested panel whose
5
+ background should sit behind ordinary content, such as the primary stage in an
6
+ issue tracker or component catalog.
7
+
8
+ ```tsx
9
+ import { SunkenPanel } from '@kerfjs/ui/sunken-panel';
10
+
11
+ <SunkenPanel ariaLabel="Release workspace">
12
+ <ReleaseSummary />
13
+ <ReleaseChecks />
14
+ </SunkenPanel>;
15
+ ```
16
+
17
+ The default `shape="rounded"` uses the shared rounded-rectangle radius. Choose
18
+ `shape="square"` for a flush or edge-to-edge area that needs `border-radius: 0`:
19
+
20
+ ```tsx
21
+ <SunkenPanel shape="square">
22
+ <Workspace />
23
+ </SunkenPanel>
24
+ ```
25
+
26
+ ## Ownership
27
+
28
+ The root owns its lowered background, 8px padding, and 8px vertical gap.
29
+ Children own their borders and internal geometry. The application owns child
30
+ order, responsive placement, and scrolling; `SunkenPanel` deliberately does not
31
+ create another scroll container.
32
+
33
+ Use `ariaLabel` only when the surface is a distinct region people need to find
34
+ by name. With a label, the root receives `role="region"`; without one it remains
35
+ a non-landmark grouping.
36
+
37
+ Do not use `SunkenPanel` merely to add padding, as a substitute for pane
38
+ header/content/footer anatomy, or around a child that already owns the same
39
+ outer surface.
40
+
41
+ ## Public styling boundary
42
+
43
+ Override the public properties at the composition boundary:
44
+
45
+ - `--kui-sunken-panel-background`
46
+ - `--kui-sunken-panel-foreground`
47
+ - `--kui-sunken-panel-padding`
48
+ - `--kui-sunken-panel-gap`
49
+ - `--kui-sunken-panel-radius`
50
+
51
+ The public root class is `.kui-sunken-panel`. Prefer the properties above over
52
+ styling descendants. The `square` shape deliberately overrides the radius
53
+ property with zero; use `rounded` when customizing the radius token.
@@ -0,0 +1,25 @@
1
+ # Dialog and popup surface scaffolds
2
+
3
+ `DialogSurface` and `PopupSurface` configure recurring Web Awesome surface
4
+ geometry without application `::part()` overrides. They do not replace Web
5
+ Awesome behavior: the application still owns open state, focus lifecycle,
6
+ dismissal policy, ids, labels, and content.
7
+
8
+ Wrap one `wa-dialog` with `DialogSurface`. Choose `size` (`small`, `medium`, or
9
+ `large`), `presentation` (`modal`, `side-sheet`, or `fullscreen`), and independent
10
+ `bodyInset` / `footerInset` (`none`, `compact`, or `comfortable`). The defaults
11
+ match Kerf's medium modal, 8px body, and 16px footer rhythm.
12
+
13
+ Treat a dialog body as a `List` by default. When its rows or sections own their
14
+ standard list geometry, set `bodyInset="none"` so the dialog does not add a
15
+ second inset. Bare prose is still a list child: wrap it in `ListInsetText` so its
16
+ text edge receives the standard 8px margin + 1px transparent border + 8px
17
+ padding and aligns with bordered siblings. Reserve `compact` or `comfortable`
18
+ body insets for exceptional content that does not already have list/content-item
19
+ geometry.
20
+
21
+ Wrap one `wa-dropdown` with `PopupSurface`. `inset="list-zero"` removes menu
22
+ padding for a child that already owns row insets, `compact` uses 4px, and
23
+ `standard` uses the shared 8px surface inset. Keep the dropdown trigger named and
24
+ preserve `data-morph-skip-children` when its upgraded light-DOM items must retain
25
+ identity across Kerf rerenders.
@@ -0,0 +1,52 @@
1
+ # Tab scaffold
2
+
3
+ `@kerfjs/ui/tab-scaffold` is a mobile-first, iOS-like bottom tab bar that switches
4
+ between major app sections, where **each tab keeps its own content mounted** —
5
+ usually a `NavStack`, so each tab's stack and scroll survive a switch. One of the
6
+ opt-in app layouts (see [`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
7
+ It is distinct from `TabBar` (document-oriented, reorderable strips).
8
+
9
+ ```ts
10
+ import { TabScaffold } from "@kerfjs/ui/tab-scaffold";
11
+ import { wireTabScaffold } from "@kerfjs/ui/wire-tab-scaffold";
12
+ import "@kerfjs/ui/tab-scaffold.css";
13
+ ```
14
+
15
+ ## Controlled selection
16
+
17
+ The app owns the active tab (a signal); `TabScaffold` renders every tab's scene
18
+ (only the active one visible) plus the bottom bar, and `wireTabScaffold` reports
19
+ clicks.
20
+
21
+ ```tsx
22
+ const active = signal("home");
23
+
24
+ <TabScaffold
25
+ id="app"
26
+ label="Sections"
27
+ active={active.value}
28
+ tabs={[
29
+ { id: "home", label: "Home", icon: <HomeIcon />, content: <HomeStack /> },
30
+ {
31
+ id: "search",
32
+ label: "Search",
33
+ icon: <SearchIcon />,
34
+ content: <SearchStack />,
35
+ },
36
+ ]}
37
+ />;
38
+
39
+ // once, after first render:
40
+ const dispose = wireTabScaffold(root, {
41
+ onSelect: (id) => {
42
+ active.value = id;
43
+ },
44
+ });
45
+ ```
46
+
47
+ Each `TabScaffoldTab` has an `id`, `label`, optional `icon`, and `content`. The
48
+ bottom bar respects the home-indicator safe area (`env(safe-area-inset-bottom)`)
49
+ and keeps 44px targets. Its single-line labels preserve their full line box and
50
+ truncate horizontally with an ellipsis when a destination name exceeds its
51
+ share of the bar. On larger device classes, promote the tab set to a
52
+ `Workbench` rail or a persistent sidebar instead of a bottom bar.