@kerfjs/ui 4.4.1 → 4.5.0-beta.1

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 (499) hide show
  1. package/README.md +425 -42
  2. package/ai/application-ui-diagnostic-ids-v1.json +92 -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 +102 -0
  12. package/ai/compile-time-contracts-v1.schema.json +49 -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 +319 -0
  17. package/ai/component-catalog-v2.d.ts +67 -0
  18. package/ai/component-catalog-v2.json +4675 -0
  19. package/ai/component-catalog-v2.schema.json +246 -0
  20. package/ai/component-catalog.json +5826 -0
  21. package/ai/component-catalog.schema.json +95 -0
  22. package/ai/public-api-signatures-v1.md +1719 -0
  23. package/ai/skill.md +129 -21
  24. package/ai/webawesome-jsx-signatures-v1.md +106 -0
  25. package/analyzer/cli.mjs +46 -0
  26. package/analyzer/index.d.mts +45 -0
  27. package/analyzer/index.mjs +737 -0
  28. package/analyzer/report.schema.json +66 -0
  29. package/dist/app-tab.d.ts +14 -2
  30. package/dist/app-tab.js +3 -1
  31. package/dist/browser/app-tab.js +3 -2
  32. package/dist/browser/disclosure-arrow.js +4 -0
  33. package/dist/browser/empty-state.js +3 -3
  34. package/dist/browser/floating-toolbar.js +3 -0
  35. package/dist/browser/list-action-row.js +4 -0
  36. package/dist/browser/list-header.js +6 -0
  37. package/dist/browser/list-inset-control.js +3 -0
  38. package/dist/browser/list-inset-text.js +3 -0
  39. package/dist/browser/list-item.js +4 -0
  40. package/dist/browser/list.js +3 -0
  41. package/dist/browser/loading-spinner.js +2 -2
  42. package/dist/browser/lucide-icon.js +2 -2
  43. package/dist/browser/pane.js +3 -0
  44. package/dist/browser/panel-header.js +7 -0
  45. package/dist/browser/resizable-region.js +2 -2
  46. package/dist/browser/segmented-control.js +3 -2
  47. package/dist/browser/select.js +4 -3
  48. package/dist/browser/skeleton.js +3 -0
  49. package/dist/browser/state-banner.js +3 -2
  50. package/dist/browser/sunken-panel.js +3 -0
  51. package/dist/browser/tab-bar.js +2 -2
  52. package/dist/browser/token-search-field.js +4 -0
  53. package/dist/browser/toolbar-control-group.js +2 -2
  54. package/dist/browser/toolbar-text.js +3 -2
  55. package/dist/browser/toolbar.js +2 -2
  56. package/dist/browser/value-table.js +3 -2
  57. package/dist/catalog-resources.d.ts +34 -0
  58. package/dist/catalog-resources.js +22 -0
  59. package/dist/catalog-resources.js.map +1 -0
  60. package/dist/catalog.d.ts +150 -0
  61. package/dist/catalog.js +377 -0
  62. package/dist/catalog.js.map +1 -0
  63. package/dist/chunk-5VTB2BKP.js +35 -0
  64. package/dist/chunk-5VTB2BKP.js.map +1 -0
  65. package/dist/chunk-6WTETUZ6.js +21 -0
  66. package/dist/chunk-6WTETUZ6.js.map +1 -0
  67. package/dist/chunk-7KEUJIIC.js +218 -0
  68. package/dist/chunk-7KEUJIIC.js.map +1 -0
  69. package/dist/chunk-7XRDUG4Y.js +58 -0
  70. package/dist/chunk-7XRDUG4Y.js.map +1 -0
  71. package/dist/chunk-B3A3IVO5.js +83 -0
  72. package/dist/chunk-B3A3IVO5.js.map +1 -0
  73. package/dist/chunk-DPDBVKNA.js +113 -0
  74. package/dist/chunk-DPDBVKNA.js.map +1 -0
  75. package/dist/chunk-FEAMR7S4.js +68 -0
  76. package/dist/chunk-FEAMR7S4.js.map +1 -0
  77. package/dist/chunk-FEY65TBV.js +15 -0
  78. package/dist/chunk-FEY65TBV.js.map +1 -0
  79. package/dist/{chunk-TRNBHPNW.js → chunk-IJBSZ4NX.js} +11 -8
  80. package/dist/chunk-IJBSZ4NX.js.map +1 -0
  81. package/dist/chunk-J5BFYY7Q.js +35 -0
  82. package/dist/chunk-J5BFYY7Q.js.map +1 -0
  83. package/dist/chunk-JRKSK2HX.js +79 -0
  84. package/dist/chunk-JRKSK2HX.js.map +1 -0
  85. package/dist/chunk-KWWR5VMS.js +45 -0
  86. package/dist/chunk-KWWR5VMS.js.map +1 -0
  87. package/dist/chunk-LPOMRC4C.js +25 -0
  88. package/dist/chunk-LPOMRC4C.js.map +1 -0
  89. package/dist/chunk-M7JBER5U.js +41 -0
  90. package/dist/chunk-M7JBER5U.js.map +1 -0
  91. package/dist/chunk-NENBUYX5.js +79 -0
  92. package/dist/chunk-NENBUYX5.js.map +1 -0
  93. package/dist/chunk-NWSEP5RT.js +117 -0
  94. package/dist/chunk-NWSEP5RT.js.map +1 -0
  95. package/dist/chunk-OJWWHFH3.js +426 -0
  96. package/dist/chunk-OJWWHFH3.js.map +1 -0
  97. package/dist/chunk-PSKN344N.js +114 -0
  98. package/dist/chunk-PSKN344N.js.map +1 -0
  99. package/dist/chunk-PX3YSS5C.js +32 -0
  100. package/dist/chunk-PX3YSS5C.js.map +1 -0
  101. package/dist/chunk-QO55FN2Y.js +20 -0
  102. package/dist/chunk-QO55FN2Y.js.map +1 -0
  103. package/dist/chunk-R7ZWUN64.js +38 -0
  104. package/dist/chunk-R7ZWUN64.js.map +1 -0
  105. package/dist/chunk-RKVQEH4J.js +61 -0
  106. package/dist/chunk-RKVQEH4J.js.map +1 -0
  107. package/dist/chunk-SRSJO5QE.js +40 -0
  108. package/dist/chunk-SRSJO5QE.js.map +1 -0
  109. package/dist/chunk-SYDMYBPG.js +31 -0
  110. package/dist/chunk-SYDMYBPG.js.map +1 -0
  111. package/dist/chunk-TB6DY7H5.js +25 -0
  112. package/dist/chunk-TB6DY7H5.js.map +1 -0
  113. package/dist/chunk-UFZKLBCF.js +108 -0
  114. package/dist/chunk-UFZKLBCF.js.map +1 -0
  115. package/dist/{chunk-MQIZAD4A.js → chunk-UKKSMZYT.js} +76 -22
  116. package/dist/chunk-UKKSMZYT.js.map +1 -0
  117. package/dist/chunk-W5L2JSBY.js +40 -0
  118. package/dist/chunk-W5L2JSBY.js.map +1 -0
  119. package/dist/chunk-Y45PPBDV.js +105 -0
  120. package/dist/chunk-Y45PPBDV.js.map +1 -0
  121. package/dist/chunk-Y6LYKFXY.js +43 -0
  122. package/dist/chunk-Y6LYKFXY.js.map +1 -0
  123. package/dist/collapsible-panel.d.ts +66 -0
  124. package/dist/collapsible-panel.js +63 -0
  125. package/dist/collapsible-panel.js.map +1 -0
  126. package/dist/device-class.d.ts +62 -0
  127. package/dist/device-class.js +103 -0
  128. package/dist/device-class.js.map +1 -0
  129. package/dist/disclosure-arrow.d.ts +14 -0
  130. package/dist/disclosure-arrow.js +4 -0
  131. package/dist/disclosure-arrow.js.map +1 -0
  132. package/dist/divider-sides-267FA7sY.d.ts +4 -0
  133. package/dist/empty-state.d.ts +1 -1
  134. package/dist/empty-state.js +1 -1
  135. package/dist/floating-toolbar.d.ts +28 -0
  136. package/dist/floating-toolbar.js +3 -0
  137. package/dist/floating-toolbar.js.map +1 -0
  138. package/dist/index.d.ts +20 -9
  139. package/dist/index.js +29 -18
  140. package/dist/list-action-row.d.ts +52 -0
  141. package/dist/list-action-row.js +5 -0
  142. package/dist/list-action-row.js.map +1 -0
  143. package/dist/list-header.d.ts +64 -0
  144. package/dist/list-header.js +7 -0
  145. package/dist/list-header.js.map +1 -0
  146. package/dist/list-inset-control.d.ts +16 -0
  147. package/dist/list-inset-control.js +3 -0
  148. package/dist/list-inset-control.js.map +1 -0
  149. package/dist/list-inset-text.d.ts +25 -0
  150. package/dist/list-inset-text.js +3 -0
  151. package/dist/list-inset-text.js.map +1 -0
  152. package/dist/list-item.d.ts +33 -0
  153. package/dist/list-item.js +5 -0
  154. package/dist/list-item.js.map +1 -0
  155. package/dist/list.d.ts +19 -0
  156. package/dist/list.js +3 -0
  157. package/dist/list.js.map +1 -0
  158. package/dist/loading-spinner.js +1 -1
  159. package/dist/nav-stack.d.ts +38 -0
  160. package/dist/nav-stack.js +5 -0
  161. package/dist/nav-stack.js.map +1 -0
  162. package/dist/pane.d.ts +49 -0
  163. package/dist/pane.js +4 -0
  164. package/dist/pane.js.map +1 -0
  165. package/dist/panel-header.d.ts +42 -0
  166. package/dist/panel-header.js +7 -0
  167. package/dist/panel-header.js.map +1 -0
  168. package/dist/resizable-region.d.ts +3 -1
  169. package/dist/resizable-region.js +1 -1
  170. package/dist/segmented-control.d.ts +8 -6
  171. package/dist/segmented-control.js +2 -1
  172. package/dist/select-register.d.ts +5 -1
  173. package/dist/select-register.js +3 -0
  174. package/dist/select-register.js.map +1 -1
  175. package/dist/select.d.ts +15 -6
  176. package/dist/select.js +2 -1
  177. package/dist/skeleton.d.ts +24 -0
  178. package/dist/skeleton.js +3 -0
  179. package/dist/skeleton.js.map +1 -0
  180. package/dist/split-view.d.ts +42 -0
  181. package/dist/split-view.js +81 -0
  182. package/dist/split-view.js.map +1 -0
  183. package/dist/state-banner.d.ts +8 -3
  184. package/dist/state-banner.js +2 -1
  185. package/dist/styles/app-tab.css +152 -0
  186. package/dist/styles/catalog.css +575 -0
  187. package/dist/styles/collapsible-panel.css +146 -0
  188. package/dist/styles/disclosure-arrow.css +23 -0
  189. package/dist/styles/empty-state.css +47 -0
  190. package/dist/styles/floating-toolbar.css +38 -0
  191. package/dist/styles/foundation.css +362 -0
  192. package/dist/styles/layout.css +55 -0
  193. package/dist/styles/list-action-row.css +160 -0
  194. package/dist/styles/list-header.css +160 -0
  195. package/dist/styles/list-inset-control.css +7 -0
  196. package/dist/styles/list-inset-text.css +14 -0
  197. package/dist/styles/list-item.css +123 -0
  198. package/dist/styles/list.css +43 -0
  199. package/dist/styles/loading-spinner.css +21 -0
  200. package/dist/styles/lucide-icon.css +6 -0
  201. package/dist/styles/nav-stack.css +111 -0
  202. package/dist/styles/pane.css +62 -0
  203. package/dist/styles/panel-header.css +64 -0
  204. package/dist/styles/resizable-region.css +111 -0
  205. package/{src → dist/styles}/segmented-control.css +58 -31
  206. package/dist/styles/select.css +140 -0
  207. package/dist/styles/skeleton.css +31 -0
  208. package/dist/styles/split-view.css +35 -0
  209. package/dist/styles/state-banner.css +198 -0
  210. package/dist/styles/styles.css +24 -0
  211. package/dist/styles/sunken-panel.css +22 -0
  212. package/dist/styles/tab-bar.css +45 -0
  213. package/dist/styles/tab-scaffold.css +84 -0
  214. package/dist/styles/token-search-field.css +289 -0
  215. package/dist/styles/toolbar-control-group.css +225 -0
  216. package/dist/styles/toolbar-text.css +92 -0
  217. package/dist/styles/toolbar.css +97 -0
  218. package/dist/styles/value-table.css +75 -0
  219. package/dist/styles/webawesome.css +399 -0
  220. package/dist/styles/workbench.css +94 -0
  221. package/dist/sunken-panel.d.ts +19 -0
  222. package/dist/sunken-panel.js +3 -0
  223. package/dist/sunken-panel.js.map +1 -0
  224. package/dist/tab-bar.d.ts +10 -2
  225. package/dist/tab-bar.js +1 -1
  226. package/dist/tab-scaffold.d.ts +30 -0
  227. package/dist/tab-scaffold.js +51 -0
  228. package/dist/tab-scaffold.js.map +1 -0
  229. package/dist/token-search-field.d.ts +59 -0
  230. package/dist/token-search-field.js +4 -0
  231. package/dist/token-search-field.js.map +1 -0
  232. package/dist/toolbar-control-group.d.ts +11 -5
  233. package/dist/toolbar-control-group.js +1 -1
  234. package/dist/toolbar-text.d.ts +31 -4
  235. package/dist/toolbar-text.js +2 -1
  236. package/dist/toolbar.d.ts +5 -3
  237. package/dist/toolbar.js +1 -1
  238. package/dist/value-table.d.ts +13 -3
  239. package/dist/value-table.js +2 -1
  240. package/dist/webawesome.d.ts +100 -0
  241. package/dist/webawesome.js +3 -0
  242. package/dist/webawesome.js.map +1 -0
  243. package/dist/wire-catalog.d.ts +57 -0
  244. package/dist/wire-catalog.js +302 -0
  245. package/dist/wire-catalog.js.map +1 -0
  246. package/dist/wire-nav-stack.d.ts +15 -0
  247. package/dist/wire-nav-stack.js +103 -0
  248. package/dist/wire-nav-stack.js.map +1 -0
  249. package/dist/wire-resizable-regions.d.ts +1 -1
  250. package/dist/wire-resizable-regions.js +1 -1
  251. package/dist/wire-sidebar.d.ts +43 -0
  252. package/dist/wire-sidebar.js +128 -0
  253. package/dist/wire-sidebar.js.map +1 -0
  254. package/dist/wire-tab-bars.d.ts +18 -2
  255. package/dist/wire-tab-bars.js +1 -1
  256. package/dist/wire-tab-scaffold.d.ts +11 -0
  257. package/dist/wire-tab-scaffold.js +21 -0
  258. package/dist/wire-tab-scaffold.js.map +1 -0
  259. package/dist/wire-token-search-fields.d.ts +111 -0
  260. package/dist/wire-token-search-fields.js +5 -0
  261. package/dist/wire-token-search-fields.js.map +1 -0
  262. package/dist/workbench.d.ts +33 -0
  263. package/dist/workbench.js +64 -0
  264. package/dist/workbench.js.map +1 -0
  265. package/docs/accessibility.md +124 -4
  266. package/docs/app-layouts.md +61 -0
  267. package/docs/catalog.md +364 -0
  268. package/docs/collapsible-panel.md +129 -0
  269. package/docs/component-contract.md +312 -5
  270. package/docs/component-selection.md +219 -0
  271. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  272. package/docs/design/templates/empty-state/filtered.svg +1 -0
  273. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  274. package/docs/design/templates/empty-state/with-action.svg +1 -0
  275. package/docs/design/templates/empty-state-dark.svg +1 -0
  276. package/docs/design/templates/empty-state.svg +1 -0
  277. package/docs/design/templates/list/compact-dark.svg +1 -0
  278. package/docs/design/templates/list/compact.svg +1 -0
  279. package/docs/design/templates/list/stack-dark.svg +1 -0
  280. package/docs/design/templates/list/stack.svg +1 -0
  281. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  282. package/docs/design/templates/list-action-row/default.svg +1 -0
  283. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  284. package/docs/design/templates/list-action-row/selected.svg +1 -0
  285. package/docs/design/templates/list-action-row-dark.svg +1 -0
  286. package/docs/design/templates/list-action-row.svg +1 -0
  287. package/docs/design/templates/list-dark.svg +1 -0
  288. package/docs/design/templates/list-header/action-dark.svg +1 -0
  289. package/docs/design/templates/list-header/action.svg +1 -0
  290. package/docs/design/templates/list-header/count-dark.svg +1 -0
  291. package/docs/design/templates/list-header/count.svg +1 -0
  292. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  293. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  294. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  295. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  296. package/docs/design/templates/list-header-dark.svg +1 -0
  297. package/docs/design/templates/list-header.svg +1 -0
  298. package/docs/design/templates/list-item/default-dark.svg +1 -0
  299. package/docs/design/templates/list-item/default.svg +1 -0
  300. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  301. package/docs/design/templates/list-item/multiline.svg +1 -0
  302. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  303. package/docs/design/templates/list-item/selected.svg +1 -0
  304. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  305. package/docs/design/templates/list-item/trailing.svg +1 -0
  306. package/docs/design/templates/list-item-dark.svg +1 -0
  307. package/docs/design/templates/list-item.svg +1 -0
  308. package/docs/design/templates/list.svg +1 -0
  309. package/docs/design/templates/panel-header/icon-actions-dark.svg +1 -0
  310. package/docs/design/templates/panel-header/icon-actions.svg +1 -0
  311. package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +1 -0
  312. package/docs/design/templates/panel-header/icon-summary-actions.svg +1 -0
  313. package/docs/design/templates/panel-header/no-icon-dark.svg +1 -0
  314. package/docs/design/templates/panel-header/no-icon.svg +1 -0
  315. package/docs/design/templates/panel-header/page-heading-dark.svg +1 -0
  316. package/docs/design/templates/panel-header/page-heading.svg +1 -0
  317. package/docs/design/templates/panel-header/title-only-dark.svg +1 -0
  318. package/docs/design/templates/panel-header/title-only.svg +1 -0
  319. package/docs/design/templates/panel-header-dark.svg +1 -0
  320. package/docs/design/templates/panel-header.svg +1 -0
  321. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  322. package/docs/design/templates/segmented-control/equal.svg +1 -0
  323. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  324. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  325. package/docs/design/templates/segmented-control-dark.svg +1 -0
  326. package/docs/design/templates/segmented-control.svg +1 -0
  327. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  328. package/docs/design/templates/skeleton/avatar.svg +1 -0
  329. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  330. package/docs/design/templates/skeleton/block.svg +1 -0
  331. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  332. package/docs/design/templates/skeleton/lines.svg +1 -0
  333. package/docs/design/templates/skeleton-dark.svg +1 -0
  334. package/docs/design/templates/skeleton.svg +1 -0
  335. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  336. package/docs/design/templates/state-banner/danger.svg +1 -0
  337. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  338. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  339. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  340. package/docs/design/templates/state-banner/info.svg +1 -0
  341. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  342. package/docs/design/templates/state-banner/success.svg +1 -0
  343. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  344. package/docs/design/templates/state-banner/warning.svg +1 -0
  345. package/docs/design/templates/state-banner-dark.svg +1 -0
  346. package/docs/design/templates/state-banner.svg +1 -0
  347. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  348. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  349. package/docs/design/templates/tab-bar-dark.svg +1 -0
  350. package/docs/design/templates/tab-bar.svg +1 -0
  351. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  352. package/docs/design/templates/token-search-field/empty.svg +1 -0
  353. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  354. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  355. package/docs/design/templates/token-search-field-dark.svg +1 -0
  356. package/docs/design/templates/token-search-field.svg +1 -0
  357. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  358. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  359. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  360. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  361. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  362. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  363. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  364. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  365. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  366. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  367. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  368. package/docs/design/templates/toolbar-control-group.svg +1 -0
  369. package/docs/design/templates/toolbar-dark.svg +1 -0
  370. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  371. package/docs/design/templates/toolbar-text/default.svg +1 -0
  372. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  373. package/docs/design/templates/toolbar-text/large.svg +1 -0
  374. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  375. package/docs/design/templates/toolbar-text/small.svg +1 -0
  376. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  377. package/docs/design/templates/toolbar-text.svg +1 -0
  378. package/docs/design/templates/toolbar.svg +1 -0
  379. package/docs/design/templates/value-table/details-dark.svg +1 -0
  380. package/docs/design/templates/value-table/details.svg +1 -0
  381. package/docs/design/templates/value-table-dark.svg +1 -0
  382. package/docs/design/templates/value-table.svg +1 -0
  383. package/docs/design/templates.md +130 -0
  384. package/docs/design-philosophy.md +59 -1
  385. package/docs/device-class.md +54 -0
  386. package/docs/examples/application-ui-profile.json +60 -0
  387. package/docs/examples/command-palette-adapter.tsx +120 -0
  388. package/docs/examples/component-catalog-extension-v2.json +81 -0
  389. package/docs/examples/component-catalog-extension.json +45 -0
  390. package/docs/layout.md +150 -0
  391. package/docs/nav-stack.md +56 -0
  392. package/docs/recipes.md +167 -0
  393. package/docs/split-view.md +49 -0
  394. package/docs/sunken-panel.md +53 -0
  395. package/docs/tab-scaffold.md +50 -0
  396. package/docs/type-contracts.md +102 -0
  397. package/docs/ui-analyzer.md +86 -0
  398. package/docs/ui-doctor.md +84 -0
  399. package/docs/ui-evaluator.md +122 -0
  400. package/docs/ux-demo.md +23 -9
  401. package/docs/webawesome-theme.md +87 -7
  402. package/docs/workbench.md +73 -0
  403. package/doctor/cli.mjs +115 -0
  404. package/doctor/config.schema.json +68 -0
  405. package/doctor/eslint-diagnostics.d.mts +8 -0
  406. package/doctor/eslint-diagnostics.mjs +7 -0
  407. package/doctor/index.d.mts +116 -0
  408. package/doctor/index.mjs +1257 -0
  409. package/doctor/report.schema.json +118 -0
  410. package/evaluator/cli.mjs +89 -0
  411. package/evaluator/index.d.mts +90 -0
  412. package/evaluator/index.mjs +1138 -0
  413. package/evaluator/report.schema.json +152 -0
  414. package/llms.txt +197 -14
  415. package/package.json +228 -46
  416. package/traversal-exclusions.mjs +27 -0
  417. package/ux-demo/recipes/app-shell.tsx +228 -0
  418. package/ux-demo/recipes/collapsible-sidebar.tsx +223 -0
  419. package/ux-demo/recipes/compact-toolbar.tsx +145 -0
  420. package/ux-demo/recipes/composer-form.tsx +151 -0
  421. package/ux-demo/recipes/list-detail-dialog.tsx +181 -0
  422. package/ux-demo/recipes/list-workspace-states.tsx +183 -0
  423. package/ux-demo/recipes/loaders.ts +36 -0
  424. package/ux-demo/recipes/loading-inspector.tsx +164 -0
  425. package/ux-demo/recipes/mount-recipe.ts +64 -0
  426. package/ux-demo/recipes/navigation-sidebar.tsx +137 -0
  427. package/ux-demo/recipes/navigation-stack.tsx +118 -0
  428. package/ux-demo/recipes/recipes.css +448 -0
  429. package/ux-demo/recipes/types.ts +19 -0
  430. package/ux-demo/recipes/workspace-header.tsx +67 -0
  431. package/dist/browser/dialog-header.js +0 -3
  432. package/dist/browser/menu-header.js +0 -3
  433. package/dist/browser/menu-item.js +0 -3
  434. package/dist/browser/page-header.js +0 -3
  435. package/dist/chunk-2PES33HS.js +0 -13
  436. package/dist/chunk-2PES33HS.js.map +0 -1
  437. package/dist/chunk-3O7ZEUW2.js +0 -27
  438. package/dist/chunk-3O7ZEUW2.js.map +0 -1
  439. package/dist/chunk-4HHCYSGT.js +0 -79
  440. package/dist/chunk-4HHCYSGT.js.map +0 -1
  441. package/dist/chunk-55DATV3A.js +0 -10
  442. package/dist/chunk-55DATV3A.js.map +0 -1
  443. package/dist/chunk-723YNJBH.js +0 -17
  444. package/dist/chunk-723YNJBH.js.map +0 -1
  445. package/dist/chunk-DAFFDO33.js +0 -14
  446. package/dist/chunk-DAFFDO33.js.map +0 -1
  447. package/dist/chunk-GY5WH7TO.js +0 -10
  448. package/dist/chunk-GY5WH7TO.js.map +0 -1
  449. package/dist/chunk-MBR3TYNL.js +0 -14
  450. package/dist/chunk-MBR3TYNL.js.map +0 -1
  451. package/dist/chunk-MFKMHK5W.js +0 -10
  452. package/dist/chunk-MFKMHK5W.js.map +0 -1
  453. package/dist/chunk-MQIZAD4A.js.map +0 -1
  454. package/dist/chunk-NHXX4JI5.js +0 -28
  455. package/dist/chunk-NHXX4JI5.js.map +0 -1
  456. package/dist/chunk-OPCJRCYO.js +0 -19
  457. package/dist/chunk-OPCJRCYO.js.map +0 -1
  458. package/dist/chunk-T5XNMVOI.js +0 -24
  459. package/dist/chunk-T5XNMVOI.js.map +0 -1
  460. package/dist/chunk-TRNBHPNW.js.map +0 -1
  461. package/dist/chunk-VHW5YNIB.js +0 -17
  462. package/dist/chunk-VHW5YNIB.js.map +0 -1
  463. package/dist/chunk-VKH2JLDZ.js +0 -10
  464. package/dist/chunk-VKH2JLDZ.js.map +0 -1
  465. package/dist/chunk-WVPYVDXY.js +0 -15
  466. package/dist/chunk-WVPYVDXY.js.map +0 -1
  467. package/dist/chunk-WY4CAEPG.js +0 -14
  468. package/dist/chunk-WY4CAEPG.js.map +0 -1
  469. package/dist/dialog-header.d.ts +0 -14
  470. package/dist/dialog-header.js +0 -3
  471. package/dist/dialog-header.js.map +0 -1
  472. package/dist/menu-header.d.ts +0 -15
  473. package/dist/menu-header.js +0 -3
  474. package/dist/menu-header.js.map +0 -1
  475. package/dist/menu-item.d.ts +0 -22
  476. package/dist/menu-item.js +0 -3
  477. package/dist/menu-item.js.map +0 -1
  478. package/dist/page-header.d.ts +0 -9
  479. package/dist/page-header.js +0 -3
  480. package/dist/page-header.js.map +0 -1
  481. package/src/app-tab.css +0 -20
  482. package/src/dialog-header.css +0 -9
  483. package/src/empty-state.css +0 -6
  484. package/src/foundation.css +0 -106
  485. package/src/loading-spinner.css +0 -4
  486. package/src/lucide-icon.css +0 -1
  487. package/src/menu-header.css +0 -13
  488. package/src/menu-item.css +0 -12
  489. package/src/page-header.css +0 -3
  490. package/src/resizable-region.css +0 -19
  491. package/src/select.css +0 -18
  492. package/src/state-banner.css +0 -31
  493. package/src/styles.css +0 -18
  494. package/src/tab-bar.css +0 -4
  495. package/src/toolbar-control-group.css +0 -25
  496. package/src/toolbar-text.css +0 -4
  497. package/src/toolbar.css +0 -9
  498. package/src/value-table.css +0 -5
  499. package/src/webawesome.css +0 -178
