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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (714) hide show
  1. package/README.md +494 -138
  2. package/ai/application-ui-diagnostic-ids-v1.json +109 -0
  3. package/ai/application-ui-profile-sync.cjs +529 -0
  4. package/ai/application-ui-profile.d.mts +1 -0
  5. package/ai/application-ui-profile.d.ts +102 -0
  6. package/ai/application-ui-profile.defaults.json +96 -0
  7. package/ai/application-ui-profile.mjs +626 -0
  8. package/ai/application-ui-profile.schema.json +130 -0
  9. package/ai/catalog-authoring.json +36 -0
  10. package/ai/catalog-authoring.schema.json +109 -0
  11. package/ai/compile-time-contracts-v1.json +149 -0
  12. package/ai/compile-time-contracts-v1.schema.json +52 -0
  13. package/ai/component-catalog-extension.schema.json +79 -0
  14. package/ai/component-catalog.json +4041 -591
  15. package/ai/component-catalog.schema.json +78 -1
  16. package/ai/component-class-owners.cjs +83 -0
  17. package/ai/component-composition-extension.d.ts +7 -0
  18. package/ai/component-composition-extension.schema.json +7 -0
  19. package/ai/component-composition-rules.json +524 -0
  20. package/ai/component-composition.d.ts +125 -0
  21. package/ai/component-composition.json +7074 -0
  22. package/ai/component-composition.schema.json +375 -0
  23. package/ai/components/README.md +171 -0
  24. package/ai/components/application-tabs.md +65 -0
  25. package/ai/components/badge.md +72 -0
  26. package/ai/components/chip.md +72 -0
  27. package/ai/components/collapsible-panel.md +102 -0
  28. package/ai/components/content-item.md +90 -0
  29. package/ai/components/disclosure-arrow.md +77 -0
  30. package/ai/components/document-baseline.md +75 -0
  31. package/ai/components/empty-state.md +80 -0
  32. package/ai/components/feedback.md +71 -0
  33. package/ai/components/floating-toolbar.md +82 -0
  34. package/ai/components/foundation.md +65 -0
  35. package/ai/components/grid.md +96 -0
  36. package/ai/components/headers.md +63 -0
  37. package/ai/components/layout.md +75 -0
  38. package/ai/components/list-action-row.md +91 -0
  39. package/ai/components/list-header.md +89 -0
  40. package/ai/components/list-inset-control.md +74 -0
  41. package/ai/components/list-inset-text.md +72 -0
  42. package/ai/components/list-item.md +94 -0
  43. package/ai/components/list.md +103 -0
  44. package/ai/components/loading-spinner.md +72 -0
  45. package/ai/components/lucide-icon.md +66 -0
  46. package/ai/components/nav-stack.md +103 -0
  47. package/ai/components/pane.md +100 -0
  48. package/ai/components/popup-menu.md +171 -0
  49. package/ai/components/recipe-app-shell.md +57 -0
  50. package/ai/components/recipe-collapsible-sidebar.md +57 -0
  51. package/ai/components/recipe-compact-toolbar.md +57 -0
  52. package/ai/components/recipe-composer-form.md +57 -0
  53. package/ai/components/recipe-list-detail-dialog.md +57 -0
  54. package/ai/components/recipe-list-workspace-states.md +57 -0
  55. package/ai/components/recipe-loading-inspector.md +57 -0
  56. package/ai/components/recipe-navigation-sidebar.md +57 -0
  57. package/ai/components/recipe-navigation-stack.md +57 -0
  58. package/ai/components/recipe-workspace-header.md +57 -0
  59. package/ai/components/resize.md +106 -0
  60. package/ai/components/row.md +96 -0
  61. package/ai/components/segmented-control.md +82 -0
  62. package/ai/components/select.md +112 -0
  63. package/ai/components/skeleton.md +84 -0
  64. package/ai/components/spacer.md +86 -0
  65. package/ai/components/split-view.md +102 -0
  66. package/ai/components/state-banner.md +94 -0
  67. package/ai/components/sunken-panel.md +79 -0
  68. package/ai/components/surface-scaffold.md +78 -0
  69. package/ai/components/tab-bar.md +112 -0
  70. package/ai/components/tab-scaffold.md +94 -0
  71. package/ai/components/tabs.md +95 -0
  72. package/ai/components/text.md +83 -0
  73. package/ai/components/token-search-field.md +103 -0
  74. package/ai/components/toolbar-control-group.md +102 -0
  75. package/ai/components/toolbar-text.md +78 -0
  76. package/ai/components/toolbar.md +97 -0
  77. package/ai/components/value-table.md +73 -0
  78. package/ai/components/wa-accordion-item.md +55 -0
  79. package/ai/components/wa-accordion.md +65 -0
  80. package/ai/components/wa-animated-image.md +55 -0
  81. package/ai/components/wa-animation.md +55 -0
  82. package/ai/components/wa-avatar.md +55 -0
  83. package/ai/components/wa-badge.md +55 -0
  84. package/ai/components/wa-breadcrumb-item.md +53 -0
  85. package/ai/components/wa-breadcrumb.md +55 -0
  86. package/ai/components/wa-button-group.md +59 -0
  87. package/ai/components/wa-button.md +55 -0
  88. package/ai/components/wa-callout.md +58 -0
  89. package/ai/components/wa-card.md +59 -0
  90. package/ai/components/wa-carousel-item.md +53 -0
  91. package/ai/components/wa-carousel.md +55 -0
  92. package/ai/components/wa-checkbox-group.md +55 -0
  93. package/ai/components/wa-checkbox.md +55 -0
  94. package/ai/components/wa-color-picker.md +55 -0
  95. package/ai/components/wa-comparison.md +55 -0
  96. package/ai/components/wa-copy-button.md +55 -0
  97. package/ai/components/wa-details.md +61 -0
  98. package/ai/components/wa-dialog.md +59 -0
  99. package/ai/components/wa-divider.md +53 -0
  100. package/ai/components/wa-drawer.md +55 -0
  101. package/ai/components/wa-dropdown-item.md +60 -0
  102. package/ai/components/wa-dropdown.md +60 -0
  103. package/ai/components/wa-format-bytes.md +53 -0
  104. package/ai/components/wa-format-date.md +53 -0
  105. package/ai/components/wa-format-number.md +53 -0
  106. package/ai/components/wa-icon.md +58 -0
  107. package/ai/components/wa-include.md +53 -0
  108. package/ai/components/wa-input.md +59 -0
  109. package/ai/components/wa-intersection-observer.md +53 -0
  110. package/ai/components/wa-known-date.md +53 -0
  111. package/ai/components/wa-markdown.md +53 -0
  112. package/ai/components/wa-mutation-observer.md +53 -0
  113. package/ai/components/wa-number-input.md +55 -0
  114. package/ai/components/wa-option.md +59 -0
  115. package/ai/components/wa-otp-input.md +53 -0
  116. package/ai/components/wa-page.md +53 -0
  117. package/ai/components/wa-pagination.md +55 -0
  118. package/ai/components/wa-popover.md +55 -0
  119. package/ai/components/wa-popup.md +53 -0
  120. package/ai/components/wa-progress-bar.md +53 -0
  121. package/ai/components/wa-progress-ring.md +53 -0
  122. package/ai/components/wa-qr-code.md +53 -0
  123. package/ai/components/wa-radio-group.md +59 -0
  124. package/ai/components/wa-radio.md +55 -0
  125. package/ai/components/wa-random-content.md +53 -0
  126. package/ai/components/wa-rating.md +55 -0
  127. package/ai/components/wa-relative-time.md +53 -0
  128. package/ai/components/wa-resize-observer.md +53 -0
  129. package/ai/components/wa-scroller.md +53 -0
  130. package/ai/components/wa-select.md +63 -0
  131. package/ai/components/wa-skeleton.md +53 -0
  132. package/ai/components/wa-slider.md +55 -0
  133. package/ai/components/wa-spinner.md +58 -0
  134. package/ai/components/wa-split-panel.md +58 -0
  135. package/ai/components/wa-switch.md +53 -0
  136. package/ai/components/wa-tab-group.md +69 -0
  137. package/ai/components/wa-tab-panel.md +60 -0
  138. package/ai/components/wa-tab.md +59 -0
  139. package/ai/components/wa-tag.md +59 -0
  140. package/ai/components/wa-textarea.md +53 -0
  141. package/ai/components/wa-time-input.md +55 -0
  142. package/ai/components/wa-toast-item.md +60 -0
  143. package/ai/components/wa-toast.md +60 -0
  144. package/ai/components/wa-tooltip.md +55 -0
  145. package/ai/components/wa-tree-item.md +55 -0
  146. package/ai/components/wa-tree.md +55 -0
  147. package/ai/components/wa-zoomable-frame.md +55 -0
  148. package/ai/components/workbench.md +114 -0
  149. package/ai/public-api-signatures-v1.md +2597 -190
  150. package/ai/skill.md +259 -65
  151. package/ai/webawesome-jsx-signatures-v1.md +9 -2
  152. package/analyzer/cli.mjs +49 -0
  153. package/analyzer/component-ownership.d.mts +56 -0
  154. package/analyzer/component-ownership.mjs +184 -0
  155. package/analyzer/index.d.mts +49 -0
  156. package/analyzer/index.mjs +1458 -0
  157. package/analyzer/loud-pairs.d.mts +49 -0
  158. package/analyzer/loud-pairs.mjs +357 -0
  159. package/analyzer/report.schema.json +66 -0
  160. package/analyzer/selectors.d.mts +21 -0
  161. package/analyzer/selectors.mjs +212 -0
  162. package/dist/app-tab.d.ts +41 -4
  163. package/dist/app-tab.js +3 -1
  164. package/dist/badge.d.ts +52 -0
  165. package/dist/badge.js +3 -0
  166. package/dist/badge.js.map +1 -0
  167. package/dist/browser/app-tab.js +1 -0
  168. package/dist/browser/badge.js +3 -0
  169. package/dist/browser/catalog.js +25 -0
  170. package/dist/browser/chip.js +3 -0
  171. package/dist/browser/collapsible-panel.js +7 -0
  172. package/dist/browser/content-item.js +3 -0
  173. package/dist/browser/floating-toolbar.js +3 -0
  174. package/dist/browser/grid.js +3 -0
  175. package/dist/browser/list-action-row.js +5 -0
  176. package/dist/browser/list-header.js +8 -0
  177. package/dist/browser/list-inset-control.js +3 -0
  178. package/dist/browser/list-inset-text.js +3 -0
  179. package/dist/browser/list-item.js +5 -0
  180. package/dist/browser/list.js +3 -0
  181. package/dist/browser/{panel-header.js → nav-stack.js} +4 -2
  182. package/dist/browser/pane.js +3 -0
  183. package/dist/browser/popup-menu.js +3 -0
  184. package/dist/browser/row.js +3 -0
  185. package/dist/browser/select.js +2 -0
  186. package/dist/browser/skeleton.js +3 -0
  187. package/dist/browser/spacer.js +3 -0
  188. package/dist/browser/split-view.js +10 -0
  189. package/dist/browser/state-banner.js +2 -0
  190. package/dist/browser/sunken-panel.js +3 -0
  191. package/dist/browser/surface-scaffold.js +3 -0
  192. package/dist/browser/tab-scaffold.js +4 -0
  193. package/dist/browser/text.js +3 -0
  194. package/dist/browser/toolbar-text.js +1 -0
  195. package/dist/browser/value-table.js +1 -0
  196. package/dist/browser/workbench.js +10 -0
  197. package/dist/catalog-resources.d.ts +46 -0
  198. package/dist/catalog-resources.js +22 -0
  199. package/dist/catalog-resources.js.map +1 -0
  200. package/dist/catalog.d.ts +90 -0
  201. package/dist/catalog.js +501 -0
  202. package/dist/catalog.js.map +1 -0
  203. package/dist/chip.d.ts +29 -0
  204. package/dist/chip.js +3 -0
  205. package/dist/chip.js.map +1 -0
  206. package/dist/chunk-2G2ZJQYK.js +152 -0
  207. package/dist/chunk-2G2ZJQYK.js.map +1 -0
  208. package/dist/chunk-3SXMEK3B.js +43 -0
  209. package/dist/chunk-3SXMEK3B.js.map +1 -0
  210. package/dist/{chunk-W2FK7ZNY.js → chunk-4R777DUZ.js} +10 -2
  211. package/dist/chunk-4R777DUZ.js.map +1 -0
  212. package/dist/chunk-4SQK27MR.js +202 -0
  213. package/dist/chunk-4SQK27MR.js.map +1 -0
  214. package/dist/chunk-5I7QGQVT.js +24 -0
  215. package/dist/chunk-5I7QGQVT.js.map +1 -0
  216. package/dist/chunk-5PESG2VP.js +115 -0
  217. package/dist/chunk-5PESG2VP.js.map +1 -0
  218. package/dist/chunk-5Q3N6EV7.js +156 -0
  219. package/dist/chunk-5Q3N6EV7.js.map +1 -0
  220. package/dist/chunk-6ZXOQEAN.js +27 -0
  221. package/dist/chunk-6ZXOQEAN.js.map +1 -0
  222. package/dist/chunk-7JUBXOIV.js +103 -0
  223. package/dist/chunk-7JUBXOIV.js.map +1 -0
  224. package/dist/chunk-A3CKOW2R.js +622 -0
  225. package/dist/chunk-A3CKOW2R.js.map +1 -0
  226. package/dist/chunk-AHEG27M7.js +66 -0
  227. package/dist/chunk-AHEG27M7.js.map +1 -0
  228. package/dist/chunk-AUKT3KMG.js +37 -0
  229. package/dist/chunk-AUKT3KMG.js.map +1 -0
  230. package/dist/chunk-AYUHMAI6.js +48 -0
  231. package/dist/chunk-AYUHMAI6.js.map +1 -0
  232. package/dist/chunk-BFPBS5Y4.js +250 -0
  233. package/dist/chunk-BFPBS5Y4.js.map +1 -0
  234. package/dist/{chunk-MQIZAD4A.js → chunk-C4OYOKWX.js} +138 -22
  235. package/dist/chunk-C4OYOKWX.js.map +1 -0
  236. package/dist/chunk-DG6G5EPF.js +35 -0
  237. package/dist/chunk-DG6G5EPF.js.map +1 -0
  238. package/dist/chunk-E36EVCKG.js +34 -0
  239. package/dist/chunk-E36EVCKG.js.map +1 -0
  240. package/dist/chunk-EAC4CDY7.js +57 -0
  241. package/dist/chunk-EAC4CDY7.js.map +1 -0
  242. package/dist/chunk-EZVNC4J6.js +104 -0
  243. package/dist/chunk-EZVNC4J6.js.map +1 -0
  244. package/dist/chunk-F4QMLOXJ.js +135 -0
  245. package/dist/chunk-F4QMLOXJ.js.map +1 -0
  246. package/dist/chunk-GLDWRCAM.js +49 -0
  247. package/dist/chunk-GLDWRCAM.js.map +1 -0
  248. package/dist/chunk-I3Q7KKYM.js +33 -0
  249. package/dist/chunk-I3Q7KKYM.js.map +1 -0
  250. package/dist/chunk-IA7TMD4Y.js +288 -0
  251. package/dist/chunk-IA7TMD4Y.js.map +1 -0
  252. package/dist/chunk-JCV4GXWB.js +249 -0
  253. package/dist/chunk-JCV4GXWB.js.map +1 -0
  254. package/dist/chunk-K47P7QZC.js +17 -0
  255. package/dist/chunk-K47P7QZC.js.map +1 -0
  256. package/dist/chunk-KLJKU6ZL.js +109 -0
  257. package/dist/chunk-KLJKU6ZL.js.map +1 -0
  258. package/dist/{chunk-56RJXPHG.js → chunk-KROD7KZK.js} +7 -4
  259. package/dist/chunk-KROD7KZK.js.map +1 -0
  260. package/dist/chunk-MA73TA6D.js +42 -0
  261. package/dist/chunk-MA73TA6D.js.map +1 -0
  262. package/dist/chunk-MERWRTBG.js +45 -0
  263. package/dist/chunk-MERWRTBG.js.map +1 -0
  264. package/dist/chunk-MEY33VWB.js +44 -0
  265. package/dist/chunk-MEY33VWB.js.map +1 -0
  266. package/dist/chunk-MP2GDBQZ.js +50 -0
  267. package/dist/chunk-MP2GDBQZ.js.map +1 -0
  268. package/dist/chunk-N3GKMPDW.js +51 -0
  269. package/dist/chunk-N3GKMPDW.js.map +1 -0
  270. package/dist/chunk-NAPE7WUS.js +75 -0
  271. package/dist/chunk-NAPE7WUS.js.map +1 -0
  272. package/dist/chunk-NN6GYOA2.js +47 -0
  273. package/dist/chunk-NN6GYOA2.js.map +1 -0
  274. package/dist/chunk-NNMLEDMD.js +399 -0
  275. package/dist/chunk-NNMLEDMD.js.map +1 -0
  276. package/dist/chunk-NYL63PSD.js +95 -0
  277. package/dist/chunk-NYL63PSD.js.map +1 -0
  278. package/dist/chunk-P7D3EMXM.js +52 -0
  279. package/dist/chunk-P7D3EMXM.js.map +1 -0
  280. package/dist/chunk-PSHUSWQH.js +18 -0
  281. package/dist/chunk-PSHUSWQH.js.map +1 -0
  282. package/dist/chunk-Q6YU3BT7.js +138 -0
  283. package/dist/chunk-Q6YU3BT7.js.map +1 -0
  284. package/dist/chunk-RRB26JRJ.js +247 -0
  285. package/dist/chunk-RRB26JRJ.js.map +1 -0
  286. package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
  287. package/dist/chunk-SRSJO5QE.js.map +1 -0
  288. package/dist/chunk-SWVW56CV.js +188 -0
  289. package/dist/chunk-SWVW56CV.js.map +1 -0
  290. package/dist/chunk-TEYARRAK.js +216 -0
  291. package/dist/chunk-TEYARRAK.js.map +1 -0
  292. package/dist/chunk-UCKKT6KO.js +71 -0
  293. package/dist/chunk-UCKKT6KO.js.map +1 -0
  294. package/dist/chunk-UIDKY4UB.js +14 -0
  295. package/dist/chunk-UIDKY4UB.js.map +1 -0
  296. package/dist/chunk-VCBTX62E.js +135 -0
  297. package/dist/chunk-VCBTX62E.js.map +1 -0
  298. package/dist/chunk-VZOFZPSO.js +71 -0
  299. package/dist/chunk-VZOFZPSO.js.map +1 -0
  300. package/dist/{chunk-TRNBHPNW.js → chunk-X5QSICJ3.js} +13 -7
  301. package/dist/chunk-X5QSICJ3.js.map +1 -0
  302. package/dist/chunk-Y74NWGQW.js +418 -0
  303. package/dist/chunk-Y74NWGQW.js.map +1 -0
  304. package/dist/chunk-ZLWQZUGF.js +27 -0
  305. package/dist/chunk-ZLWQZUGF.js.map +1 -0
  306. package/dist/collapsible-panel.d.ts +122 -0
  307. package/dist/collapsible-panel.js +115 -0
  308. package/dist/collapsible-panel.js.map +1 -0
  309. package/dist/content-item.d.ts +46 -0
  310. package/dist/content-item.js +4 -0
  311. package/dist/content-item.js.map +1 -0
  312. package/dist/css-values.d.ts +107 -0
  313. package/dist/css-values.js +3 -0
  314. package/dist/css-values.js.map +1 -0
  315. package/dist/device-class.js +33 -5
  316. package/dist/device-class.js.map +1 -1
  317. package/dist/disclosure-arrow.d.ts +6 -1
  318. package/dist/disclosure-arrow.js +1 -1
  319. package/dist/empty-state.d.ts +5 -2
  320. package/dist/empty-state.js +1 -1
  321. package/dist/flex-alignment-4ms8ZbV8.d.ts +5 -0
  322. package/dist/floating-toolbar.d.ts +51 -0
  323. package/dist/floating-toolbar.js +3 -0
  324. package/dist/floating-toolbar.js.map +1 -0
  325. package/dist/grid.d.ts +42 -0
  326. package/dist/grid.js +5 -0
  327. package/dist/grid.js.map +1 -0
  328. package/dist/help-tags-register.d.ts +13 -0
  329. package/dist/help-tags-register.js +9 -0
  330. package/dist/help-tags-register.js.map +1 -0
  331. package/dist/index.d.ts +35 -14
  332. package/dist/index.js +43 -22
  333. package/dist/list-action-row.d.ts +64 -0
  334. package/dist/list-action-row.js +8 -0
  335. package/dist/list-action-row.js.map +1 -0
  336. package/dist/list-header.d.ts +99 -0
  337. package/dist/list-header.js +10 -0
  338. package/dist/list-header.js.map +1 -0
  339. package/dist/list-inset-control.d.ts +22 -0
  340. package/dist/list-inset-control.js +3 -0
  341. package/dist/list-inset-control.js.map +1 -0
  342. package/dist/list-inset-text.d.ts +30 -0
  343. package/dist/list-inset-text.js +4 -0
  344. package/dist/list-inset-text.js.map +1 -0
  345. package/dist/list-item.d.ts +62 -0
  346. package/dist/list-item.js +8 -0
  347. package/dist/list-item.js.map +1 -0
  348. package/dist/list.d.ts +59 -0
  349. package/dist/list.js +6 -0
  350. package/dist/list.js.map +1 -0
  351. package/dist/loading-spinner.d.ts +3 -1
  352. package/dist/loading-spinner.js +1 -1
  353. package/dist/lucide-icon.d.ts +3 -1
  354. package/dist/lucide-icon.js +1 -1
  355. package/dist/nav-stack.d.ts +60 -10
  356. package/dist/nav-stack.js +6 -1
  357. package/dist/pane.d.ts +102 -0
  358. package/dist/pane.js +4 -0
  359. package/dist/pane.js.map +1 -0
  360. package/dist/panel-toolbar-DqwtBxQL.d.ts +63 -0
  361. package/dist/popup-menu-register.d.ts +9 -0
  362. package/dist/popup-menu-register.js +31 -0
  363. package/dist/popup-menu-register.js.map +1 -0
  364. package/dist/popup-menu.d.ts +99 -0
  365. package/dist/popup-menu.js +3 -0
  366. package/dist/popup-menu.js.map +1 -0
  367. package/dist/resizable-region.d.ts +32 -3
  368. package/dist/resizable-region.js +1 -1
  369. package/dist/row.d.ts +48 -0
  370. package/dist/row.js +6 -0
  371. package/dist/row.js.map +1 -0
  372. package/dist/segmented-control.d.ts +13 -8
  373. package/dist/segmented-control.js +1 -1
  374. package/dist/select-register.d.ts +5 -1
  375. package/dist/select-register.js +243 -2
  376. package/dist/select-register.js.map +1 -1
  377. package/dist/select.d.ts +101 -8
  378. package/dist/select.js +4 -1
  379. package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
  380. package/dist/sides-BPSWde0A.d.ts +4 -0
  381. package/dist/skeleton.d.ts +31 -0
  382. package/dist/skeleton.js +4 -0
  383. package/dist/skeleton.js.map +1 -0
  384. package/dist/spacer.d.ts +18 -0
  385. package/dist/spacer.js +4 -0
  386. package/dist/spacer.js.map +1 -0
  387. package/dist/split-view.d.ts +39 -7
  388. package/dist/split-view.js +120 -10
  389. package/dist/split-view.js.map +1 -1
  390. package/dist/state-banner.d.ts +19 -5
  391. package/dist/state-banner.js +4 -1
  392. package/dist/styles/app-tab.css +104 -0
  393. package/dist/styles/badge.css +118 -0
  394. package/dist/styles/catalog/components/catalog-example-stack.css +9 -0
  395. package/dist/styles/catalog/components/catalog-example.css +164 -0
  396. package/dist/styles/catalog/components/catalog-stage.css +55 -0
  397. package/dist/styles/catalog/components/catalog.css +12 -0
  398. package/dist/styles/catalog.css +5 -0
  399. package/dist/styles/chip.css +118 -0
  400. package/dist/styles/collapsible-panel.css +575 -0
  401. package/dist/styles/content-item.css +30 -0
  402. package/dist/styles/document.css +23 -0
  403. package/dist/styles/floating-toolbar.css +119 -0
  404. package/dist/styles/foundation.css +178 -40
  405. package/dist/styles/grid.css +28 -0
  406. package/dist/styles/layout.css +3 -41
  407. package/dist/styles/list-action-row.css +257 -0
  408. package/dist/styles/list-header.css +228 -0
  409. package/dist/styles/list-inset-control.css +30 -0
  410. package/dist/styles/list-inset-text.css +29 -0
  411. package/dist/styles/list-item.css +245 -0
  412. package/dist/styles/list.css +133 -0
  413. package/dist/styles/nav-stack.css +158 -50
  414. package/dist/styles/pane.css +229 -0
  415. package/dist/styles/popup-menu.css +271 -0
  416. package/dist/styles/resizable-region.css +429 -4
  417. package/dist/styles/row.css +111 -0
  418. package/dist/styles/segmented-control.css +33 -9
  419. package/dist/styles/select.css +262 -9
  420. package/dist/styles/skeleton.css +92 -0
  421. package/dist/styles/spacer.css +11 -0
  422. package/dist/styles/split-view.css +108 -3
  423. package/dist/styles/state-banner.css +94 -19
  424. package/dist/styles/styles.css +14 -4
  425. package/dist/styles/sunken-panel.css +22 -0
  426. package/dist/styles/surface-scaffold.css +56 -0
  427. package/dist/styles/tab-bar.css +132 -9
  428. package/dist/styles/tab-scaffold.css +113 -3
  429. package/dist/styles/text.css +65 -0
  430. package/dist/styles/token-search-field.css +49 -43
  431. package/dist/styles/toolbar-control-group.css +465 -28
  432. package/dist/styles/toolbar-text.css +88 -4
  433. package/dist/styles/toolbar.css +322 -11
  434. package/dist/styles/value-table.css +34 -4
  435. package/dist/styles/webawesome.css +348 -10
  436. package/dist/styles/workbench.css +828 -7
  437. package/dist/sunken-panel.d.ts +22 -0
  438. package/dist/sunken-panel.js +3 -0
  439. package/dist/sunken-panel.js.map +1 -0
  440. package/dist/surface-scaffold.d.ts +30 -0
  441. package/dist/surface-scaffold.js +3 -0
  442. package/dist/surface-scaffold.js.map +1 -0
  443. package/dist/tab-bar.d.ts +31 -6
  444. package/dist/tab-bar.js +1 -1
  445. package/dist/tab-scaffold.d.ts +54 -7
  446. package/dist/tab-scaffold.js +82 -9
  447. package/dist/tab-scaffold.js.map +1 -1
  448. package/dist/text.d.ts +38 -0
  449. package/dist/text.js +3 -0
  450. package/dist/text.js.map +1 -0
  451. package/dist/token-search-field-DvgFLhLL.d.ts +130 -0
  452. package/dist/token-search-field.d.ts +3 -43
  453. package/dist/token-search-field.js +1 -1
  454. package/dist/token-search-model.d.ts +3 -0
  455. package/dist/token-search-model.js +3 -0
  456. package/dist/token-search-model.js.map +1 -0
  457. package/dist/toolbar-control-group.d.ts +65 -7
  458. package/dist/toolbar-control-group.js +1 -1
  459. package/dist/toolbar-text.d.ts +37 -4
  460. package/dist/toolbar-text.js +3 -1
  461. package/dist/toolbar.d.ts +56 -7
  462. package/dist/toolbar.js +1 -1
  463. package/dist/types-BuHELdXR.d.ts +117 -0
  464. package/dist/value-table.d.ts +13 -3
  465. package/dist/value-table.js +3 -1
  466. package/dist/webawesome.d.ts +8 -1
  467. package/dist/wire-catalog.d.ts +82 -0
  468. package/dist/wire-catalog.js +327 -0
  469. package/dist/wire-catalog.js.map +1 -0
  470. package/dist/wire-nav-stack.d.ts +4 -1
  471. package/dist/wire-nav-stack.js +147 -7
  472. package/dist/wire-nav-stack.js.map +1 -1
  473. package/dist/wire-resizable-regions.d.ts +2 -1
  474. package/dist/wire-resizable-regions.js +2 -1
  475. package/dist/wire-scroll-dividers.d.ts +69 -0
  476. package/dist/wire-scroll-dividers.js +3 -0
  477. package/dist/wire-scroll-dividers.js.map +1 -0
  478. package/dist/wire-sidebar.d.ts +63 -0
  479. package/dist/wire-sidebar.js +231 -0
  480. package/dist/wire-sidebar.js.map +1 -0
  481. package/dist/wire-tab-bars.d.ts +19 -2
  482. package/dist/wire-tab-bars.js +1 -1
  483. package/dist/wire-tab-scaffold.js +9 -4
  484. package/dist/wire-tab-scaffold.js.map +1 -1
  485. package/dist/wire-token-search-fields.d.ts +65 -3
  486. package/dist/wire-token-search-fields.js +1 -1
  487. package/dist/wire-workbench.d.ts +109 -0
  488. package/dist/wire-workbench.js +6 -0
  489. package/dist/wire-workbench.js.map +1 -0
  490. package/dist/workbench.d.ts +218 -7
  491. package/dist/workbench.js +13 -15
  492. package/dist/workbench.js.map +1 -1
  493. package/docs/accessibility.md +413 -23
  494. package/docs/app-layouts.md +106 -8
  495. package/docs/catalog.md +440 -0
  496. package/docs/collapsible-panel.md +363 -0
  497. package/docs/component-contract.md +560 -52
  498. package/docs/component-integration.md +75 -0
  499. package/docs/component-selection.md +421 -89
  500. package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
  501. package/docs/design/templates/empty-state/filtered.svg +1 -0
  502. package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
  503. package/docs/design/templates/empty-state/with-action.svg +1 -0
  504. package/docs/design/templates/empty-state-dark.svg +1 -0
  505. package/docs/design/templates/empty-state.svg +1 -0
  506. package/docs/design/templates/list/compact-dark.svg +1 -0
  507. package/docs/design/templates/list/compact.svg +1 -0
  508. package/docs/design/templates/list/stack-dark.svg +1 -0
  509. package/docs/design/templates/list/stack.svg +1 -0
  510. package/docs/design/templates/list-action-row/default-dark.svg +1 -0
  511. package/docs/design/templates/list-action-row/default.svg +1 -0
  512. package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
  513. package/docs/design/templates/list-action-row/selected.svg +1 -0
  514. package/docs/design/templates/list-action-row-dark.svg +1 -0
  515. package/docs/design/templates/list-action-row.svg +1 -0
  516. package/docs/design/templates/list-dark.svg +1 -0
  517. package/docs/design/templates/list-header/action-dark.svg +1 -0
  518. package/docs/design/templates/list-header/action.svg +1 -0
  519. package/docs/design/templates/list-header/count-dark.svg +1 -0
  520. package/docs/design/templates/list-header/count.svg +1 -0
  521. package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
  522. package/docs/design/templates/list-header/inline-action.svg +1 -0
  523. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
  524. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
  525. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
  526. package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
  527. package/docs/design/templates/list-header-dark.svg +1 -0
  528. package/docs/design/templates/list-header.svg +1 -0
  529. package/docs/design/templates/list-item/default-dark.svg +1 -0
  530. package/docs/design/templates/list-item/default.svg +1 -0
  531. package/docs/design/templates/list-item/multiline-dark.svg +1 -0
  532. package/docs/design/templates/list-item/multiline.svg +1 -0
  533. package/docs/design/templates/list-item/selected-dark.svg +1 -0
  534. package/docs/design/templates/list-item/selected.svg +1 -0
  535. package/docs/design/templates/list-item/trailing-dark.svg +1 -0
  536. package/docs/design/templates/list-item/trailing.svg +1 -0
  537. package/docs/design/templates/list-item-dark.svg +1 -0
  538. package/docs/design/templates/list-item.svg +1 -0
  539. package/docs/design/templates/list.svg +1 -0
  540. package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
  541. package/docs/design/templates/segmented-control/equal.svg +1 -0
  542. package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
  543. package/docs/design/templates/segmented-control/pill-small.svg +1 -0
  544. package/docs/design/templates/segmented-control-dark.svg +1 -0
  545. package/docs/design/templates/segmented-control.svg +1 -0
  546. package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
  547. package/docs/design/templates/skeleton/avatar.svg +1 -0
  548. package/docs/design/templates/skeleton/block-dark.svg +1 -0
  549. package/docs/design/templates/skeleton/block.svg +1 -0
  550. package/docs/design/templates/skeleton/lines-dark.svg +1 -0
  551. package/docs/design/templates/skeleton/lines.svg +1 -0
  552. package/docs/design/templates/skeleton-dark.svg +1 -0
  553. package/docs/design/templates/skeleton.svg +1 -0
  554. package/docs/design/templates/state-banner/danger-dark.svg +1 -0
  555. package/docs/design/templates/state-banner/danger.svg +1 -0
  556. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  557. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  558. package/docs/design/templates/state-banner/info-dark.svg +1 -0
  559. package/docs/design/templates/state-banner/info.svg +1 -0
  560. package/docs/design/templates/state-banner/success-dark.svg +1 -0
  561. package/docs/design/templates/state-banner/success.svg +1 -0
  562. package/docs/design/templates/state-banner/warning-dark.svg +1 -0
  563. package/docs/design/templates/state-banner/warning.svg +1 -0
  564. package/docs/design/templates/state-banner-dark.svg +1 -0
  565. package/docs/design/templates/state-banner.svg +1 -0
  566. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
  567. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
  568. package/docs/design/templates/tab-bar-dark.svg +1 -0
  569. package/docs/design/templates/tab-bar.svg +1 -0
  570. package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
  571. package/docs/design/templates/token-search-field/empty.svg +1 -0
  572. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
  573. package/docs/design/templates/token-search-field/tokens.svg +1 -0
  574. package/docs/design/templates/token-search-field-dark.svg +1 -0
  575. package/docs/design/templates/token-search-field.svg +1 -0
  576. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
  577. package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
  578. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
  579. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
  580. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
  581. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
  582. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
  583. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
  584. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
  585. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
  586. package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
  587. package/docs/design/templates/toolbar-control-group.svg +1 -0
  588. package/docs/design/templates/toolbar-dark.svg +1 -0
  589. package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
  590. package/docs/design/templates/toolbar-text/default.svg +1 -0
  591. package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
  592. package/docs/design/templates/toolbar-text/large.svg +1 -0
  593. package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
  594. package/docs/design/templates/toolbar-text/small.svg +1 -0
  595. package/docs/design/templates/toolbar-text-dark.svg +1 -0
  596. package/docs/design/templates/toolbar-text.svg +1 -0
  597. package/docs/design/templates/toolbar.svg +1 -0
  598. package/docs/design/templates/value-table/details-dark.svg +1 -0
  599. package/docs/design/templates/value-table/details.svg +1 -0
  600. package/docs/design/templates/value-table-dark.svg +1 -0
  601. package/docs/design/templates/value-table.svg +1 -0
  602. package/docs/design/templates.md +138 -0
  603. package/docs/design-philosophy.md +48 -20
  604. package/docs/device-class.md +10 -9
  605. package/docs/document-baseline.md +38 -0
  606. package/docs/examples/application-ui-profile.json +60 -0
  607. package/docs/examples/command-palette-adapter.tsx +69 -17
  608. package/docs/examples/component-catalog-extension.json +45 -0
  609. package/docs/examples/component-composition-extension.json +80 -0
  610. package/docs/layout.md +409 -59
  611. package/docs/nav-stack.md +126 -14
  612. package/docs/recipes.md +137 -54
  613. package/docs/split-view.md +69 -6
  614. package/docs/sunken-panel.md +58 -0
  615. package/docs/surface-scaffold.md +25 -0
  616. package/docs/tab-scaffold.md +94 -10
  617. package/docs/type-contracts.md +134 -0
  618. package/docs/ui-analyzer.md +306 -0
  619. package/docs/ui-doctor.md +107 -0
  620. package/docs/ui-evaluator.md +139 -0
  621. package/docs/ux-demo.md +40 -16
  622. package/docs/webawesome-theme.md +175 -16
  623. package/docs/workbench.md +534 -12
  624. package/doctor/cli.mjs +115 -0
  625. package/doctor/config.schema.json +68 -0
  626. package/doctor/eslint-diagnostics.d.mts +24 -0
  627. package/doctor/eslint-diagnostics.mjs +24 -0
  628. package/doctor/index.d.mts +116 -0
  629. package/doctor/index.mjs +1277 -0
  630. package/doctor/report.schema.json +118 -0
  631. package/evaluator/cli.mjs +89 -0
  632. package/evaluator/index.d.mts +90 -0
  633. package/evaluator/index.mjs +1203 -0
  634. package/evaluator/report.schema.json +152 -0
  635. package/llms.txt +423 -42
  636. package/package.json +241 -37
  637. package/traversal-exclusions.mjs +27 -0
  638. package/ux-demo/recipes/app-shell.tsx +205 -17
  639. package/ux-demo/recipes/collapsible-sidebar.tsx +260 -0
  640. package/ux-demo/recipes/compact-toolbar.tsx +135 -14
  641. package/ux-demo/recipes/composer-form.tsx +133 -21
  642. package/ux-demo/recipes/list-detail-dialog.tsx +282 -0
  643. package/ux-demo/recipes/list-workspace-states.tsx +183 -11
  644. package/ux-demo/recipes/loaders.ts +15 -5
  645. package/ux-demo/recipes/loading-inspector.tsx +204 -0
  646. package/ux-demo/recipes/mount-recipe.ts +28 -10
  647. package/ux-demo/recipes/navigation-sidebar.tsx +139 -13
  648. package/ux-demo/recipes/navigation-stack.tsx +79 -24
  649. package/ux-demo/recipes/types.ts +23 -2
  650. package/ux-demo/recipes/workspace-header.tsx +86 -5
  651. package/dist/browser/menu-action-row.js +0 -3
  652. package/dist/browser/menu-header.js +0 -5
  653. package/dist/browser/menu-item.js +0 -3
  654. package/dist/chunk-24Z2XE6C.js +0 -10
  655. package/dist/chunk-24Z2XE6C.js.map +0 -1
  656. package/dist/chunk-2RJBFNB6.js +0 -172
  657. package/dist/chunk-2RJBFNB6.js.map +0 -1
  658. package/dist/chunk-55DATV3A.js +0 -10
  659. package/dist/chunk-55DATV3A.js.map +0 -1
  660. package/dist/chunk-56RJXPHG.js.map +0 -1
  661. package/dist/chunk-7BR3DO2A.js.map +0 -1
  662. package/dist/chunk-7LDCTU7D.js +0 -134
  663. package/dist/chunk-7LDCTU7D.js.map +0 -1
  664. package/dist/chunk-AQNKJRFH.js +0 -79
  665. package/dist/chunk-AQNKJRFH.js.map +0 -1
  666. package/dist/chunk-CTQKI2XL.js +0 -28
  667. package/dist/chunk-CTQKI2XL.js.map +0 -1
  668. package/dist/chunk-DAFFDO33.js +0 -14
  669. package/dist/chunk-DAFFDO33.js.map +0 -1
  670. package/dist/chunk-EGHL5S4P.js +0 -33
  671. package/dist/chunk-EGHL5S4P.js.map +0 -1
  672. package/dist/chunk-KZKSBUKC.js +0 -19
  673. package/dist/chunk-KZKSBUKC.js.map +0 -1
  674. package/dist/chunk-M5JRI6MN.js +0 -27
  675. package/dist/chunk-M5JRI6MN.js.map +0 -1
  676. package/dist/chunk-MBR3TYNL.js +0 -14
  677. package/dist/chunk-MBR3TYNL.js.map +0 -1
  678. package/dist/chunk-MFKMHK5W.js +0 -10
  679. package/dist/chunk-MFKMHK5W.js.map +0 -1
  680. package/dist/chunk-MQIZAD4A.js.map +0 -1
  681. package/dist/chunk-NPPD2A6O.js +0 -40
  682. package/dist/chunk-NPPD2A6O.js.map +0 -1
  683. package/dist/chunk-R6JHHBKQ.js +0 -19
  684. package/dist/chunk-R6JHHBKQ.js.map +0 -1
  685. package/dist/chunk-RBTVBGRD.js +0 -23
  686. package/dist/chunk-RBTVBGRD.js.map +0 -1
  687. package/dist/chunk-S3TPM2LP.js +0 -23
  688. package/dist/chunk-S3TPM2LP.js.map +0 -1
  689. package/dist/chunk-TRNBHPNW.js.map +0 -1
  690. package/dist/chunk-VHW5YNIB.js +0 -17
  691. package/dist/chunk-VHW5YNIB.js.map +0 -1
  692. package/dist/chunk-W2FK7ZNY.js.map +0 -1
  693. package/dist/chunk-WVPYVDXY.js +0 -15
  694. package/dist/chunk-WVPYVDXY.js.map +0 -1
  695. package/dist/chunk-XZXRLY5Q.js +0 -33
  696. package/dist/chunk-XZXRLY5Q.js.map +0 -1
  697. package/dist/menu-action-row.d.ts +0 -50
  698. package/dist/menu-action-row.js +0 -4
  699. package/dist/menu-action-row.js.map +0 -1
  700. package/dist/menu-header.d.ts +0 -41
  701. package/dist/menu-header.js +0 -6
  702. package/dist/menu-header.js.map +0 -1
  703. package/dist/menu-item.d.ts +0 -31
  704. package/dist/menu-item.js +0 -4
  705. package/dist/menu-item.js.map +0 -1
  706. package/dist/panel-header.d.ts +0 -26
  707. package/dist/panel-header.js +0 -6
  708. package/dist/panel-header.js.map +0 -1
  709. package/dist/styles/menu-action-row.css +0 -160
  710. package/dist/styles/menu-header.css +0 -160
  711. package/dist/styles/menu-item.css +0 -123
  712. package/dist/styles/panel-header.css +0 -59
  713. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
  714. package/ux-demo/recipes/recipes.css +0 -355
