@kerfjs/ui 5.0.0-beta.24 → 5.0.0-beta.26

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 (149) hide show
  1. package/README.md +44 -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 +3 -11
  5. package/ai/component-catalog-v2-overrides.json +7 -37
  6. package/ai/component-catalog-v2.d.ts +1 -0
  7. package/ai/component-catalog-v2.json +415 -91
  8. package/ai/component-catalog-v2.schema.json +2 -1
  9. package/ai/component-catalog.json +602 -158
  10. package/ai/public-api-signatures-v1.md +132 -65
  11. package/ai/skill.md +20 -20
  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/surface-scaffold.js +3 -0
  21. package/dist/catalog.js +5 -4
  22. package/dist/catalog.js.map +1 -1
  23. package/dist/{chunk-FEAMR7S4.js → chunk-4EF2CMYS.js} +24 -5
  24. package/dist/chunk-4EF2CMYS.js.map +1 -0
  25. package/dist/{chunk-NWSEP5RT.js → chunk-FRVS5RFM.js} +23 -5
  26. package/dist/chunk-FRVS5RFM.js.map +1 -0
  27. package/dist/{chunk-PSKN344N.js → chunk-GWEAPEZI.js} +15 -2
  28. package/dist/chunk-GWEAPEZI.js.map +1 -0
  29. package/dist/chunk-HP2B2L26.js +102 -0
  30. package/dist/chunk-HP2B2L26.js.map +1 -0
  31. package/dist/{chunk-OJWWHFH3.js → chunk-JFGXQBIP.js} +49 -23
  32. package/dist/chunk-JFGXQBIP.js.map +1 -0
  33. package/dist/chunk-OBLTAKSX.js +43 -0
  34. package/dist/chunk-OBLTAKSX.js.map +1 -0
  35. package/dist/{chunk-Y45PPBDV.js → chunk-OT6RPYAP.js} +16 -2
  36. package/dist/chunk-OT6RPYAP.js.map +1 -0
  37. package/dist/{chunk-UFZKLBCF.js → chunk-VCE4MPVE.js} +25 -3
  38. package/dist/chunk-VCE4MPVE.js.map +1 -0
  39. package/dist/{chunk-5VTB2BKP.js → chunk-WNWD54GR.js} +16 -2
  40. package/dist/chunk-WNWD54GR.js.map +1 -0
  41. package/dist/{chunk-M7JBER5U.js → chunk-X2U3QJCJ.js} +8 -2
  42. package/dist/chunk-X2U3QJCJ.js.map +1 -0
  43. package/dist/{chunk-DPDBVKNA.js → chunk-YMKH5XTF.js} +5 -3
  44. package/dist/chunk-YMKH5XTF.js.map +1 -0
  45. package/dist/collapsible-panel.d.ts +9 -1
  46. package/dist/collapsible-panel.js +36 -15
  47. package/dist/collapsible-panel.js.map +1 -1
  48. package/dist/index.d.ts +2 -2
  49. package/dist/index.js +12 -12
  50. package/dist/list-action-row.d.ts +11 -1
  51. package/dist/list-action-row.js +2 -1
  52. package/dist/list-header.d.ts +15 -2
  53. package/dist/list-header.js +1 -1
  54. package/dist/list-item.d.ts +12 -1
  55. package/dist/list-item.js +2 -1
  56. package/dist/resizable-region.d.ts +19 -2
  57. package/dist/resizable-region.js +1 -1
  58. package/dist/select.d.ts +12 -1
  59. package/dist/select.js +1 -1
  60. package/dist/split-view.js +1 -1
  61. package/dist/styles/app-tab.css +26 -0
  62. package/dist/styles/collapsible-panel.css +66 -4
  63. package/dist/styles/list-action-row.css +64 -0
  64. package/dist/styles/list-header.css +32 -0
  65. package/dist/styles/list-item.css +39 -0
  66. package/dist/styles/resizable-region.css +132 -0
  67. package/dist/styles/select.css +68 -0
  68. package/dist/styles/styles.css +1 -1
  69. package/dist/styles/surface-scaffold.css +51 -0
  70. package/dist/styles/tab-bar.css +24 -0
  71. package/dist/styles/toolbar-control-group.css +73 -1
  72. package/dist/styles/toolbar-text.css +1 -1
  73. package/dist/styles/webawesome.css +12 -0
  74. package/dist/styles/workbench.css +89 -0
  75. package/dist/surface-scaffold.d.ts +25 -0
  76. package/dist/surface-scaffold.js +3 -0
  77. package/dist/surface-scaffold.js.map +1 -0
  78. package/dist/tab-bar.d.ts +11 -2
  79. package/dist/tab-bar.js +1 -1
  80. package/dist/toolbar-control-group.d.ts +16 -2
  81. package/dist/toolbar-control-group.js +1 -1
  82. package/dist/webawesome.d.ts +1 -0
  83. package/dist/wire-resizable-regions.js +1 -1
  84. package/dist/wire-sidebar.d.ts +6 -1
  85. package/dist/wire-sidebar.js +27 -8
  86. package/dist/wire-sidebar.js.map +1 -1
  87. package/dist/wire-token-search-fields.js +1 -1
  88. package/dist/workbench.d.ts +8 -0
  89. package/dist/workbench.js +42 -0
  90. package/dist/workbench.js.map +1 -1
  91. package/docs/accessibility.md +55 -12
  92. package/docs/collapsible-panel.md +7 -0
  93. package/docs/component-contract.md +5 -8
  94. package/docs/component-selection.md +63 -33
  95. package/docs/design/templates.md +5 -7
  96. package/docs/examples/component-catalog-extension-v2.json +3 -6
  97. package/docs/examples/component-catalog-extension.json +1 -1
  98. package/docs/layout.md +7 -9
  99. package/docs/recipes.md +8 -7
  100. package/docs/surface-scaffold.md +17 -0
  101. package/docs/type-contracts.md +6 -8
  102. package/docs/ui-analyzer.md +19 -1
  103. package/docs/ui-doctor.md +1 -1
  104. package/docs/ux-demo.md +6 -3
  105. package/docs/webawesome-theme.md +4 -1
  106. package/docs/workbench.md +49 -2
  107. package/doctor/eslint-diagnostics.d.mts +16 -0
  108. package/doctor/eslint-diagnostics.mjs +17 -0
  109. package/doctor/index.mjs +16 -1
  110. package/llms.txt +19 -9
  111. package/package.json +7 -7
  112. package/ux-demo/recipes/app-shell.tsx +36 -23
  113. package/ux-demo/recipes/collapsible-sidebar.tsx +22 -16
  114. package/ux-demo/recipes/composer-form.tsx +15 -6
  115. package/ux-demo/recipes/list-detail-dialog.tsx +31 -15
  116. package/ux-demo/recipes/list-workspace-states.tsx +16 -7
  117. package/ux-demo/recipes/loading-inspector.tsx +39 -18
  118. package/ux-demo/recipes/recipes.css +7 -2
  119. package/ux-demo/recipes/workspace-header.tsx +17 -7
  120. package/dist/browser/panel-header.js +0 -7
  121. package/dist/chunk-5VTB2BKP.js.map +0 -1
  122. package/dist/chunk-7XRDUG4Y.js +0 -58
  123. package/dist/chunk-7XRDUG4Y.js.map +0 -1
  124. package/dist/chunk-DPDBVKNA.js.map +0 -1
  125. package/dist/chunk-FEAMR7S4.js.map +0 -1
  126. package/dist/chunk-M7JBER5U.js.map +0 -1
  127. package/dist/chunk-NENBUYX5.js +0 -79
  128. package/dist/chunk-NENBUYX5.js.map +0 -1
  129. package/dist/chunk-NWSEP5RT.js.map +0 -1
  130. package/dist/chunk-OJWWHFH3.js.map +0 -1
  131. package/dist/chunk-PSKN344N.js.map +0 -1
  132. package/dist/chunk-UFZKLBCF.js.map +0 -1
  133. package/dist/chunk-Y45PPBDV.js.map +0 -1
  134. package/dist/panel-header.d.ts +0 -42
  135. package/dist/panel-header.js +0 -7
  136. package/dist/panel-header.js.map +0 -1
  137. package/dist/styles/panel-header.css +0 -64
  138. package/docs/design/templates/panel-header/icon-actions-dark.svg +0 -1
  139. package/docs/design/templates/panel-header/icon-actions.svg +0 -1
  140. package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +0 -1
  141. package/docs/design/templates/panel-header/icon-summary-actions.svg +0 -1
  142. package/docs/design/templates/panel-header/no-icon-dark.svg +0 -1
  143. package/docs/design/templates/panel-header/no-icon.svg +0 -1
  144. package/docs/design/templates/panel-header/page-heading-dark.svg +0 -1
  145. package/docs/design/templates/panel-header/page-heading.svg +0 -1
  146. package/docs/design/templates/panel-header/title-only-dark.svg +0 -1
  147. package/docs/design/templates/panel-header/title-only.svg +0 -1
  148. package/docs/design/templates/panel-header-dark.svg +0 -1
  149. package/docs/design/templates/panel-header.svg +0 -1