package/docs/layout.md ADDED
@@ -0,0 +1,150 @@
1
+ # Pane and content layout
2
+
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.
8
+
9
+ ## Anatomy
10
+
11
+ ```tsx
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
+ >
21
+ <section>...</section>
22
+ <section>...</section>
23
+ </Pane>
24
+ ```
25
+
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.
37
+
38
+ `.kui-content` is a vertical stack with a 24px gap between major children.
39
+ Sections may contain adjacent `ListItem` rows without adding another major gap.
40
+ Ordinary surface-like children use `.kui-content-item` and own their complete
41
+ geometry:
42
+
43
+ - 8px inline margin from the pane edge
44
+ - 1px border, transparent by default
45
+ - 8px internal padding and 8px internal gap
46
+ - `calc(1px + remify(11px))`, or 12px, rounded corners
47
+
48
+ Use `.kui-content-item--pill` for the 22px pill radius expressed as
49
+ `calc(1px + remify(21px))`. Consumers can make a content item visible without
50
+ changing its geometry by setting `--kui-content-item-border` and
51
+ `--kui-content-item-background`.
52
+
53
+ Nested rounded edges stay concentric by subtracting their full inset from the
54
+ outer radius. For example, `SegmentedControl` places each item behind a 1px
55
+ control border and 1px control padding, so its default rounded-rectangle radius
56
+ is 10px inside the control's 12px radius.
57
+
58
+ A visible parent surface does not make every child another visible card. The
59
+ composer recipe keeps its form as the single surface, uses `PanelHeader` for
60
+ its task hierarchy, and places field and action control edges on the shared 8px
61
+ inline gutter instead of nesting them inside another padded content item. A
62
+ conditional `StateBanner` remains visibly distinct because it communicates
63
+ semantic status.
64
+
65
+ ## Public roles and tokens
66
+
67
+ | Need | Class | Token / default |
68
+ | ---------------------------------------- | ------------------------- | ------------------------------------- |
69
+ | Unpadded header/content/footer structure | `Pane`, `.kui-pane` | — |
70
+ | Scrolling pane content | `.kui-pane__content` | — |
71
+ | Optional logical-edge separators | `Pane.separators` | `--kui-pane-separator-width: 1px` |
72
+ | Major vertical rhythm | `.kui-content` | `--kui-layout-content-gap: 24px` |
73
+ | Self-contained child geometry | `.kui-content-item` | 8px margin + 1px border + 8px padding |
74
+ | Pill child | `.kui-content-item--pill` | `--kui-layout-pill-radius: 22px` |
75
+ | Related controls | `.kui-control-cluster` | `--kui-layout-control-gap: 8px` |
76
+ | Inline metadata | `.kui-inline-metadata` | `--kui-layout-metadata-gap: 4px` |
77
+ | Explicit scroll owner outside a pane | `.kui-scroll-owner` | `overflow: auto` |
78
+
79
+ The component layer applies the same contract to `Toolbar`, `ListHeader`,
80
+ `ListItem`, `PanelHeader`, `StateBanner`, `ValueTable`,
81
+ `ValueTableRow`, tabs, and form controls. A value-table row separator starts at
82
+ the row's 8px content inset, or at 40px when the row contains its 24px leading
83
+ icon and 8px gap, and always ends 8px from the right edge. Each value-table row
84
+ also keeps 8px of root-scaled block padding independently of its semantic inline
85
+ inset. Most interactive rows and toolbar groups are 44px tall.
86
+ Toolbar groups reserve a real 1px outer border around a 42px inner area, even
87
+ when their border and background are transparent.
88
+ `PanelHeader` is a plain top `Toolbar` used as a panel, dialog, or page heading
89
+ and overrides no Toolbar styles. The leading zone holds the optional icon (a
90
+ normal bordered `ToolbarControlGroup` given a brand fill with a matching border,
91
+ a 22px glyph) and the title as extra-large `ToolbarText`; the app's trailing
92
+ controls go straight into the trailing zone, and the icon group is omitted when
93
+ no icon is passed. The optional subtitle is a separate row below the toolbar, its
94
+ text left-aligned with the title text.
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
+ `--kui-space-s` (12px) and `--kui-space-xl` (32px) exist but are **off the
116
+ canonical rhythm** — reach for them only as a deliberate exception, never as a
117
+ default step. Prefer the five canonical tokens so spacing stays legible and
118
+ consistent across every surface.
119
+
120
+ ## Ownership rules
121
+
122
+ 1. Do not pad a sidebar, main area, dialog, or `.kui-pane` shell. Children own
123
+ their own margin, border, background, padding, and radius.
124
+ 2. Use 24px gaps for major vertical separation and 8px gaps inside an item or
125
+ between toolbar groups. Do not confuse the two relationships.
126
+ 3. Wrap toolbar content in `ToolbarControlGroup`, including dormant text. A
127
+ transparent group still reserves the same 44px geometry as a visible group.
128
+ 4. Keep one scrolling content owner per pane. Toolbar and footer siblings stay
129
+ fixed while the content scrolls.
130
+ 5. A split item keeps dormant and interactive regions separate. For example,
131
+ `ListHeader` renders its title/count-or-badge cluster separately from its optional
132
+ logical-end 44px action. The header fills the available inline width and its
133
+ action glyph defaults to 18px; disclosure mode makes the title cluster itself
134
+ the button and supplies the production `DisclosureArrow` unless `actionIcon`
135
+ replaces it.
136
+ `ListActionRow` uses a noninteractive row root around sibling 44px primary
137
+ and trailing buttons. `ListItem.trailing` remains dormant content.
138
+ In multiline `ListItem` and `ListActionRow` rows, the leading icon stays
139
+ centered on the label's first line rather than the full wrapped label.
140
+ 6. Reading width, column placement, and responsive relocation remain application
141
+ decisions. The shared classes define local geometry, not the whole shell.
142
+ 7. A visible collapsible pane owns its collapse control in that pane's toolbar.
143
+ When the pane is hidden, move the restore control into the adjacent main
144
+ toolbar on the same logical edge: an inline-start pane restores from the
145
+ main toolbar's leading group, and an inline-end pane restores from its
146
+ trailing group. Do not leave an otherwise empty icon-only rail behind.
147
+
148
+ At narrow widths or 200% zoom, relocate or stack panes before shrinking targets.
149
+ The 8/1/8 item contract and 44px controls remain stable, so screenshots and
150
+ focus-order tests exercise the same model at every viewport.
@@ -0,0 +1,56 @@
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
+ { key: "inbox", title: "Inbox", content: <InboxView /> },
25
+ ]);
26
+
27
+ // render inside mount():
28
+ <NavStack id="mail" label="Mail" views={views.value} />;
29
+
30
+ // once, after first render:
31
+ const dispose = wireNavStack(root, {
32
+ onBack: () => {
33
+ views.value = views.value.slice(0, -1);
34
+ },
35
+ });
36
+
37
+ // push / pop by editing the signal:
38
+ views.value = [
39
+ ...views.value,
40
+ { key: id, title: "Message", content: <MessageView id={id} /> },
41
+ ];
42
+ ```
43
+
44
+ `NavStack` renders every entry stacked, the last one active and the rest kept
45
+ mounted (so their DOM state and focus survive) but hidden. Each entry carries a
46
+ `key` (stable identity), `content`, an optional `title`, and optional per-view
47
+ `toolbar` actions. The back control appears automatically once the stack has more
48
+ than one entry; `wireNavStack`'s `onBack` is where the app pops its own signal.
49
+
50
+ ## Transitions
51
+
52
+ `wireNavStack(root, { onBack, duration? })` observes the rendered stack and
53
+ animates each change: a pushed view slides in from the trailing edge; a popped
54
+ view slides back off it over the revealed view. It returns a disposer. The
55
+ animation honors `prefers-reduced-motion` (transitions collapse to instant) and
56
+ `duration: 0` disables it. Applicable at every device size and inside dialogs.
@@ -0,0 +1,167 @@
1
+ # Production composition recipes
2
+
3
+ These seven reference compositions sit between individual primitives and product
4
+ code. Open each stable UX-catalog route to run it, then copy the linked TSX and
5
+ CSS. The examples import public package subpaths, use semantic layout owners,
6
+ and keep state in a per-instance application adapter. They are not new
7
+ monolithic components.
8
+
9
+ Copy the recipe source together with the catalog-independent
10
+ [`mount-recipe.ts`](../ux-demo/recipes/mount-recipe.ts) adapter. It mounts the
11
+ controller at one stable application root, uses `delegateActions()` for recipe
12
+ commands, forwards form and dialog lifecycle events, wires resize commits with
13
+ the public `onCommit` callback, retains every disposer, and returns one
14
+ idempotent disposer:
15
+
16
+ ```ts
17
+ import { createRecipe } from "./navigation-sidebar.js";
18
+ import { mountRecipe } from "./mount-recipe.js";
19
+
20
+ const root = document.querySelector<HTMLElement>("#navigation")!;
21
+ const stopRecipe = mountRecipe(root, createRecipe(announce));
22
+ window.addEventListener("pagehide", stopRecipe, { once: true });
23
+ ```
24
+
25
+ The adapter is delivered as reference source, not a new package runtime export.
26
+ `delegate()` remains a valid alternative when an application needs selector-
27
+ specific dispatch; either way, wire once at a stable root and retain disposal.
28
+
29
+ ## Desktop application shell
30
+
31
+ [Open the recipe](../ux-demo/?component=recipe-app-shell) · [TSX source](../ux-demo/recipes/app-shell.tsx) · [shared CSS](../ux-demo/recipes/recipes.css)
32
+
33
+ Use `Toolbar`, controlled `ResizableRegion` panes, and one
34
+ `.kui-pane__content` scroll owner per pane. The recipe owns the shell topology;
35
+ the app owns routing, responsive pane visibility, sizes, persistence, and data.
36
+ Keep each visible pane's collapse action in its own toolbar. Once hidden, put
37
+ its restore action in the adjacent main toolbar on the same logical edge:
38
+ leading for an inline-start sidebar and trailing for an inline-end inspector.
39
+ Collapse the pane completely rather than preserving an empty icon rail.
40
+ Adapt only public `--kui-layout-*` and component variables.
41
+
42
+ ## Navigation sidebar
43
+
44
+ [Open the recipe](../ux-demo/?component=recipe-navigation-sidebar) · [TSX source](../ux-demo/recipes/navigation-sidebar.tsx)
45
+
46
+ One unpadded `.kui-pane` owns toolbar/content/footer structure. Its
47
+ `.kui-content` uses 24px major gaps; `ListHeader`, `ListItem`, and other
48
+ `.kui-content-item` children own their 8px margin, 1px border, and 8px padding.
49
+ Rows and footer toolbar groups remain 44px tall. The app owns routes,
50
+ permissions, labels, selection, valid section counts and their localized
51
+ `countLabel` phrases, non-count badge content, disclosure state, and revealed
52
+ content. A toggled `ListHeader` supplies the production `DisclosureArrow` when
53
+ no custom `actionIcon` is needed; ordinary navigation rows stay chevron-free.
54
+
55
+ ## Workspace header
56
+
57
+ [Open the recipe](../ux-demo/?component=recipe-workspace-header) · [TSX source](../ux-demo/recipes/workspace-header.tsx)
58
+
59
+ `PanelHeader` owns the page heading while one control cluster holds secondary,
60
+ overflow, and primary actions. The app owns authorization and command policy;
61
+ controls relocate without changing focus order.
62
+
63
+ ## List-detail dialog
64
+
65
+ [Open the recipe](../ux-demo/?component=recipe-list-detail-dialog) · [TSX source](../ux-demo/recipes/list-detail-dialog.tsx)
66
+
67
+ The production Web Awesome dialog owns modal focus and Escape; the thin recipe
68
+ adapter restores the invoking control consistently after the hide event.
69
+ `PanelHeader`, `ListHeader`, `ListItem`, and `ValueTable` own their included
70
+ anatomy. The application owns open state, selection, dismissal policy, and
71
+ record actions. `PanelHeader` places the trailing controls the app passes
72
+ directly into its top toolbar's trailing zone.
73
+ The header sits on the dialog edge while retaining its internal control inset;
74
+ the selected title receives the full content gutter, the metadata table fills
75
+ the available detail width between the usual outer margins, and the action
76
+ cluster uses one outer gutter without a second content-item inset.
77
+ Do not rebuild the dialog or reach into private shadow parts.
78
+
79
+ ## Composer form
80
+
81
+ [Open the recipe](../ux-demo/?component=recipe-composer-form) · [TSX source](../ux-demo/recipes/composer-form.tsx)
82
+
83
+ `PanelHeader` supplies the task title and summary, with their ids referenced
84
+ by the form. Production fields own labels, help, and native focus. The field
85
+ and footer control edges sit directly on the shared 8px inline gutter rather
86
+ than acquiring a second content-item padding inset; major children remain 24px
87
+ apart and related controls use 8px gaps. The app owns
88
+ validation, drafts, permissions, and transport. Persistent error or success
89
+ feedback is the only nested semantic surface and uses `StateBanner`, not a toast.
90
+ Because upgraded Web Awesome fields retain live value properties, controlled
91
+ resets synchronize both those properties and the rendered value attributes;
92
+ the Reset action also announces `Draft reset` through the catalog live region.
93
+
94
+ ## List workspace states
95
+
96
+ [Open the recipe](../ux-demo/?component=recipe-list-workspace-states) · [TSX source](../ux-demo/recipes/list-workspace-states.tsx)
97
+
98
+ The same content region moves deterministically through loading, empty,
99
+ populated, stale/background refresh, and error/retry states. The recipe owns
100
+ feedback placement; the app owns fetching, cache age, retry policy, and domain
101
+ rows.
102
+
103
+ ## Compact toolbar choices and actions
104
+
105
+ [Open the recipe](../ux-demo/?component=recipe-compact-toolbar) · [TSX source](../ux-demo/recipes/compact-toolbar.tsx)
106
+
107
+ Use `ToolbarControlGroup` for related commands, `SegmentedControl` for a few
108
+ 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.
111
+
112
+ ## Navigation stack
113
+
114
+ [Open the recipe](../ux-demo/?component=recipe-navigation-stack) · [TSX source](../ux-demo/recipes/navigation-stack.tsx)
115
+
116
+ Drill from a library list into a detail and back with `NavStack`
117
+ (`@kerfjs/ui/nav-stack`): the app owns the stack as a signal of views and
118
+ pushes/pops it, `NavStack` renders it, and `wireNavStack` slides the content and
119
+ settles the chrome (reduced motion collapses the slide to instant). A live
120
+ `deviceClass()` badge shows the current size/orientation. See the layout guide
121
+ [`app-layouts.md`](app-layouts.md) for choosing among `NavStack`, `SplitView`,
122
+ `Workbench`, and `TabScaffold`.
123
+
124
+ ## Loading inspector
125
+
126
+ [Open the recipe](../ux-demo/?component=recipe-loading-inspector) · [TSX source](../ux-demo/recipes/loading-inspector.tsx)
127
+
128
+ A record inspector whose per-record values load asynchronously. Every
129
+ value-bearing component (`PanelHeader`, `ValueTable`/`ValueTableRow`, `Select`,
130
+ `SegmentedControl`, `ListItem`, `StateBanner`) takes its `placeholder` from one
131
+ loading flag, so the same real chrome renders a faithful loading state and then
132
+ the populated record — no separate skeleton markup. The composition is the point;
133
+ `Skeleton` is the primitive it builds on. The app owns the loading lifecycle and
134
+ which values are still unknown.
135
+
136
+ ## Collapsible sidebar
137
+
138
+ [Open the recipe](../ux-demo/?component=recipe-collapsible-sidebar) · [TSX source](../ux-demo/recipes/collapsible-sidebar.tsx)
139
+
140
+ A mini app frame whose left navigation rail and bottom activity drawer are
141
+ standalone `CollapsiblePanel`s (`@kerfjs/ui/collapsible-panel`) driven by
142
+ `wireSidebar` (`@kerfjs/ui/wire-sidebar`). `CollapsiblePanelToggle` supplies the
143
+ standard per-side glyph: a collapse toggle lives inside each panel and an expand
144
+ toggle lives in the always-visible main header, so a collapsed panel is still
145
+ reachable. `wireSidebar` owns the toggle delegation, moves focus into a panel on
146
+ open and restores it to the trigger on close, and — when a `deviceClass()` reports
147
+ `compact` — switches the open panel to a dismissable **overlay** (backdrop, Escape
148
+ and backdrop-click collapse, and a trapped Tab ring, the ARIA dialog pattern). It
149
+ also persists each panel's collapsed state through a supplied storage hook. The app
150
+ owns each `collapsed` signal, the panel sizes, and the content; the wire owns the
151
+ ephemeral interaction. For a full three-pane shell use `Workbench` instead — see
152
+ [`app-layouts.md`](app-layouts.md). This recipe is covered end-to-end across
153
+ Chromium, Firefox, and WebKit by `tests/browser/collapsible-sidebar-recipe.spec.ts`.
154
+
155
+ ## Rules shared by every recipe
156
+
157
+ - Import `@kerfjs/ui/layout.css`; keep every pane unpadded and use exactly one
158
+ `.kui-pane__content` scroll owner for each real boundary.
159
+ - For direct `wa-*` JSX, import types from `@kerfjs/ui/webawesome`. Import only
160
+ individual Web Awesome registration modules and theme them with
161
+ `@kerfjs/ui/webawesome.css`.
162
+ - Prefer public props and variables at the composition boundary. When a recipe
163
+ needs responsive topology, join only classes listed in the catalog's
164
+ `publicClasses`; do not copy component markup or select descendants by tag,
165
+ id, attribute alone, or an unlisted implementation class.
166
+ - Start from the copyable mount adapter, or reproduce its complete boundary:
167
+ wire stable `data-action` hooks once and retain every disposer.
@@ -0,0 +1,49 @@
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
+ Compose the interactive wiring from the existing helpers — `SplitView` adds no
49
+ 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,50 @@
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. On larger device classes, promote the tab set to a
50
+ `Workbench` rail or a persistent sidebar instead of a bottom bar.
@@ -0,0 +1,102 @@
1
+ # Compile-time contracts
2
+
3
+ Kerf UI rejects invalid integrations at compile time only when TypeScript can
4
+ prove the relationship from one call. The versioned
5
+ [`compile-time-contracts-v1.json`](../ai/compile-time-contracts-v1.json)
6
+ artifact names those guarantees with stable `KUI-T###` ids. Its source fixture
7
+ is compiled twice: against `src/` and against declarations extracted from the
8
+ actual `npm pack` tarball. `npm run check:catalog` also verifies that every
9
+ contract still points to a real catalog entry/import and a symbol in the emitted
10
+ public-signature artifact.
11
+
12
+ ## Strengthened contracts
13
+
14
+ | ID | Compile-time guarantee |
15
+ | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
16
+ | `KUI-T001` | `ListHeader` is a passive heading, a fully named/icon-bearing trailing action, or a controlled disclosure with `action` + `expanded`. Props ignored by the selected mode are rejected. |
17
+ | `KUI-T002` | `Select` has a visible `label` or an `ariaLabel`. |
18
+ | `KUI-T003` | A literal `Select.value` belongs to its literal `choices`. |
19
+ | `KUI-T004` | A literal `SegmentedControl.value` belongs to its literal `choices`. |
20
+ | `KUI-T005` | `expanded`, `expandAction`, and `expandLabel` exist only on `TokenSearchField({ collapsible: true })`. |
21
+ | `KUI-T006` | `TokenSearchEditorAttributes` accepts application `data-*` metadata but rejects component-owned identity, morph, count, and placeholder attributes. |
22
+ | `KUI-T007` | `PanelHeader.summaryId` cannot exist without rendered `summary` content. |
23
+ | `KUI-T008` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code. |
24
+ | `KUI-T009` | A literal `TabScaffold.active` id belongs to its literal `tabs`. |
25
+ | `KUI-T010` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback. |
26
+ | `KUI-T011` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all ToolbarControlGroup presentation axes. |
27
+
28
+ Literal identity inference is intentionally additive for dynamic applications.
29
+ When choices or ids arrive at runtime and the state signal is correctly typed as
30
+ `string`, widen the component explicitly:
31
+
32
+ ```tsx
33
+ <Select<string> value={loadedValue.value} choices={loadedChoices.value} ... />
34
+ <SegmentedControl<string> value={mode.value} choices={loadedModes.value} ... />
35
+ <TabScaffold<string> active={active.value} tabs={loadedTabs.value} ... />
36
+ ```
37
+
38
+ The widening is an honest statement that TypeScript cannot know the runtime
39
+ dataset. Do not cast a known literal typo merely to bypass the relationship.
40
+
41
+ ## Complete public-surface audit
42
+
43
+ The following existing contracts were already exact and remain covered by the
44
+ ordinary source/type gates:
45
+
46
+ - required identity or accessible-name inputs on `AppTab`, `FloatingToolbar`,
47
+ `NavStack`, `SplitView`, `Workbench`, `TabBar`, `ResizableRegion`,
48
+ `SegmentedControl`, `TokenSearchField`, `PanelHeader`, and `ValueTable`;
49
+ - finite variants exported or represented as literal props on `DisclosureArrow`,
50
+ `FloatingToolbar`, `Pane`, `ResizableRegion`, `SegmentedControl`, `StateBanner`,
51
+ `SunkenPanel`, `ToolbarText`, `CollapsiblePanel`, `Catalog`, and the wire helpers;
52
+ - required controlled-state callbacks on `wireResizableRegions`, `wireTabBars`,
53
+ and `wireTabScaffold`;
54
+ - mutually exclusive count/badge metadata on `ListHeader`; and
55
+ - protected application metadata boundaries on `AppTab`, `ListItem`,
56
+ `ListActionRow`, `ListHeader`, `Pane`, `CatalogExample`, and
57
+ `CatalogExampleStack`.
58
+
59
+ The remaining components (`LucideIcon`, `DisclosureArrow`, `Toolbar`,
60
+ `ToolbarControlGroup`, `ListInsetControl`, `ListInsetText`, `LoadingSpinner`,
61
+ `Skeleton`, `EmptyState`, `StateBanner`, `ValueTableRow`, and the
62
+ pure helpers) have independent props with no further conditional invalid state
63
+ that TypeScript can truthfully remove. Optional labels on decorative icons and
64
+ unnamed non-landmark surfaces are intentional variants, not missing contracts.
65
+
66
+ ## Boundaries TypeScript cannot prove
67
+
68
+ Types do not claim to validate:
69
+
70
+ - the contents of `SafeHtml` slots (for example, “only AppTab children” or “no
71
+ interactive descendants”);
72
+ - nonempty arrays, unique ids, numeric min/max ordering, or a controlled value
73
+ loaded from dynamic data;
74
+ - equality between ids rendered in separate calls, elements discovered later in
75
+ the DOM, or an ARIA relationship's live target;
76
+ - that a caller retained and invoked a wiring disposer; or
77
+ - CSS class/token use inside arbitrary strings.
78
+
79
+ Those remain catalog `KUI-C###` diagnostics, runtime filtering/guards, lint or
80
+ browser assertions. Encoding them as permissive-looking TypeScript brands would
81
+ move errors into casts without making the integration safer.
82
+
83
+ ## Migration from the earlier declarations
84
+
85
+ - Add `actionLabel` and `actionIcon` to a non-toggle `ListHeader` action. For a
86
+ disclosure, set literal `toggle: true`, provide `action` and controlled
87
+ `expanded`, and remove trailing-action-only props.
88
+ - Give every `Select` either `label` or `ariaLabel`. Use an explicit `<string>`
89
+ generic only for genuinely dynamic choice data.
90
+ - Put `expanded`/expand-action props behind `collapsible: true`.
91
+ - Replace `wireTokenSearchFields({ keyboard: true })` with either
92
+ `{ keyboard: { onRemoveToken } }` or
93
+ `{ keyboard: { removeAdjacentToken: false } }` when only caret navigation is
94
+ wanted.
95
+ - Remove orphaned `summaryId`, or render the matching `summary`; add
96
+ `wrap: true` anywhere `ToolbarText.maxLines` is used.
97
+ - Replace `Toolbar({ divider: false })` with `dividerSides: ''`; the default
98
+ remains a bottom divider, while canonical combinations such as `tr` and
99
+ `trbl` select more physical edges.
100
+
101
+ JavaScript runtime behavior remains defensive for previously emitted calls, but
102
+ new TypeScript builds report these invalid or ignored combinations.