@kerfjs/ui 5.0.0-beta.23 → 5.0.0-beta.25

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 (288) hide show
  1. package/README.md +45 -23
  2. package/ai/application-ui-diagnostic-ids-v1.json +3 -0
  3. package/ai/application-ui-profile.defaults.json +1 -1
  4. package/ai/compile-time-contracts-v1.json +5 -12
  5. package/ai/component-catalog-v2-overrides.json +12 -49
  6. package/ai/component-catalog-v2.d.ts +1 -0
  7. package/ai/component-catalog-v2.json +458 -123
  8. package/ai/component-catalog-v2.schema.json +2 -1
  9. package/ai/component-catalog.json +658 -195
  10. package/ai/public-api-signatures-v1.md +160 -80
  11. package/ai/skill.md +22 -22
  12. package/ai/webawesome-jsx-signatures-v1.md +2 -1
  13. package/analyzer/cli.mjs +5 -2
  14. package/analyzer/index.d.mts +1 -0
  15. package/analyzer/index.mjs +117 -10
  16. package/dist/app-tab.d.ts +10 -2
  17. package/dist/app-tab.js +1 -1
  18. package/dist/browser/list-action-row.js +1 -0
  19. package/dist/browser/list-item.js +1 -0
  20. package/dist/browser/list.js +3 -0
  21. package/dist/browser/surface-scaffold.js +3 -0
  22. package/dist/catalog.d.ts +8 -4
  23. package/dist/catalog.js +21 -11
  24. package/dist/catalog.js.map +1 -1
  25. package/dist/{chunk-ELSJX4W7.js → chunk-4EF2CMYS.js} +25 -6
  26. package/dist/chunk-4EF2CMYS.js.map +1 -0
  27. package/dist/{chunk-OHU6CI22.js → chunk-B3A3IVO5.js} +2 -2
  28. package/dist/{chunk-OHU6CI22.js.map → chunk-B3A3IVO5.js.map} +1 -1
  29. package/dist/{chunk-K57KRAOV.js → chunk-FRVS5RFM.js} +24 -6
  30. package/dist/chunk-FRVS5RFM.js.map +1 -0
  31. package/dist/{chunk-DSNIYOZ3.js → chunk-GWEAPEZI.js} +16 -3
  32. package/dist/chunk-GWEAPEZI.js.map +1 -0
  33. package/dist/chunk-HP2B2L26.js +102 -0
  34. package/dist/chunk-HP2B2L26.js.map +1 -0
  35. package/dist/{chunk-OZWU5ZPZ.js → chunk-IJBSZ4NX.js} +2 -2
  36. package/dist/{chunk-OZWU5ZPZ.js.map → chunk-IJBSZ4NX.js.map} +1 -1
  37. package/dist/chunk-J5BFYY7Q.js +35 -0
  38. package/dist/chunk-J5BFYY7Q.js.map +1 -0
  39. package/dist/{chunk-GP5DP5DL.js → chunk-JFGXQBIP.js} +85 -24
  40. package/dist/chunk-JFGXQBIP.js.map +1 -0
  41. package/dist/{chunk-QQFJMRIB.js → chunk-KWWR5VMS.js} +2 -2
  42. package/dist/{chunk-QQFJMRIB.js.map → chunk-KWWR5VMS.js.map} +1 -1
  43. package/dist/chunk-OBLTAKSX.js +43 -0
  44. package/dist/chunk-OBLTAKSX.js.map +1 -0
  45. package/dist/{chunk-PIDTEQ3U.js → chunk-OT6RPYAP.js} +17 -3
  46. package/dist/chunk-OT6RPYAP.js.map +1 -0
  47. package/dist/{chunk-W7YD6E4N.js → chunk-R7ZWUN64.js} +2 -2
  48. package/dist/{chunk-W7YD6E4N.js.map → chunk-R7ZWUN64.js.map} +1 -1
  49. package/dist/{chunk-PZQVSDLH.js → chunk-RKVQEH4J.js} +1 -1
  50. package/dist/chunk-RKVQEH4J.js.map +1 -0
  51. package/dist/{chunk-7L6EHUMV.js → chunk-SYDMYBPG.js} +3 -3
  52. package/dist/chunk-SYDMYBPG.js.map +1 -0
  53. package/dist/{chunk-W4CXQYSG.js → chunk-TB6DY7H5.js} +3 -1
  54. package/dist/chunk-TB6DY7H5.js.map +1 -0
  55. package/dist/{chunk-AASLYRGE.js → chunk-UKKSMZYT.js} +18 -7
  56. package/dist/chunk-UKKSMZYT.js.map +1 -0
  57. package/dist/{chunk-BBRXX6PF.js → chunk-VCE4MPVE.js} +26 -4
  58. package/dist/chunk-VCE4MPVE.js.map +1 -0
  59. package/dist/{chunk-VLYV65LK.js → chunk-W5L2JSBY.js} +5 -3
  60. package/dist/chunk-W5L2JSBY.js.map +1 -0
  61. package/dist/{chunk-5VTB2BKP.js → chunk-WNWD54GR.js} +16 -2
  62. package/dist/chunk-WNWD54GR.js.map +1 -0
  63. package/dist/{chunk-M7JBER5U.js → chunk-X2U3QJCJ.js} +8 -2
  64. package/dist/chunk-X2U3QJCJ.js.map +1 -0
  65. package/dist/{chunk-DPDBVKNA.js → chunk-YMKH5XTF.js} +5 -3
  66. package/dist/chunk-YMKH5XTF.js.map +1 -0
  67. package/dist/collapsible-panel.d.ts +9 -1
  68. package/dist/collapsible-panel.js +36 -15
  69. package/dist/collapsible-panel.js.map +1 -1
  70. package/dist/divider-sides-267FA7sY.d.ts +4 -0
  71. package/dist/index.d.ts +5 -3
  72. package/dist/index.js +20 -19
  73. package/dist/list-action-row.d.ts +11 -1
  74. package/dist/list-action-row.js +2 -1
  75. package/dist/list-header.d.ts +15 -2
  76. package/dist/list-header.js +1 -1
  77. package/dist/list-item.d.ts +12 -1
  78. package/dist/list-item.js +2 -1
  79. package/dist/list.d.ts +19 -0
  80. package/dist/list.js +3 -0
  81. package/dist/list.js.map +1 -0
  82. package/dist/nav-stack.js +1 -1
  83. package/dist/resizable-region.d.ts +19 -2
  84. package/dist/resizable-region.js +1 -1
  85. package/dist/segmented-control.js +1 -1
  86. package/dist/select.d.ts +12 -1
  87. package/dist/select.js +1 -1
  88. package/dist/skeleton.d.ts +1 -1
  89. package/dist/skeleton.js +1 -1
  90. package/dist/split-view.js +2 -2
  91. package/dist/state-banner.d.ts +4 -2
  92. package/dist/state-banner.js +1 -1
  93. package/dist/styles/app-tab.css +26 -0
  94. package/dist/styles/catalog.css +8 -2
  95. package/dist/styles/collapsible-panel.css +66 -4
  96. package/dist/styles/list-action-row.css +64 -0
  97. package/dist/styles/list-header.css +32 -0
  98. package/dist/styles/list-item.css +45 -6
  99. package/dist/styles/list.css +43 -0
  100. package/dist/styles/resizable-region.css +132 -0
  101. package/dist/styles/select.css +68 -0
  102. package/dist/styles/state-banner.css +32 -1
  103. package/dist/styles/styles.css +2 -1
  104. package/dist/styles/sunken-panel.css +4 -0
  105. package/dist/styles/surface-scaffold.css +51 -0
  106. package/dist/styles/tab-bar.css +24 -0
  107. package/dist/styles/toolbar-control-group.css +73 -1
  108. package/dist/styles/toolbar-text.css +1 -1
  109. package/dist/styles/toolbar.css +38 -2
  110. package/dist/styles/webawesome.css +12 -0
  111. package/dist/styles/workbench.css +89 -0
  112. package/dist/sunken-panel.d.ts +5 -2
  113. package/dist/sunken-panel.js +1 -1
  114. package/dist/surface-scaffold.d.ts +25 -0
  115. package/dist/surface-scaffold.js +3 -0
  116. package/dist/surface-scaffold.js.map +1 -0
  117. package/dist/tab-bar.d.ts +11 -2
  118. package/dist/tab-bar.js +1 -1
  119. package/dist/toolbar-control-group.d.ts +16 -2
  120. package/dist/toolbar-control-group.js +1 -1
  121. package/dist/toolbar-text.js +1 -1
  122. package/dist/toolbar.d.ts +5 -3
  123. package/dist/toolbar.js +1 -1
  124. package/dist/value-table.js +1 -1
  125. package/dist/webawesome.d.ts +1 -0
  126. package/dist/wire-catalog.d.ts +4 -2
  127. package/dist/wire-catalog.js +95 -36
  128. package/dist/wire-catalog.js.map +1 -1
  129. package/dist/wire-resizable-regions.js +1 -1
  130. package/dist/wire-sidebar.d.ts +6 -1
  131. package/dist/wire-sidebar.js +27 -8
  132. package/dist/wire-sidebar.js.map +1 -1
  133. package/dist/wire-tab-bars.js +1 -1
  134. package/dist/wire-token-search-fields.d.ts +1 -1
  135. package/dist/wire-token-search-fields.js +1 -1
  136. package/dist/workbench.d.ts +8 -0
  137. package/dist/workbench.js +42 -0
  138. package/dist/workbench.js.map +1 -1
  139. package/docs/accessibility.md +67 -15
  140. package/docs/catalog.md +17 -12
  141. package/docs/collapsible-panel.md +7 -0
  142. package/docs/component-contract.md +19 -9
  143. package/docs/component-selection.md +65 -31
  144. package/docs/design/templates/empty-state/filtered-dark.svg +1 -1
  145. package/docs/design/templates/empty-state/filtered.svg +1 -1
  146. package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
  147. package/docs/design/templates/empty-state/with-action.svg +1 -1
  148. package/docs/design/templates/empty-state-dark.svg +1 -7
  149. package/docs/design/templates/empty-state.svg +1 -7
  150. package/docs/design/templates/list/compact-dark.svg +1 -0
  151. package/docs/design/templates/list/compact.svg +1 -0
  152. package/docs/design/templates/list/stack-dark.svg +1 -0
  153. package/docs/design/templates/list/stack.svg +1 -0
  154. package/docs/design/templates/list-action-row/default-dark.svg +1 -1
  155. package/docs/design/templates/list-action-row/default.svg +1 -1
  156. package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
  157. package/docs/design/templates/list-action-row/selected.svg +1 -1
  158. package/docs/design/templates/list-action-row-dark.svg +1 -7
  159. package/docs/design/templates/list-action-row.svg +1 -7
  160. package/docs/design/templates/list-dark.svg +1 -0
  161. package/docs/design/templates/list-header/action-dark.svg +1 -1
  162. package/docs/design/templates/list-header/action.svg +1 -1
  163. package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -1
  164. package/docs/design/templates/list-header/toggle-collapsed.svg +1 -1
  165. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -1
  166. package/docs/design/templates/list-header/toggle-expanded.svg +1 -1
  167. package/docs/design/templates/list-header-dark.svg +1 -11
  168. package/docs/design/templates/list-header.svg +1 -11
  169. package/docs/design/templates/list-item/default-dark.svg +1 -1
  170. package/docs/design/templates/list-item/default.svg +1 -1
  171. package/docs/design/templates/list-item/multiline-dark.svg +1 -1
  172. package/docs/design/templates/list-item/multiline.svg +1 -1
  173. package/docs/design/templates/list-item/selected-dark.svg +1 -1
  174. package/docs/design/templates/list-item/selected.svg +1 -1
  175. package/docs/design/templates/list-item/trailing-dark.svg +1 -1
  176. package/docs/design/templates/list-item/trailing.svg +1 -1
  177. package/docs/design/templates/list-item-dark.svg +1 -11
  178. package/docs/design/templates/list-item.svg +1 -11
  179. package/docs/design/templates/list.svg +1 -0
  180. package/docs/design/templates/segmented-control-dark.svg +1 -7
  181. package/docs/design/templates/segmented-control.svg +1 -7
  182. package/docs/design/templates/skeleton-dark.svg +1 -9
  183. package/docs/design/templates/skeleton.svg +1 -9
  184. package/docs/design/templates/state-banner/danger-dark.svg +1 -1
  185. package/docs/design/templates/state-banner/danger.svg +1 -1
  186. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
  187. package/docs/design/templates/state-banner/info-badge.svg +1 -0
  188. package/docs/design/templates/state-banner/info-dark.svg +1 -1
  189. package/docs/design/templates/state-banner/info.svg +1 -1
  190. package/docs/design/templates/state-banner/success-dark.svg +1 -1
  191. package/docs/design/templates/state-banner/success.svg +1 -1
  192. package/docs/design/templates/state-banner/warning-dark.svg +1 -1
  193. package/docs/design/templates/state-banner/warning.svg +1 -1
  194. package/docs/design/templates/state-banner-dark.svg +1 -11
  195. package/docs/design/templates/state-banner.svg +1 -11
  196. package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -1
  197. package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -1
  198. package/docs/design/templates/tab-bar-dark.svg +1 -5
  199. package/docs/design/templates/tab-bar.svg +1 -5
  200. package/docs/design/templates/token-search-field/empty-dark.svg +1 -1
  201. package/docs/design/templates/token-search-field/empty.svg +1 -1
  202. package/docs/design/templates/token-search-field/tokens-dark.svg +1 -1
  203. package/docs/design/templates/token-search-field/tokens.svg +1 -1
  204. package/docs/design/templates/token-search-field-dark.svg +1 -7
  205. package/docs/design/templates/token-search-field.svg +1 -7
  206. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  207. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  208. package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -1
  209. package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -1
  210. package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -1
  211. package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -1
  212. package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -1
  213. package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -1
  214. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
  215. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
  216. package/docs/design/templates/toolbar-control-group-dark.svg +1 -11
  217. package/docs/design/templates/toolbar-control-group.svg +1 -11
  218. package/docs/design/templates/toolbar-dark.svg +1 -5
  219. package/docs/design/templates/toolbar-text-dark.svg +1 -9
  220. package/docs/design/templates/toolbar-text.svg +1 -9
  221. package/docs/design/templates/toolbar.svg +1 -5
  222. package/docs/design/templates/value-table/details-dark.svg +1 -1
  223. package/docs/design/templates/value-table/details.svg +1 -1
  224. package/docs/design/templates/value-table-dark.svg +1 -5
  225. package/docs/design/templates/value-table.svg +1 -5
  226. package/docs/design/templates.md +29 -20
  227. package/docs/examples/component-catalog-extension-v2.json +3 -6
  228. package/docs/examples/component-catalog-extension.json +1 -1
  229. package/docs/layout.md +7 -9
  230. package/docs/recipes.md +8 -7
  231. package/docs/sunken-panel.md +11 -1
  232. package/docs/surface-scaffold.md +17 -0
  233. package/docs/type-contracts.md +11 -10
  234. package/docs/ui-analyzer.md +19 -1
  235. package/docs/ui-doctor.md +7 -5
  236. package/docs/ux-demo.md +6 -3
  237. package/docs/webawesome-theme.md +4 -1
  238. package/docs/workbench.md +49 -2
  239. package/doctor/eslint-diagnostics.d.mts +16 -0
  240. package/doctor/eslint-diagnostics.mjs +17 -0
  241. package/doctor/index.mjs +21 -3
  242. package/llms.txt +33 -11
  243. package/package.json +14 -8
  244. package/traversal-exclusions.mjs +7 -1
  245. package/ux-demo/recipes/app-shell.tsx +36 -23
  246. package/ux-demo/recipes/collapsible-sidebar.tsx +23 -17
  247. package/ux-demo/recipes/composer-form.tsx +15 -6
  248. package/ux-demo/recipes/list-detail-dialog.tsx +31 -15
  249. package/ux-demo/recipes/list-workspace-states.tsx +16 -7
  250. package/ux-demo/recipes/loading-inspector.tsx +39 -18
  251. package/ux-demo/recipes/navigation-sidebar.tsx +1 -1
  252. package/ux-demo/recipes/recipes.css +7 -2
  253. package/ux-demo/recipes/workspace-header.tsx +17 -7
  254. package/dist/browser/panel-header.js +0 -7
  255. package/dist/chunk-5VTB2BKP.js.map +0 -1
  256. package/dist/chunk-7L6EHUMV.js.map +0 -1
  257. package/dist/chunk-AASLYRGE.js.map +0 -1
  258. package/dist/chunk-BBRXX6PF.js.map +0 -1
  259. package/dist/chunk-DPDBVKNA.js.map +0 -1
  260. package/dist/chunk-DSNIYOZ3.js.map +0 -1
  261. package/dist/chunk-ELSJX4W7.js.map +0 -1
  262. package/dist/chunk-GP5DP5DL.js.map +0 -1
  263. package/dist/chunk-K57KRAOV.js.map +0 -1
  264. package/dist/chunk-M7JBER5U.js.map +0 -1
  265. package/dist/chunk-NENBUYX5.js +0 -79
  266. package/dist/chunk-NENBUYX5.js.map +0 -1
  267. package/dist/chunk-PIDTEQ3U.js.map +0 -1
  268. package/dist/chunk-PZQVSDLH.js.map +0 -1
  269. package/dist/chunk-RYRIFZCT.js +0 -58
  270. package/dist/chunk-RYRIFZCT.js.map +0 -1
  271. package/dist/chunk-VLYV65LK.js.map +0 -1
  272. package/dist/chunk-W4CXQYSG.js.map +0 -1
  273. package/dist/panel-header.d.ts +0 -42
  274. package/dist/panel-header.js +0 -7
  275. package/dist/panel-header.js.map +0 -1
  276. package/dist/styles/panel-header.css +0 -64
  277. package/docs/design/templates/panel-header/icon-actions-dark.svg +0 -1
  278. package/docs/design/templates/panel-header/icon-actions.svg +0 -1
  279. package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +0 -1
  280. package/docs/design/templates/panel-header/icon-summary-actions.svg +0 -1
  281. package/docs/design/templates/panel-header/no-icon-dark.svg +0 -1
  282. package/docs/design/templates/panel-header/no-icon.svg +0 -1
  283. package/docs/design/templates/panel-header/page-heading-dark.svg +0 -1
  284. package/docs/design/templates/panel-header/page-heading.svg +0 -1
  285. package/docs/design/templates/panel-header/title-only-dark.svg +0 -1
  286. package/docs/design/templates/panel-header/title-only.svg +0 -1
  287. package/docs/design/templates/panel-header-dark.svg +0 -13
  288. package/docs/design/templates/panel-header.svg +0 -13