package/dist/workbench.js CHANGED
@@ -24,11 +24,25 @@ function Workbench({
24
24
  class: "kui-workbench__rail kui-workbench__rail--left",
25
25
  "data-workbench-rail": "left",
26
26
  "data-collapsed": String(leftRail.collapsed ?? false),
27
+ "data-separator": leftRail.separator ?? "auto",
28
+ "data-collapse-motion": leftRail.collapseMotion ?? "slide",
29
+ "data-content-overflow": leftRail.contentOverflow ?? "clip",
30
+ "data-presentation": leftRail.presentation ?? "inline",
27
31
  "aria-label": leftRail.label || void 0,
32
+ "aria-hidden": leftRail.presentation === "hidden" ? "true" : void 0,
28
33
  style: leftRail.size ? `--kui-workbench-rail-width: ${leftRail.size}px` : void 0,
29
34
  children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: leftRail.content })
30
35
  }
31
36
  ),
37
+ leftRail?.collapsed && leftRail.restoreControl && leftRail.presentation !== "hidden" && /* @__PURE__ */ jsx(
38
+ "div",
39
+ {
40
+ class: "kui-workbench__restore",
41
+ "data-panel": "left",
42
+ "data-position": leftRail.restorePosition ?? "bottom-start",
43
+ children: leftRail.restoreControl
44
+ }
45
+ ),
32
46
  /* @__PURE__ */ jsxs("div", { class: "kui-workbench__center", children: [
33
47
  /* @__PURE__ */ jsx("div", { class: "kui-workbench__main", "data-workbench-main": true, children: main }),
34
48
  bottomDrawer && /* @__PURE__ */ jsx(
@@ -37,10 +51,24 @@ function Workbench({
37
51
  class: "kui-workbench__drawer",
38
52
  "data-workbench-drawer": true,
39
53
  "data-collapsed": String(bottomDrawer.collapsed ?? false),
54
+ "data-separator": bottomDrawer.separator ?? "auto",
55
+ "data-collapse-motion": bottomDrawer.collapseMotion ?? "slide",
56
+ "data-content-overflow": bottomDrawer.contentOverflow ?? "clip",
57
+ "data-presentation": bottomDrawer.presentation ?? "inline",
40
58
  "aria-label": bottomDrawer.label || void 0,
59
+ "aria-hidden": bottomDrawer.presentation === "hidden" ? "true" : void 0,
41
60
  style: bottomDrawer.size ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px` : void 0,
42
61
  children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: bottomDrawer.content })
43
62
  }
63
+ ),
64
+ bottomDrawer?.collapsed && bottomDrawer.restoreControl && bottomDrawer.presentation !== "hidden" && /* @__PURE__ */ jsx(
65
+ "div",
66
+ {
67
+ class: "kui-workbench__restore",
68
+ "data-panel": "bottom",
69
+ "data-position": bottomDrawer.restorePosition ?? "bottom-end",
70
+ children: bottomDrawer.restoreControl
71
+ }
44
72
  )
45
73
  ] }),
46
74
  rightRail && /* @__PURE__ */ jsx(
@@ -49,10 +77,24 @@ function Workbench({
49
77
  class: "kui-workbench__rail kui-workbench__rail--right",
50
78
  "data-workbench-rail": "right",
51
79
  "data-collapsed": String(rightRail.collapsed ?? false),
80
+ "data-separator": rightRail.separator ?? "auto",
81
+ "data-collapse-motion": rightRail.collapseMotion ?? "slide",
82
+ "data-content-overflow": rightRail.contentOverflow ?? "clip",
83
+ "data-presentation": rightRail.presentation ?? "inline",
52
84
  "aria-label": rightRail.label || void 0,
85
+ "aria-hidden": rightRail.presentation === "hidden" ? "true" : void 0,
53
86
  style: rightRail.size ? `--kui-workbench-rail-width: ${rightRail.size}px` : void 0,
54
87
  children: /* @__PURE__ */ jsx("div", { class: "kui-workbench__panel-content", children: rightRail.content })
55
88
  }
89
+ ),
90
+ rightRail?.collapsed && rightRail.restoreControl && rightRail.presentation !== "hidden" && /* @__PURE__ */ jsx(
91
+ "div",
92
+ {
93
+ class: "kui-workbench__restore",
94
+ "data-panel": "right",
95
+ "data-position": rightRail.restorePosition ?? "bottom-end",
96
+ children: rightRail.restoreControl
97
+ }
56
98
  )
57
99
  ]
58
100
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/workbench.tsx"],"names":[],"mappings":";;;AAgCO,SAAS,SAAA,CAAU;AAAA,EACxB,EAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,SAAA,GAAY;AACd,CAAA,EAAmB;AACjB,EAAA,uBACE,IAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,cAAA,EAAiB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACzC,EAAA;AAAA,MACA,gBAAA,EAAe,WAAA;AAAA,MACf,YAAA,EAAY,KAAA;AAAA,MAEX,QAAA,EAAA;AAAA,QAAA,QAAA,oBACC,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,+CAAA;AAAA,YACN,qBAAA,EAAoB,MAAA;AAAA,YACpB,gBAAA,EAAgB,MAAA,CAAO,QAAA,CAAS,SAAA,IAAa,KAAK,CAAA;AAAA,YAClD,YAAA,EAAY,SAAS,KAAA,IAAS,MAAA;AAAA,YAC9B,OACE,QAAA,CAAS,IAAA,GACL,CAAA,4BAAA,EAA+B,QAAA,CAAS,IAAI,CAAA,EAAA,CAAA,GAC5C,MAAA;AAAA,YAGN,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8BAAA,EAAgC,mBAAS,OAAA,EAAQ;AAAA;AAAA,SAC9D;AAAA,wBAEF,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,uBAAA,EACT,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,qBAAA,EAAsB,qBAAA,EAAmB,MACjD,QAAA,EAAA,IAAA,EACH,CAAA;AAAA,UACC,YAAA,oBACC,GAAA;AAAA,YAAC,SAAA;AAAA,YAAA;AAAA,cACC,KAAA,EAAM,uBAAA;AAAA,cACN,uBAAA,EAAqB,IAAA;AAAA,cACrB,gBAAA,EAAgB,MAAA,CAAO,YAAA,CAAa,SAAA,IAAa,KAAK,CAAA;AAAA,cACtD,YAAA,EAAY,aAAa,KAAA,IAAS,MAAA;AAAA,cAClC,OACE,YAAA,CAAa,IAAA,GACT,CAAA,+BAAA,EAAkC,YAAA,CAAa,IAAI,CAAA,EAAA,CAAA,GACnD,MAAA;AAAA,cAGN,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8BAAA,EACR,uBAAa,OAAA,EAChB;AAAA;AAAA;AACF,SAAA,EAEJ,CAAA;AAAA,QACC,SAAA,oBACC,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,gDAAA;AAAA,YACN,qBAAA,EAAoB,OAAA;AAAA,YACpB,gBAAA,EAAgB,MAAA,CAAO,SAAA,CAAU,SAAA,IAAa,KAAK,CAAA;AAAA,YACnD,YAAA,EAAY,UAAU,KAAA,IAAS,MAAA;AAAA,YAC/B,OACE,SAAA,CAAU,IAAA,GACN,CAAA,4BAAA,EAA+B,SAAA,CAAU,IAAI,CAAA,EAAA,CAAA,GAC7C,MAAA;AAAA,YAGN,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,8BAAA,EAAgC,oBAAU,OAAA,EAAQ;AAAA;AAAA;AAC/D;AAAA;AAAA,GAEJ;AAEJ","file":"workbench.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\n/** A collapsible Workbench panel — a side rail or the bottom drawer. */\nexport interface WorkbenchPanel {\n content: SafeHtml;\n /** Whether the panel is currently collapsed (the app owns this). */\n collapsed?: boolean;\n /** Rail width, or drawer height, in px. Overrides the CSS default. */\n size?: number;\n /** Accessible name for the panel region. */\n label?: string;\n}\n\nexport interface WorkbenchProps {\n id: string;\n label: string;\n /** The central work area. */\n main: SafeHtml;\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n bottomDrawer?: WorkbenchPanel;\n className?: string;\n}\n\n/**\n * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and\n * bottom drawer around a central work area (any absent). Collapsing snaps the\n * panel's track to zero in one reflow while its fixed-size content slides out via\n * a composited transform — the instant-width / sliding-content technique, so the\n * work area relayouts once, not per frame. The app owns each `collapsed` flag;\n * the collapse is pure CSS (no wire). See `docs/23-app-layouts.md` §3.3.\n */\nexport function Workbench({\n id,\n label,\n main,\n leftRail,\n rightRail,\n bottomDrawer,\n className = '',\n}: WorkbenchProps) {\n return (\n <section\n class={`kui-workbench ${className}`.trim()}\n id={id}\n data-component=\"workbench\"\n aria-label={label}\n >\n {leftRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--left\"\n data-workbench-rail=\"left\"\n data-collapsed={String(leftRail.collapsed ?? false)}\n aria-label={leftRail.label || undefined}\n style={\n leftRail.size\n ? `--kui-workbench-rail-width: ${leftRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{leftRail.content}</div>\n </aside>\n )}\n <div class=\"kui-workbench__center\">\n <div class=\"kui-workbench__main\" data-workbench-main>\n {main}\n </div>\n {bottomDrawer && (\n <section\n class=\"kui-workbench__drawer\"\n data-workbench-drawer\n data-collapsed={String(bottomDrawer.collapsed ?? false)}\n aria-label={bottomDrawer.label || undefined}\n style={\n bottomDrawer.size\n ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">\n {bottomDrawer.content}\n </div>\n </section>\n )}\n </div>\n {rightRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--right\"\n data-workbench-rail=\"right\"\n data-collapsed={String(rightRail.collapsed ?? false)}\n aria-label={rightRail.label || undefined}\n style={\n rightRail.size\n ? `--kui-workbench-rail-width: ${rightRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{rightRail.content}</div>\n </aside>\n )}\n </section>\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/workbench.tsx"],"names":[],"mappings":"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","file":"workbench.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport type {\n ResizableRegionCollapseMotion,\n ResizableRegionContentOverflow,\n ResizableRegionPresentation,\n ResizableRegionRestorePosition,\n ResizableRegionSeparator,\n} from './resizable-region.js';\n\n/** A collapsible Workbench panel — a side rail or the bottom drawer. */\nexport interface WorkbenchPanel {\n content: SafeHtml;\n /** Whether the panel is currently collapsed (the app owns this). */\n collapsed?: boolean;\n /** Rail width, or drawer height, in px. Overrides the CSS default. */\n size?: number;\n /** Accessible name for the panel region. */\n label?: string;\n separator?: ResizableRegionSeparator;\n collapseMotion?: ResizableRegionCollapseMotion;\n contentOverflow?: ResizableRegionContentOverflow;\n presentation?: ResizableRegionPresentation;\n /** Control shown in a safe-area-aware viewport corner while collapsed. */\n restoreControl?: SafeHtml;\n restorePosition?: ResizableRegionRestorePosition;\n}\n\nexport interface WorkbenchProps {\n id: string;\n label: string;\n /** The central work area. */\n main: SafeHtml;\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n bottomDrawer?: WorkbenchPanel;\n className?: string;\n}\n\n/**\n * The Xcode-like multi-panel workspace: a collapsible left rail, right rail, and\n * bottom drawer around a central work area (any absent). Collapsing snaps the\n * panel's track to zero in one reflow while its fixed-size content slides out via\n * a composited transform — the instant-width / sliding-content technique, so the\n * work area relayouts once, not per frame. The app owns each `collapsed` flag;\n * the collapse is pure CSS (no wire). See `docs/23-app-layouts.md` §3.3.\n */\nexport function Workbench({\n id,\n label,\n main,\n leftRail,\n rightRail,\n bottomDrawer,\n className = '',\n}: WorkbenchProps) {\n return (\n <section\n class={`kui-workbench ${className}`.trim()}\n id={id}\n data-component=\"workbench\"\n aria-label={label}\n >\n {leftRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--left\"\n data-workbench-rail=\"left\"\n data-collapsed={String(leftRail.collapsed ?? false)}\n data-separator={leftRail.separator ?? 'auto'}\n data-collapse-motion={leftRail.collapseMotion ?? 'slide'}\n data-content-overflow={leftRail.contentOverflow ?? 'clip'}\n data-presentation={leftRail.presentation ?? 'inline'}\n aria-label={leftRail.label || undefined}\n aria-hidden={leftRail.presentation === 'hidden' ? 'true' : undefined}\n style={\n leftRail.size\n ? `--kui-workbench-rail-width: ${leftRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{leftRail.content}</div>\n </aside>\n )}\n {leftRail?.collapsed &&\n leftRail.restoreControl &&\n leftRail.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"left\"\n data-position={leftRail.restorePosition ?? 'bottom-start'}\n >\n {leftRail.restoreControl}\n </div>\n )}\n <div class=\"kui-workbench__center\">\n <div class=\"kui-workbench__main\" data-workbench-main>\n {main}\n </div>\n {bottomDrawer && (\n <section\n class=\"kui-workbench__drawer\"\n data-workbench-drawer\n data-collapsed={String(bottomDrawer.collapsed ?? false)}\n data-separator={bottomDrawer.separator ?? 'auto'}\n data-collapse-motion={bottomDrawer.collapseMotion ?? 'slide'}\n data-content-overflow={bottomDrawer.contentOverflow ?? 'clip'}\n data-presentation={bottomDrawer.presentation ?? 'inline'}\n aria-label={bottomDrawer.label || undefined}\n aria-hidden={\n bottomDrawer.presentation === 'hidden' ? 'true' : undefined\n }\n style={\n bottomDrawer.size\n ? `--kui-workbench-drawer-height: ${bottomDrawer.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">\n {bottomDrawer.content}\n </div>\n </section>\n )}\n {bottomDrawer?.collapsed &&\n bottomDrawer.restoreControl &&\n bottomDrawer.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"bottom\"\n data-position={bottomDrawer.restorePosition ?? 'bottom-end'}\n >\n {bottomDrawer.restoreControl}\n </div>\n )}\n </div>\n {rightRail && (\n <aside\n class=\"kui-workbench__rail kui-workbench__rail--right\"\n data-workbench-rail=\"right\"\n data-collapsed={String(rightRail.collapsed ?? false)}\n data-separator={rightRail.separator ?? 'auto'}\n data-collapse-motion={rightRail.collapseMotion ?? 'slide'}\n data-content-overflow={rightRail.contentOverflow ?? 'clip'}\n data-presentation={rightRail.presentation ?? 'inline'}\n aria-label={rightRail.label || undefined}\n aria-hidden={rightRail.presentation === 'hidden' ? 'true' : undefined}\n style={\n rightRail.size\n ? `--kui-workbench-rail-width: ${rightRail.size}px`\n : undefined\n }\n >\n <div class=\"kui-workbench__panel-content\">{rightRail.content}</div>\n </aside>\n )}\n {rightRail?.collapsed &&\n rightRail.restoreControl &&\n rightRail.presentation !== 'hidden' && (\n <div\n class=\"kui-workbench__restore\"\n data-panel=\"right\"\n data-position={rightRail.restorePosition ?? 'bottom-end'}\n >\n {rightRail.restoreControl}\n </div>\n )}\n </section>\n );\n}\n"]}
@@ -17,6 +17,37 @@ These are normative contracts for package components and consuming applications.
17
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
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
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.
50
+
20
51
  ## ListActionRow