@@ -10,20 +10,101 @@ changes. The layouts:
10
10
  - [`SplitView`](split-view.md) — list-detail (two panes, collapsing to a stack).
11
11
  - [`Workbench`](workbench.md) — the Xcode-like collapsible rails + drawer.
12
12
  - [`TabScaffold`](tab-scaffold.md) — the iOS bottom tab bar (each tab a stack).
13
+ - [`CollapsiblePanel` + `wireSidebar`](collapsible-panel.md) — a standalone
14
+ collapsible rail or bottom drawer (with `CollapsiblePanelToggle`), outside a full
15
+ shell: the standard collapse animation, icon convention, and `wireSidebar`
16
+ semantics (focus, compact overlay, persistence).
13
17
 
14
18
  Derive responsiveness from `deviceClass()`: `compact` (a handset or portrait
15
19
  tablet) means "one pane at a time"; `atLeast('tablet')` / `atLeast('desktop')`
16
20
  gate the roomier presentations.
17
21
 
22
+ Full-height shells retain `height: 100%` so they remain embeddable. Give a
23
+ top-level shell a definite height chain by importing `@kerfjs/ui/document.css`
24
+ and applying `.kui-app-root` to the direct mount container; see the
25
+ [document baseline](document-baseline.md). Do not replace an embeddable shell's
26
+ height with `100dvh`.
27
+
28
+ ## Safe areas
29
+
30
+ On a device with unsafe areas (a notch, rounded corners, a home indicator) and a
31
+ page that opts into them with `<meta name="viewport" content="…, viewport-fit=cover">`,
32
+ the layouts handle the insets for you. There is nothing to configure:
33
+
34
+ - **Surfaces paint edge to edge.** Pane and panel backgrounds, separators, and
35
+ dividers run through the unsafe area. A collapsible rail or drawer grows by
36
+ the inset it reaches, so its content keeps its configured width or height.
37
+ - **Content is padded on the sides it touches.** Inline insets pad the sides a
38
+ region actually reaches. Block insets become padding _inside_ the scroll
39
+ container (with matching `scroll-padding`), so content scrolls under the
40
+ status bar or home indicator but its first and last items can always be
41
+ scrolled clear.
42
+ - **Interior edges get nothing.** A `Workbench` center next to an expanded rail,
43
+ a `SplitView` detail beside its list, or a `NavStack` view under its chrome is
44
+ not inset on that edge. Collapse the rail and the center picks up the edge.
45
+ - **No double inset.** Whichever region applies an inset clears it for its
46
+ descendants. A layout region whose only child is a `Pane` or another layout
47
+ lets that child own the insets. That child fills the region — a `Workbench`
48
+ area or panel, a `NavStack` view, a `TabScaffold` scene — so a sole `Pane`'s
49
+ header stays pinned while its own content scrolls. A `Pane` header or footer whose only child is
50
+ a `Toolbar` hands the inline insets to the toolbar, so the toolbar's dividers
51
+ still reach the edge.
52
+
53
+ A plain `Pane` follows the same rules. It assumes it may touch every screen edge
54
+ unless its layout says otherwise. When your own markup puts panes side by side,
55
+ say which sides each pane reaches with `safeAreaEdges`:
56
+
57
+ ```tsx
58
+ <div class="app-columns">
59
+ <Pane label="Library" safeAreaEdges={['block-start', 'block-end', 'inline-start']}>…</Pane>
60
+ <Pane label="Reader" safeAreaEdges={['block-start', 'block-end', 'inline-end']}>…</Pane>
61
+ </div>
62
+ ```
63
+
64
+ An app bar or bottom bar that sits directly at a screen edge, outside any
65
+ `Pane` header or footer, claims its edges the same way. A `Toolbar` only pads
66
+ the inline edges an owner hands it until you list the sides it reaches:
67
+
68
+ ```tsx
69
+ <List fill>
70
+ <Toolbar
71
+ label="App bar"
72
+ safeAreaEdges={['block-start', 'inline-start', 'inline-end']}
73
+ leading={<ToolbarText text="Atlas" />}
74
+ />
75
+ <Row gap="none" flex>…panes that list block-end and their outer side…</Row>
76
+ </List>
77
+ ```
78
+
79
+ Each claimed side pads by the edge context, or the full device inset when
80
+ nothing routes that edge, while the bar's surface and divider still reach the
81
+ screen edge. A claimed toolbar inside a `Pane` header or footer adds nothing
82
+ extra, because the pane already owns those edges.
83
+
84
+ Pass `safeAreaEdges={[]}` for a pane that never sits at a screen edge. In an
85
+ arrangement you own, give each `Pane` (and each edge-claiming `Toolbar`)
86
+ `safeAreaEdges` listing only the sides its region reaches. The toolbar
87
+ configurations layouts accept (a Workbench `mainToolbar` / `mainBottomToolbar`,
88
+ and a Workbench panel's or `CollapsiblePanel`'s `toolbar`) forward
89
+ `safeAreaEdges` to their `Toolbar` the same way.
90
+ A `CollapsiblePanel` clears the edge it covers for its direct flex siblings
91
+ automatically; a panel wrapped in your own grid cell cannot see its siblings,
92
+ so pass the neighboring panes' `safeAreaEdges` yourself.
93
+
94
+ The device insets come from `--kui-safe-area-block-start`, `-block-end`,
95
+ `-inline-start`, and `-inline-end`, which default to `env(safe-area-inset-*)`.
96
+ Override them on `:root` to reserve room for app-owned chrome or to simulate a
97
+ device in tests.
98
+
18
99
  ## Decision matrix