@@ -6,14 +6,47 @@ These are normative contracts for package components and consuming applications.
6
6
 
7
7
  - All controls have accessible names and visible `:focus-visible` treatment.
8
8
  - Decorative `LucideIcon` output is `aria-hidden`; pass `label` only when the SVG itself conveys meaning.
9
- - `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Selected rows keep the normal foreground over their brand-tinted fill so text retains WCAG AA contrast in light and dark themes. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
9
+ - `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Selected rows keep the normal foreground over their brand-tinted fill so text retains WCAG AA contrast in light and dark themes. Its leading icon visual is 18px while the row retains its 44px minimum target; multiline icons align to the first text line. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
10
10
  - `ListHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
11
+ - Dispose `wireTabBars` and `wireTokenSearchFields` with their owning view. Disposal cancels queued controlled-render focus restoration, so a torn-down view cannot reclaim focus from its successor.
11
12
  - In any `.kui-pane`, menu rows, `ListHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `ListHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-list-header-action-icon-size`; do not reduce the target to the visible icon.
12
13
  - `LoadingSpinner` is either labeled (`role="img"`) or hidden. Its rotation stops for reduced motion.
13
- - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure.
14
+ - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure. Its optional `badge` is persistent inline status/count content beside the title and is announced as part of the banner; keep it terse and do not rely on color alone for its meaning.
14
15
  - `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
15
16
  - `Select` follows the Web Awesome host's standard `input`/`change` events. Application tests verify the live `value`, focus, and events—not attributes alone. Its decorative option icons and value-dependent selected content remain present after controlled rerenders, so the visible choice does not silently lose its non-text cue.
16
17
  - `DisclosureArrow` is an 18px root-scaled decorative visual by default, not an interaction target. Its owning native control supplies a stable accessible name, pointer and keyboard interaction, and `aria-expanded`; multiple arrows keep independently controlled state. Consumers may override `--kui-disclosure-arrow-size` without changing that ownership. Configured directions animate over the shortest path; a 180-degree closed-to-open tie uses counterclockwise rotation.
18
+ - `List` is layout-only and adds no `list` role. Children own their native or ARIA semantics. Give a scrollable List a bounded block size, avoid nested scroll owners, and keep focused children visible while scrolling.
19
+
20
+ ## Select
21
+
22
+ Supply a visible `label`, or `ariaLabel` when the surrounding interface already
23
+ provides visual context. A nonempty `label` takes precedence when both are
24
+ supplied. Kerf forwards that name through Web Awesome's internal label contract;
25
+ an `aria-label` on the custom-element host alone cannot name its shadow
26
+ combobox. With `ariaLabel` alone, Kerf visually hides the internal label without
27
+ adding height or spacing. This also applies to `renderSelected`: selected content
28
+ stays separate from the control's stable accessible name. Applications do not
29
+ need shadow-DOM patches or extra label styling.
30
+
31
+ Use `presentation="toolbar-borderless"` with `size="compact"` inside a
32
+ `ToolbarControlGroup`; set `focusRingOwner="group"` when that parent paints the
33
+ composed focus ring. `selectedPresentation="icon-only"` hides only the visible
34
+ selected label—the required `label` or `ariaLabel` still names the combobox.
35
+ Navigation selects can use intrinsic `presentation="navigation"` plus
36
+ `labelMaxWidth` for component-owned ellipsis. These props own the control's
37
+ appearance; its parent continues to own outer placement.
38
+
39
+ Use `hint` for persistent supporting text below the control; use
40
+ `placeholderText` only for the empty value shown inside the closed control.
41
+ Kerf passes hint text through Web Awesome's form-control contract, which renders
42
+ the hint part and connects the shadow combobox to it with `aria-describedby`.
43
+ Loading placeholders keep the same visible hint while replacing the interactive
44
+ control with inert chrome.
45
+
46
+ `tests/unit/components.test.tsx` covers name projection and visible/hidden label
47
+ variants. `tests/browser/select-accessibility.spec.ts` verifies actual accessible
48
+ names, unchanged unlabeled geometry, keyboard selection, controlled rerenders,
49
+ native hint wiring, and wide/narrow presentation in Chromium, Firefox, and WebKit.
17
50
 
18
51
  ## ListActionRow
19
52
 
@@ -43,21 +76,21 @@ The handle exposes separator role, orientation, name, minimum, maximum, and curr
43
76
 
44
77
  The application owns persistence and collapsed/expanded policy. Keep the last expanded size outside the component and restore it when reopening. An optional `handleIcon` replaces only decorative dormant content; it must not contain controls or interactive roles because the separator remains the sole focus and interaction owner.
45
78
 
46
- ## PanelHeader
79
+ ## Toolbar headings
47
80
 
48
- `PanelHeader` is a plain `Toolbar` heading. Its leading zone holds an optional
49
- icon (a normal bordered `ToolbarControlGroup`) and the title as extra-large
50
- `ToolbarText`, and the app's trailing controls go straight into the trailing
51
- zone. By default the title carries no native heading role, so for a **dialog or
52
- panel** the application connects `titleId` and an optional `summaryId` to the
53
- owning host through `aria-labelledby` and `aria-describedby`. For a **page or
54
- view** title, pass `headingLevel` (usually `1`): the title then exposes
81
+ Compose panel, dialog, and page headings as a plain `Toolbar`. Its leading zone
82
+ holds an optional icon `ToolbarControlGroup` and a direct extra-large
83
+ `ToolbarText`; controls belong in a trailing `ToolbarControlGroup`. By default
84
+ the title carries no native heading role, so for a **dialog or panel** the
85
+ application connects the title id and optional supporting-copy id to the owning
86
+ host through `aria-labelledby` and `aria-describedby`. For a **page or view**
87
+ title, pass `headingLevel` (usually `1`) to `ToolbarText`: it then exposes
55
88
  `role="heading"` with a matching `aria-level`, giving the view a heading landmark
56
89
  so screen-reader heading navigation and "main heading" semantics work — the same
57
- role/level pair `ToolbarText` exposes when it is given `headingLevel` directly.
58
- Keep the levels meaningful and non-skipping within a view. Pass the trailing
90
+ role/level pair used throughout Kerf UI. Keep the levels meaningful and
91
+ non-skipping within a view. Pass trailing
59
92
  controls as a labeled `ToolbarControlGroup` when that group needs an accessible
60
- name.
93
+ name. Keep supporting copy as app-owned content below the toolbar.
61
94
 
62
95
  ## FloatingToolbar
63
96
 
@@ -73,6 +106,13 @@ and — via `position` and `--kui-floating-toolbar-inset` — where it sits.
73
106
 
74
107
  `AppTab` renders one controlled tab. `TabBar` supplies the containing tab list,
75
108
  fixed leading/trailing regions, and a horizontally scrollable strip.
109
+ Use `TabBar.presentation` for rail, segmented, or inspector chrome,
110
+ `allocation="fill"` when peers should divide the strip, and
111
+ `trailingPlacement="adjacent"` when an action belongs beside the final tab.
112
+ `AppTab` provides compact 32px, segmented, truncating-label, and icon-only
113
+ presentations. Icon-only tabs keep the required `name` as the tab button's
114
+ accessible name while visually hiding the duplicate label. These props own
115
+ component appearance only; the application still owns the bar's outer placement.
76
116
  `AppTab.rootAttributes` accepts application `data-*` metadata only; runtime
77
117
  filtering rejects roles plus case variants of component- or wiring-owned
78
118
  action, identity, selection, drag, drop, and component attributes. An optional
@@ -86,7 +126,8 @@ stops on drop/drag end/disposal, and does not change keyboard behavior. It
86
126
  returns a disposer.
87
127
 
88
128
  Arrow / Home / End follow the ARIA Tabs **automatic-activation** pattern by
89
- default: they move roving focus and select the focused tab. Pass
129
+ default: they move roving focus and select the focused tab. If selection synchronously
130
+ replaces the strip, focus returns to the replacement with the same bar and tab IDs. Pass
90
131
  `activation: 'manual'` (or set `data-tab-activation="manual"` on a strip via the
91
132
  `TabBar` `activation` prop, which overrides the option per bar) for
92
133
  **manual activation**: arrow keys move roving focus only and the user selects
@@ -123,7 +164,12 @@ and `placeTokenSearchCaret()` to restore a text caret without landing inside a
123
164
  chip. Call `wireTokenSearchFields()` once at a stable root so Enter submits
124
165
  without inserting a contenteditable line break and keyboard chip deletion
125
166
  restores focus plus the text-relative caret after controlled rendering replaces
126
- the editor; the editor still wraps text visually at its inline edge. Editable text is DOM-owned between token changes; a clear handler empties
167
+ the editor. Restoration finishes after synchronous input listeners and before
168
+ another keystroke; it does not wait for an animation frame that could overwrite
169
+ a later selection. Select All + Backspace/Delete shares that replacement path
170
+ and keeps the adopted expanded signal open while managed focus is enabled.
171
+ Real outside focus, empty Escape, disposal, and removed fields retain their usual
172
+ behavior. The editor still wraps text visually at its inline edge. Editable text is DOM-owned between token changes; a clear handler empties
127
173
  the editor's `textContent` before updating application state. Leading and
128
174
  trailing controls share the first text line's fixed vertical center and remain
129
175
  there as the editor wraps. In `collapsible` mode, the closed state is one named
@@ -147,6 +193,12 @@ originating `InputEvent` so a caller can gate on `inputType`/`data` — fires on
147
193
  `input`, letting a caller drop its own `input` listener; the application still
148
194
  owns query parsing and result-count/loading announcements.
149
195
 
196
+ Managed clear captures the action before application handlers run, keeps the adopted
197
+ expanded signal open during editor replacement, and focuses the current editor after
198
+ rendering so typing can continue. The app still owns clearing query/tokens and emptying
199
+ DOM-owned text. This focus step respects `manageFocus: false`, disposal, removed fields,
200
+ and focus deliberately moved to another control; no app-level reopen callback is needed.
201
+
150
202
  ## CollapsiblePanel / sidebar
151
203
 
152
204
  `CollapsiblePanel` is a labeled `aside` region that is `aria-hidden` while
package/docs/catalog.md CHANGED
@@ -109,9 +109,10 @@ const buttonPreview = (
109
109
  );
110
110
  ```
