@kerfjs/ui 5.0.0-beta.29 → 5.0.0-beta.30

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 (224) hide show
  1. package/README.md +43 -8
  2. package/ai/application-ui-diagnostic-ids-v1.json +5 -0
  3. package/ai/compile-time-contracts-v1.json +37 -0
  4. package/ai/compile-time-contracts-v1.schema.json +3 -1
  5. package/ai/component-catalog-v2-overrides.json +198 -1
  6. package/ai/component-catalog-v2.d.ts +22 -0
  7. package/ai/component-catalog-v2.json +488 -32
  8. package/ai/component-catalog-v2.schema.json +51 -0
  9. package/ai/component-catalog.json +251 -36
  10. package/ai/component-catalog.schema.json +23 -0
  11. package/ai/public-api-signatures-v1.md +231 -84
  12. package/ai/skill.md +32 -10
  13. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  14. package/analyzer/index.mjs +247 -5
  15. package/dist/app-tab.d.ts +3 -2
  16. package/dist/app-tab.js +2 -1
  17. package/dist/catalog-resources.d.ts +1 -0
  18. package/dist/catalog.d.ts +7 -6
  19. package/dist/catalog.js +8 -7
  20. package/dist/catalog.js.map +1 -1
  21. package/dist/{chunk-PX3YSS5C.js → chunk-6NDBE2OK.js} +1 -1
  22. package/dist/chunk-6NDBE2OK.js.map +1 -0
  23. package/dist/{chunk-B3A3IVO5.js → chunk-AQFWBG23.js} +3 -3
  24. package/dist/chunk-AQFWBG23.js.map +1 -0
  25. package/dist/{chunk-WNWD54GR.js → chunk-B6JYRX3P.js} +4 -2
  26. package/dist/chunk-B6JYRX3P.js.map +1 -0
  27. package/dist/{chunk-HP2B2L26.js → chunk-CAB6NQ2T.js} +1 -1
  28. package/dist/chunk-CAB6NQ2T.js.map +1 -0
  29. package/dist/{chunk-2NMQW7FX.js → chunk-CK6P5AUK.js} +4 -3
  30. package/dist/chunk-CK6P5AUK.js.map +1 -0
  31. package/dist/{chunk-VCE4MPVE.js → chunk-DAO2VFTT.js} +5 -6
  32. package/dist/chunk-DAO2VFTT.js.map +1 -0
  33. package/dist/{chunk-LPOMRC4C.js → chunk-DYBPI5O2.js} +1 -1
  34. package/dist/chunk-DYBPI5O2.js.map +1 -0
  35. package/dist/{chunk-IJBSZ4NX.js → chunk-E34TE2ZI.js} +4 -3
  36. package/dist/chunk-E34TE2ZI.js.map +1 -0
  37. package/dist/{chunk-4EF2CMYS.js → chunk-I45QO2IZ.js} +8 -7
  38. package/dist/chunk-I45QO2IZ.js.map +1 -0
  39. package/dist/{chunk-JRKSK2HX.js → chunk-I4V3UDSA.js} +1 -1
  40. package/dist/chunk-I4V3UDSA.js.map +1 -0
  41. package/dist/{chunk-FEY65TBV.js → chunk-KFUUFNTS.js} +1 -1
  42. package/dist/chunk-KFUUFNTS.js.map +1 -0
  43. package/dist/{chunk-QO55FN2Y.js → chunk-MFR2IIFG.js} +1 -1
  44. package/dist/chunk-MFR2IIFG.js.map +1 -0
  45. package/dist/{chunk-GWEAPEZI.js → chunk-OC275HHS.js} +4 -3
  46. package/dist/chunk-OC275HHS.js.map +1 -0
  47. package/dist/chunk-OL4TC3U2.js +156 -0
  48. package/dist/chunk-OL4TC3U2.js.map +1 -0
  49. package/dist/{chunk-OT6RPYAP.js → chunk-OMSRSL3B.js} +4 -3
  50. package/dist/chunk-OMSRSL3B.js.map +1 -0
  51. package/dist/{chunk-J5BFYY7Q.js → chunk-RL6COPKF.js} +12 -3
  52. package/dist/chunk-RL6COPKF.js.map +1 -0
  53. package/dist/{chunk-TB6DY7H5.js → chunk-SOK5EUF7.js} +1 -1
  54. package/dist/chunk-SOK5EUF7.js.map +1 -0
  55. package/dist/{chunk-RKVQEH4J.js → chunk-T7DOWS6T.js} +3 -3
  56. package/dist/chunk-T7DOWS6T.js.map +1 -0
  57. package/dist/{chunk-7KEUJIIC.js → chunk-TIGSG6BX.js} +1 -1
  58. package/dist/chunk-TIGSG6BX.js.map +1 -0
  59. package/dist/{chunk-W5L2JSBY.js → chunk-UDHHKZWO.js} +6 -5
  60. package/dist/chunk-UDHHKZWO.js.map +1 -0
  61. package/dist/{chunk-R7ZWUN64.js → chunk-V6GQKOX2.js} +4 -3
  62. package/dist/chunk-V6GQKOX2.js.map +1 -0
  63. package/dist/{chunk-KWWR5VMS.js → chunk-V6HCTCVD.js} +4 -3
  64. package/dist/chunk-V6HCTCVD.js.map +1 -0
  65. package/dist/{chunk-JFGXQBIP.js → chunk-VDUJZF5G.js} +76 -13
  66. package/dist/chunk-VDUJZF5G.js.map +1 -0
  67. package/dist/{chunk-SYDMYBPG.js → chunk-VXFGMDA7.js} +1 -1
  68. package/dist/chunk-VXFGMDA7.js.map +1 -0
  69. package/dist/{chunk-YMKH5XTF.js → chunk-WN52USHV.js} +2 -2
  70. package/dist/{chunk-YMKH5XTF.js.map → chunk-WN52USHV.js.map} +1 -1
  71. package/dist/{chunk-OBLTAKSX.js → chunk-WSXUPSID.js} +1 -1
  72. package/dist/chunk-WSXUPSID.js.map +1 -0
  73. package/dist/{chunk-X2U3QJCJ.js → chunk-ZPWZIVI6.js} +1 -1
  74. package/dist/chunk-ZPWZIVI6.js.map +1 -0
  75. package/dist/collapsible-panel.d.ts +5 -2
  76. package/dist/collapsible-panel.js.map +1 -1
  77. package/dist/css-values.d.ts +75 -0
  78. package/dist/css-values.js +3 -0
  79. package/dist/css-values.js.map +1 -0
  80. package/dist/empty-state.d.ts +2 -1
  81. package/dist/empty-state.js +1 -1
  82. package/dist/floating-toolbar.d.ts +4 -3
  83. package/dist/floating-toolbar.js +1 -1
  84. package/dist/index.d.ts +2 -0
  85. package/dist/index.js +26 -25
  86. package/dist/list-action-row.d.ts +1 -2
  87. package/dist/list-action-row.js +2 -1
  88. package/dist/list-header.d.ts +1 -1
  89. package/dist/list-header.js +2 -1
  90. package/dist/list-inset-control.d.ts +4 -3
  91. package/dist/list-inset-control.js +1 -1
  92. package/dist/list-inset-text.d.ts +4 -3
  93. package/dist/list-inset-text.js +1 -1
  94. package/dist/list-item.d.ts +4 -3
  95. package/dist/list-item.js +2 -1
  96. package/dist/list.d.ts +10 -8
  97. package/dist/list.js +2 -1
  98. package/dist/nav-stack.d.ts +9 -6
  99. package/dist/nav-stack.js +2 -1
  100. package/dist/pane.d.ts +6 -5
  101. package/dist/pane.js +1 -1
  102. package/dist/resizable-region.d.ts +2 -1
  103. package/dist/resizable-region.js +1 -1
  104. package/dist/segmented-control.d.ts +4 -3
  105. package/dist/segmented-control.js +2 -1
  106. package/dist/select.d.ts +3 -1
  107. package/dist/select.js +2 -1
  108. package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
  109. package/dist/skeleton.d.ts +7 -6
  110. package/dist/skeleton.js +2 -1
  111. package/dist/split-view.d.ts +5 -4
  112. package/dist/split-view.js +3 -2
  113. package/dist/split-view.js.map +1 -1
  114. package/dist/state-banner.d.ts +3 -2
  115. package/dist/state-banner.js +2 -1
  116. package/dist/styles/catalog.css +8 -1
  117. package/dist/styles/collapsible-panel.css +6 -2
  118. package/dist/styles/foundation.css +44 -0
  119. package/dist/styles/list-header.css +6 -0
  120. package/dist/styles/list-item.css +9 -0
  121. package/dist/styles/nav-stack.css +32 -2
  122. package/dist/styles/segmented-control.css +8 -1
  123. package/dist/styles/select.css +2 -1
  124. package/dist/styles/state-banner.css +14 -0
  125. package/dist/styles/tab-scaffold.css +1 -0
  126. package/dist/styles/token-search-field.css +4 -0
  127. package/dist/styles/toolbar-control-group.css +98 -16
  128. package/dist/styles/webawesome.css +22 -0
  129. package/dist/styles/workbench.css +4 -0
  130. package/dist/sunken-panel.d.ts +4 -3
  131. package/dist/sunken-panel.js +1 -1
  132. package/dist/surface-scaffold.d.ts +6 -5
  133. package/dist/surface-scaffold.js +1 -1
  134. package/dist/tab-bar.d.ts +6 -5
  135. package/dist/tab-bar.js +1 -1
  136. package/dist/tab-scaffold.d.ts +2 -1
  137. package/dist/tab-scaffold.js.map +1 -1
  138. package/dist/token-search-field.d.ts +5 -4
  139. package/dist/token-search-field.js +1 -1
  140. package/dist/toolbar-control-group.d.ts +10 -4
  141. package/dist/toolbar-control-group.js +1 -1
  142. package/dist/toolbar-text.js +2 -1
  143. package/dist/toolbar.d.ts +6 -5
  144. package/dist/toolbar.js +1 -1
  145. package/dist/value-table.d.ts +4 -2
  146. package/dist/value-table.js +2 -1
  147. package/dist/wire-nav-stack.d.ts +4 -1
  148. package/dist/wire-nav-stack.js +125 -0
  149. package/dist/wire-nav-stack.js.map +1 -1
  150. package/dist/wire-resizable-regions.js +1 -1
  151. package/dist/wire-tab-bars.d.ts +1 -0
  152. package/dist/wire-token-search-fields.js +1 -1
  153. package/dist/workbench.d.ts +7 -4
  154. package/dist/workbench.js.map +1 -1
  155. package/docs/accessibility.md +9 -3
  156. package/docs/collapsible-panel.md +4 -1
  157. package/docs/component-contract.md +56 -8
  158. package/docs/component-selection.md +45 -8
  159. package/docs/design/templates/list/compact-dark.svg +1 -1
  160. package/docs/design/templates/list/compact.svg +1 -1
  161. package/docs/design/templates/list/stack-dark.svg +1 -1
  162. package/docs/design/templates/list/stack.svg +1 -1
  163. package/docs/design/templates/list-action-row/default-dark.svg +1 -1
  164. package/docs/design/templates/list-action-row/default.svg +1 -1
  165. package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
  166. package/docs/design/templates/list-action-row/selected.svg +1 -1
  167. package/docs/design/templates/list-action-row-dark.svg +1 -1
  168. package/docs/design/templates/list-action-row.svg +1 -1
  169. package/docs/design/templates/list-dark.svg +1 -1
  170. package/docs/design/templates/list-item/default-dark.svg +1 -1
  171. package/docs/design/templates/list-item/default.svg +1 -1
  172. package/docs/design/templates/list-item/multiline-dark.svg +1 -1
  173. package/docs/design/templates/list-item/multiline.svg +1 -1
  174. package/docs/design/templates/list-item/selected-dark.svg +1 -1
  175. package/docs/design/templates/list-item/selected.svg +1 -1
  176. package/docs/design/templates/list-item/trailing-dark.svg +1 -1
  177. package/docs/design/templates/list-item/trailing.svg +1 -1
  178. package/docs/design/templates/list-item-dark.svg +1 -1
  179. package/docs/design/templates/list-item.svg +1 -1
  180. package/docs/design/templates/list.svg +1 -1
  181. package/docs/design-philosophy.md +4 -2
  182. package/docs/examples/component-catalog-extension-v2.json +2 -0
  183. package/docs/layout.md +12 -4
  184. package/docs/nav-stack.md +29 -8
  185. package/docs/recipes.md +8 -4
  186. package/docs/split-view.md +6 -0
  187. package/docs/surface-scaffold.md +8 -0
  188. package/docs/tab-scaffold.md +3 -1
  189. package/docs/type-contracts.md +39 -12
  190. package/docs/ui-analyzer.md +17 -3
  191. package/docs/ui-doctor.md +7 -0
  192. package/docs/ux-demo.md +10 -4
  193. package/docs/webawesome-theme.md +6 -0
  194. package/docs/workbench.md +6 -4
  195. package/llms.txt +46 -6
  196. package/package.json +5 -1
  197. package/ux-demo/recipes/compact-toolbar.tsx +10 -9
  198. package/ux-demo/recipes/composer-form.tsx +6 -3
  199. package/ux-demo/recipes/loading-inspector.tsx +2 -1
  200. package/dist/chunk-2NMQW7FX.js.map +0 -1
  201. package/dist/chunk-4EF2CMYS.js.map +0 -1
  202. package/dist/chunk-7KEUJIIC.js.map +0 -1
  203. package/dist/chunk-B3A3IVO5.js.map +0 -1
  204. package/dist/chunk-FEY65TBV.js.map +0 -1
  205. package/dist/chunk-GWEAPEZI.js.map +0 -1
  206. package/dist/chunk-HP2B2L26.js.map +0 -1
  207. package/dist/chunk-IJBSZ4NX.js.map +0 -1
  208. package/dist/chunk-J5BFYY7Q.js.map +0 -1
  209. package/dist/chunk-JFGXQBIP.js.map +0 -1
  210. package/dist/chunk-JRKSK2HX.js.map +0 -1
  211. package/dist/chunk-KWWR5VMS.js.map +0 -1
  212. package/dist/chunk-LPOMRC4C.js.map +0 -1
  213. package/dist/chunk-OBLTAKSX.js.map +0 -1
  214. package/dist/chunk-OT6RPYAP.js.map +0 -1
  215. package/dist/chunk-PX3YSS5C.js.map +0 -1
  216. package/dist/chunk-QO55FN2Y.js.map +0 -1
  217. package/dist/chunk-R7ZWUN64.js.map +0 -1
  218. package/dist/chunk-RKVQEH4J.js.map +0 -1
  219. package/dist/chunk-SYDMYBPG.js.map +0 -1
  220. package/dist/chunk-TB6DY7H5.js.map +0 -1
  221. package/dist/chunk-VCE4MPVE.js.map +0 -1
  222. package/dist/chunk-W5L2JSBY.js.map +0 -1
  223. package/dist/chunk-WNWD54GR.js.map +0 -1
  224. package/dist/chunk-X2U3QJCJ.js.map +0 -1
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 452 512" width="452" height="512"><defs><clipPath id="cp0"><rect x="16" y="38" width="420" height="230"/></clipPath><clipPath id="cp1"><rect x="16" y="306" width="420" height="190"/></clipPath><clipPath id="ct2"><rect x="16" y="16" width="420" height="22"/></clipPath><clipPath id="irc3"><rect x="16" y="38" width="420" height="230" rx="0"/></clipPath><clipPath id="ct5"><rect x="16" y="284" width="420" height="22"/></clipPath><clipPath id="irc6"><rect x="16" y="306" width="420" height="190" rx="0"/></clipPath></defs><rect x="0" y="0" width="452" height="512" rx="0" fill="rgb(255,255,255)"/><text x="16" y="28" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(110,110,115)">Standard gap and four dividers</text><g clip-path="url(#cp0)"><g clip-path="url(#irc3)"><clipPath id="svgimg4vclip"><rect x="16" y="38" width="420" height="230"/></clipPath><g clip-path="url(#svgimg4vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg4cp0"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="svgimg4ct1"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="svgimg4ct2"><rect x="126.8" y="31.2" width="21.6" height="21.6"/></clipPath><clipPath id="svgimg4cp3"><rect x="37" y="85" width="18" height="18"/></clipPath><clipPath id="svgimg4cp5"><rect x="63" y="85" width="272" height="18"/></clipPath><clipPath id="svgimg4ct6"><rect x="63" y="85" width="272" height="18"/></clipPath><clipPath id="svgimg4cp7"><rect x="37" y="137" width="18" height="18"/></clipPath><clipPath id="svgimg4cp9"><rect x="63" y="137" width="272" height="18"/></clipPath><clipPath id="svgimg4ct10"><rect x="63" y="137" width="272" height="18"/></clipPath></defs><rect x="28.5" y="20.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg4cp0)"><g clip-path="url(#svgimg4ct1)"><text x="37" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(110,110,115)">WORKSPACE</text></g></g><rect x="126.8" y="31.2" width="21.6" height="21.6" rx="10.8" fill="rgb(242,242,247)"/><text x="134.33" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="10" font-weight="700" fill="rgb(110,110,115)">2</text><rect x="28" y="72" width="324" height="44" rx="12" fill="rgb(214,236,255)"/><rect x="28.5" y="72.5" width="323" height="43" rx="11.5" fill="none" stroke="rgb(166,213,255)" stroke-width="1"/><g clip-path="url(#svgimg4cp3)"><g color="rgb(29, 29, 31)"><clipPath id="svgimg4svgic4"><rect x="37" y="85" width="18" height="18"/></clipPath><g clip-path="url(#svgimg4svgic4)"><g transform="matrix(.75 0 0 .75 37 85)"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M5.5 5.1 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.4-6.9A2 2 0 0 0 16.8 4H7.2a2 2 0 0 0-1.7 1.1" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#svgimg4cp5)"><g clip-path="url(#svgimg4ct6)"><text x="63" y="100" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(29,29,31)">Inbox</text></g></g><rect x="28.5" y="124.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg4cp7)"><g color="rgb(29, 29, 31)"><clipPath id="svgimg4svgic8"><rect x="37" y="137" width="18" height="18"/></clipPath><g clip-path="url(#svgimg4svgic8)"><g transform="matrix(.75 0 0 .75 37 137)"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.7-.9l-.8-1.2A2 2 0 0 0 7.9 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#svgimg4cp9)"><g clip-path="url(#svgimg4ct10)"><text x="63" y="152" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" fill="rgb(29,29,31)">Projects</text></g></g></g></g></g></g><text x="16" y="296" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(110,110,115)">Compact, no gap</text><g clip-path="url(#cp1)"><g clip-path="url(#irc6)"><clipPath id="svgimg7vclip"><rect x="16" y="306" width="420" height="190"/></clipPath><g clip-path="url(#svgimg7vclip)"><g transform="translate(16 306)"><defs><clipPath id="svgimg7cp0"><rect x="37" y="33" width="18" height="18"/></clipPath><clipPath id="svgimg7cp2"><rect x="63" y="33" width="272" height="18"/></clipPath><clipPath id="svgimg7ct3"><rect x="63" y="33" width="272" height="18"/></clipPath><clipPath id="svgimg7cp4"><rect x="37" y="77" width="18" height="18"/></clipPath><clipPath id="svgimg7cp6"><rect x="63" y="77" width="272" height="18"/></clipPath><clipPath id="svgimg7ct7"><rect x="63" y="77" width="272" height="18"/></clipPath></defs><rect x="28.5" y="20.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg7cp0)"><g color="rgb(29, 29, 31)"><clipPath id="svgimg7svgic1"><rect x="37" y="33" width="18" height="18"/></clipPath><g clip-path="url(#svgimg7svgic1)"><g transform="matrix(.75 0 0 .75 37 33)"><path d="M11.5 2.3a.5.5 0 0 1 1 0L14.8 7a2 2 0 0 0 1.6 1l5.1.8a.5.5 0 0 1 .3.9l-3.7 3.6a2 2 0 0 0-.6 2l.9 5a.5.5 0 0 1-.8.6L13 18.6a2 2 0 0 0-2 0L6.4 21a.5.5 0 0 1-.8-.6l1-5a2 2 0 0 0-.7-2L2.2 9.8a.5.5 0 0 1 .3-1l5.1-.7A2 2 0 0 0 9.2 7Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#svgimg7cp2)"><g clip-path="url(#svgimg7ct3)"><text x="63" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" fill="rgb(29,29,31)">Priority</text></g></g><rect x="28.5" y="64.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg7cp4)"><g color="rgb(29, 29, 31)"><clipPath id="svgimg7svgic5"><rect x="37" y="77" width="18" height="18"/></clipPath><g clip-path="url(#svgimg7svgic5)"><g transform="matrix(.75 0 0 .75 37 77)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 12z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 19z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 5h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 12h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 19h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#svgimg7cp6)"><g clip-path="url(#svgimg7ct7)"><text x="63" y="92" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" fill="rgb(29,29,31)">All work</text></g></g></g></g></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 452 512" width="452" height="512"><defs><clipPath id="cp0"><rect x="16" y="38" width="420" height="230"/></clipPath><clipPath id="cp1"><rect x="16" y="306" width="420" height="190"/></clipPath><clipPath id="ct2"><rect x="16" y="16" width="420" height="22"/></clipPath><clipPath id="irc3"><rect x="16" y="38" width="420" height="230" rx="0"/></clipPath><clipPath id="ct5"><rect x="16" y="284" width="420" height="22"/></clipPath><clipPath id="irc6"><rect x="16" y="306" width="420" height="190" rx="0"/></clipPath></defs><rect x="0" y="0" width="452" height="512" rx="0" fill="rgb(255,255,255)"/><text x="16" y="28" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(110,110,115)">Standard gap and four dividers</text><g clip-path="url(#cp0)"><g clip-path="url(#irc3)"><clipPath id="svgimg4vclip"><rect x="16" y="38" width="420" height="230"/></clipPath><g clip-path="url(#svgimg4vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg4cp0"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="svgimg4ct1"><rect x="37" y="34.5" width="81.8" height="15"/></clipPath><clipPath id="svgimg4ct2"><rect x="126.8" y="31.2" width="21.6" height="21.6"/></clipPath><clipPath id="svgimg4cp3"><rect x="28" y="72" width="324" height="44"/></clipPath><clipPath id="svgimg4ov4"><rect x="29" y="73" width="322" height="42" rx="11"/></clipPath><clipPath id="svgimg4cp5"><rect x="37" y="85" width="18" height="18"/></clipPath><clipPath id="svgimg4cp7"><rect x="63" y="85" width="272" height="18"/></clipPath><clipPath id="svgimg4ov8"><rect x="63" y="85" width="272" height="18" rx="0"/></clipPath><clipPath id="svgimg4ct9"><rect x="63" y="85" width="272" height="18"/></clipPath><clipPath id="svgimg4cp10"><rect x="28" y="124" width="324" height="44"/></clipPath><clipPath id="svgimg4ov11"><rect x="29" y="125" width="322" height="42" rx="11"/></clipPath><clipPath id="svgimg4cp12"><rect x="37" y="137" width="18" height="18"/></clipPath><clipPath id="svgimg4cp14"><rect x="63" y="137" width="272" height="18"/></clipPath><clipPath id="svgimg4ov15"><rect x="63" y="137" width="272" height="18" rx="0"/></clipPath><clipPath id="svgimg4ct16"><rect x="63" y="137" width="272" height="18"/></clipPath></defs><rect x="28.5" y="20.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg4cp0)"><g clip-path="url(#svgimg4ct1)"><text x="37" y="46.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(110,110,115)">WORKSPACE</text></g></g><rect x="126.8" y="31.2" width="21.6" height="21.6" rx="10.8" fill="rgb(242,242,247)"/><text x="134.33" y="46" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="10" font-weight="700" fill="rgb(110,110,115)">2</text><g clip-path="url(#svgimg4cp3)"><rect x="28" y="72" width="324" height="44" rx="12" fill="rgb(214,236,255)"/><rect x="28.5" y="72.5" width="323" height="43" rx="11.5" fill="none" stroke="rgb(166,213,255)" stroke-width="1"/><g clip-path="url(#svgimg4ov4)"><g clip-path="url(#svgimg4cp5)"><g color="rgb(29, 29, 31)"><clipPath id="svgimg4svgic6"><rect x="37" y="85" width="18" height="18"/></clipPath><g clip-path="url(#svgimg4svgic6)"><g transform="matrix(.75 0 0 .75 37 85)"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M5.5 5.1 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.4-6.9A2 2 0 0 0 16.8 4H7.2a2 2 0 0 0-1.7 1.1" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#svgimg4cp7)"><g clip-path="url(#svgimg4ov8)"><text x="63" y="100" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" font-weight="650" fill="rgb(29,29,31)">Inbox</text></g></g></g></g><g clip-path="url(#svgimg4cp10)"><rect x="28.5" y="124.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg4ov11)"><g clip-path="url(#svgimg4cp12)"><g color="rgb(29, 29, 31)"><clipPath id="svgimg4svgic13"><rect x="37" y="137" width="18" height="18"/></clipPath><g clip-path="url(#svgimg4svgic13)"><g transform="matrix(.75 0 0 .75 37 137)"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.7-.9l-.8-1.2A2 2 0 0 0 7.9 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#svgimg4cp14)"><g clip-path="url(#svgimg4ov15)"><text x="63" y="152" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" fill="rgb(29,29,31)">Projects</text></g></g></g></g></g></g></g></g><text x="16" y="296" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(110,110,115)">Compact, no gap</text><g clip-path="url(#cp1)"><g clip-path="url(#irc6)"><clipPath id="svgimg7vclip"><rect x="16" y="306" width="420" height="190"/></clipPath><g clip-path="url(#svgimg7vclip)"><g transform="translate(16 306)"><defs><clipPath id="svgimg7cp0"><rect x="28" y="20" width="324" height="44"/></clipPath><clipPath id="svgimg7ov1"><rect x="29" y="21" width="322" height="42" rx="11"/></clipPath><clipPath id="svgimg7cp2"><rect x="37" y="33" width="18" height="18"/></clipPath><clipPath id="svgimg7cp4"><rect x="63" y="33" width="272" height="18"/></clipPath><clipPath id="svgimg7ov5"><rect x="63" y="33" width="272" height="18" rx="0"/></clipPath><clipPath id="svgimg7ct6"><rect x="63" y="33" width="272" height="18"/></clipPath><clipPath id="svgimg7cp7"><rect x="28" y="64" width="324" height="44"/></clipPath><clipPath id="svgimg7ov8"><rect x="29" y="65" width="322" height="42" rx="11"/></clipPath><clipPath id="svgimg7cp9"><rect x="37" y="77" width="18" height="18"/></clipPath><clipPath id="svgimg7cp11"><rect x="63" y="77" width="272" height="18"/></clipPath><clipPath id="svgimg7ov12"><rect x="63" y="77" width="272" height="18" rx="0"/></clipPath><clipPath id="svgimg7ct13"><rect x="63" y="77" width="272" height="18"/></clipPath></defs><g clip-path="url(#svgimg7cp0)"><rect x="28.5" y="20.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg7ov1)"><g clip-path="url(#svgimg7cp2)"><g color="rgb(29, 29, 31)"><clipPath id="svgimg7svgic3"><rect x="37" y="33" width="18" height="18"/></clipPath><g clip-path="url(#svgimg7svgic3)"><g transform="matrix(.75 0 0 .75 37 33)"><path d="M11.5 2.3a.5.5 0 0 1 1 0L14.8 7a2 2 0 0 0 1.6 1l5.1.8a.5.5 0 0 1 .3.9l-3.7 3.6a2 2 0 0 0-.6 2l.9 5a.5.5 0 0 1-.8.6L13 18.6a2 2 0 0 0-2 0L6.4 21a.5.5 0 0 1-.8-.6l1-5a2 2 0 0 0-.7-2L2.2 9.8a.5.5 0 0 1 .3-1l5.1-.7A2 2 0 0 0 9.2 7Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#svgimg7cp4)"><g clip-path="url(#svgimg7ov5)"><text x="63" y="48" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" fill="rgb(29,29,31)">Priority</text></g></g></g></g><g clip-path="url(#svgimg7cp7)"><rect x="28.5" y="64.5" width="323" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg7ov8)"><g clip-path="url(#svgimg7cp9)"><g color="rgb(29, 29, 31)"><clipPath id="svgimg7svgic10"><rect x="37" y="77" width="18" height="18"/></clipPath><g clip-path="url(#svgimg7svgic10)"><g transform="matrix(.75 0 0 .75 37 77)"><path d="M3 5z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 12z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M3 19z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 5h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 12h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M8 19h13" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(29, 29, 31)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#svgimg7cp11)"><g clip-path="url(#svgimg7ov12)"><text x="63" y="92" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="16" fill="rgb(29,29,31)">All work</text></g></g></g></g></g></g></g></g></svg>
@@ -88,8 +88,10 @@ Reuse a component when surfaces share its purpose, anatomy, state model, and int
88
88
  Color, type, spacing, radius, elevation, and motion express semantic roles. Prefer package tokens over raw values in consuming code. State is never communicated by color alone.
89
89
 
90
90
  The default vocabulary is opinionated rather than empty: neutral, brand/info,
91
- success, warning, and danger provide quiet/normal/loud fills, borders, and
92
- foregrounds compatible with Hot Sheet 2 and Web Awesome. Consumers may replace
91
+ pop, success, warning, and danger provide quiet/normal/loud fills, borders, and
92
+ foregrounds compatible with Hot Sheet 2 and Web Awesome. Pop is vibrant,
93
+ non-status emphasis for featured, novel, or celebratory content; never use it as
94
+ a success, warning, or danger substitute. Consumers may replace
93
95
  the global palette, one semantic role, or one component instance through
94
96
  documented custom properties without rewriting component CSS.
95
97
 
@@ -19,12 +19,14 @@
19
19
  "zones": [
20
20
  {
21
21
  "id": "header",
22
+ "jsx": { "prop": "header" },
22
23
  "accepts": ["@kerfjs/ui:toolbar"],
23
24
  "cardinality": { "min": 1, "max": 1 },
24
25
  "exclusiveWith": []
25
26
  },
26
27
  {
27
28
  "id": "facts",
29
+ "jsx": { "prop": "facts" },
28
30
  "accepts": ["@kerfjs/ui:value-table"],
29
31
  "cardinality": { "min": 1, "max": 1 },
30
32
  "exclusiveWith": []
package/docs/layout.md CHANGED
@@ -50,10 +50,12 @@ Use `.kui-content-item--pill` for the 22px pill radius expressed as
50
50
  changing its geometry by setting `--kui-content-item-border` and
51
51
  `--kui-content-item-background`.
52
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.
53
+ Nested selection and hover highlights stay concentric and match their owning
54
+ control's shape. Their inner radius is the outer radius minus the full inset,
55
+ expressed by the shared `--kui-layout-highlight-inset` token (2px by default).
56
+ For example, `SegmentedControl` and `ToolbarControlGroup` place each highlight
57
+ behind a 1px control border and 1px control padding, so a rounded control's 12px
58
+ outer radius produces a 10px highlight radius; a 22px pill produces 20px.
57
59
 
58
60
  A visible parent surface does not make every child another visible card. The
59
61
  composer recipe keeps its form as the single surface, uses a heading `Toolbar` for
@@ -110,6 +112,12 @@ The two relationships that get confused most are 8px vs 24px: 8px is _inside_ a
110
112
  group, 24px is _between_ major, differing regions. 16px sits between them for
111
113
  same-kind groups.
112
114
 
115
+ For `List.gap`, pass these token names directly (`gap="xs"`, `gap="m"`) or use
116
+ `space('xs')` from `@kerfjs/ui/css-values` when composing a value in JavaScript.
117
+ Use `rem`, `em`, `px`, `pct`, `lengthVar`, and `calc(plus(...))` only when a
118
+ named spacing relationship does not express the requirement; do not pass raw
119
+ CSS strings.
120
+
113
121
  `--kui-space-s` (12px) and `--kui-space-xl` (32px) exist but are **off the
114
122
  canonical rhythm** — reach for them only as a deliberate exception, never as a
115
123
  default step. Prefer the five canonical tokens so spacing stays legible and
package/docs/nav-stack.md CHANGED
@@ -21,7 +21,12 @@ stack as a `signal<NavStackView[]>`, `NavStack({ views })` renders it, and
21
21
 
22
22
  ```tsx
23
23
  const views = signal<NavStackView[]>([
24
- { key: "inbox", title: "Inbox", content: <InboxView /> },
24
+ {
25
+ key: "inbox",
26
+ title: "Inbox",
27
+ content: <InboxView />,
28
+ bottomToolbar: <InboxStatus />,
29
+ },
25
30
  ]);
26
31
 
27
32
  // render inside mount():
@@ -42,15 +47,31 @@ views.value = [
42
47
  ```
43
48
 
44
49
  `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.
50
+ mounted (so their DOM state survives) but hidden. Each entry carries a
51
+ `key` (stable identity), `content`, an optional `title`, optional per-view
52
+ `toolbar` actions, and an optional per-view `bottomToolbar`. The component-level
53
+ `bottomToolbar` remains a persistent fallback for views that do not provide one.
54
+ The back control appears automatically once the stack has more than one entry;
55
+ `wireNavStack`'s `onBack` is where the app pops its own signal.
56
+
57
+ Focus follows the controlled stack automatically. On push, `wireNavStack`
58
+ remembers the focused descendant of the departing view and moves focus into the
59
+ new top view. On pop it restores that exact descendant when it still exists. A
60
+ new view prefers a descendant marked `data-nav-focus`, then its first enabled
61
+ focusable descendant, and finally the view container itself. Use
62
+ `data-nav-focus` for a heading or primary control when DOM order is not the best
63
+ initial reading position; it may use `tabindex="-1"` for programmatic focus.
64
+ When a remembered control was removed during the controlled rerender, the same
65
+ fallback order applies. Disposing the helper stops future transitions and
66
+ cleans up any temporary fallback `tabindex` it added.
49
67
 
50
68
  ## Transitions
51
69
 
52
70
  `wireNavStack(root, { onBack, duration? })` observes the rendered stack and
53
71
  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.
72
+ view slides back off it over the revealed view; and snapshots of the previous
73
+ top and bottom chrome cross-fade into the active view's chrome. Focus moves to
74
+ the new top view independently of animation duration. It returns a disposer.
75
+ The animation honors `prefers-reduced-motion` (transitions collapse to instant)
76
+ and `duration: 0` disables it. Applicable at every device size and inside
77
+ dialogs.
package/docs/recipes.md CHANGED
@@ -81,8 +81,10 @@ Do not rebuild the dialog or reach into private shadow parts.
81
81
  [Open the recipe](../ux-demo/?component=recipe-composer-form) · [TSX source](../ux-demo/recipes/composer-form.tsx)
82
82
 
83
83
  A direct `ToolbarText` supplies the task title, with app-owned supporting copy
84
- below; their ids are referenced by the form. Production fields own labels, help,
85
- and native focus. The field
84
+ below; their ids are referenced by the form. `ListInsetText` gives that bare
85
+ supporting copy the same content-item text inset as the fields instead of
86
+ duplicating its geometry in recipe CSS. Production fields own labels, help, and
87
+ native focus. The field
86
88
  and footer control edges sit directly on the shared 8px inline gutter rather
87
89
  than acquiring a second content-item padding inset; major children remain 24px
88
90
  apart and related controls use 8px gaps. The app owns
@@ -107,8 +109,10 @@ rows.
107
109
 
108
110
  Use `ToolbarControlGroup` for related commands, `SegmentedControl` for a few
109
111
  visible exclusive choices, `Select` for a longer value list, and an ordinary
110
- button for an independent command. The app owns values, actions, persistence,
111
- and responsive priority.
112
+ button for an independent command. Even that independent button sits in a
113
+ single-control `ToolbarControlGroup`, which keeps its height, pill shape, focus,
114
+ and hover treatment aligned with the adjacent toolbar controls. The app owns
115
+ values, actions, persistence, and responsive priority.
112
116
 
113
117
  ## Navigation stack
114
118
 
@@ -45,5 +45,11 @@ const selected = signal<string | null>(null);
45
45
  app's selection). This is the portrait-tablet / handset presentation; as a
46
46
  dialog the compact form is a full-screen or large partial-cover modal.
47
47
 
48
+ The catalog's **Interactive compact drill-down** example exercises this exact
49
+ controlled flow: a list action sets the selected message, `detailActive` pushes
50
+ its detail, and the wired Back action clears the selection to reveal the
51
+ preserved list. Use the same structure in application demos so compact examples
52
+ show the interaction rather than rendering a disconnected detail state.
53
+
48
54
  Compose the interactive wiring from the existing helpers — `SplitView` adds no
49
55
  wire of its own.
@@ -10,6 +10,14 @@ Wrap one `wa-dialog` with `DialogSurface`. Choose `size` (`small`, `medium`, or
10
10
  `bodyInset` / `footerInset` (`none`, `compact`, or `comfortable`). The defaults
11
11
  match Kerf's medium modal, 8px body, and 16px footer rhythm.
12
12
 
13
+ Treat a dialog body as a `List` by default. When its rows or sections own their
14
+ standard list geometry, set `bodyInset="none"` so the dialog does not add a
15
+ second inset. Bare prose is still a list child: wrap it in `ListInsetText` so its
16
+ text edge receives the standard 8px margin + 1px transparent border + 8px
17
+ padding and aligns with bordered siblings. Reserve `compact` or `comfortable`
18
+ body insets for exceptional content that does not already have list/content-item
19
+ geometry.
20
+
13
21
  Wrap one `wa-dropdown` with `PopupSurface`. `inset="list-zero"` removes menu
14
22
  padding for a child that already owns row insets, `compact` uses 4px, and
15
23
  `standard` uses the shared 8px surface inset. Keep the dropdown trigger named and
@@ -46,5 +46,7 @@ const dispose = wireTabScaffold(root, {
46
46
 
47
47
  Each `TabScaffoldTab` has an `id`, `label`, optional `icon`, and `content`. The
48
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
49
+ and keeps 44px targets. Its single-line labels preserve their full line box and
50
+ truncate horizontally with an ellipsis when a destination name exceeds its
51
+ share of the bar. On larger device classes, promote the tab set to a
50
52
  `Workbench` rail or a persistent sidebar instead of a bottom bar.
@@ -11,18 +11,37 @@ public-signature artifact.
11
11
 
12
12
  ## Strengthened contracts
13
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` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code. |
23
- | `KUI-T008` | A literal `TabScaffold.active` id belongs to its literal `tabs`. |
24
- | `KUI-T009` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback. |
25
- | `KUI-T010` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all AppTab, TabBar, ToolbarControlGroup, and Select presentation axes; the convenience root barrel re-exports them. |
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` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code. |
23
+ | `KUI-T008` | A literal `TabScaffold.active` id belongs to its literal `tabs`. |
24
+ | `KUI-T009` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback. |
25
+ | `KUI-T010` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all AppTab, TabBar, ToolbarControlGroup, and Select presentation axes; the convenience root barrel re-exports them. |
26
+ | `KUI-T011` | Semantic component zones use the recursive `KerfUiContent` type: `SafeHtml`, runtime-empty booleans/nullish values, and readonly nested arrays are valid; arbitrary strings, numbers, and signals are rejected. Explicit text positions such as `ListInsetText` retain their text exception. |
27
+ | `KUI-T012` | `List.gap` accepts finite `UiSpaceName` shorthands or a complete branded `CssLength`; raw strings and non-standalone `CssLengthExpression` arithmetic are rejected. `calc(plus(...))` promotes a typed expression to a complete value. |
28
+ | `KUI-T013` | CSS-valued props preserve property grammar: `List.flex` uses `CssFlex`, `Skeleton` dimensions use `CssSize`/`CssLength`, and choice icons use `CssColor`; row declaration strings are absent. |
29
+
30
+ `KerfUiContent` lets conditionals and mapped component collections be direct
31
+ siblings without an otherwise-unnecessary `Fragment`:
32
+
33
+ ```tsx
34
+ <List>
35
+ {intro ? <ListInsetText>{intro}</ListInsetText> : null}
36
+ {rows.map((row) => (
37
+ <ListItem label={row.label} />
38
+ ))}
39
+ </List>
40
+ ```
41
+
42
+ The type is intentionally narrower than core JSX children. Wrap prose in a
43
+ semantic text component, and read signals into components or structural
44
+ conditionals rather than passing signal objects as content.
26
45
 
27
46
  Literal identity inference is intentionally additive for dynamic applications.
28
47
  When choices or ids arrive at runtime and the state signal is correctly typed as
@@ -95,6 +114,14 @@ move errors into casts without making the integration safer.
95
114
  - Replace `Toolbar({ divider: false })` with `dividerSides: ''`; the default
96
115
  remains a bottom divider, while canonical combinations such as `tr` and
97
116
  `trbl` select more physical edges.
117
+ - Replace raw `List.gap` strings with a direct spacing shorthand (`"xs"`,
118
+ `"m"`) or a complete value from `@kerfjs/ui/css-values`. Replace
119
+ `gap="0.25rem"` with `gap={rem(0.25)}` and wrap sums with `calc(plus(...))`.
120
+ - Replace raw `List.flex` shorthands with `flex(grow, shrink, basis)` or a finite
121
+ keyword; replace raw Skeleton dimensions with `px`/`rem`/`em`/`pct` builders;
122
+ and replace raw choice colors with `uiColor()` or `colorVar()`.
123
+ - Remove `style` from `ListItem` and `ListActionRow`; use `className`, public
124
+ tokens, and cataloged props. These breaking cleanups land before 5.0 stable.
98
125
 
99
126
  JavaScript runtime behavior remains defensive for previously emitted calls, but
100
127
  new TypeScript builds report these invalid or ignored combinations.
@@ -13,8 +13,9 @@ npx kerf-ui-analyze --root . --format sarif --output artifacts/kerf-ui.sarif
13
13
  The analyzer is opt-in. Add a project script such as
14
14
  `"check:kerf-ui-layout": "kerf-ui-analyze --root . src"`; it never executes
15
15
  application code. It discovers `.kerf-ui-profile.json`, joins every declared
16
- composition catalog by `package:id`, parses literal TSX/JSX class usage and CSS,
17
- and reports portable repository-relative locations.
16
+ composition catalog by `package:id`, parses literal JavaScript/TypeScript
17
+ component calls, TSX/JSX, and CSS, and reports portable repository-relative
18
+ locations.
18
19
 
19
20
  Analysis is scoped per source file. Each TSX/JSX file resolves package,
20
21
  workspace, and parent-to-child directory profiles from its own location, then
@@ -49,6 +50,11 @@ policy, or analysis inputs to the containing application.
49
50
  | `KUI-L010` | error | A selector reaches a private Kerf descendant from a public root. |
50
51
  | `KUI-L011` | error | A `::part()` target is not cataloged as a public extension point. |
51
52
  | `KUI-L012` | error | CSS assigns a Kerf token without a public configuration contract. |
53
+ | `KUI-L013` | error | A CSS-adjacent prop uses an unknown shorthand or raw literal. |
54
+ | `KUI-L014` | error | A typed helper produces the wrong property grammar. |
55
+ | `KUI-L015` | error | An expression-only helper is passed without a composer. |
56
+ | `KUI-L016` | error | A removed declaration-list escape hatch is used. |
57
+ | `KUI-L017` | review | A valid but exceptional off-scale shorthand needs justification. |
52
58
 
53
59
  Errors are provable contract violations and make the command exit 1. Review
54
60
  findings are deliberately heuristic and do not fail by default; pass
@@ -70,6 +76,13 @@ Parent-owned placement and selectors for application-owned content remain
70
76
  allowed because the analyzer only reserves `.kui-*`, `--kui-*`, and cataloged
71
77
  shadow boundaries.
72
78
 
79
+ Catalog entries may also publish `cssValueProps`. Each path names its grammar,
80
+ finite shorthands, canonical and exceptional scale steps, accepted typed
81
+ helpers, expression-only helpers, raw escape policy, and examples. The analyzer
82
+ uses that same metadata for direct component calls and JSX, including nested
83
+ paths such as `choices[].color`; consumer catalogs receive identical checks.
84
+ Dynamic values remain a type-system responsibility rather than being guessed.
85
+
73
86
  The JSON report schema is exported as
74
87
  `@kerfjs/ui/analyzer/report.schema.json`.
75
88
 
@@ -100,5 +113,6 @@ validator.
100
113
 
101
114
  Review findings remain visible until explicitly suppressed. Avoid suppressing
102
115
  `KUI-L001`, `KUI-L002`, `KUI-L003`, `KUI-L007`, `KUI-L009`, `KUI-L010`,
103
- `KUI-L011`, or `KUI-L012`: those indicate a
116
+ `KUI-L011`, `KUI-L012`, `KUI-L013`, `KUI-L014`, `KUI-L015`, or `KUI-L016`:
117
+ those indicate a
104
118
  definite boundary or parsing failure rather than an aesthetic judgment.
package/docs/ui-doctor.md CHANGED
@@ -17,6 +17,13 @@ The catalog stage discovers the package-default application UI profile and works
17
17
 
18
18
  TypeScript uses the compiler API with `noEmit`. ESLint loads the installed `eslint-plugin-kerfjs` `recommended-ui` preset (`--eslint strict-ui` opts into advisory rules as errors). This is deliberately an isolated Kerf lint pass rather than the consumer's complete ESLint configuration. For each file, the doctor projects the applicable core ESLint rules and `linterOptions` from the consumer configuration so core-rule suppression directives retain the same used/unused semantics as the application's normal lint command. Consumer plugin rules are not executed, and inline directives for plugins such as `@typescript-eslint` therefore do not produce false "rule definition not found" diagnostics; the consumer's normal ESLint command remains authoritative for those rules. Unknown `kerfjs/*` directives still fail the doctor pass. Stable KUI identifiers come from the packaged `application-ui-diagnostic-ids-v1.json` registry; the doctor also reads installed ESLint message metadata for semantic conflict detection instead of duplicating rule definitions. The analyzer calls the public `@kerfjs/ui/analyzer` contract. None of these stages executes generated application code.
19
19
 
20
+ Property-specific CSS values are evaluated from each first- or third-party
21
+ catalog entry's `cssValueProps`. `KUI-L013`–`KUI-L016` are blocking grammar
22
+ violations and name a preferred shorthand/helper; `KUI-L017` keeps an
23
+ exceptional but valid spacing choice in the review queue. The ESLint and
24
+ analyzer stages share these ids, so the normalized report can be consumed as
25
+ one repair loop without tool-specific translations.
26
+
20
27
  The browser evaluator is different: it runs the application and is disabled by default. It only runs when configuration supplies `browser.url` or the command receives `--browser-url`. Start and authorize the target application separately.
21
28
 
22
29
  An unavailable or failed stage does not prevent independent stages from reporting. Its final exit is still a configuration failure, so a partial run cannot appear clean.
package/docs/ux-demo.md CHANGED
@@ -8,7 +8,7 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
8
8
 
9
9
  - It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
10
10
  - The `ListActionRow` route demonstrates sibling primary/trailing native controls, independent disabled states, controlled selection, first-line leading-icon alignment for wrapped labels, and an application-owned native popover relationship without nesting controls. The `ListItem` route demonstrates the same multiline leading-icon anchor alongside its single-control navigation contract.
11
- - The `AppTab` route demonstrates a runtime-filtered domain metadata slot and a decorative replacement close icon without changing TabBar selection, close, reorder, or focus ownership. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
11
+ - The `AppTab` and `TabBar` routes are focused component specimens: AppTab shows its selected, compact, segmented, icon-only, truncation, placeholder, metadata, and close-icon axes inside the required tablist context; TabBar shows its rail, segmented, inspector, allocation, placement, and accessory-zone axes without application readouts. The separate `Application tabs composition` route demonstrates controlled selection, matching panel content, overflow, adding, closing, keyboard/pointer reordering, and application-owned ordering. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
12
12
  - Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, feedback, and theme scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component.
13
13
  - `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, recommendation, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Non-supported Web Awesome recommendations project to a visible `Discouraged` sidebar tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<public-subpath>.tsx` from their canonical `delivery.browserImport` or opt-in `delivery.moduleImport`.
14
14
  - Every detail footer groups compact `Demo source` and `Guidance` actions; first-party component details also expose `Component source`. Web Awesome details say `Integration guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
@@ -19,12 +19,18 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
19
19
  - Catalog-only brand marks, preview dimensions, checkerboard tiles, and the compound display-settings control retain local geometry because they are specimen chrome or intrinsic control anatomy rather than reusable application spacing roles.
20
20
  - The menu composition route demonstrates toolbar/content/footer anatomy, 24px major gaps, 8/1/8 child geometry, the full-width `ListHeader` count-or-badge/action split, logical-end 44px action targets with 18px visuals, and 44px row/header/toolbar targets. Its one chevron is a production 18px `DisclosureArrow` inside a controlled Tools header that actually reveals and hides content; ordinary Projects navigation has no false disclosure affordance. Browser assertions cover stable naming, expanded/content transitions, visual rotation, node identity, reduced motion, RTL alignment and containment, and wide, narrow, dark, and 200% layouts. The focused ListHeader route separately shows the shared neutral count pill for Attachments, Notes (including zero), and Duplicates plus a non-count legacy badge, full-width logical-end actions, and no disclosure without content.
21
21
  - The `DisclosureArrow` route renders the production 18px root-scaled default without a catalog-only size override. Its two native buttons independently control the default right-to-down chevron and a recognizable right-facing replacement arrow with left-to-up custom directions; both use shortest-path rotation and their names stay stable while `aria-expanded` communicates state. Browser coverage verifies the custom clockwise path, pointer and native keyboard activation, focus, narrow and 200%-zoom stacking with contained labels, consumer sizing, and the independent `.5` Web Awesome glyph scale retained by Kerf `Select`.
22
- - A detail with relationships renders one grouped footer selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
22
+ - A detail with relationships renders one content-fitted `Components` footer selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Its popup owns the standard surface inset so headings and even long component names stay clear of the menu border. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
23
23
  - Demo-only CSS supplies only the catalog shell and stages; component appearance stays in package CSS. Decorative chrome and Web Awesome controls share the same semantic system palette instead of carrying a separate demo identity.
24
- - Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; ListItem and ListHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; the Headers composition demonstrates direct toolbar headings with an extra-large title, optional supporting copy, and grouped icon/actions; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone with tone-tinted badges plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
24
+ - Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; ListItem and ListHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; the Headers composition demonstrates direct toolbar headings with an extra-large title, optional supporting copy, and grouped icon/actions; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone with tone-tinted badges plus a scoped override; the Application tabs composition demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
25
25
  - The ToolbarControlGroup route also demonstrates the public compact/tight,
26
26
  mixed-content, filled-selection, nested-dropdown, and avatar/scrim contracts
27
- added for consumers that previously restyled group descendants.
27
+ added for consumers that previously restyled group descendants. The compact
28
+ mixed specimen keeps its separator, trigger label, and caret inside both pill
29
+ and rounded group shapes. Avatar images are chrome rather than nested `img`
30
+ elements: a lone avatar paints the group, while a multi-button avatar group
31
+ paints only the pressed selection, with contain fitting in both cases. Its
32
+ collapsible-search specimen follows the selected group shape with a
33
+ concentric inner radius in both collapsed and expanded states.
28
34
  - Recipe ownership notes are hidden by default. A page-toolbar toggle reveals them as floating warning-toned notes with a folded corner, keeping instructional copy visually distinct and removable from the specimen flow.
29
35
  - Settings exercise light/dark, increased contrast, and reduced motion without changing component code. The theme action starts from the operating system's effective color scheme, always names the appearance it will switch to, and installs an explicit light or dark override when activated; contrast and reduced motion expose their independent preferences through `aria-pressed`.
30
36
  - The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
@@ -103,6 +103,12 @@ tiers:
103
103
  content stays compact and the action group has a clearer boundary. These two
104
104
  parts do not inherit the dialog's shared `--spacing` value.
105
105
 
106
+ Add `class="hide-actions"` to a `wa-dialog` when the dialog supplies its own
107
+ dismissal affordance and should omit Web Awesome's header action region. The
108
+ theme sets `display: none` on the directly exported `header-actions` part; it
109
+ does not attempt to chain `::part()` through the internal `dialog` and `header`
110
+ parts, which CSS shadow parts do not support.
111
+
106
112
  Badge remains intentionally compact at Web Awesome's intrinsic `0.375em` block /
107
113
  `0.625em` inline padding (4.5px / 7.5px at its default 12px text size): it is a
108
114
  short status or count, not a content item. Breadcrumb has no bordered or filled
package/docs/workbench.md CHANGED
@@ -115,7 +115,9 @@ attributes or descendant tags as styling contracts.
115
115
  Collapsing snaps the panel's flex track to zero in a single reflow (so the work
116
116
  area relayouts once, not per frame) while the panel's fixed-size content slides
117
117
  out via a composited `transform` — a rail slides horizontally, the drawer
118
- vertically — clipped by the shell's overflow. It honors `prefers-reduced-motion`
119
- (the slide collapses to instant). On smaller device classes, present the rails'
120
- contents through a `NavStack` or overlay drawers rather than shrinking the
121
- three-panel shell.
118
+ vertically — clipped by the shell's overflow. The drawer content is positioned
119
+ against the shell's stable bottom edge, so opening and closing move monotonically
120
+ through the transform rather than inheriting a changing normal-flow origin. It
121
+ honors `prefers-reduced-motion` (the slide collapses to instant). On smaller
122
+ device classes, present the rails' contents through a `NavStack` or overlay
123
+ drawers rather than shrinking the three-panel shell.
package/llms.txt CHANGED
@@ -45,6 +45,17 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
45
45
  child; synchronous hosts use [application-ui-profile-sync.cjs](./ai/application-ui-profile-sync.cjs).
46
46
  See the
47
47
  [workspace profile example](./docs/examples/application-ui-profile.json).
48
+ A zone's optional `jsx.prop` is its only authoritative JSX binding; never
49
+ infer one from the zone id. Use `children` for one homogeneous primary
50
+ region and explicit named content prop slots for semantic positions or
51
+ replacement content. These slots use the recursive `KerfUiContent` type:
52
+ `SafeHtml`, runtime-empty boolean/nullish values, and readonly nested arrays.
53
+ Put conditionals and mapped component arrays directly in them; do not add a
54
+ `Fragment` only to satisfy types. Arbitrary strings, numbers, and signals are
55
+ rejected unless a component explicitly exposes a text position such as
56
+ `ListInsetText`. These are typed props, not native `<slot>` elements,
57
+ wrapper slot components, or a generic `slots` object. Leave unresolved
58
+ dynamic expressions unknown.
48
59
  4. **When authoring a component gallery**, load
49
60
  [catalog-authoring.json](./ai/catalog-authoring.json), then follow its
50
61
  authoritative [Catalog demo authoring contract](./docs/catalog.md#catalog-demo-authoring-contract).
@@ -52,6 +63,8 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
52
63
  6. **Before calling an integration complete**, run
53
64
  [`kerf-ui-doctor`](./docs/ui-doctor.md). Its portable JSON report merges
54
65
  profile/catalog, TypeScript, Kerf ESLint, and static analyzer diagnostics;
66
+ CSS-adjacent component props are checked from catalog `cssValueProps`, so
67
+ prefer its canonical shorthand or typed helper over raw CSS strings;
55
68
  repair source-located errors until exit 0. Use full mode for release gates;
56
69
  generated `dist`/`coverage`, dependency, cache, evidence, and nested-worktree
57
70
  directories are excluded consistently from every static stage.
@@ -79,9 +92,14 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
79
92
  container padding on top of a content-item's own margin — pick one owner. Use
80
93
  `.kui-content` for 24px major vertical rhythm.
81
94
  - **Use `List` for vertical layout.** It supplies a stretch-aligned column plus
82
- optional standard/custom `gap`, `flex`, vertical scrolling, and canonical
95
+ optional typed `gap`, `flex`, vertical scrolling, and canonical
83
96
  `dividerSides` (`t`/`r`/`b`/`l` order). It adds no list semantics; children own
84
- those, and only one bounded region should own scrolling.
97
+ those, and only one bounded region should own scrolling. Pass a finite spacing
98
+ shorthand (`gap="xs"`, `gap="m"`) or a complete `CssLength` from the CSS-free
99
+ `@kerfjs/ui/css-values` builders; raw CSS strings are rejected. Use `flex()`
100
+ for List flex, typed lengths for Skeleton geometry, and
101
+ `uiColor()`/`colorVar()` for choice-icon colors. These brands are intentionally
102
+ not interchangeable; rows use classes/tokens/props rather than raw `style`.
85
103
  - **Keep Web Awesome on the shared inset tiers.** `webawesome.css` uses
86
104
  `--kui-wa-control-inset` (8px controls/items) and
87
105
  `--kui-wa-surface-margin` / `--kui-wa-surface-inset` (8px around/inside
@@ -90,6 +108,9 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
90
108
  the surface (8px) and container (16px) tiers respectively, independent of the
91
109
  dialog's shared `--spacing`. Override the tier at the narrowest useful scope
92
110
  instead of restyling individual shadow parts.
111
+ When a dialog provides another dismissal affordance, add `hide-actions` to
112
+ the `wa-dialog` host; the theme hides its directly exported `header-actions`
113
+ part without an unsupported chained `::part()` selector.
93
114
  Checkbox and Radio Group option regions also receive the shared 8px inline
94
115
  outer inset because those groups have no bordered field shell. The Color
95
116
  Picker trigger receives the same inset for the same unbordered geometry.
@@ -117,7 +138,9 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
117
138
  need a Web Awesome feature, chiefly the `slot="trigger"` button of a `wa-dropdown`
118
139
  popup menu. A toolbar popup menu is a `single` `ToolbarControlGroup` around a
119
140
  `wa-dropdown` (its `slot="trigger"` `wa-button` is the toolbar button, kept under
120
- `data-morph-skip-children`).
141
+ `data-morph-skip-children`). For an avatar control, set `content="avatar"` and
142
+ `avatarImage` on the group instead of inserting an `img`; a lone control paints
143
+ the group and a multi-button group paints only its pressed highlight.
121
144
  - **Use one direct toolbar composition for panel/dialog/page headings.** Put an
122
145
  optional icon in a `ToolbarControlGroup`, an extra-large `ToolbarText` directly
123
146
  in the leading zone, and actions in a trailing group. Supporting copy is
@@ -126,7 +149,13 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
126
149
  - **Space by the five-step scale, by relationship not by eye.** `0`
127
150
  `--kui-space-none`, `4px` `--kui-space-2xs`, `8px` `--kui-space-xs` (inside a
128
151
  group), `16px` `--kui-space-m`, `24px` `--kui-space-l` (between major differing
129
- regions). 12px/32px are off-scale exceptions.
152
+ regions). 12px/32px are off-scale exceptions. Prefer direct `List.gap`
153
+ shorthands; use `space`, `px`, `rem`, `em`, `pct`, `lengthVar`, and
154
+ `calc(plus(...))` for typed runtime composition. `remify()` is source-CSS only.
155
+ - **Keep nested highlight corners concentric.** Inset hover and selection surfaces
156
+ follow the owning control's pill or rounded shape; their radius is the outer
157
+ radius minus `--kui-layout-highlight-inset`. Do not give an inner highlight an
158
+ unrelated radius.
130
159
  - **Panes share one child-owned geometry.** Sidebars, main areas, inspectors, and
131
160
  dialogs all use an unpadded `.kui-pane` (optional `.kui-pane__toolbar`, one
132
161
  scrolling `.kui-pane__content`, optional `.kui-pane__footer`). A visible pane owns
@@ -141,11 +170,14 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
141
170
  navigation stacks, tab selection and order, pane sizes, and `collapsed`/`expanded`
142
171
  disclosure flags in the app — they are data or persisted layout, and the app reads
143
172
  them to render. The `wire…` helpers own the _ephemeral_ parts: `wireNavStack` the
144
- push/pop animation, `wireTabBars` the overflow autoscroll, drag preview, and
173
+ content-slide, per-view top/bottom chrome cross-fade, and focus move/restore
174
+ (mark a preferred new-view target with `data-nav-focus`), `wireTabBars` the
175
+ overflow autoscroll, drag preview, and
145
176
  replacement focus after controlled automatic tab activation,
146
177
  `wireResizableRegions` the live drag preview; they report committed changes via
147
178
  callbacks. Managed TokenSearchField clear also preserves open state and replacement
148
- editor focus for continued typing. Select All deletion does the same; deletion
179
+ editor focus before the next input task, so shortcut-letter typing stays in search.
180
+ Select All deletion does the same; deletion
149
181
  restoration finishes before the next keystroke rather than on a delayed frame.
150
182
  Persist both query and remaining tokens read from an edited field. The one richer case is `wireTokenSearchFields`, which manages the
151
183
  collapsible field's expand/collapse/focus by default (on, per-behavior opt-out) and
@@ -168,6 +200,14 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
168
200
  markup. Use the standalone `Skeleton` for a custom slot. Prefer it over `wa-skeleton`
169
201
  (the pure-Kerf primitives avoid Web Awesome), and never animate it — use
170
202
  `LoadingSpinner` for known busy activity instead.
203
+ - **Keep ListItem content inside the row.** The rounded `ListItem` root clips
204
+ overflowing label, status, and dormant trailing content. Set `multiline` when
205
+ the primary label should wrap; use `ListActionRow` instead when trailing content
206
+ must be interactive.
207
+ - **Dialog bodies are lists by default.** Compose rows and sections in `List`,
208
+ use `DialogSurface bodyInset="none"` when those children own their geometry,
209
+ and wrap bare prose in `ListInsetText` so it shares the standard list gutter
210
+ without stacking a second body inset.
171
211
  - **Use a StateBanner badge only for compact supporting status.** Pass the
172
212
  optional string `badge` for a terse count or status beside the title. It
173
213
  inherits the banner tone and is announced with the banner, so keep its text
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kerfjs/ui",
3
- "version": "5.0.0-beta.29",
3
+ "version": "5.0.0-beta.30",
4
4
  "description": "Accessible, composable UI primitives for kerf applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -44,6 +44,10 @@
44
44
  "types": "./dist/index.d.ts",
45
45
  "import": "./dist/index.js"
46
46
  },
47
+ "./css-values": {
48
+ "types": "./dist/css-values.d.ts",
49
+ "import": "./dist/css-values.js"
50
+ },
47
51
  "./lucide-icon": {
48
52
  "types": "./dist/lucide-icon.d.ts",
49
53
  "browser": "./dist/browser/lucide-icon.js",