19
100
 
20
- | Situation | Layout | Device threshold |
21
- | --- | --- | --- |
22
- | Simple app, a few flat sections | `NavStack` with one entry (single pane); add `TabScaffold` for 2–5 co-equal sections on handset | `TabScaffold` on `compact`; promote its tabs to a `Workbench` rail / sidebar `atLeast('desktop')` |
23
- | Drill-down browsing (list → item → sub-item) | `NavStack`; upgrade to `SplitView` once list + detail fit together | `SplitView` two-pane `atLeast('tablet')` landscape / non-`compact`; `NavStack` form on `compact` |
24
- | Two related panes, selecting on the left updates the right | `SplitView` | two panes when not `compact`; collapses to `NavStack` (list → detail) on `compact` |
25
- | Complex tool / editor with peripheral panels (navigator, inspector, console) | `Workbench` | full three-panel `atLeast('desktop')`; on smaller classes present the rails via `NavStack` / overlay drawers, not a shrunken shell |
26
- | Mobile app with 2–5 top-level destinations, each its own drill-down | `TabScaffold`, each tab a `NavStack` | bottom bar on `compact`; promote to a rail / sidebar `atLeast('desktop')` |
101
+ | Situation | Layout | Device threshold |
102
+ | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
103
+ | Simple app, a few flat sections | `NavStack` with one entry (single pane); add `TabScaffold` for 2–5 co-equal sections on handset | `TabScaffold` on `compact`; promote its tabs to a `Workbench` rail / sidebar `atLeast('desktop')` |
104
+ | Drill-down browsing (list → item → sub-item) | `NavStack`; upgrade to `SplitView` once list + detail fit together | `SplitView` two-pane `atLeast('tablet')` landscape / non-`compact`; `NavStack` form on `compact` |
105
+ | Two related panes, selecting on the left updates the right | `SplitView` | two panes when not `compact`; collapses to `NavStack` (list → detail) on `compact` |
106
+ | Complex tool / editor with peripheral panels (navigator, inspector, console) | `Workbench` | full three-panel `atLeast('desktop')`; on smaller classes present the rails via `NavStack` / overlay drawers, not a shrunken shell |
107
+ | Mobile app with 2–5 top-level destinations, each its own drill-down | `TabScaffold`, each tab a `NavStack` | bottom bar on `compact`; promote to a rail / sidebar `atLeast('desktop')` |
27
108
 