111
111
 
112
- The overlay selects every immediate child of a `CatalogExample` except the
113
- helper-generated label and note. It does not recursively promote a nested child
114
- to be the specimen. Outside an example row, it selects only top-level
112
+ The overlay selects every immediate child of a `CatalogExample`; the helper
113
+ marks its generated label and note so they are excluded automatically. It does
114
+ not recursively promote a nested child to be the specimen. Outside an example
115
+ row, it selects only top-level
115
116
  `[data-component]` roots in the canvas and ignores nested component descendants.
116
117
  These rules keep a row's label/group scaffolding out of the measurement and make
117
118
  the authored nesting determine exactly what is inspected.
@@ -138,15 +139,19 @@ Pass the conditional `geometryOverlay` boolean to `Catalog`, then call
138
139
  `wireCatalogGeometryOverlay(root)` once after the first render and retain its
139
140
  disposer alongside `wireCatalog`'s.
140
141
 
141
- | Overlay mark | Meaning | It is not |
142
- | ----------------------- | ---------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
143
- | Orange translucent band | A positive computed margin on the selected specimen, after subtracting the helper's alignment inset. | Padding, gap, or empty content. Zero and negative margins are not drawn. |
144
- | Quiet dashed outline | The border-box outer bound of a selected specimen whose computed background is transparent. | A real CSS border, focus ring, padding edge, or proof that the specimen owns its size. |
145
-
146
- The overlay does not visualize padding, borders, gaps, negative/zero margins,
147
- scroll overflow, hit targets, nested descendants, or geometry ownership. Inspect
148
- computed styles and the machine-readable `geometry` metadata for those facts.
149
- Opaque specimens do not receive the transparent-bound outline.
142
+ | Overlay mark | Meaning | It is not |
143
+ | ----------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
144
+ | Orange translucent band | A positive computed margin on the selected specimen, after subtracting the helper's alignment inset. | Padding, gap, or empty content. Zero and negative margins are not drawn. |
145
+ | Accent solid edge | The selected specimen's computed border widths and radius, except `none`/`hidden` styles. | A focus ring, outline, child border, or metadata ownership claim. |
146
+ | Quiet dashed outline | The border-box bound of a transparent selected specimen with no visible border. | A real CSS border, padding edge, or proof that the specimen owns size. |
147
+
148
+ The overlay recomputes from live rendered styles when specimens resize, preview
149
+ markup changes, theme/root attributes change, or loaded/inline stylesheets
150
+ change. Component CSS is therefore the source of truth; do not duplicate margin
151
+ or border values in demo data. The overlay does not visualize padding, gaps,
152
+ negative/zero margins, scroll overflow, hit targets, or nested descendants.
153
+ Inspect computed styles and the machine-readable `geometry` metadata for those
154
+ facts. Opaque borderless specimens do not receive the dashed bound.
150
155
 
