@kerfjs/ui 5.0.0-beta.56 → 5.0.0-beta.58

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 (287) hide show
  1. package/README.md +81 -37
  2. package/ai/application-ui-diagnostic-ids-v1.json +9 -1
  3. package/ai/application-ui-profile-sync.cjs +1 -1
  4. package/ai/application-ui-profile.defaults.json +63 -8
  5. package/ai/application-ui-profile.mjs +1 -1
  6. package/ai/component-catalog.json +211 -33
  7. package/ai/component-catalog.schema.json +2 -0
  8. package/ai/component-class-owners.cjs +83 -0
  9. package/ai/component-composition-extension.d.ts +7 -0
  10. package/ai/component-composition-extension.schema.json +7 -0
  11. package/ai/{component-catalog-v2-overrides.json → component-composition-rules.json} +2 -1
  12. package/ai/{component-catalog-v2.d.ts → component-composition.d.ts} +9 -5
  13. package/ai/{component-catalog-v2.json → component-composition.json} +155 -24
  14. package/ai/{component-catalog-v2.schema.json → component-composition.schema.json} +21 -5
  15. package/ai/components/README.md +170 -0
  16. package/ai/components/application-tabs.md +65 -0
  17. package/ai/components/badge.md +72 -0
  18. package/ai/components/collapsible-panel.md +102 -0
  19. package/ai/components/content-item.md +90 -0
  20. package/ai/components/disclosure-arrow.md +77 -0
  21. package/ai/components/document-baseline.md +75 -0
  22. package/ai/components/empty-state.md +80 -0
  23. package/ai/components/feedback.md +71 -0
  24. package/ai/components/floating-toolbar.md +80 -0
  25. package/ai/components/foundation.md +65 -0
  26. package/ai/components/grid.md +93 -0
  27. package/ai/components/headers.md +63 -0
  28. package/ai/components/layout.md +75 -0
  29. package/ai/components/list-action-row.md +91 -0
  30. package/ai/components/list-header.md +89 -0
  31. package/ai/components/list-inset-control.md +74 -0
  32. package/ai/components/list-inset-text.md +72 -0
  33. package/ai/components/list-item.md +88 -0
  34. package/ai/components/list.md +103 -0
  35. package/ai/components/loading-spinner.md +72 -0
  36. package/ai/components/lucide-icon.md +66 -0
  37. package/ai/components/nav-stack.md +103 -0
  38. package/ai/components/pane.md +100 -0
  39. package/ai/components/popup-menu.md +82 -0
  40. package/ai/components/recipe-app-shell.md +57 -0
  41. package/ai/components/recipe-collapsible-sidebar.md +57 -0
  42. package/ai/components/recipe-compact-toolbar.md +57 -0
  43. package/ai/components/recipe-composer-form.md +57 -0
  44. package/ai/components/recipe-list-detail-dialog.md +57 -0
  45. package/ai/components/recipe-list-workspace-states.md +57 -0
  46. package/ai/components/recipe-loading-inspector.md +57 -0
  47. package/ai/components/recipe-navigation-sidebar.md +57 -0
  48. package/ai/components/recipe-navigation-stack.md +57 -0
  49. package/ai/components/recipe-workspace-header.md +57 -0
  50. package/ai/components/resize.md +101 -0
  51. package/ai/components/row.md +96 -0
  52. package/ai/components/segmented-control.md +82 -0
  53. package/ai/components/select.md +107 -0
  54. package/ai/components/skeleton.md +84 -0
  55. package/ai/components/spacer.md +86 -0
  56. package/ai/components/split-view.md +102 -0
  57. package/ai/components/state-banner.md +92 -0
  58. package/ai/components/sunken-panel.md +79 -0
  59. package/ai/components/surface-scaffold.md +78 -0
  60. package/ai/components/tab-bar.md +112 -0
  61. package/ai/components/tab-scaffold.md +94 -0
  62. package/ai/components/tabs.md +90 -0
  63. package/ai/components/text.md +82 -0
  64. package/ai/components/token-search-field.md +101 -0
  65. package/ai/components/toolbar-control-group.md +102 -0
  66. package/ai/components/toolbar-text.md +76 -0
  67. package/ai/components/toolbar.md +95 -0
  68. package/ai/components/value-table.md +72 -0
  69. package/ai/components/wa-accordion-item.md +55 -0
  70. package/ai/components/wa-accordion.md +65 -0
  71. package/ai/components/wa-animated-image.md +55 -0
  72. package/ai/components/wa-animation.md +55 -0
  73. package/ai/components/wa-avatar.md +55 -0
  74. package/ai/components/wa-badge.md +55 -0
  75. package/ai/components/wa-breadcrumb-item.md +53 -0
  76. package/ai/components/wa-breadcrumb.md +55 -0
  77. package/ai/components/wa-button-group.md +59 -0
  78. package/ai/components/wa-button.md +55 -0
  79. package/ai/components/wa-callout.md +58 -0
  80. package/ai/components/wa-card.md +59 -0
  81. package/ai/components/wa-carousel-item.md +53 -0
  82. package/ai/components/wa-carousel.md +55 -0
  83. package/ai/components/wa-checkbox-group.md +55 -0
  84. package/ai/components/wa-checkbox.md +55 -0
  85. package/ai/components/wa-color-picker.md +55 -0
  86. package/ai/components/wa-comparison.md +55 -0
  87. package/ai/components/wa-copy-button.md +55 -0
  88. package/ai/components/wa-details.md +61 -0
  89. package/ai/components/wa-dialog.md +59 -0
  90. package/ai/components/wa-divider.md +53 -0
  91. package/ai/components/wa-drawer.md +55 -0
  92. package/ai/components/wa-dropdown-item.md +60 -0
  93. package/ai/components/wa-dropdown.md +60 -0
  94. package/ai/components/wa-format-bytes.md +53 -0
  95. package/ai/components/wa-format-date.md +53 -0
  96. package/ai/components/wa-format-number.md +53 -0
  97. package/ai/components/wa-icon.md +58 -0
  98. package/ai/components/wa-include.md +53 -0
  99. package/ai/components/wa-input.md +59 -0
  100. package/ai/components/wa-intersection-observer.md +53 -0
  101. package/ai/components/wa-known-date.md +53 -0
  102. package/ai/components/wa-markdown.md +53 -0
  103. package/ai/components/wa-mutation-observer.md +53 -0
  104. package/ai/components/wa-number-input.md +55 -0
  105. package/ai/components/wa-option.md +59 -0
  106. package/ai/components/wa-otp-input.md +53 -0
  107. package/ai/components/wa-page.md +53 -0
  108. package/ai/components/wa-pagination.md +55 -0
  109. package/ai/components/wa-popover.md +55 -0
  110. package/ai/components/wa-popup.md +53 -0
  111. package/ai/components/wa-progress-bar.md +53 -0
  112. package/ai/components/wa-progress-ring.md +53 -0
  113. package/ai/components/wa-qr-code.md +53 -0
  114. package/ai/components/wa-radio-group.md +59 -0
  115. package/ai/components/wa-radio.md +55 -0
  116. package/ai/components/wa-random-content.md +53 -0
  117. package/ai/components/wa-rating.md +55 -0
  118. package/ai/components/wa-relative-time.md +53 -0
  119. package/ai/components/wa-resize-observer.md +53 -0
  120. package/ai/components/wa-scroller.md +53 -0
  121. package/ai/components/wa-select.md +63 -0
  122. package/ai/components/wa-skeleton.md +53 -0
  123. package/ai/components/wa-slider.md +55 -0
  124. package/ai/components/wa-spinner.md +58 -0
  125. package/ai/components/wa-split-panel.md +58 -0
  126. package/ai/components/wa-switch.md +53 -0
  127. package/ai/components/wa-tab-group.md +69 -0
  128. package/ai/components/wa-tab-panel.md +60 -0
  129. package/ai/components/wa-tab.md +59 -0
  130. package/ai/components/wa-tag.md +59 -0
  131. package/ai/components/wa-textarea.md +53 -0
  132. package/ai/components/wa-time-input.md +55 -0
  133. package/ai/components/wa-toast-item.md +60 -0
  134. package/ai/components/wa-toast.md +60 -0
  135. package/ai/components/wa-tooltip.md +55 -0
  136. package/ai/components/wa-tree-item.md +55 -0
  137. package/ai/components/wa-tree.md +55 -0
  138. package/ai/components/wa-zoomable-frame.md +55 -0
  139. package/ai/components/workbench.md +114 -0
  140. package/ai/public-api-signatures-v1.md +174 -43
  141. package/ai/skill.md +100 -48
  142. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  143. package/analyzer/component-ownership.d.mts +56 -0
  144. package/analyzer/component-ownership.mjs +184 -0
  145. package/analyzer/index.d.mts +3 -0
  146. package/analyzer/index.mjs +274 -20
  147. package/analyzer/selectors.d.mts +21 -0
  148. package/analyzer/selectors.mjs +212 -0
  149. package/dist/browser/catalog.js +25 -0
  150. package/dist/browser/collapsible-panel.js +7 -0
  151. package/dist/browser/nav-stack.js +8 -0
  152. package/dist/browser/split-view.js +10 -0
  153. package/dist/browser/tab-scaffold.js +4 -0
  154. package/dist/browser/workbench.js +10 -0
  155. package/dist/catalog-resources.d.ts +2 -2
  156. package/dist/catalog.d.ts +3 -3
  157. package/dist/catalog.js +8 -7
  158. package/dist/catalog.js.map +1 -1
  159. package/dist/{chunk-QQOTQTDD.js → chunk-2CCMJTVY.js} +13 -10
  160. package/dist/chunk-2CCMJTVY.js.map +1 -0
  161. package/dist/{chunk-PK4N5QTO.js → chunk-553ASUK6.js} +2 -3
  162. package/dist/chunk-553ASUK6.js.map +1 -0
  163. package/dist/{chunk-G33XC2NB.js → chunk-6YFHCLHG.js} +2 -2
  164. package/dist/chunk-6YFHCLHG.js.map +1 -0
  165. package/dist/{chunk-ZIYOJFZD.js → chunk-7JUBXOIV.js} +17 -4
  166. package/dist/chunk-7JUBXOIV.js.map +1 -0
  167. package/dist/{chunk-T4UZTNZ4.js → chunk-EVYKTVBQ.js} +2 -11
  168. package/dist/chunk-EVYKTVBQ.js.map +1 -0
  169. package/dist/{chunk-AK67F6QN.js → chunk-EZVNC4J6.js} +4 -1
  170. package/dist/chunk-EZVNC4J6.js.map +1 -0
  171. package/dist/{chunk-MA6Z6EIM.js → chunk-F4QMLOXJ.js} +3 -2
  172. package/dist/chunk-F4QMLOXJ.js.map +1 -0
  173. package/dist/chunk-JCV4GXWB.js +249 -0
  174. package/dist/chunk-JCV4GXWB.js.map +1 -0
  175. package/dist/chunk-K47P7QZC.js +17 -0
  176. package/dist/chunk-K47P7QZC.js.map +1 -0
  177. package/dist/{chunk-MOBRFZH3.js → chunk-OUK6QRDR.js} +2 -12
  178. package/dist/chunk-OUK6QRDR.js.map +1 -0
  179. package/dist/{chunk-VNPQDCQZ.js → chunk-QOWG4ZCL.js} +3 -2
  180. package/dist/chunk-QOWG4ZCL.js.map +1 -0
  181. package/dist/{chunk-V65EOWNF.js → chunk-QPUPLNNB.js} +20 -8
  182. package/dist/chunk-QPUPLNNB.js.map +1 -0
  183. package/dist/{chunk-O3EXIBEZ.js → chunk-W4XKTST6.js} +8 -7
  184. package/dist/chunk-W4XKTST6.js.map +1 -0
  185. package/dist/{chunk-K6UNVKDK.js → chunk-XDKAYSVD.js} +3 -1
  186. package/dist/chunk-XDKAYSVD.js.map +1 -0
  187. package/dist/collapsible-panel.d.ts +9 -9
  188. package/dist/collapsible-panel.js +2 -2
  189. package/dist/collapsible-panel.js.map +1 -1
  190. package/dist/index.d.ts +2 -1
  191. package/dist/index.js +11 -9
  192. package/dist/list-action-row.js +2 -1
  193. package/dist/list-header.js +1 -1
  194. package/dist/list-item.js +2 -1
  195. package/dist/nav-stack.d.ts +13 -1
  196. package/dist/nav-stack.js +1 -1
  197. package/dist/pane.d.ts +20 -3
  198. package/dist/pane.js +1 -1
  199. package/dist/{panel-toolbar-C8sF-YZC.d.ts → panel-toolbar-DqwtBxQL.d.ts} +13 -13
  200. package/dist/select.js +1 -1
  201. package/dist/split-view.d.ts +3 -3
  202. package/dist/split-view.js +3 -2
  203. package/dist/split-view.js.map +1 -1
  204. package/dist/state-banner.js +1 -1
  205. package/dist/styles/app-tab.css +31 -0
  206. package/dist/styles/badge.css +19 -11
  207. package/dist/styles/collapsible-panel.css +13 -9
  208. package/dist/styles/floating-toolbar.css +25 -6
  209. package/dist/styles/list-header.css +0 -12
  210. package/dist/styles/list.css +4 -4
  211. package/dist/styles/nav-stack.css +46 -19
  212. package/dist/styles/pane.css +50 -0
  213. package/dist/styles/popup-menu.css +151 -0
  214. package/dist/styles/resizable-region.css +22 -10
  215. package/dist/styles/select.css +28 -14
  216. package/dist/styles/skeleton.css +30 -3
  217. package/dist/styles/split-view.css +10 -7
  218. package/dist/styles/state-banner.css +0 -19
  219. package/dist/styles/surface-scaffold.css +8 -3
  220. package/dist/styles/tab-bar.css +79 -28
  221. package/dist/styles/tab-scaffold.css +25 -1
  222. package/dist/styles/text.css +16 -0
  223. package/dist/styles/toolbar-control-group.css +121 -66
  224. package/dist/styles/toolbar-text.css +21 -0
  225. package/dist/styles/toolbar.css +8 -32
  226. package/dist/styles/workbench.css +18 -18
  227. package/dist/tab-scaffold.d.ts +11 -1
  228. package/dist/tab-scaffold.js +2 -0
  229. package/dist/tab-scaffold.js.map +1 -1
  230. package/dist/toolbar-control-group.d.ts +3 -1
  231. package/dist/toolbar-control-group.js +1 -1
  232. package/dist/toolbar-text.d.ts +7 -1
  233. package/dist/toolbar-text.js +1 -1
  234. package/dist/toolbar.d.ts +8 -1
  235. package/dist/toolbar.js +1 -1
  236. package/dist/{types-DXAGogOS.d.ts → types-BuHELdXR.d.ts} +1 -1
  237. package/dist/wire-catalog.d.ts +3 -1
  238. package/dist/wire-catalog.js +2 -0
  239. package/dist/wire-catalog.js.map +1 -1
  240. package/dist/wire-scroll-dividers.d.ts +69 -0
  241. package/dist/wire-scroll-dividers.js +3 -0
  242. package/dist/wire-scroll-dividers.js.map +1 -0
  243. package/dist/workbench.d.ts +17 -16
  244. package/dist/workbench.js +1 -1
  245. package/docs/app-layouts.md +9 -1
  246. package/docs/catalog.md +10 -5
  247. package/docs/collapsible-panel.md +22 -13
  248. package/docs/component-contract.md +147 -21
  249. package/docs/component-selection.md +21 -8
  250. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -1
  251. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -1
  252. package/docs/design/templates/tab-bar-dark.svg +1 -1
  253. package/docs/design/templates/tab-bar.svg +1 -1
  254. package/docs/design-philosophy.md +30 -3
  255. package/docs/examples/application-ui-profile.json +4 -4
  256. package/docs/examples/{component-catalog-extension-v2.json → component-composition-extension.json} +3 -3
  257. package/docs/layout.md +82 -3
  258. package/docs/nav-stack.md +33 -3
  259. package/docs/split-view.md +10 -5
  260. package/docs/tab-scaffold.md +23 -1
  261. package/docs/type-contracts.md +4 -3
  262. package/docs/ui-analyzer.md +99 -5
  263. package/docs/ui-doctor.md +10 -0
  264. package/docs/workbench.md +31 -20
  265. package/doctor/index.mjs +6 -1
  266. package/llms.txt +85 -20
  267. package/package.json +17 -5
  268. package/ux-demo/recipes/collapsible-sidebar.tsx +0 -1
  269. package/ux-demo/recipes/composer-form.tsx +0 -1
  270. package/ux-demo/recipes/list-detail-dialog.tsx +0 -2
  271. package/ux-demo/recipes/list-workspace-states.tsx +0 -1
  272. package/ux-demo/recipes/loading-inspector.tsx +0 -1
  273. package/ux-demo/recipes/workspace-header.tsx +0 -1
  274. package/ai/component-catalog-extension-v2.d.ts +0 -7
  275. package/ai/component-catalog-extension-v2.schema.json +0 -7
  276. package/dist/chunk-AK67F6QN.js.map +0 -1
  277. package/dist/chunk-G33XC2NB.js.map +0 -1
  278. package/dist/chunk-K6UNVKDK.js.map +0 -1
  279. package/dist/chunk-MA6Z6EIM.js.map +0 -1
  280. package/dist/chunk-MOBRFZH3.js.map +0 -1
  281. package/dist/chunk-O3EXIBEZ.js.map +0 -1
  282. package/dist/chunk-PK4N5QTO.js.map +0 -1
  283. package/dist/chunk-QQOTQTDD.js.map +0 -1
  284. package/dist/chunk-T4UZTNZ4.js.map +0 -1
  285. package/dist/chunk-V65EOWNF.js.map +0 -1
  286. package/dist/chunk-VNPQDCQZ.js.map +0 -1
  287. package/dist/chunk-ZIYOJFZD.js.map +0 -1
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 150" width="672" height="150"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="96"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="96" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="150" rx="0" fill="rgb(28,28,30)"/><text x="16" y="28" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(174,174,178)">Selected tab among peers</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="96"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="28" y="28" width="584" height="44"/></clipPath><clipPath id="svgimg3ov1"><rect x="29" y="29" width="582" height="42" rx="21"/></clipPath><filter id="svgimg3sh2" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="svgimg3cp3"><rect x="67" y="42" width="16.4" height="16"/></clipPath><clipPath id="svgimg3cp5"><rect x="91.4" y="42.5" width="74.4" height="15"/></clipPath><clipPath id="svgimg3ct6"><rect x="91.4" y="42.5" width="74.4" height="15"/></clipPath><clipPath id="svgimg3cp7"><rect x="240.5" y="42" width="16.5" height="16"/></clipPath><clipPath id="svgimg3cp9"><rect x="264.5" y="42.5" width="47.7" height="15"/></clipPath><clipPath id="svgimg3ct10"><rect x="264.5" y="42.5" width="47.7" height="15"/></clipPath><clipPath id="svgimg3cp11"><rect x="387" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp13"><rect x="411" y="42.5" width="60" height="15"/></clipPath><clipPath id="svgimg3ct14"><rect x="411" y="42.5" width="60" height="15"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"><rect x="28.5" y="28.5" width="583" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/></g><g clip-path="url(#svgimg3ov1)"><rect x="30" y="31" width="173.1" height="40" rx="20" fill="rgba(0,0,0,0.5)" filter="url(#svgimg3sh2)"/><rect x="30" y="30" width="173.1" height="40" rx="20" fill="rgb(28,28,30)"/><rect x="30.5" y="30.5" width="172" height="39" rx="19.5" fill="none" stroke="rgba(72,72,74,0.8)" stroke-width="1"/><g clip-path="url(#svgimg3cp3)"><g color="rgb(138, 203, 255)"><clipPath id="svgimg3svgic4"><rect x="67.4" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic4)"><g transform="translate(67.39 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" 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(#svgimg3cp5)"><g clip-path="url(#svgimg3ct6)"><text x="91.39" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(138,203,255)">README.md</text></g></g></g><g clip-path="url(#svgimg3ov1)"><rect x="203.5" y="30.5" width="146" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp7)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic8"><rect x="240.5" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic8)"><g transform="translate(240.531 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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(#svgimg3cp9)"><g clip-path="url(#svgimg3ct10)"><text x="264.53" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(174,174,178)">index.ts</text></g></g></g><g clip-path="url(#svgimg3ov1)"><rect x="350.5" y="30.5" width="157" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp11)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic12"><rect x="387" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic12)"><g transform="translate(387.031 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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(#svgimg3cp13)"><g clip-path="url(#svgimg3ct14)"><text x="411.03" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(174,174,178)">styles.css</text></g></g></g></g></g></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 150" width="672" height="150"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="96"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="96" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="150" rx="0" fill="rgb(28,28,30)"/><text x="16" y="28" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="600" fill="rgb(174,174,178)">Selected tab among peers</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="96"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="28" y="28" width="584" height="44"/></clipPath><clipPath id="svgimg3ov1"><rect x="29" y="29" width="582" height="42" rx="21"/></clipPath><filter id="svgimg3sh2" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="svgimg3cp3"><rect x="67" y="42" width="16.4" height="16"/></clipPath><clipPath id="svgimg3cp5"><rect x="91.4" y="42.5" width="74.4" height="15"/></clipPath><clipPath id="svgimg3ct6"><rect x="91.4" y="42.5" width="74.4" height="15"/></clipPath><clipPath id="svgimg3cp7"><rect x="240.5" y="42" width="16.5" height="16"/></clipPath><clipPath id="svgimg3cp9"><rect x="264.5" y="42.5" width="47.7" height="15"/></clipPath><clipPath id="svgimg3ct10"><rect x="264.5" y="42.5" width="47.7" height="15"/></clipPath><clipPath id="svgimg3cp11"><rect x="387" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp13"><rect x="411" y="42.5" width="60" height="15"/></clipPath><clipPath id="svgimg3ct14"><rect x="411" y="42.5" width="60" height="15"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"><rect x="28.5" y="28.5" width="583" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/></g><g data-domotion-pseudo-source="blink-pseudo-fragment-v1" data-domotion-pseudo="::before" data-domotion-pseudo-owner="source-fragments"><g/></g><g data-domotion-pseudo-source="blink-pseudo-fragment-v1" data-domotion-pseudo="::after" data-domotion-pseudo-owner="source-fragments"><g/></g><g clip-path="url(#svgimg3ov1)"><rect x="30" y="31" width="173.1" height="40" rx="20" fill="rgba(0,0,0,0.5)" filter="url(#svgimg3sh2)"/><rect x="30" y="30" width="173.1" height="40" rx="20" fill="rgb(28,28,30)"/><rect x="30.5" y="30.5" width="172" height="39" rx="19.5" fill="none" stroke="rgba(72,72,74,0.8)" stroke-width="1"/><g clip-path="url(#svgimg3cp3)"><g color="rgb(138, 203, 255)"><clipPath id="svgimg3svgic4"><rect x="67.4" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic4)"><g transform="translate(67.39 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(138, 203, 255)" 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(#svgimg3cp5)"><g clip-path="url(#svgimg3ct6)"><text x="91.39" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(138,203,255)">README.md</text></g></g></g><g clip-path="url(#svgimg3ov1)"><rect x="203.5" y="30.5" width="146" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp7)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic8"><rect x="240.5" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic8)"><g transform="translate(240.531 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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(#svgimg3cp9)"><g clip-path="url(#svgimg3ct10)"><text x="264.53" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(174,174,178)">index.ts</text></g></g></g><g clip-path="url(#svgimg3ov1)"><rect x="350.5" y="30.5" width="157" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp11)"><g color="rgb(174, 174, 178)"><clipPath id="svgimg3svgic12"><rect x="387" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic12)"><g transform="translate(387.031 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(174, 174, 178)" 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(#svgimg3cp13)"><g clip-path="url(#svgimg3ct14)"><text x="411.03" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(174,174,178)">styles.css</text></g></g></g></g></g></g></g></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 150" width="672" height="150"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="96"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="96" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="150" 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)">Selected tab among peers</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="96"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="28" y="28" width="584" height="44"/></clipPath><clipPath id="svgimg3ov1"><rect x="29" y="29" width="582" height="42" rx="21"/></clipPath><filter id="svgimg3sh2" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="svgimg3cp3"><rect x="67" y="42" width="16.4" height="16"/></clipPath><clipPath id="svgimg3cp5"><rect x="91.4" y="42.5" width="74.4" height="15"/></clipPath><clipPath id="svgimg3ct6"><rect x="91.4" y="42.5" width="74.4" height="15"/></clipPath><clipPath id="svgimg3cp7"><rect x="240.5" y="42" width="16.5" height="16"/></clipPath><clipPath id="svgimg3cp9"><rect x="264.5" y="42.5" width="47.7" height="15"/></clipPath><clipPath id="svgimg3ct10"><rect x="264.5" y="42.5" width="47.7" height="15"/></clipPath><clipPath id="svgimg3cp11"><rect x="387" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp13"><rect x="411" y="42.5" width="60" height="15"/></clipPath><clipPath id="svgimg3ct14"><rect x="411" y="42.5" width="60" height="15"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"><rect x="28.5" y="28.5" width="583" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/></g><g clip-path="url(#svgimg3ov1)"><rect x="30" y="31" width="173.1" height="40" rx="20" fill="rgba(0,0,0,0.14)" filter="url(#svgimg3sh2)"/><rect x="30" y="30" width="173.1" height="40" rx="20" fill="rgb(255,255,255)"/><rect x="30.5" y="30.5" width="172" height="39" rx="19.5" fill="none" stroke="rgba(209,209,214,0.8)" stroke-width="1"/><g clip-path="url(#svgimg3cp3)"><g color="rgb(12, 98, 243)"><clipPath id="svgimg3svgic4"><rect x="67.4" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic4)"><g transform="translate(67.39 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" 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(#svgimg3cp5)"><g clip-path="url(#svgimg3ct6)"><text x="91.39" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(12,98,243)">README.md</text></g></g></g><g clip-path="url(#svgimg3ov1)"><rect x="203.5" y="30.5" width="146" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp7)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic8"><rect x="240.5" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic8)"><g transform="translate(240.531 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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(#svgimg3cp9)"><g clip-path="url(#svgimg3ct10)"><text x="264.53" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(110,110,115)">index.ts</text></g></g></g><g clip-path="url(#svgimg3ov1)"><rect x="350.5" y="30.5" width="157" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp11)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic12"><rect x="387" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic12)"><g transform="translate(387.031 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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(#svgimg3cp13)"><g clip-path="url(#svgimg3ct14)"><text x="411.03" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(110,110,115)">styles.css</text></g></g></g></g></g></g></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 672 150" width="672" height="150"><defs><clipPath id="cp0"><rect x="16" y="38" width="640" height="96"/></clipPath><clipPath id="ct1"><rect x="16" y="16" width="640" height="22"/></clipPath><clipPath id="irc2"><rect x="16" y="38" width="640" height="96" rx="0"/></clipPath></defs><rect x="0" y="0" width="672" height="150" 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)">Selected tab among peers</text><g clip-path="url(#cp0)"><g clip-path="url(#irc2)"><clipPath id="svgimg3vclip"><rect x="16" y="38" width="640" height="96"/></clipPath><g clip-path="url(#svgimg3vclip)"><g transform="translate(16 38)"><defs><clipPath id="svgimg3cp0"><rect x="28" y="28" width="584" height="44"/></clipPath><clipPath id="svgimg3ov1"><rect x="29" y="29" width="582" height="42" rx="21"/></clipPath><filter id="svgimg3sh2" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="1.5"/></filter><clipPath id="svgimg3cp3"><rect x="67" y="42" width="16.4" height="16"/></clipPath><clipPath id="svgimg3cp5"><rect x="91.4" y="42.5" width="74.4" height="15"/></clipPath><clipPath id="svgimg3ct6"><rect x="91.4" y="42.5" width="74.4" height="15"/></clipPath><clipPath id="svgimg3cp7"><rect x="240.5" y="42" width="16.5" height="16"/></clipPath><clipPath id="svgimg3cp9"><rect x="264.5" y="42.5" width="47.7" height="15"/></clipPath><clipPath id="svgimg3ct10"><rect x="264.5" y="42.5" width="47.7" height="15"/></clipPath><clipPath id="svgimg3cp11"><rect x="387" y="42" width="16" height="16"/></clipPath><clipPath id="svgimg3cp13"><rect x="411" y="42.5" width="60" height="15"/></clipPath><clipPath id="svgimg3ct14"><rect x="411" y="42.5" width="60" height="15"/></clipPath></defs><g clip-path="url(#svgimg3cp0)"><rect x="28.5" y="28.5" width="583" height="43" rx="21.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/></g><g data-domotion-pseudo-source="blink-pseudo-fragment-v1" data-domotion-pseudo="::before" data-domotion-pseudo-owner="source-fragments"><g/></g><g data-domotion-pseudo-source="blink-pseudo-fragment-v1" data-domotion-pseudo="::after" data-domotion-pseudo-owner="source-fragments"><g/></g><g clip-path="url(#svgimg3ov1)"><rect x="30" y="31" width="173.1" height="40" rx="20" fill="rgba(0,0,0,0.14)" filter="url(#svgimg3sh2)"/><rect x="30" y="30" width="173.1" height="40" rx="20" fill="rgb(255,255,255)"/><rect x="30.5" y="30.5" width="172" height="39" rx="19.5" fill="none" stroke="rgba(209,209,214,0.8)" stroke-width="1"/><g clip-path="url(#svgimg3cp3)"><g color="rgb(12, 98, 243)"><clipPath id="svgimg3svgic4"><rect x="67.4" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic4)"><g transform="translate(67.39 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(12, 98, 243)" 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(#svgimg3cp5)"><g clip-path="url(#svgimg3ct6)"><text x="91.39" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(12,98,243)">README.md</text></g></g></g><g clip-path="url(#svgimg3ov1)"><rect x="203.5" y="30.5" width="146" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp7)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic8"><rect x="240.5" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic8)"><g transform="translate(240.531 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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(#svgimg3cp9)"><g clip-path="url(#svgimg3ct10)"><text x="264.53" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(110,110,115)">index.ts</text></g></g></g><g clip-path="url(#svgimg3ov1)"><rect x="350.5" y="30.5" width="157" height="39" rx="19.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#svgimg3cp11)"><g color="rgb(110, 110, 115)"><clipPath id="svgimg3svgic12"><rect x="387" y="42" width="16" height="16"/></clipPath><g clip-path="url(#svgimg3svgic12)"><g transform="translate(387.031 42)scale(.66667)"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2 2 0 0 1 1.7.7l3.6 3.6A2 2 0 0 1 20 8v12a2 2 0 0 1-2 2Z" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M14 2v5a1 1 0 0 0 1 1h5" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M10 9H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 13H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M16 17H8" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(110, 110, 115)" 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(#svgimg3cp13)"><g clip-path="url(#svgimg3ct14)"><text x="411.03" y="54.5" font-family="system-ui, &quot;-apple-system&quot;, sans-serif" font-size="12" font-weight="650" fill="rgb(110,110,115)">styles.css</text></g></g></g></g></g></g></g></svg>
@@ -22,13 +22,17 @@ Unrelated updates must not close controls, move focus, reset a draft, replace a
22
22
 
23
23
  ### Hierarchy precedes decoration
24
24
 
25
- Establish one reading order and one dominant action per decision point. Group first with alignment, spacing, typography, and shared surfaces. A border, fill, badge, or nested card must communicate a real distinction.
25
+ Establish one reading order and one dominant action per decision point. Group first with alignment, spacing, typography, and shared surfaces. A border, fill, badge, or nested card must communicate a real distinction. A divider is chrome too: a line between a pane's own header and its list, or under a toolbar whose content already spaces itself, marks nothing and is dropped (`dividerSides=""`), never padded around. Put a control near where its effect appears — a collapsed drawer's restore beside the drawer, not in a distant top toolbar.
26
26
 
27
27
  ### Reach for the primitive, not for CSS
28
28
 
29
29
  The package is designed to look right _unstyled_. A screen built from the
30
30
  primitives, their props, and the semantic tokens should already read well, so
31
- applications do not carry a component-customization stylesheet. Before adding any `padding`, `margin`, `width`,
31
+ applications do not carry a component-customization stylesheet. Prefer a Kerf
32
+ UI component over a Web Awesome component over a raw HTML tag, using each for
33
+ what it is good at; a `Discouraged` Web Awesome element is reached only through
34
+ the Kerf component that wraps it (`PopupMenu`, `Select`), never rendered
35
+ directly. Before adding any `padding`, `margin`, `width`,
32
36
  `height`, `border`, `background`, wrapper card, or decoration, check whether the
33
37
  component, the pane, or the content-item already owns it — it almost always does,
34
38
  and adding more usually **double-insets** or fights the component (the most common
@@ -40,13 +44,36 @@ configure those children through props and documented tokens instead. Never size
40
44
  space, or re-skin a primitive by hand, and never build a heading, toolbar, card,
41
45
  or pane geometry that a primitive provides.
42
46
 
47
+ ### Components own their styles; configure, never override
48
+
49
+ Every component styles only itself. How it looks and sits is configuration —
50
+ typed props and data attributes such as size, density, inset, presentation,
51
+ tone, `dividerSides`, `ToolbarConfig`, and `PaneConfig` — never a stylesheet
52
+ somewhere else reaching into it. When a component must adapt inside another
53
+ (a `PopupMenu` inside a compact `ToolbarControlGroup`, an `AppTab` in a
54
+ segmented `TabBar`), the **child** styles itself in that context, in its own
55
+ stylesheet; the parent keys only its own elements on a child's state
56
+ (`:has(> .kui-select[open])`) and names kerf children by class, not by `wa-*`
57
+ tag. Values a parent provides to its children are custom properties named after
58
+ the parent. A component never writes another component's private variables and
59
+ never puts a hook class on a child's root to restyle it.
60
+
61
+ The same holds for applications and component packages built on Kerf UI: expose
62
+ configuration on your own components, configure kerf components through their
63
+ props, and treat a missing prop as a component gap to report, not a reason to
64
+ override. `npm run check:css-ownership` enforces this inside the package.
65
+
43
66
  ### Panes share one child-owned geometry
44
67
 
45
68
  Sidebars, main areas, inspectors, and dialogs use the same unpadded pane. Their
46
69
  children own margin, border, background, padding, and radius, so a transparent
47
70
  surface occupies exactly the same geometry as a visible one. Major content
48
71
  groups use 24px vertical separation; the inside of an item and the gap between
49
- toolbar groups use 8px. Rows and actions keep 44px targets.
72
+ toolbar groups use 8px. Rows and actions keep 44px targets. Align text to text
73
+ and icons to icons: a title, a row label, and bare prose share one text column,
74
+ and a border is decoration, never the alignment anchor — a bordered surface
75
+ extends to the pane gutter rather than being indented so its border lines up
76
+ with a heading's text.
50
77
 
51
78
  Use the package composition so the geometry has one owner:
52
79
 
@@ -10,15 +10,15 @@
10
10
  "schemaVersion": 1
11
11
  },
12
12
  "composition": {
13
- "path": "../../ai/component-catalog-v2.json",
14
- "schemaVersion": 2
13
+ "path": "../../ai/component-composition.json",
14
+ "schemaVersion": 1
15
15
  }
16
16
  },
17
17
  {
18
18
  "package": "@acme/ui",
19
19
  "composition": {
20
- "path": "./component-catalog-extension-v2.json",
21
- "schemaVersion": 2
20
+ "path": "./component-composition-extension.json",
21
+ "schemaVersion": 1
22
22
  }
23
23
  }
24
24
  ],