28
109
  ### Worked examples
29
110
 
@@ -36,11 +117,28 @@ gate the roomier presentations.
36
117
  selection).
37
118
  - **IDE (complex tool):** `Workbench` with a left navigator rail, a right
38
119
  inspector rail, and a bottom console drawer, each `collapsed` bound to a
39
- signal. Only offer this `atLeast('desktop')`.
120
+ signal. Only offer this `atLeast('desktop')`. To let people size the
121
+ navigator or console, opt those panels in with `resizable` and call
122
+ `wireWorkbench` with their size signals and `deviceClass`; resizing is
123
+ suspended on `compact`, where the rails become overlay drawers — or give each
124
+ rail `responsiveOverlayAt: "narrow"` and let the Workbench's own width decide
125
+ when it overlays, with no device check; pass each rail's `collapsed` signal
126
+ to `wireWorkbench` so the overlay starts collapsed, takes focus and keeps Tab
127
+ inside it while open, and closes on Escape or an outside press. Inline
128
+ rails, fixed or resizable, leave the editor its `mainMinSize` (320px by
129
+ default) and shrink in proportion when the window narrows; the bottom drawer
130
+ leaves it `mainMinHeight` (120px by default) and shrinks when the window gets
131
+ shorter.
40
132
  - **Social app (tabbed):** `TabScaffold` with Home / Search / Profile tabs, each