21
52
 
22
53
  `ListActionRow` renders a noninteractive visual root containing primary and
@@ -45,21 +76,21 @@ The handle exposes separator role, orientation, name, minimum, maximum, and curr
45
76
 
46
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.
47
78
 
48
- ## PanelHeader
79
+ ## Toolbar headings
49
80
 
50
- `PanelHeader` is a plain `Toolbar` heading. Its leading zone holds an optional
51
- icon (a normal bordered `ToolbarControlGroup`) and the title as extra-large
52
- `ToolbarText`, and the app's trailing controls go straight into the trailing
53
- zone. By default the title carries no native heading role, so for a **dialog or
54
- panel** the application connects `titleId` and an optional `summaryId` to the
55
- owning host through `aria-labelledby` and `aria-describedby`. For a **page or
56
- 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
57
88
  `role="heading"` with a matching `aria-level`, giving the view a heading landmark
58
89
  so screen-reader heading navigation and "main heading" semantics work — the same
59
- role/level pair `ToolbarText` exposes when it is given `headingLevel` directly.
60
- 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
61
92
  controls as a labeled `ToolbarControlGroup` when that group needs an accessible
62
- name.
93
+ name. Keep supporting copy as app-owned content below the toolbar.
63
94
 
64
95
  ## FloatingToolbar