151
156
  ```tsx
152
157
  <Catalog
@@ -20,6 +20,9 @@ Import the panel CSS (`@kerfjs/ui/collapsible-panel.css`) alongside `foundation.
20
20
  the fixed-size content slides out via `transform` (composited, clipped) — never
21
21
  a per-frame width/height animation. The app owns the `collapsed` signal; `size`
22
22
  overrides the CSS default width/height.
23
+ Reusable shell policies are typed props: `separator`, `collapseMotion`,
24
+ `contentOverflow`, and `presentation`. A collapsed panel may also receive a
25
+ `restoreControl`, which Kerf places at the safe-area-aware `restorePosition`.
23
26
  - **`CollapsiblePanelToggle({ side, collapsed, action, panelId?, label? })`** and
24
27
  **`collapsiblePanelToggleIcon(side, collapsed)`** — the standard toggle
25
28
  affordance and its icon convention, so every sidebar reads the same: `PanelLeft*`
@@ -38,6 +41,10 @@ Import the panel CSS (`@kerfjs/ui/collapsible-panel.css`) alongside `foundation.
38
41
  `deviceClass()` signal): the open panel floats over the content with a
39
42
  dismissable backdrop, Escape and backdrop-click collapse it, and Tab is trapped
40
43
  within the panel (the ARIA dialog pattern);
44
+ - accepts `compactPresentation: "hidden"` when a compact application replaces
45
+ the panel with different navigation instead of overlaying it;
46
+ - keeps compact overlays exclusive by default, collapsing another open panel
47
+ when a new one opens (`exclusiveCompact: false` opts out);
41
48
  - **persists** the collapsed state to `storage` (default `localStorage`) under
42
49
  `storageKey`, seeding the signal on wire-up.
43
50
 
@@ -193,6 +193,10 @@ an unlisted implementation class. Selectors such as `.kui-state-banner span`,
193
193
  private structure. If no prop, token, or cataloged class expresses a recurring
194
194
  need, request a supported hook instead of inferring one from rendered markup.
195
195
 
196
+ `ListItem` renders its leading icon and nested SVG at a root-scaled 18px by
197
+ default while retaining the row's 44px minimum interactive target. Multiline
198
+ rows align that 18px visual with the first inherited text line.
199
+
196
200
  `DisclosureArrow` uses an 18px root-scaled visual by default. Override
197
201
  `--kui-disclosure-arrow-size` on the component or its containing scope when a
198
202
  consumer needs another size. Kerf `Select` uses a separate Web Awesome expand
@@ -210,7 +214,9 @@ glyph defaults to 18px through `--kui-list-header-action-icon-size`.
210
214
 
211
215
  `StateBanner` exposes instance-level `--kui-state-banner-background`,
212
216
  `--kui-state-banner-border`, `--kui-state-banner-foreground`,
213
- `--kui-state-banner-detail`, and action background variables. Its five built-in
217
+ `--kui-state-banner-detail`, `--kui-state-banner-badge-background`,
218
+ `--kui-state-banner-badge-foreground`, and action background variables. Its optional
219
+ badge is a compact pill beside the title and follows the banner tone by default. Its five built-in
214
220
  tones can be rethemed globally with
215
221
  `--kui-state-banner-{tone}-{background|border|foreground}`. Toolbar control,
216
222
  segmented-control, app-tab, and tab-bar colors likewise use their public
@@ -231,6 +237,13 @@ header, one scrolling vertical `.kui-pane__content`, and an optional footer.
231
237
  Logical-edge separator lines are independently opt-in and default off.
232
238
  `@kerfjs/ui/layout.css` retains the pane roles and supplies the related content
233
239
  geometry classes.
240
+ `List` is the corresponding layout-only vertical stack: its children stretch by
241
+ default, while `gap`, `flex`, and `scrollable` opt into standard/custom spacing,
242
+ flex growth, and vertical scroll ownership. `dividerSides` accepts canonical
243
+ physical top/right/bottom/left combinations such as `tr` and `trbl`; the stack
244
+ adds no list semantics, margin, or padding of its own. String props receive
245
+ browser CSS, so use `gap="var(--kui-space-2xs)"` for the root-scaled 4px token or
246
+ `gap="0.25rem"`; source-only `remify(4px)` is not valid at runtime.
234
247
  `.kui-content` gives major children 24px vertical separation.
235
248
  `.kui-content-item` gives one child 8px inline margin, a real 1px border,
236
249
  8px padding, and 12px corners; border and background may be transparent without
@@ -250,14 +263,11 @@ into the section label. Do not add padding to pane shells,
250
263
  double child-owned geometry with wrapper insets, or create competing scroll
251
264
  owners. The [layout contract](./layout.md) lists the public roles and tokens.
252
265
 
253
- `PanelHeader` is a plain top `Toolbar` used as a panel, dialog, or page heading;
254
- it overrides no Toolbar styles. The leading zone holds an optional icon (a normal
255
- bordered `ToolbarControlGroup` given a brand fill with a matching border) and the
256
- title as extra-large `ToolbarText`, and the app's `actions` go straight into the
257
- trailing zone (typically as a `ToolbarControlGroup`). The icon group is omitted
258
- when no icon is passed. The optional summary is a separate row aligned below the
259
- title, so it cannot pull the icon group out of vertical alignment with the
260
- title and action row.
266
+ Panel, dialog, and page headings are direct `Toolbar` compositions. The leading
267
+ zone holds an optional icon `ToolbarControlGroup` and a direct extra-large
268
+ `ToolbarText`; app actions belong in a trailing `ToolbarControlGroup`. Omit the
269
+ icon group when it has no content. Supporting copy is app-owned content below
270
+ the toolbar so it cannot pull the icon or actions out of alignment.
261
271
 
262
272
  `ValueTable` composes typed `ValueTableRow` entries. A row owns its `dt`/`dd`
263
273
  semantics and may receive a leading `SafeHtml` icon. Every row keeps 8px of