41
133
  `content` a `NavStack`. On a tablet/desktop, render the same sections as a
42
134
  `Workbench` left rail instead of a bottom bar.
43
135
 
136
+ Whatever the layout, call `wireScrollDividers(appRoot)` from
137
+ `@kerfjs/ui/wire-scroll-dividers` once: every `Pane` a layout renders, every
138
+ `NavStack`'s top chrome and bottom toolbar, and every `TabScaffold` bar then
139
+ draw the line between the pinned chrome and the scrolling content only while
140
+ content is scrolled beneath them, and toolbars need no divider of their own. See [Scroll dividers](layout.md#scroll-dividers).
141
+
44
142
  ## Dialogs
45
143
 
46
144
  Pick the dialog's inner layout by the same complexity axis, then apply the device
@@ -0,0 +1,440 @@
1
+ # Catalog — a reusable component-gallery shell
2
+
3
+ `@kerfjs/ui/catalog` is an opt-in, whole-screen shell for building a **component
4
+ catalog** — the collapsible category sidebar + titled preview stage + resources
5
+ footer that the kerf UI catalog itself uses. Point it at your own components and
6
+ you get the same shell without rebuilding it. Like the app layouts, it is a
7
+ subpath-only, tree-shakeable module that adds nothing to the main barrel.
8
+
9
+ The shell is a [`Workbench`](workbench.md) built entirely from `@kerfjs/ui`
10
+ components: the sidebar is its left rail and the active entry its work area. So
11
+ the sidebar behaves like any Workbench rail — its standard toggle moves into the
12
+ entry toolbar while it is collapsed, and on a small screen (a Workbench narrower
13
+ than 704px) it overlays the stage, one tap from the entry toolbar, filling the
14
+ width less a 44px strip that closes it, instead of squeezing the stage. Pass the
15
+ app's `collapsed` signal to `wireCatalog` so that overlay closes on Escape, on a
16
+ press outside, and when an entry is chosen from it.
17
+
18
+ ```bash
19
+ npm install @kerfjs/ui # kerfjs is a peer; @kerfjs/ui/select/register is needed only if entries use `related`
20
+ ```
21
+
22
+ - `Catalog(props)` returns the shell as `SafeHtml` (a `<main class="kui-catalog">`).
23
+ It is **controlled and stateless**: your app owns the `active`, `collapsed`, and
24
+ `theme` signals and computes the preview `content` from `active` in its own
25
+ `mount()` render.
26
+ - `wireCatalog(root, options)` wires the interactions (sidebar selection, the
27
+ related-entry popup menu, and the collapse/theme toggles) with one delegated
28
+ listener set and returns a disposer; it can also mirror the active id into the
29
+ URL and reveal the active sidebar row after a controlled render.
30
+
31
+ ## What you supply
32
+
33
+ - **`sections`** — category-grouped entries: `{ category, entries: [{ id, name,
34
+ description?, tags?, resources?, related? }] }`. Each entry becomes a sidebar
35
+ `ListItem` under a `ListHeader` for its category. Short `tags` render through
36
+ the row's quiet status treatment for decision metadata such as `Discouraged`.
37
+ - **`content`** — the rendered preview for the active entry. Keep a map of `id →
38
+ () => SafeHtml` in your app and call `renderers[active]()` in your render.
39
+ - **`brand`** — `{ title, subtitle?, logoUrl? }` for the sidebar header.
40
+ - **`secondarySections`** — an optional secondary "ecosystem" group shown below the
41
+ primary sections with a quieter treatment: `{ label, sections, collapsible?,
42
+ expanded? }`. When `collapsible`, the label is a disclosure toggle controlling
43
+ `expanded` (the app owns it; wire it with `wireCatalog`'s `onToggleSecondary`).
44
+ - Optional slots: `headerActions` (extra header controls), `sidebarFooter` (extra
45
+ sidebar content), and `status` (a footer status line).
46
+
47
+ Per-entry `resources` render as "open in new tab" links in the footer, and
48
+ `related` renders a "Related entries" `PopupMenu` in a `single`
49
+ `ToolbarControlGroup` (grouped by each entry's `group`), so register its elements
50
+ with `@kerfjs/ui/popup-menu/register` when you use it.
51
+
52
+ ## Catalog demo authoring contract
53
+
54
+ This section is the single authoritative contract for tools and people that
55
+ author Catalog previews. The machine-readable discovery entry is
56
+ [`catalog-authoring.json`](../ai/catalog-authoring.json); exact props remain in
57
+ [`public-api-signatures-v1.md`](../ai/public-api-signatures-v1.md#kerfjsuicatalog).
58
+ The component catalog deliberately does not duplicate these rules: it describes
59
+ which component to choose, while this contract describes how to present the
60
+ chosen component.
61
+
62
+ ### Choose the demo mode
63
+
64
+ Classify every entry before rendering it:
65
+
66
+ | Entry kind | Preview purpose | Geometry overlay |
67
+ | --------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
68
+ | **Component** | Show one public component, its meaningful variants, and adverse states. | `true`; the overlay inspects each selected specimen. |
69
+ | **Composition** | Show components commonly used together in one focused container or surface. | `false`; child geometry remains unmarked so the composition can be read as a whole. |
70
+ | **Recipe** | Teach the recommended production approach to a specific task, including ownership and application glue. | `false`; child geometry remains unmarked so the complete approach can be read. |
71
+
72
+ These three kinds are exhaustive. Theme/foundation showcases use a functional
73
+ category, third-party specimens use source metadata, and state galleries retain
74
+ the kind of the thing they demonstrate; none needs another demo kind.
75
+
76
+ Organize the catalog by functional group, with groups in descending product
77
+ importance. Within every group, list all component demos before all composition
78
+ demos. Order each kind by descending importance where a meaningful distinction
79
+ exists, then alphabetically by display name to break ties. Put recipes after all
80
+ component/composition groups, ordered by the same importance-then-alphabetical
81
+ rule. Treat source order as the authored importance order when no explicit rank
82
+ field exists. Show a `Composition` tag rather than repeating the word in a
83
+ composition's display name; the generic `Catalog` accepts that through the
84
+ entry's `tags` array.
85
+
86
+ Keep `geometryOverlay` present and compute it from the active entry. Do not make
87
+ it a permanent catalog-wide `true`, and do not give individual specimens their
88
+ own overlay implementation.
89
+
90
+ ### Required structure
91
+
92
+ - `Catalog` is the one shell. The app owns active-entry state and passes one
93
+ active preview through `content`.
94
+ - `CatalogExampleStack` is the group for one preview's rows. Put route/test
95
+ metadata such as `data-demo` on its rendered root through `rootAttributes`.
96
+ Pass `label` when the stack needs an accessible name: the rendered `section`
97
+ is then exposed as a named region. An unlabeled stack remains an ordinary
98
+ grouping rather than adding an unnecessary landmark.
99
+ - `CatalogExample` is one row: optional generated `ListHeader` label, optional
100
+ generated note, then one specimen or one intentionally coupled specimen
101
+ cluster. Use one row per variant/state; do not hand-author the helper's private
102
+ classes.
103
+ - Focused demos do not import local stylesheets, use inline `style`, or invent
104
+ styling-only `demo-*` classes. Prefer a public Kerf component, then a Web
105
+ Awesome surface when Kerf has no equivalent, then ordinary semantic HTML.
106
+ Use `CatalogExample.viewport` for bounded specimen width, height, frame,
107
+ surface, overflow, responsive visibility, and public custom-property values;
108
+ keep those constraints catalog-owned instead of recreating a demo stylesheet.
109
+ Component heights run `short` (220px) through `tall` (360px); frame an app
110
+ shell, whole-screen layout, or other application-sized recipe with
111
+ `height: "app"` (592px), and give its root `fill` rather than a frame `Pane`.
112
+ An app frame stands in for the screen: it is the containing block for
113
+ screen-fixed chrome, so a compact `CollapsiblePanel` overlay, its backdrop,
114
+ and restore controls dock to the frame's edges rather than the page's.
115
+ - The specimen is an immediate child of `CatalogExample`. A focused component
116
+ row should place the component root there, without a decorative card or
117
+ spacing wrapper. A composition row may place the composition root there.
118
+
119
+ Use `align="glyph"` for a bare glyph/text specimen, `align="inline-control"`
120
+ for a control whose own inline padding contributes about 8px, and `align="none"`
121
+ (the default) for a content item or composition that owns its geometry.
122
+
123
+ ```tsx
124
+ import { CatalogExample, CatalogExampleStack } from "@kerfjs/ui/catalog";
125
+
126
+ const buttonPreview = (
127
+ <CatalogExampleStack
128
+ label="Button variants"
129
+ rootAttributes={{ "data-demo": "button" }}
130
+ >
131
+ <CatalogExample label="Icon" note="A bare glyph." align="glyph">
132
+ <LucideIcon icon={Plus} name="plus" />
133
+ </CatalogExample>
134
+ <CatalogExample label="Control" align="inline-control">
135
+ <SegmentedControl id="view" label="View" value="list" choices={choices} />
136
+ </CatalogExample>
137
+ <CatalogExample
138
+ label="Authoring note"
139
+ note="Explanatory chrome is not a specimen."
140
+ rootAttributes={{ "data-catalog-geometry-overlay-skip": "" }}
141
+ >
142
+ <p>Use the public helper contract.</p>
143
+ </CatalogExample>
144
+ </CatalogExampleStack>
145
+ );
146
+ ```
147
+
148
+ The overlay selects every immediate child of a `CatalogExample`; the helper
149
+ marks its generated label and note so they are excluded automatically. It does
150
+ not recursively promote a nested child to be the specimen. Outside an example
151
+ row, it selects only top-level
152
+ `[data-component]` roots in the canvas and ignores nested component descendants.
153
+ These rules keep a row's label/group scaffolding out of the measurement and make
154
+ the authored nesting determine exactly what is inspected.
155
+
156
+ ### Metadata ownership
157
+
158
+ Use `rootAttributes` on either helper for authoring metadata such as `data-demo`
159
+ or `data-catalog-geometry-overlay-skip`; the metadata lands on that helper's
160
+ rendered root. The slot accepts only `data-*` strings. Structural
161
+ `data-catalog-example`, `data-catalog-example-stack`, and `data-align` semantics
162
+ remain helper-owned and are rejected case-insensitively at runtime, including
163
+ from structurally widened or JavaScript objects. Do not copy the helpers'
164
+ private `kui-catalog-*` classes into preview markup.
165
+
166
+ The app owns entry ids, `kind`, routing, sources, relationships, and test hooks.
167
+ `Catalog.stageRootAttributes` places app-owned `data-*` state on the
168
+ catalog-owned preview stage without an extra wrapper; `data-catalog-stage`
169
+ remains protected.
170
+ The helpers own their structural markers, label/note anatomy, alignment marker,
171
+ and private classes. Component metadata such as margin/border/padding ownership
172
+ lives in `component-catalog.json`; do not infer or overwrite it from overlay
173
+ pixels.
174
+
175
+ ### Geometry overlay and legend
176
+
177
+ Pass the conditional `geometryOverlay` boolean to `Catalog`, then call
178
+ `wireCatalogGeometryOverlay(root)` once after the first render and retain its
179
+ disposer alongside `wireCatalog`'s.
180
+
181
+ | Overlay mark | Meaning | It is not |
182
+ | ----------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
183
+ | Orange translucent band | A positive computed margin on the selected specimen, after subtracting the helper's alignment inset. | Padding, gap, or empty content. Zero and negative margins are not drawn. |
184
+ | Accent solid edge | The selected specimen's computed border widths and radius, except `none`/`hidden` styles. | A focus ring, outline, child border, or metadata ownership claim. |
185
+ | Quiet dashed outline | The border-box bound of a transparent selected specimen with no visible border. | A real CSS border, padding edge, or proof that the specimen owns size. |
186
+
187
+ The overlay recomputes from live rendered styles when specimens resize, preview
188
+ markup changes, theme/root attributes change, or loaded/inline stylesheets
189
+ change. Component CSS is therefore the source of truth; do not duplicate margin
190
+ or border values in demo data. The overlay does not visualize padding, gaps,
191
+ negative/zero margins, scroll overflow, hit targets, or nested descendants.
192
+ Inspect computed styles and the machine-readable `geometry` metadata for those
193
+ facts. Opaque borderless specimens do not receive the dashed bound.
194
+
195
+ ```tsx
196
+ <Catalog
197
+ {...props}
198
+ geometryOverlay={activeEntry.kind === "component"}
199
+ content={renderers[active.value]()}
200
+ />
201
+ ```
202
+
203
+ Put `rootAttributes={{ "data-catalog-geometry-overlay-skip": "" }}` on a
204
+ `CatalogExample` or `CatalogExampleStack` only when that whole subtree is
205
+ explanatory chrome rather than a specimen. The marker excludes the marked root
206
+ and every descendant from selection; it does not merely hide one band. It is
207
+ normally unnecessary in a composition because the active entry already sets the
208
+ global overlay to `false`.
209
+
210
+ Use the overlay together with machine-readable geometry ownership metadata; the
211
+ overlay verifies what is rendered, while metadata tells people and AI tools
212
+ whether the component, its parent, or its children are responsible for margin,
213
+ border, and padding. Catalogs for downstream components should conform to the
214
+ [`component-catalog-extension.schema.json`](../ai/component-catalog-extension.schema.json)
215
+ contract and can start from the checked
216
+ [`component-catalog-extension.json`](./examples/component-catalog-extension.json)
217
+ example; provide those entries beside Kerf's shipped catalog to AI tools.
218
+
219
+ ### Automated conformance and reviewed exceptions
220
+
221
+ Run `npm run check:demo-conformance` after changing a first-party demo, its
222
+ catalog kind, or the shell's overlay logic. The TypeScript-AST gate verifies
223
+ facts that source can prove without guessing at rendered intent:
224
+
225
+ - focused component routes import and use `CatalogExampleStack` and
226
+ `CatalogExample` from the public package;
227
+ - focused route metadata uses the helpers' `rootAttributes` slot, example rows
228
+ are not empty, and composition routes do not add redundant skip markers;
229
+ - every `@kerfjs/ui` import is a published package export and relative imports
230
+ do not reach into `ui/src`;
231
+ - focused demos have no relative stylesheet imports, inline style attributes,
232
+ or styling-only `demo-*`, `wa-demo-*`, and token-search class names;
233
+ - demo JSX does not copy private `kui-catalog-*` structural classes;
234
+ - recipe sources (checked by `npm run check:recipes` with the same shared
235
+ analyzer) import no stylesheet other than public `@kerfjs/ui` CSS, set no
236
+ inline style, and name only published layout and Web Awesome classes; each
237
+ recipe declares its catalog frame through an exported `presentation`; and
238
+ - the shell derives both geometry-overlay enablement and documented demo mode
239
+ from the active entry's source and kind.
240
+
241
+ The gate deliberately does not infer component ownership from arbitrary class
242
+ names, margins, borders, or nested descendants. Runtime selection and geometry
243
+ remain the browser suite's job.
244
+
245
+ A focused route may bypass the two public layout helpers only when the route's
246
+ stage geometry is itself the reviewed specimen. Add the narrow waiver to
247
+ [`catalog-conformance-exceptions.json`](../ux-demo/catalog-conformance-exceptions.json)
248
+ with the exact route, source file, stable diagnostic ids, a substantive reason,
249
+ and the reviewing `KF-*` ticket. Only helper/metadata rules are waivable;
250
+ private imports, local styling, private markup, empty examples, composition overlay drift, and
251
+ shell-mode drift always fail. The gate rejects duplicate, malformed, unused,
252
+ and stale exceptions, so delete a waiver when its route adopts the standard
253
+ helpers.
254
+
255
+ ## Selection reveal
256
+
257
+ Set `revealSelection: true` on `wireCatalog` for a long desktop sidebar. After
258
+ `onSelect` updates controlled state, the helper waits one animation frame, finds
259
+ the exact matching `data-item-id`, and scrolls it into view without changing
260
+ focus. A newer selection or disposal cancels the pending reveal. The default
261
+ media guard follows the Workbench's inline sidebar (`min-width: 44.01rem`), so a
262
+ closed phone overlay is never scrolled behind the user's back.
263
+
264
+ Pass an options object instead of `true` to customize `block`, `inline`,
265
+ `behavior`, or `media`; `media: false` deliberately enables the behavior at all
266
+ sizes. For an initial deep link that did not come through `wireCatalog`, call
267
+ `revealCatalogEntry(app, initialId, { block: "center" })` after the first mount.
268
+
269
+ ## Complete example
270
+
271
+ ```tsx
272
+ import { mount, signal, type SafeHtml } from "kerfjs";
273
+ import {
274
+ Catalog,
275
+ CatalogExample,
276
+ CatalogExampleStack,
277
+ type CatalogSection,
278
+ } from "@kerfjs/ui/catalog";
279
+ import {
280
+ revealCatalogEntry,
281
+ wireCatalog,
282
+ wireCatalogGeometryOverlay,
283
+ } from "@kerfjs/ui/wire-catalog";
284
+ // A CSS-aware (browser-condition) bundler loads the Catalog's CSS with its import.
285
+
286
+ type DemoKind = "component" | "composition";
287
+ type DemoEntry = CatalogSection["entries"][number] & { kind: DemoKind };
288
+
289
+ // 1. Describe selection and overlay mode once.
290
+ const entries: DemoEntry[] = [
291
+ {
292
+ id: "button",
293
+ name: "Button",
294
+ kind: "component",
295
+ description: "A pressable control.",
296
+ resources: [
297
+ { label: "Source", href: "/src/button.tsx", detail: "src/button.tsx" },
298
+ ],
299
+ },
300
+ {
301
+ id: "profile-form",
302
+ name: "Profile form",
303
+ kind: "composition",
304
+ description: "A labeled field and save action working together.",
305
+ },
306
+ ];
307
+ const sections: CatalogSection[] = [
308
+ {
309
+ category: "Examples",
310
+ entries: entries.map(({ kind, ...entry }) => ({
311
+ ...entry,
312
+ tags: kind === "composition" ? ["Composition"] : undefined,
313
+ })),
314
+ },
315
+ ];
316
+
317
+ // 2. Every preview uses one public group and public example rows.
318
+ const renderers: Record<string, () => SafeHtml> = {
319
+ button: () => (
320
+ <CatalogExampleStack
321
+ label="Button states"
322
+ rootAttributes={{ "data-demo": "button" }}
323
+ >
324
+ <CatalogExample label="Default" align="inline-control">
325
+ <Button label="Save" />
326
+ </CatalogExample>
327
+ <CatalogExample
328
+ label="Authoring note"
329
+ note="This explanatory row is deliberately excluded from inspection."
330
+ rootAttributes={{ "data-catalog-geometry-overlay-skip": "" }}
331
+ >
332
+ <p>The application owns product copy and actions.</p>
333
+ </CatalogExample>
334
+ </CatalogExampleStack>
335
+ ),
336
+ "profile-form": () => (
337
+ <CatalogExampleStack
338
+ label="Profile form composition"
339
+ rootAttributes={{ "data-demo": "profile-form" }}
340
+ >
341
+ <CatalogExample label="Complete composition">
342
+ <ProfileForm />
343
+ </CatalogExample>
344
+ </CatalogExampleStack>
345
+ ),
346
+ };
347
+
348
+ // 3. App-owned state (domain: which entry; transient: collapsed; global: theme).
349
+ const initial =
350
+ new URLSearchParams(location.search).get("c") ?? sections[0].entries[0].id;
351
+ const active = signal(initial);
352
+ const collapsed = signal(false);
353
+ const theme = signal<"light" | "dark">("light");
354
+ const activeEntry = () => entries.find(({ id }) => id === active.value) ?? entries[0];
355
+
356
+ const app = document.getElementById("app")!;
357
+ mount(app, () => (
358
+ <Catalog
359
+ brand={{ title: "Acme UI", subtitle: "Design system" }}
360
+ sections={sections}
361
+ active={active.value}
362
+ content={renderers[active.value]?.() ?? <></>}
363
+ collapsed={collapsed.value}
364
+ theme={theme.value}
365
+ geometryOverlay={activeEntry().kind === "component"}
366
+ />
367
+ ));
368
+
369
+ wireCatalog(app, {
370
+ onSelect: (id) => {
371
+ active.value = id;
372
+ },
373
+ onToggleSidebar: () => {
374
+ collapsed.value = !collapsed.value;
375
+ },
376
+ onToggleTheme: () => {
377
+ theme.value = theme.value === "dark" ? "light" : "dark";
378
+ document.documentElement.dataset.theme = theme.value; // apply your theme however you like
379
+ },
380
+ urlParam: "c", // mirror the active id into ?c=<id>
381
+ revealSelection: true, // reveal long desktop sidebars without moving focus
382
+ });
383
+ wireCatalogGeometryOverlay(app);
384
+
385
+ // Optional for an initial deep link whose row may start outside the viewport.
386
+ revealCatalogEntry(app, initial, { block: "center" });
387
+ ```
388
+
389
+ ## Ownership boundary
390
+
391
+ `Catalog` renders the shell; the app owns everything stateful:
392
+
393
+ - **`active`** is domain state (which entry is shown) — the app's signal, updated in
394
+ `onSelect`, read to compute `content`.
395
+ - **`collapsed`** is transient UI — the app's signal, flipped in `onToggleSidebar`.
396
+ Pass the same signal as `wireCatalog`'s `collapsed` option so the sidebar is
397
+ wired as a Workbench rail (transient on small screens, focus handed to the
398
+ relocated toggle).
399
+ - **`headerPlacement` / `footerPlacement`** choose whether the entry toolbar and
400
+ description, and the status + resource footer, stay pinned (`fixed`), scroll with
401
+ the preview (`scroll`), or stay pinned while the entry pane is tall enough and
402
+ scroll with the preview when it is short (`auto`, the default) — so a short
403
+ window or large text never squeezes the preview out.
404
+ - **`sidebar`** configures the sidebar rail — it forwards to the Workbench's left
405
+ rail: `size` (default 288px), `resizable` (`true` or `{ min, max }`),
406
+ `separator`, `collapseMotion`, `presentation`, `responsiveOverlayAt` (default
407
+ `narrow`), `compactOverlay` (default `inset`), and `toolbar` (the sidebar
408
+ header's `ToolbarConfig`). A resizable sidebar's width is app state like
409
+ `collapsed`: render your size signal as `sidebar.size` and pass the same signal
410
+ as `wireCatalog`'s `sidebarSize` (plus `sidebarStorageKey` to remember it).
411
+ - **`mainToolbar` / `footerToolbar`** take the entry toolbar's and the resource
412
+ footer toolbar's `ToolbarConfig` (`dividerSides`, `centerAlign`, `responsive`,
413
+ `responsiveAt`, `safeAreaEdges`). The defaults are a wrapping entry toolbar
414
+ and a footer toolbar that stacks at `narrow`, all without dividers of their
415
+ own: `wireCatalog` wires `wireScrollDividers`, so the shell's panes draw
416
+ their chrome dividers only while scrolled; an omitted or
417
+ `undefined` field keeps them. Configure these instead of styling the shell.
418
+ - **`theme`** is a global preference — the app's signal; `wireCatalog` only reports
419
+ the toggle, the app applies the theme (the shell reads `theme` to show the toggle's
420
+ opposite-state label). Omit `theme` to hide the toggle entirely.
421
+
422
+ ## Custom action names
423
+
424
+ The shell emits `data-action="catalog-select"` (sidebar items),
425
+ `catalog-toggle-sidebar`, and `catalog-toggle-theme`. Override them with
426
+ `selectAction` / `toggleSidebarAction` / `toggleThemeAction` on `Catalog` (and the
427
+ matching options on `wireCatalog`) if they collide with your own action table.
428
+
429
+ ## CSS
430
+
431
+ `Catalog` composes public components (`Workbench`, `Pane`, `Toolbar`, `List`,
432
+ `ListHeader`, `ListItem`, `ListInsetText`, `Text`, …) and owns only the preview stage
433
+ (the checkerboard and centered canvas), the geometry overlay, the example viewport
434
+ options, and the brand mark's size. A bundler that honors the `browser` export
435
+ condition loads the Catalog's stylesheets and those of every component it renders
436
+ internally with the `@kerfjs/ui/catalog` import alone — no dependence on which other
437
+ subpaths the app happens to import. Without that condition, import
438
+ `@kerfjs/ui/styles.css` for the whole layer, or `@kerfjs/ui/catalog.css` plus
439
+ `@kerfjs/ui/workbench.css` and each composed primitive's CSS. Like any Workbench it needs a definite containing height — the
440
+ `@kerfjs/ui/document.css` baseline's `.kui-app-root`.