65
96
 
@@ -75,6 +106,13 @@ and — via `position` and `--kui-floating-toolbar-inset` — where it sits.
75
106
 
76
107
  `AppTab` renders one controlled tab. `TabBar` supplies the containing tab list,
77
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.
78
116
  `AppTab.rootAttributes` accepts application `data-*` metadata only; runtime
79
117
  filtering rejects roles plus case variants of component- or wiring-owned
80
118
  action, identity, selection, drag, drop, and component attributes. An optional
@@ -126,7 +164,12 @@ and `placeTokenSearchCaret()` to restore a text caret without landing inside a
126
164
  chip. Call `wireTokenSearchFields()` once at a stable root so Enter submits
127
165
  without inserting a contenteditable line break and keyboard chip deletion
128
166
  restores focus plus the text-relative caret after controlled rendering replaces
129
- 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
130
173
  the editor's `textContent` before updating application state. Leading and
131
174
  trailing controls share the first text line's fixed vertical center and remain
132
175
  there as the editor wraps. In `collapsible` mode, the closed state is one named
@@ -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
 
@@ -263,14 +263,11 @@ into the section label. Do not add padding to pane shells,
263
263
  double child-owned geometry with wrapper insets, or create competing scroll
264
264
  owners. The [layout contract](./layout.md) lists the public roles and tokens.
265
265
 
266
- `PanelHeader` is a plain top `Toolbar` used as a panel, dialog, or page heading;
267
- it overrides no Toolbar styles. The leading zone holds an optional icon (a normal
268
- bordered `ToolbarControlGroup` given a brand fill with a matching border) and the
269
- title as extra-large `ToolbarText`, and the app's `actions` go straight into the
270
- trailing zone (typically as a `ToolbarControlGroup`). The icon group is omitted
271
- when no icon is passed. The optional summary is a separate row aligned below the
272
- title, so it cannot pull the icon group out of vertical alignment with the
273
- 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.
274
271
 
275
272
  `ValueTable` composes typed `ValueTableRow` entries. A row owns its `dt`/`dd`
276
273
  semantics and may receive a leading `SafeHtml` icon. Every row keeps 8px of