@@ -1,9 +1,9 @@
1
1
  {
2
- "$schema": "../../ai/component-catalog-extension-v2.schema.json",
3
- "schemaVersion": 2,
2
+ "$schema": "../../ai/component-composition-extension.schema.json",
3
+ "schemaVersion": 1,
4
4
  "package": "@acme/ui",
5
5
  "compatibility": {
6
- "v1Catalog": "./component-catalog-extension.json",
6
+ "componentCatalog": "./component-catalog-extension.json",
7
7
  "identity": "package:id"
8
8
  },
9
9
  "entries": [
package/docs/layout.md CHANGED
@@ -42,6 +42,72 @@ each enabled side uses `--kui-pane-separator-width` (1px) and
42
42
  `--kui-pane-separator-color` (`--kui-color-neutral-border-normal`). Logical sides keep pane
43
43
  boundaries correct in both left-to-right and right-to-left layouts.
44
44
 
45
+ ### Scroll dividers
46
+
47
+ Toolbars draw no divider by default. The line between pinned chrome and the
48
+ content that scrolls beside it is scroll state, not decoration: it shows only
49
+ while content is scrolled away from that edge, the way platform navigation bars
50
+ gain a hairline once content moves beneath them.
51
+
52
+ - A near edge (top, left) hides at the scroll start.
53
+ - A far edge (bottom, right) hides at the scroll end, and whenever the content
54
+ fits and nothing scrolls.
55
+
56
+ Call `wireScrollDividers` from `@kerfjs/ui/wire-scroll-dividers` once at the
57
+ application root and keep its disposer:
58
+
59
+ ```ts
60
+ import { wireScrollDividers } from "@kerfjs/ui/wire-scroll-dividers";
61
+
62
+ const stop = wireScrollDividers(appRoot);
63
+ ```
64
+
65
+ It covers, by structure, every `Pane` below the root (the header's bottom line
66
+ and the footer's top line, whatever the chrome holds — a `Toolbar`, a
67
+ `mainHeader` list, a `TabBar`) and every `TabBar` strip (a line on each side
68
+ whose tabs are scrolled out of view). That includes the panes a `Workbench`,
69
+ `CollapsiblePanel`, or `Catalog` renders; `wireCatalog` installs it for its
70
+ own root. It also covers every `NavStack` (the top chrome's bottom line and the
71
+ bottom toolbar's top line, around the active view) and every `TabScaffold`
72
+ (the bar's top line, over the active scene). The layout keys on whichever
73
+ element actually scrolls there: the view or scene itself, or, through a sole
74
+ child with no chrome of its own on that edge, a `Pane`'s content or a nested
75
+ `NavStack`'s active view. A sole `Pane` fills its view or scene, so its content
76
+ scrolls under its pinned header; with a header it draws the line under that
77
+ header itself, so the stack's chrome never doubles it. It follows re-renders: panes that appear later are paired, and an
78
+ attribute a morph drops returns before paint.
79
+
80
+ The wiring only reports state; every component draws its own line. It writes
81
+ `data-scroll-overflow` (the edges with content hidden beyond them, in canonical
82
+ `t`/`r`/`b`/`l` order) on each scroller and `data-scroll-divider` (the sides to
83
+ draw) on each piece of chrome, and the Pane, NavStack, TabScaffold, TabBar,
84
+ Toolbar, and List stylesheets draw from those. The line is an inset shadow or a pseudo-element
85
+ inside the component, so no state ever moves the chrome, the content, or a tab.
86
+ Never write a border onto a component to show one.
87
+
88
+ Configure a pane with `chromeDividers`: `scroll` (the default) follows the
89
+ scroll state and draws nothing until the wiring runs; `always` keeps both lines;
90
+ `none` drops them. Layouts forward it through `PaneConfig`
91
+ (`mainPane.chromeDividers`, a panel's `pane.chromeDividers`). A `NavStack` and a
92
+ `TabScaffold` take the same `chromeDividers` for their own chrome (the stack's
93
+ top chrome and bottom toolbar, the scaffold's bar), and a `SplitView` forwards
94
+ it to its compact stack through `compactStack`.
95
+
96
+ For an app-owned arrangement outside a `Pane`, name the scroller and its chrome
97
+ by `id` in `targets`; a `Toolbar` or `List` named as chrome draws the divider
98
+ on its side facing the scroller:
99
+
100
+ ```ts
101
+ wireScrollDividers(appRoot, {
102
+ targets: [{ scroller: "results", top: "results-toolbar", bottom: "status" }],
103
+ });
104
+ ```
105
+
106
+ A CSS-only version is not used: scroll-driven animations are not available in
107
+ Firefox, scroll-state container queries only in Chromium, and neither lets
108
+ the pinned chrome — a sibling of the scroller — read the scroller's state on
109
+ its own. One wiring keeps the behavior identical in every engine.
110
+
45
111
  A pane is safe-area aware. Its background and separators paint through a
46
112
  device's unsafe areas while its header, content, and footer pad for each side
47
113
  the pane still reaches; the content's block insets are scroll padding, so its
@@ -107,9 +173,22 @@ The rendered classes — `.kui-content-item`, `.kui-content-item--framed`, and
107
173
  `.kui-content-item--pill` — stay public for the rare element that must carry the
108
174
  geometry itself (for example a `<ul>` list or a `Text` paragraph in an
109
175
  application-owned adapter). Prefer `ContentItem` everywhere else so the framing
110
- choice is typed. `npm run check:guidance` rejects a plain
111
- `<div class="kui-content-item">` in the package's examples, browser fixtures,
112
- and UX catalog, since that div is exactly what `ContentItem` renders.
176
+ choice is typed. A plain `<div class="kui-content-item">` is exactly what
177
+ `ContentItem` renders, so the catalog records that element
178
+ (`rootElement: "div"`): `eslint-plugin-kerfjs`'s `ui-public-boundaries` reports
179
+ it in application code as `KUI-L103` ("render `ContentItem`"), and
180
+ `npm run check:guidance` derives the same boundary from the catalog to reject it
181
+ in the package's examples, browser fixtures, and UX catalog.
182
+
183
+ These classes, the layout utilities (`.kui-content`, `.kui-scroll-owner`,
184
+ `.kui-control-cluster`, `.kui-inline-metadata`), and `.kui-app-root` are the
185
+ catalog's `boundaries.placeableClasses`: the only public classes an application
186
+ writes onto its own elements. Every other public class is a component's
187
+ rendered anatomy — render `Pane` rather than writing `.kui-pane` /
188
+ `.kui-pane__content` onto an `<aside>` — and `eslint-plugin-kerfjs`'s
189
+ `ui-public-boundaries` reports it on an application-owned element as
190
+ `KUI-L103`. Selecting those classes in CSS to place your own content in a
191
+ component's context stays allowed (see `KUI-L019`).
113
192
 
114
193
  ## Public roles and tokens
115
194
 
package/docs/nav-stack.md CHANGED
@@ -5,14 +5,19 @@ and out over one another while the top chrome settles. A **single-pane layout is
5
5
  a `NavStack` with one entry**. It is one of the opt-in app layouts (see
6
6
  [`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
7
7
 
8
- Import the component and its companion stylesheet:
8
+ Import the component and its wiring helper:
9
9
 
10
10
  ```ts
11
11
  import { NavStack, type NavStackView } from "@kerfjs/ui/nav-stack";
12
12
  import { wireNavStack } from "@kerfjs/ui/wire-nav-stack";
13
- import "@kerfjs/ui/nav-stack.css";
14
13
  ```
15
14
 
15
+ In a browser bundler that honors the `browser` export condition (Vite, esbuild, and
16
+ webpack do by default), the import above also
17
+ loads NavStack's stylesheet and those of the components it renders internally
18
+ (`Toolbar`, `ToolbarControlGroup`, `ToolbarText`, `LucideIcon`). Without that condition, import the manual stylesheets instead:
19
+ `@kerfjs/ui/nav-stack.css` plus those components' CSS, or `@kerfjs/ui/styles.css`.
20
+
16
21
  ## State lives in the app
17
22
 
18
23
  Like every `@kerfjs/ui` component, `NavStack` is declarative: the app owns the
@@ -87,7 +92,7 @@ Configure the toolbar with `toolbarConfig` instead of styling it:
87
92
  `label` (the toolbar's accessible name), `titleSize` (the title's
88
93
  `ToolbarText` size, default `large`), and `headingLevel` (expose the title as
89
94
  a heading; default a plain span). The defaults keep the stack's own chrome:
90
- no divider, one 44px control band, and the top and side safe-area edges
95
+ no divider, the standard Toolbar height (a 44px control band with 8px above and below), and the top and side safe-area edges
91
96
  claimed.
92
97
  - `backIcon` replaces the default chevron-left icon. `backText` adds visible
93
98
  text beside it (for example, the previous view's title); visible text names
@@ -109,6 +114,31 @@ When a remembered control was removed during the controlled rerender, the same
109
114
  fallback order applies. Disposing the helper stops future transitions and
110
115
  cleans up any temporary fallback `tabindex` it added.
111
116
 
117
+ ## Scroll dividers
118
+
119
+ The stack draws no permanent line between its chrome and the view. With
120
+ `wireScrollDividers(appRoot)` installed (see
121
+ [Layout › Scroll dividers](layout.md#scroll-dividers)), the top chrome shows a
122
+ line along its bottom edge only while the active view's content is scrolled
123
+ beneath it, and the bottom toolbar a line along its top edge only while more
124
+ content lies below it — neither when the content fits. The line follows the
125
+ active view across a push or pop. The chrome's line is overlaid on its bottom
126
+ edge and the bottom toolbar's is its always-present 1px top border, colored
127
+ only while shown, so neither ever moves the chrome or the content. When the view's only child is a `Pane`,
128
+ the line keys on whichever actually scrolls — the view, or the Pane's content —
129
+ and a Pane header or footer draws its own boundary instead. An explicit
130
+ `toolbarConfig.dividerSides` still draws a permanent Toolbar edge.
131
+
132
+ `chromeDividers` configures both lines, with the same values as a `Pane`'s:
133
+ `scroll` (the default) follows the scroll state above and draws nothing until
134
+ the wiring runs; `always` keeps both lines, with or without the wiring; `none`
135
+ drops them even when the wiring reports scroll state. It does not change the
136
+ top Toolbar's own `dividerSides`.
137
+
138
+ ```tsx
139
+ <NavStack id="settings" label="Settings" views={views.value} chromeDividers="always" />
140
+ ```
141
+
112
142
  ## Safe areas
113
143
 
114
144
  The stack paints through a device's unsafe areas. Its chrome pads for the top
@@ -8,11 +8,16 @@ on compact ones. One of the opt-in app layouts (see
8
8
  ```ts
9
9
  import { SplitView } from "@kerfjs/ui/split-view";
10
10
  import { deviceClass } from "@kerfjs/ui/device-class";
11
- import "@kerfjs/ui/split-view.css";
12
- // plus nav-stack.css when the compact path is reachable, and
13
- // wireResizableRegions / wireNavStack for the interactive behavior.
11
+ // plus wireResizableRegions / wireNavStack for the interactive behavior.
14
12
  ```
15
13
 
14
+ In a browser bundler that honors the `browser` export condition (Vite, esbuild, and
15
+ webpack do by default), the import above also
16
+ loads SplitView's stylesheet and those of the components it renders internally
17
+ (its compact `NavStack`, `ResizableRegion`, and the toolbar
18
+ components). Without that condition, import the manual stylesheets instead:
19
+ `@kerfjs/ui/split-view.css` plus those components' CSS, or `@kerfjs/ui/styles.css`.
20
+
16
21
  ## Safe areas
17
22
 
18
23
  The list pane grows by the inline-start unsafe inset and pads for the edges it
@@ -75,8 +80,8 @@ resizable={{
75
80
  ```
76
81
 
77
82
  `compactStack` configures the compact `NavStack`: `toolbarConfig`, `backIcon`,
78
- `backText`, `hideToolbar`, and the persistent `bottomToolbar` forward to the
79
- stack, and `list` / `detail` give each view its own top-toolbar groups
83
+ `backText`, `hideToolbar`, the persistent `bottomToolbar`, and `chromeDividers`
84
+ forward to the stack, and `list` / `detail` give each view its own top-toolbar groups
80
85
  (`leading`, `center`, trailing `toolbar`) and `bottomToolbar`:
81
86
 
82
87
  ```tsx
@@ -9,9 +9,14 @@ It is distinct from `TabBar` (document-oriented, reorderable strips).
9
9
  ```ts
10
10
  import { TabScaffold } from "@kerfjs/ui/tab-scaffold";
11
11
  import { wireTabScaffold } from "@kerfjs/ui/wire-tab-scaffold";
12
- import "@kerfjs/ui/tab-scaffold.css";
13
12
  ```
14
13
 
14
+ In a browser bundler that honors the `browser` export condition (Vite, esbuild, and
15
+ webpack do by default), the import above also
16
+ loads TabScaffold's stylesheet and those of the components it renders internally
17
+ (the tab `Badge`). Without that condition, import the manual stylesheets instead:
18
+ `@kerfjs/ui/tab-scaffold.css` plus those components' CSS, or `@kerfjs/ui/styles.css`.
19
+
15
20
  ## Controlled selection
16
21
 
17
22
  The app owns the active tab (a signal); `TabScaffold` renders every tab's scene
@@ -54,6 +59,23 @@ truncate horizontally with an ellipsis when a destination name exceeds its
54
59
  share of the bar. On larger device classes, promote the tab set to a
55
60
  `Workbench` rail or a persistent sidebar instead of a bottom bar.
56
61
 
62
+ ## Scroll divider
63
+
64
+ The bar has no permanent top border. With `wireScrollDividers(appRoot)`
65
+ installed (see [Layout › Scroll dividers](layout.md#scroll-dividers)), it shows
66
+ a line along its top edge only while the active scene's content continues below
67
+ it — not at the scroll end and not when the content fits. The line keys on
68
+ whatever actually scrolls in the scene: the scene itself or, through a sole
69
+ child, a `NavStack`'s active view or a `Pane`'s content. A `NavStack` with its
70
+ own bottom toolbar sits between them, so that toolbar's line applies instead.
71
+ The line is the bar's always-present 1px top border, colored only while shown,
72
+ so it never moves the bar or a tab.
73
+
74
+ `chromeDividers` configures it, with the same values as a `Pane`'s: `scroll`
75
+ (the default) follows the scroll state and draws nothing until the wiring runs;
76
+ `always` keeps the line, with or without the wiring; `none` drops it even when
77
+ the wiring reports content below.
78
+
57
79
  ## Tab badges
58
80
 
59
81
  Give a tab a `badge` (a count or short string such as `3`, `"99+"`, or
@@ -113,9 +113,10 @@ move errors into casts without making the integration safer.
113
113
  `{ keyboard: { removeAdjacentToken: false } }` when only caret navigation is
114
114
  wanted.
115
115
  - Add `wrap: true` anywhere `ToolbarText.maxLines` is used.
116
- - Replace `Toolbar({ divider: false })` with `dividerSides: ''`; the default
117
- remains a bottom divider, while canonical combinations such as `tr` and
118
- `trbl` select more physical edges.
116
+ - Replace `Toolbar({ divider: false })` with `dividerSides: ''`, which is now
117
+ the default (a toolbar pinned over scrolling content gets its divider from
118
+ `wireScrollDividers` through its `Pane`); canonical combinations such as `b`,
119
+ `tr`, and `trbl` select permanent physical edges.
119
120
  - Replace raw `List.gap` strings with a direct spacing shorthand (`"xs"`,
120
121
  `"m"`) or a complete value from `@kerfjs/ui/css-values`. Replace
121
122
  `gap="0.25rem"` with `gap={rem(0.25)}` and wrap sums with `calc(plus(...))`.
@@ -61,7 +61,7 @@ policy, or analysis inputs to the containing application.
61
61
  | `KUI-L002` | error | A stylesheet references an unknown or private `--kui-*` token. |
62
62
  | `KUI-L003` | error | One element combines public classes that claim the same geometry. |
63
63
  | `KUI-L004` | review | Nested literal classes both add a content inset. |
64
- | `KUI-L005` | review | Consumer CSS forces a public component's dimensions. |
64
+ | `KUI-L005` | — | Retired; folded into `KUI-L019` (see below). |
65
65
  | `KUI-L006` | review | Literal spacing falls outside Kerf's approved scale. |
66
66
  | `KUI-L007` | error | A declared scroll owner is nested inside another scroll owner. |
67
67
  | `KUI-L008` | review | A dynamic class expression cannot be classified soundly. |
@@ -75,6 +75,10 @@ policy, or analysis inputs to the containing application.
75
75
  | `KUI-L016` | error | A removed declaration-list escape hatch is used. |
76
76
  | `KUI-L017` | review | A valid but exceptional off-scale shorthand needs justification. |
77
77
  | `KUI-L018` | review | A loud fill override lacks a governing `on-loud`, or the literal pair is below 4.5:1. |
78
+ | `KUI-L019` | error | A rule's subject is another package's component (class, `data-component`, or tag). |
79
+ | `KUI-L020` | error | CSS reads or writes another component's private `--_*` variable. |
80
+ | `KUI-L021` | error | CSS overrides a component token that a typed prop sets. |
81
+ | `KUI-L022` | error | A class placed on a component's root is styled by the application (a hook class). |
78
82
 
79
83
  Errors are provable contract violations and make the command exit 1. Review
80
84
  findings are deliberately heuristic and do not fail by default; pass
@@ -92,9 +96,10 @@ CSS extension points. A component catalog may additionally list shadow-part
92
96
  names in `boundaries.publicParts`; all other `::part()` targets are private.
93
97
  Part names are scoped to that entry's `boundaries.rootClass`, so an extension
94
98
  point on one component does not authorize the same-named part on another.
95
- Parent-owned placement and selectors for application-owned content remain
96
- allowed because the analyzer only reserves `.kui-*`, `--kui-*`, and cataloged
97
- shadow boundaries.
99
+ Selectors for application-owned content remain allowed, including in a
100
+ component's context (`.kui-pane > .my-content`): a public class is an
101
+ extension point for naming a context, not for restyling the component itself,
102
+ which `KUI-L019` reports.
98
103
 
99
104
  Catalog entries may also publish `cssValueProps`. Each path names its grammar,
100
105
  finite shorthands, canonical and exceptional scale steps, accepted typed
@@ -103,6 +108,94 @@ uses that same metadata for direct component calls and JSX, including nested
103
108
  paths such as `choices[].color`; consumer catalogs receive identical checks.
104
109
  Dynamic values remain a type-system responsibility rather than being guessed.
105
110
 
111
+ ## Component ownership (`KUI-L019`–`KUI-L022`)
112
+
113
+ Components own their styles and are configured, never overridden. These
114
+ diagnostics apply the package's own `check:css-ownership` rules to
115
+ applications and component packages; the selector parsing is the same module
116
+ (`analyzer/selectors.mjs`), so the package rule and the consumer rule agree on
117
+ which element a selector styles.
118
+
119
+ - **`KUI-L019` — a rule restyles a cataloged component.** The rule's subject
120
+ (the rightmost compound of each selector, ignoring `:has()` and `:not()`
121
+ arguments) names a public class of a cataloged component (`.kui-toolbar`,
122
+ `.kui-list-item__label`, `.acme-meter`), its `[data-component="…"]` root, or
123
+ a Web Awesome tag the package themes (`wa-button`, `wa-select`), and the rule
124
+ sets at least one ordinary property. A `::part()` subject is left to
125
+ `KUI-L011`; a rule that only sets custom properties is configuration, judged
126
+ by the token rules. Forcing a component's dimensions is one such restyle
127
+ and reports here once: `.kui-pane { width: 300px }`.
128
+ - **`KUI-L020` — another component's private variable.** CSS reads or writes a
129
+ `--_<root>-*` variable of a cataloged component (`--_kui-list-gap`), or any
130
+ `--_kui-*` variable.
131
+ - **`KUI-L021` — a token a typed prop sets.** CSS assigns `--<root>-<prop>`
132
+ where the component has that typed prop (`--kui-list-gap` is `List gap`,
133
+ `--kui-floating-toolbar-inset` is `FloatingToolbar inset`). The message
134
+ quotes the prop's cataloged example.
135
+ - **`KUI-L022` — a hook class on a component's root.** A literal class on an
136
+ imported component's JSX (`<Toolbar className="header" />`) is the subject
137
+ of a rule in a stylesheet that file imports, so the application restyles the
138
+ component through it. The evidence names the stylesheet, line, and selector.
139
+
140
+ Every message names the configuration to use (the component's typed props,
141
+ public tokens, and variants from its catalog entry) and ends with the same
142
+ route for a real need that has none: report the component gap to the owning
143
+ package (open a feature request) instead of overriding it.
144
+
145
+ What stays allowed mirrors the package rule, where a child styles itself in a
146
+ parent's context:
147
+
148
+ ```css
149
+ /* Your own element in a component's context. */
150
+ .kui-toolbar > .my-widget {
151
+ margin-inline-start: auto;
152
+ }
153
+ /* Keying your own element on a component's state. */
154
+ .shell:has(.kui-select[open]) .my-status {
155
+ opacity: 0.5;
156
+ }
157
+ /* Configuring a component through a public token with no typed prop. */
158
+ .kui-list {
159
+ --kui-list-divider-color: var(--kui-color-neutral-border-quiet);
160
+ }
161
+
162
+ /* KUI-L019, KUI-L020, KUI-L021. */
163
+ .kui-toolbar {
164
+ padding: 0;
165
+ }
166
+ .app {
167
+ --_kui-list-gap: 2px;
168
+ --kui-list-gap: 4px;
169
+ }
170
+ ```
171
+
172
+ Ownership is decided per package: a stylesheet belongs to the package whose
173
+ `package.json` is nearest it, and only other packages' catalog entries are
174
+ foreign to it. A component package built on Kerf UI therefore styles its own
175
+ cataloged components (including `.kui-toolbar > .acme-meter` from its own
176
+ stylesheet) without findings, while an application that restyles
177
+ `.acme-meter` is reported against `@acme/widgets`. All four ids are
178
+ ownership-boundary diagnostics, so `--adoption` reports them as review
179
+ findings. `eslint-plugin-kerfjs`'s `ui-component-ownership` rule reports
180
+ `KUI-L020` and `KUI-L021` in JavaScript and TypeScript (style strings, style
181
+ objects, and `style.setProperty()`), where this analyzer reads only CSS.
182
+
183
+ ### Retired: `KUI-L005` (forced component dimension)
184
+
185
+ `KUI-L005` used to report, as a review finding, any `width`/`height`/`min-*`/
186
+ `max-*` declaration in a rule that mentioned a public component class
187
+ anywhere in its selector. `KUI-L019` now reports every restyle whose subject is
188
+ a cataloged component — dimensions included — so the subject case was
189
+ double-reported (an error and a review finding for one declaration). Its only
190
+ other shape, a public class as an ancestor
191
+ (`.kui-pane .app-sidebar { width: 240px }`), sizes the application's own
192
+ element in the component's context, which the ownership rules explicitly
193
+ allow. With no unique true positive left,
194
+ the rule was retired rather than narrowed. The analyzer no longer emits it;
195
+ the id stays in `ai/application-ui-diagnostic-ids-v1.json` (listed under
196
+ `retired`) so a profile exception that already names it still loads. Remove
197
+ such exceptions at your convenience.
198
+
106
199
  ## Loud fill / on-loud pairing (`KUI-L018`)
107
200
 
108
201
  The shipped Web Awesome theme pairs every `--wa-color-{tone}-fill-loud` with a
@@ -207,6 +300,7 @@ validator.
207
300
 
208
301
  Review findings remain visible until explicitly suppressed. Avoid suppressing
209
302
  `KUI-L001`, `KUI-L002`, `KUI-L003`, `KUI-L007`, `KUI-L009`, `KUI-L010`,
210
- `KUI-L011`, `KUI-L012`, `KUI-L013`, `KUI-L014`, `KUI-L015`, or `KUI-L016`:
303
+ `KUI-L011`, `KUI-L012`, `KUI-L013`, `KUI-L014`, `KUI-L015`, `KUI-L016`,
304
+ `KUI-L019`, `KUI-L020`, `KUI-L021`, or `KUI-L022`:
211
305
  those indicate a
212
306
  definite boundary or parsing failure rather than an aesthetic judgment.
package/docs/ui-doctor.md CHANGED
@@ -30,6 +30,16 @@ that lacks a governing `--wa-color-{tone}-on-loud` (in the same or an ancestor
30
30
  scope, in any stylesheet the same entry loads), or whose literal pair measures
31
31
  below 4.5:1 (see [the analyzer rule](./ui-analyzer.md#loud-fill--on-loud-pairing-kui-l018)).
32
32
 
33
+ Component ownership is part of the same loop. The analyzer stage reports
34
+ application or third-party CSS that restyles another package's component
35
+ (`KUI-L019`), touches its private `--_*` variables (`KUI-L020`), overrides a
36
+ token a typed prop sets (`KUI-L021`), or styles it through a hook class on its
37
+ root (`KUI-L022`); the ESLint stage's `ui-component-ownership` rule reports
38
+ `KUI-L020` / `KUI-L021` in script. Each finding's `action` points at the
39
+ component's configuration, and its message routes a need with no prop to a
40
+ component-gap report instead of an override (see
41
+ [the analyzer rule](./ui-analyzer.md#component-ownership-kui-l019kui-l022)).
42
+
33
43
  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.
34
44
 
35
45
  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.