@kerfjs/ui 5.0.0-beta.49 → 5.0.0-beta.50

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 (257) hide show
  1. package/README.md +41 -11
  2. package/ai/catalog-authoring.json +12 -1
  3. package/ai/catalog-authoring.schema.json +49 -1
  4. package/ai/compile-time-contracts-v1.json +14 -0
  5. package/ai/compile-time-contracts-v1.schema.json +1 -0
  6. package/ai/component-catalog-v2-overrides.json +7 -0
  7. package/ai/component-catalog-v2.json +79 -23
  8. package/ai/component-catalog.json +86 -27
  9. package/ai/public-api-signatures-v1.md +217 -169
  10. package/ai/skill.md +23 -9
  11. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  12. package/dist/app-tab.d.ts +3 -1
  13. package/dist/app-tab.js +1 -1
  14. package/dist/badge.d.ts +24 -0
  15. package/dist/badge.js +3 -0
  16. package/dist/badge.js.map +1 -0
  17. package/dist/browser/badge.js +3 -0
  18. package/dist/browser/list-header.js +1 -0
  19. package/dist/browser/state-banner.js +1 -0
  20. package/dist/catalog-resources.d.ts +2 -2
  21. package/dist/catalog.d.ts +24 -125
  22. package/dist/catalog.js +387 -261
  23. package/dist/catalog.js.map +1 -1
  24. package/dist/{chunk-OGTJNHRG.js → chunk-2WMSE4MK.js} +5 -3
  25. package/dist/chunk-2WMSE4MK.js.map +1 -0
  26. package/dist/{chunk-GYGM363P.js → chunk-2YOUUEWR.js} +16 -6
  27. package/dist/chunk-2YOUUEWR.js.map +1 -0
  28. package/dist/{chunk-UDHHKZWO.js → chunk-3V75EQIP.js} +8 -4
  29. package/dist/chunk-3V75EQIP.js.map +1 -0
  30. package/dist/{chunk-W2FK7ZNY.js → chunk-4R777DUZ.js} +10 -2
  31. package/dist/chunk-4R777DUZ.js.map +1 -0
  32. package/dist/{chunk-T7DOWS6T.js → chunk-54HM36IQ.js} +6 -3
  33. package/dist/chunk-54HM36IQ.js.map +1 -0
  34. package/dist/{chunk-DAO2VFTT.js → chunk-5CE64WGC.js} +6 -4
  35. package/dist/chunk-5CE64WGC.js.map +1 -0
  36. package/dist/{chunk-SOK5EUF7.js → chunk-6ZXOQEAN.js} +4 -2
  37. package/dist/chunk-6ZXOQEAN.js.map +1 -0
  38. package/dist/{chunk-OGNE3OFT.js → chunk-AQSKJ6U6.js} +4 -2
  39. package/dist/chunk-AQSKJ6U6.js.map +1 -0
  40. package/dist/{chunk-VDUJZF5G.js → chunk-CMUIXJZM.js} +2 -2
  41. package/dist/{chunk-VDUJZF5G.js.map → chunk-CMUIXJZM.js.map} +1 -1
  42. package/dist/{chunk-OMSRSL3B.js → chunk-COUPLRZG.js} +5 -3
  43. package/dist/chunk-COUPLRZG.js.map +1 -0
  44. package/dist/{chunk-2TYUFZUC.js → chunk-D7GWXIAS.js} +4 -2
  45. package/dist/chunk-D7GWXIAS.js.map +1 -0
  46. package/dist/{chunk-AQFWBG23.js → chunk-DARTMI5Z.js} +5 -3
  47. package/dist/chunk-DARTMI5Z.js.map +1 -0
  48. package/dist/{chunk-V6GQKOX2.js → chunk-DJKWFYAA.js} +5 -3
  49. package/dist/chunk-DJKWFYAA.js.map +1 -0
  50. package/dist/{chunk-6NDBE2OK.js → chunk-E36EVCKG.js} +5 -3
  51. package/dist/chunk-E36EVCKG.js.map +1 -0
  52. package/dist/{chunk-IU4QUY5I.js → chunk-ELRMBQEC.js} +4 -2
  53. package/dist/chunk-ELRMBQEC.js.map +1 -0
  54. package/dist/{chunk-TIGSG6BX.js → chunk-HJKRCKDR.js} +7 -3
  55. package/dist/chunk-HJKRCKDR.js.map +1 -0
  56. package/dist/{chunk-Y6LYKFXY.js → chunk-HMQ2ZDGG.js} +5 -3
  57. package/dist/chunk-HMQ2ZDGG.js.map +1 -0
  58. package/dist/{chunk-WN52USHV.js → chunk-J3EE6XWD.js} +2 -2
  59. package/dist/{chunk-WN52USHV.js.map → chunk-J3EE6XWD.js.map} +1 -1
  60. package/dist/{chunk-CK6P5AUK.js → chunk-KKNK3JUT.js} +7 -4
  61. package/dist/chunk-KKNK3JUT.js.map +1 -0
  62. package/dist/{chunk-ZPWZIVI6.js → chunk-N3GKMPDW.js} +7 -3
  63. package/dist/chunk-N3GKMPDW.js.map +1 -0
  64. package/dist/{chunk-DYBPI5O2.js → chunk-NDUGJ4TU.js} +4 -2
  65. package/dist/chunk-NDUGJ4TU.js.map +1 -0
  66. package/dist/{chunk-WSXUPSID.js → chunk-NN6GYOA2.js} +7 -3
  67. package/dist/chunk-NN6GYOA2.js.map +1 -0
  68. package/dist/{chunk-VXFGMDA7.js → chunk-OQI7RCRP.js} +10 -2
  69. package/dist/chunk-OQI7RCRP.js.map +1 -0
  70. package/dist/{chunk-E34TE2ZI.js → chunk-S6ZTTTV7.js} +5 -3
  71. package/dist/chunk-S6ZTTTV7.js.map +1 -0
  72. package/dist/{chunk-V6HCTCVD.js → chunk-SIU2AC5W.js} +8 -4
  73. package/dist/chunk-SIU2AC5W.js.map +1 -0
  74. package/dist/{chunk-WVLMZ5SG.js → chunk-UMGY5JSP.js} +4 -2
  75. package/dist/chunk-UMGY5JSP.js.map +1 -0
  76. package/dist/{chunk-I4V3UDSA.js → chunk-V6RZWDII.js} +7 -5
  77. package/dist/chunk-V6RZWDII.js.map +1 -0
  78. package/dist/{chunk-CAB6NQ2T.js → chunk-VOCPXGQN.js} +3 -1
  79. package/dist/chunk-VOCPXGQN.js.map +1 -0
  80. package/dist/chunk-XD7DWYXG.js +34 -0
  81. package/dist/chunk-XD7DWYXG.js.map +1 -0
  82. package/dist/{chunk-B6JYRX3P.js → chunk-ZIYOJFZD.js} +43 -4
  83. package/dist/chunk-ZIYOJFZD.js.map +1 -0
  84. package/dist/{chunk-EYALJQQD.js → chunk-ZK5EGVFP.js} +4 -2
  85. package/dist/chunk-ZK5EGVFP.js.map +1 -0
  86. package/dist/{chunk-6WTETUZ6.js → chunk-ZLWQZUGF.js} +8 -2
  87. package/dist/chunk-ZLWQZUGF.js.map +1 -0
  88. package/dist/{chunk-XKFRMGMW.js → chunk-ZNZ2JDBW.js} +3 -1
  89. package/dist/chunk-ZNZ2JDBW.js.map +1 -0
  90. package/dist/{chunk-I45QO2IZ.js → chunk-ZSYLBVWG.js} +6 -4
  91. package/dist/chunk-ZSYLBVWG.js.map +1 -0
  92. package/dist/collapsible-panel.d.ts +3 -1
  93. package/dist/collapsible-panel.js +4 -2
  94. package/dist/collapsible-panel.js.map +1 -1
  95. package/dist/disclosure-arrow.d.ts +3 -1
  96. package/dist/disclosure-arrow.js +1 -1
  97. package/dist/empty-state.d.ts +3 -1
  98. package/dist/empty-state.js +1 -1
  99. package/dist/floating-toolbar.d.ts +3 -1
  100. package/dist/floating-toolbar.js +1 -1
  101. package/dist/grid.d.ts +3 -1
  102. package/dist/grid.js +1 -1
  103. package/dist/index.d.ts +3 -2
  104. package/dist/index.js +33 -32
  105. package/dist/list-action-row.d.ts +3 -1
  106. package/dist/list-action-row.js +1 -1
  107. package/dist/list-header.d.ts +6 -1
  108. package/dist/list-header.js +2 -1
  109. package/dist/list-inset-control.d.ts +3 -1
  110. package/dist/list-inset-control.js +1 -1
  111. package/dist/list-inset-text.d.ts +3 -1
  112. package/dist/list-inset-text.js +1 -1
  113. package/dist/list-item.d.ts +3 -1
  114. package/dist/list-item.js +1 -1
  115. package/dist/list.d.ts +3 -1
  116. package/dist/list.js +1 -1
  117. package/dist/loading-spinner.d.ts +3 -1
  118. package/dist/loading-spinner.js +1 -1
  119. package/dist/lucide-icon.d.ts +3 -1
  120. package/dist/lucide-icon.js +1 -1
  121. package/dist/nav-stack.d.ts +3 -1
  122. package/dist/nav-stack.js +1 -1
  123. package/dist/pane.d.ts +3 -1
  124. package/dist/pane.js +1 -1
  125. package/dist/resizable-region.d.ts +5 -2
  126. package/dist/resizable-region.js +1 -1
  127. package/dist/row.d.ts +3 -1
  128. package/dist/row.js +1 -1
  129. package/dist/segmented-control.d.ts +3 -1
  130. package/dist/segmented-control.js +1 -1
  131. package/dist/select.d.ts +3 -1
  132. package/dist/select.js +1 -1
  133. package/dist/skeleton.d.ts +3 -1
  134. package/dist/skeleton.js +1 -1
  135. package/dist/spacer.d.ts +3 -1
  136. package/dist/spacer.js +1 -1
  137. package/dist/split-view.d.ts +3 -1
  138. package/dist/split-view.js +24 -5
  139. package/dist/split-view.js.map +1 -1
  140. package/dist/state-banner.d.ts +3 -1
  141. package/dist/state-banner.js +2 -1
  142. package/dist/styles/badge.css +86 -0
  143. package/dist/styles/catalog/components/catalog-detail.css +35 -0
  144. package/dist/styles/catalog/components/catalog-example-stack.css +6 -0
  145. package/dist/styles/catalog/components/catalog-example.css +35 -0
  146. package/dist/styles/catalog/components/catalog-resource-footer.css +37 -0
  147. package/dist/styles/catalog/components/catalog-secondary-sections.css +38 -0
  148. package/dist/styles/catalog/components/catalog-section-list.css +22 -0
  149. package/dist/styles/catalog/components/catalog-sidebar.css +82 -0
  150. package/dist/styles/catalog/components/catalog-stage.css +60 -0
  151. package/dist/styles/catalog/components/catalog.css +22 -0
  152. package/dist/styles/catalog.css +10 -587
  153. package/dist/styles/list-header.css +4 -60
  154. package/dist/styles/resizable-region.css +24 -0
  155. package/dist/styles/select.css +0 -3
  156. package/dist/styles/split-view.css +1 -1
  157. package/dist/styles/state-banner.css +0 -27
  158. package/dist/styles/styles.css +1 -0
  159. package/dist/styles/tab-bar.css +6 -1
  160. package/dist/styles/text.css +4 -0
  161. package/dist/styles/token-search-field.css +5 -40
  162. package/dist/styles/toolbar-control-group.css +110 -16
  163. package/dist/styles/toolbar-text.css +4 -0
  164. package/dist/styles/toolbar.css +72 -0
  165. package/dist/styles/webawesome.css +22 -7
  166. package/dist/sunken-panel.d.ts +3 -1
  167. package/dist/sunken-panel.js +1 -1
  168. package/dist/surface-scaffold.d.ts +6 -2
  169. package/dist/surface-scaffold.js +1 -1
  170. package/dist/tab-bar.d.ts +8 -1
  171. package/dist/tab-bar.js +1 -1
  172. package/dist/tab-scaffold.d.ts +3 -1
  173. package/dist/tab-scaffold.js +3 -1
  174. package/dist/tab-scaffold.js.map +1 -1
  175. package/dist/text.d.ts +5 -2
  176. package/dist/text.js +1 -1
  177. package/dist/token-search-field.d.ts +5 -1
  178. package/dist/token-search-field.js +1 -1
  179. package/dist/toolbar-control-group.d.ts +33 -3
  180. package/dist/toolbar-control-group.js +1 -1
  181. package/dist/toolbar-text.d.ts +3 -1
  182. package/dist/toolbar-text.js +1 -1
  183. package/dist/toolbar.d.ts +9 -1
  184. package/dist/toolbar.js +1 -1
  185. package/dist/types-CzgSrFXP.d.ts +64 -0
  186. package/dist/value-table.d.ts +6 -2
  187. package/dist/value-table.js +1 -1
  188. package/dist/wire-resizable-regions.js +1 -1
  189. package/dist/wire-token-search-fields.js +1 -1
  190. package/dist/workbench.d.ts +3 -1
  191. package/dist/workbench.js +3 -1
  192. package/dist/workbench.js.map +1 -1
  193. package/docs/accessibility.md +11 -1
  194. package/docs/catalog.md +25 -7
  195. package/docs/component-contract.md +71 -22
  196. package/docs/component-selection.md +11 -9
  197. package/docs/design-philosophy.md +6 -5
  198. package/docs/recipes.md +5 -5
  199. package/docs/type-contracts.md +1 -0
  200. package/docs/ux-demo.md +4 -4
  201. package/docs/webawesome-theme.md +5 -4
  202. package/llms.txt +30 -10
  203. package/package.json +9 -2
  204. package/ux-demo/recipes/app-shell.css +87 -0
  205. package/ux-demo/recipes/app-shell.tsx +207 -178
  206. package/ux-demo/recipes/collapsible-sidebar.css +83 -0
  207. package/ux-demo/recipes/collapsible-sidebar.tsx +152 -122
  208. package/ux-demo/recipes/compact-toolbar.css +35 -0
  209. package/ux-demo/recipes/compact-toolbar.tsx +104 -100
  210. package/ux-demo/recipes/composer-form.css +19 -0
  211. package/ux-demo/recipes/composer-form.tsx +19 -15
  212. package/ux-demo/recipes/list-detail-dialog.css +42 -0
  213. package/ux-demo/recipes/list-detail-dialog.tsx +127 -125
  214. package/ux-demo/recipes/list-workspace-states.css +30 -0
  215. package/ux-demo/recipes/list-workspace-states.tsx +66 -60
  216. package/ux-demo/recipes/loading-inspector.css +7 -0
  217. package/ux-demo/recipes/loading-inspector.tsx +151 -131
  218. package/ux-demo/recipes/navigation-sidebar.css +12 -0
  219. package/ux-demo/recipes/navigation-sidebar.tsx +116 -105
  220. package/ux-demo/recipes/navigation-stack.css +12 -0
  221. package/ux-demo/recipes/navigation-stack.tsx +47 -37
  222. package/ux-demo/recipes/recipe-root.css +67 -0
  223. package/ux-demo/recipes/recipe-root.tsx +87 -0
  224. package/ux-demo/recipes/workspace-header.css +7 -0
  225. package/ux-demo/recipes/workspace-header.tsx +60 -61
  226. package/dist/chunk-2TYUFZUC.js.map +0 -1
  227. package/dist/chunk-6NDBE2OK.js.map +0 -1
  228. package/dist/chunk-6WTETUZ6.js.map +0 -1
  229. package/dist/chunk-AQFWBG23.js.map +0 -1
  230. package/dist/chunk-B6JYRX3P.js.map +0 -1
  231. package/dist/chunk-CAB6NQ2T.js.map +0 -1
  232. package/dist/chunk-CK6P5AUK.js.map +0 -1
  233. package/dist/chunk-DAO2VFTT.js.map +0 -1
  234. package/dist/chunk-DYBPI5O2.js.map +0 -1
  235. package/dist/chunk-E34TE2ZI.js.map +0 -1
  236. package/dist/chunk-EYALJQQD.js.map +0 -1
  237. package/dist/chunk-GYGM363P.js.map +0 -1
  238. package/dist/chunk-I45QO2IZ.js.map +0 -1
  239. package/dist/chunk-I4V3UDSA.js.map +0 -1
  240. package/dist/chunk-IU4QUY5I.js.map +0 -1
  241. package/dist/chunk-OGNE3OFT.js.map +0 -1
  242. package/dist/chunk-OGTJNHRG.js.map +0 -1
  243. package/dist/chunk-OMSRSL3B.js.map +0 -1
  244. package/dist/chunk-SOK5EUF7.js.map +0 -1
  245. package/dist/chunk-T7DOWS6T.js.map +0 -1
  246. package/dist/chunk-TIGSG6BX.js.map +0 -1
  247. package/dist/chunk-UDHHKZWO.js.map +0 -1
  248. package/dist/chunk-V6GQKOX2.js.map +0 -1
  249. package/dist/chunk-V6HCTCVD.js.map +0 -1
  250. package/dist/chunk-VXFGMDA7.js.map +0 -1
  251. package/dist/chunk-W2FK7ZNY.js.map +0 -1
  252. package/dist/chunk-WSXUPSID.js.map +0 -1
  253. package/dist/chunk-WVLMZ5SG.js.map +0 -1
  254. package/dist/chunk-XKFRMGMW.js.map +0 -1
  255. package/dist/chunk-Y6LYKFXY.js.map +0 -1
  256. package/dist/chunk-ZPWZIVI6.js.map +0 -1
  257. package/ux-demo/recipes/recipes.css +0 -453
@@ -1,6 +1,6 @@
1
1
  import '@kerfjs/ui/layout.css';
2
2
  import '@kerfjs/ui/collapsible-panel.css';
3
- import './recipes.css';
3
+ import './collapsible-sidebar.css';
4
4
 
5
5
  import {
6
6
  CollapsiblePanel,
@@ -10,7 +10,7 @@ import { deviceClass } from '@kerfjs/ui/device-class';
10
10
  import { ListHeader } from '@kerfjs/ui/list-header';
11
11
  import { ListItem } from '@kerfjs/ui/list-item';
12
12
  import { LucideIcon } from '@kerfjs/ui/lucide-icon';
13
- import { Text } from '@kerfjs/ui/text';
13
+ import { Pane } from '@kerfjs/ui/pane';
14
14
  import { Toolbar } from '@kerfjs/ui/toolbar';
15
15
  import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
16
16
  import { ToolbarText } from '@kerfjs/ui/toolbar-text';
@@ -18,6 +18,11 @@ import { type SidebarStorage, wireSidebar } from '@kerfjs/ui/wire-sidebar';
18
18
  import { signal } from 'kerfjs';
19
19
  import { Bell, Folder, Inbox, Users } from 'lucide';
20
20
 
21
+ import {
22
+ RecipeMutedText,
23
+ RecipeOwnershipNote,
24
+ RecipeRoot,
25
+ } from './recipe-root.js';
21
26
  import type { RecipeFactory } from './types.js';
22
27
 
23
28
  const RAIL_ACTION = 'recipe-sidebar-rail';
@@ -67,132 +72,157 @@ export const createRecipe: RecipeFactory = (announce) => {
67
72
  );
68
73
 
69
74
  const render = () => (
70
- <section
71
- class="kui-recipe recipe-collapsible-sidebar kui-recipe__surface"
72
- data-recipe="recipe-collapsible-sidebar"
73
- data-rail-collapsed={String(railCollapsed.value)}
74
- data-drawer-collapsed={String(drawerCollapsed.value)}
75
+ <RecipeRoot
76
+ recipe="recipe-collapsible-sidebar"
77
+ dataAttributes={{
78
+ 'data-rail-collapsed': String(railCollapsed.value),
79
+ 'data-drawer-collapsed': String(drawerCollapsed.value),
80
+ }}
75
81
  >
76
- <CollapsiblePanel
77
- id="sidebar-rail"
78
- side="left"
79
- size={232}
80
- collapsed={railCollapsed.value}
81
- label="Workspace navigation"
82
- className="recipe-collapsible-sidebar__rail kui-pane"
83
- >
84
- <div class="recipe-collapsible-sidebar__rail-head">
85
- <ToolbarText text="Atlas" />
86
- <ToolbarControlGroup appearance="borderless" single>
87
- {railToggle(true)}
88
- </ToolbarControlGroup>
82
+ <div class="recipe-collapsible-sidebar">
83
+ <div class="recipe-collapsible-sidebar__rail">
84
+ <CollapsiblePanel
85
+ id="sidebar-rail"
86
+ side="left"
87
+ size={232}
88
+ collapsed={railCollapsed.value}
89
+ label="Workspace navigation"
90
+ >
91
+ <Pane
92
+ contentElement="nav"
93
+ contentClassName="recipe-collapsible-sidebar__nav"
94
+ contentLabel="Workspace"
95
+ header={
96
+ <Toolbar
97
+ label="Workspace navigation"
98
+ dividerSides=""
99
+ leading={<ToolbarText text="Atlas" />}
100
+ trailing={
101
+ <ToolbarControlGroup appearance="borderless" single>
102
+ {railToggle(true)}
103
+ </ToolbarControlGroup>
104
+ }
105
+ />
106
+ }
107
+ >
108
+ <section>
109
+ <ListHeader label="Workspace" />
110
+ <ListItem
111
+ action="recipe-action"
112
+ itemId="inbox"
113
+ label="Inbox"
114
+ icon={<LucideIcon icon={Inbox} name="inbox" />}
115
+ trailing={<span>12</span>}
116
+ selected={selected.value === 'inbox'}
117
+ />
118
+ <ListItem
119
+ action="recipe-action"
120
+ itemId="projects"
121
+ label="Projects"
122
+ icon={<LucideIcon icon={Folder} name="folder" />}
123
+ selected={selected.value === 'projects'}
124
+ />
125
+ <ListItem
126
+ action="recipe-action"
127
+ itemId="shared"
128
+ label="Shared with me"
129
+ icon={<LucideIcon icon={Users} name="users" />}
130
+ selected={selected.value === 'shared'}
131
+ />
132
+ </section>
133
+ </Pane>
134
+ </CollapsiblePanel>
89
135
  </div>
90
- <nav
91
- class="recipe-collapsible-sidebar__nav kui-pane__content kui-content"
92
- aria-label="Workspace"
93
- >
94
- <section>
95
- <ListHeader label="Workspace" />
96
- <ListItem
97
- action="recipe-action"
98
- itemId="inbox"
99
- label="Inbox"
100
- icon={<LucideIcon icon={Inbox} name="inbox" />}
101
- trailing={<span>12</span>}
102
- selected={selected.value === 'inbox'}
103
- />
104
- <ListItem
105
- action="recipe-action"
106
- itemId="projects"
107
- label="Projects"
108
- icon={<LucideIcon icon={Folder} name="folder" />}
109
- selected={selected.value === 'projects'}
110
- />
111
- <ListItem
112
- action="recipe-action"
113
- itemId="shared"
114
- label="Shared with me"
115
- icon={<LucideIcon icon={Users} name="users" />}
116
- selected={selected.value === 'shared'}
117
- />
118
- </section>
119
- </nav>
120
- </CollapsiblePanel>
121
- <main class="recipe-collapsible-sidebar__main kui-pane">
122
- <Toolbar
123
- label="Current navigation view"
124
- dividerSides=""
125
- leading={
126
- <>
127
- <ToolbarControlGroup appearance="borderless" single>
128
- {railToggle(false)}
129
- </ToolbarControlGroup>
130
- <ToolbarText
131
- text={
132
- selected.value === 'projects'
133
- ? 'Projects'
134
- : selected.value === 'shared'
135
- ? 'Shared with me'
136
- : 'Inbox'
136
+ <div class="recipe-collapsible-sidebar__main">
137
+ <Pane
138
+ element="main"
139
+ contentClassName="recipe-collapsible-sidebar__body"
140
+ header={
141
+ <Toolbar
142
+ label="Current navigation view"
143
+ dividerSides=""
144
+ leading={
145
+ <>
146
+ <ToolbarControlGroup appearance="borderless" single>
147
+ {railToggle(false)}
148
+ </ToolbarControlGroup>
149
+ <ToolbarText
150
+ text={
151
+ selected.value === 'projects'
152
+ ? 'Projects'
153
+ : selected.value === 'shared'
154
+ ? 'Shared with me'
155
+ : 'Inbox'
156
+ }
157
+ size="xlarge"
158
+ id="recipe-collapsible-main-title"
159
+ />
160
+ </>
161
+ }
162
+ trailing={
163
+ <ToolbarControlGroup appearance="borderless" single>
164
+ {drawerToggle()}
165
+ </ToolbarControlGroup>
137
166
  }
138
- size="xlarge"
139
- id="recipe-collapsible-main-title"
140
167
  />
141
- </>
142
- }
143
- trailing={
144
- <ToolbarControlGroup appearance="borderless" single>
145
- {drawerToggle()}
146
- </ToolbarControlGroup>
147
- }
148
- />
149
- <div class="recipe-collapsible-sidebar__body kui-pane__content kui-content">
150
- <Text class="kui-recipe__ownership kui-content-item">
151
- The app owns each panel's <code>collapsed</code> signal, sizes, and
152
- content; <code>wireSidebar</code> owns the toggle, focus
153
- move/restore, the compact overlay, and persistence.
154
- </Text>
155
- <article class="recipe-collapsible-sidebar__card kui-content-item">
156
- <strong>Narrow the window to a compact width</strong>
157
- <Text class="kui-recipe__muted">
158
- The rail and drawer become a dismissable overlay: a backdrop,
159
- Escape, and a trapped Tab ring, all managed by the wire.
160
- </Text>
161
- </article>
168
+ }
169
+ >
170
+ <RecipeOwnershipNote>
171
+ The app owns each panel's <code>collapsed</code> signal, sizes,
172
+ and content; <code>wireSidebar</code> owns the toggle, focus
173
+ move/restore, the compact overlay, and persistence.
174
+ </RecipeOwnershipNote>
175
+ <article class="recipe-collapsible-sidebar__card kui-content-item">
176
+ <strong>Narrow the window to a compact width</strong>
177
+ <RecipeMutedText>
178
+ The rail and drawer become a dismissable overlay: a backdrop,
179
+ Escape, and a trapped Tab ring, all managed by the wire.
180
+ </RecipeMutedText>
181
+ </article>
182
+ </Pane>
162
183
  </div>
163
- </main>
164
- <CollapsiblePanel
165
- id="sidebar-console"
166
- side="bottom"
167
- size={168}
168
- collapsed={drawerCollapsed.value}
169
- label="Activity"
170
- className="recipe-collapsible-sidebar__drawer kui-pane"
171
- >
172
- <Toolbar
173
- label="Activity"
174
- dividerSides="b"
175
- leading={<ToolbarText text="Activity" size="small" />}
176
- trailing={
177
- <ToolbarControlGroup appearance="borderless" single>
178
- {drawerToggle()}
179
- </ToolbarControlGroup>
180
- }
181
- />
182
- <div class="recipe-collapsible-sidebar__drawer-body kui-pane__content kui-content">
183
- <ul class="recipe-collapsible-sidebar__log">
184
- <li>
185
- <LucideIcon icon={Bell} name="bell" />
186
- Deploy finished · 2m ago
187
- </li>
188
- <li>
189
- <LucideIcon icon={Bell} name="bell" />
190
- Review requested · 9m ago
191
- </li>
192
- </ul>
184
+ <div class="recipe-collapsible-sidebar__drawer">
185
+ <CollapsiblePanel
186
+ id="sidebar-console"
187
+ side="bottom"
188
+ size={168}
189
+ collapsed={drawerCollapsed.value}
190
+ label="Activity"
191
+ >
192
+ <Pane
193
+ contentClassName="recipe-collapsible-sidebar__drawer-body"
194
+ header={
195
+ <Toolbar
196
+ label="Activity"
197
+ dividerSides="b"
198
+ leading={<ToolbarText text="Activity" size="small" />}
199
+ trailing={
200
+ <ToolbarControlGroup appearance="borderless" single>
201
+ {drawerToggle()}
202
+ </ToolbarControlGroup>
203
+ }
204
+ />
205
+ }
206
+ >
207
+ <ul class="recipe-collapsible-sidebar__log">
208
+ <li>
209
+ <span class="recipe-collapsible-sidebar__log-icon">
210
+ <LucideIcon icon={Bell} name="bell" />
211
+ </span>
212
+ Deploy finished · 2m ago
213
+ </li>
214
+ <li>
215
+ <span class="recipe-collapsible-sidebar__log-icon">
216
+ <LucideIcon icon={Bell} name="bell" />
217
+ </span>
218
+ Review requested · 9m ago
219
+ </li>
220
+ </ul>
221
+ </Pane>
222
+ </CollapsiblePanel>
193
223
  </div>
194
- </CollapsiblePanel>
195
- </section>
224
+ </div>
225
+ </RecipeRoot>
196
226
  );
197
227
 
198
228
  return {
@@ -0,0 +1,35 @@
1
+ .recipe-compact-toolbar {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--kui-layout-content-gap);
5
+ }
6
+
7
+ .recipe-compact-toolbar__guide {
8
+ display: grid;
9
+ grid-template-columns: repeat(4, minmax(0, 1fr));
10
+ margin-inline: 0;
11
+ gap: var(--kui-layout-control-gap);
12
+ }
13
+
14
+ .recipe-compact-toolbar__guide > div {
15
+ padding: var(--kui-layout-item-padding);
16
+ border: var(--kui-layout-item-border-width) solid
17
+ var(--kui-color-border-quiet);
18
+ border-radius: var(--kui-layout-rounded-radius);
19
+ }
20
+
21
+ .recipe-compact-toolbar__guide strong {
22
+ display: block;
23
+ }
24
+
25
+ @container (max-width: 44rem) {
26
+ .recipe-compact-toolbar__guide {
27
+ grid-template-columns: 1fr 1fr;
28
+ }
29
+ }
30
+
31
+ @container (max-width: 28rem) {
32
+ .recipe-compact-toolbar__guide {
33
+ grid-template-columns: 1fr;
34
+ }
35
+ }
@@ -1,17 +1,21 @@
1
1
  import '@kerfjs/ui/layout.css';
2
2
  import '@kerfjs/ui/select/register';
3
- import './recipes.css';
3
+ import './compact-toolbar.css';
4
4
 
5
5
  import { LucideIcon } from '@kerfjs/ui/lucide-icon';
6
6
  import { SegmentedControl } from '@kerfjs/ui/segmented-control';
7
7
  import { Select } from '@kerfjs/ui/select';
8
- import { Text } from '@kerfjs/ui/text';
9
8
  import { Toolbar } from '@kerfjs/ui/toolbar';
10
9
  import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
11
10
  import { ToolbarText } from '@kerfjs/ui/toolbar-text';
12
11
  import { signal } from 'kerfjs';
13
12
  import { Filter, MoreHorizontal, RefreshCw } from 'lucide';
14
13
 
14
+ import {
15
+ RecipeMutedText,
16
+ RecipeOwnershipNote,
17
+ RecipeRoot,
18
+ } from './recipe-root.js';
15
19
  import type { RecipeFactory } from './types.js';
16
20
 
17
21
  export const createRecipe: RecipeFactory = (announce) => {
@@ -19,108 +23,108 @@ export const createRecipe: RecipeFactory = (announce) => {
19
23
  const sort = signal('updated');
20
24
  const filtered = signal(false);
21
25
  const render = () => (
22
- <section
23
- class="kui-recipe recipe-compact-toolbar kui-recipe__surface kui-layout"
24
- data-recipe="recipe-compact-toolbar"
25
- >
26
- <Toolbar
27
- label="Task workspace controls"
28
- leading={
29
- <ToolbarControlGroup appearance="borderless" single>
30
- <ToolbarText text="Tasks" size="small" />
31
- </ToolbarControlGroup>
32
- }
33
- center={
34
- <ToolbarControlGroup>
35
- <SegmentedControl<string>
36
- id="recipe-view"
37
- label="View"
38
- value={view.value}
39
- action="recipe-action"
40
- appearance="toolbar"
41
- shape="pill"
42
- size="small"
43
- choices={[
44
- { value: 'list', label: 'List' },
45
- { value: 'board', label: 'Board' },
46
- { value: 'timeline', label: 'Timeline' },
47
- ]}
48
- />
49
- </ToolbarControlGroup>
50
- }
51
- trailing={
52
- <div class="kui-control-cluster">
53
- <Select<string>
54
- name="recipe-sort"
55
- value={sort.value}
56
- ariaLabel="Sort tasks"
57
- fitMenu
58
- choices={[
59
- { value: 'updated', label: 'Recently updated' },
60
- { value: 'priority', label: 'Priority' },
61
- { value: 'owner', label: 'Owner' },
62
- { value: 'created', label: 'Created date' },
63
- ]}
64
- />
65
- <ToolbarControlGroup label="Task actions" buttonAppearance="push">
66
- <button
67
- type="button"
68
- aria-label="Toggle filters"
69
- aria-pressed={String(filtered.value)}
70
- data-action="recipe-action"
71
- data-recipe-command="filter"
72
- >
73
- <LucideIcon icon={Filter} name="filter" />
74
- </button>
75
- <button
76
- type="button"
77
- aria-label="Refresh tasks"
78
- data-action="recipe-action"
79
- data-recipe-command="refresh"
80
- >
81
- <LucideIcon icon={RefreshCw} name="refresh-cw" />
82
- </button>
83
- </ToolbarControlGroup>
84
- <ToolbarControlGroup label="More task actions" single>
85
- <button
86
- type="button"
87
- aria-label="More task actions"
88
- data-action="recipe-action"
89
- data-recipe-command="more"
90
- >
91
- <LucideIcon icon={MoreHorizontal} name="more-horizontal" />
92
- </button>
26
+ <RecipeRoot recipe="recipe-compact-toolbar">
27
+ <div class="recipe-compact-toolbar kui-layout">
28
+ <Toolbar
29
+ label="Task workspace controls"
30
+ responsive="stack"
31
+ responsiveAt="compact"
32
+ leading={<ToolbarText text="Tasks" size="small" />}
33
+ center={
34
+ <ToolbarControlGroup>
35
+ <SegmentedControl<string>
36
+ id="recipe-view"
37
+ label="View"
38
+ value={view.value}
39
+ action="recipe-action"
40
+ appearance="toolbar"
41
+ shape="pill"
42
+ size="small"
43
+ choices={[
44
+ { value: 'list', label: 'List' },
45
+ { value: 'board', label: 'Board' },
46
+ { value: 'timeline', label: 'Timeline' },
47
+ ]}
48
+ />
93
49
  </ToolbarControlGroup>
50
+ }
51
+ trailing={
52
+ <>
53
+ <ToolbarControlGroup single focusRing="outline">
54
+ <Select<string>
55
+ name="recipe-sort"
56
+ value={sort.value}
57
+ ariaLabel="Sort tasks"
58
+ fitMenu
59
+ presentation="toolbar-borderless"
60
+ size="compact"
61
+ focusRingOwner="group"
62
+ choices={[
63
+ { value: 'updated', label: 'Recently updated' },
64
+ { value: 'priority', label: 'Priority' },
65
+ { value: 'owner', label: 'Owner' },
66
+ { value: 'created', label: 'Created date' },
67
+ ]}
68
+ />
69
+ </ToolbarControlGroup>
70
+ <ToolbarControlGroup label="Task actions" buttonAppearance="push">
71
+ <button
72
+ type="button"
73
+ aria-label="Toggle filters"
74
+ aria-pressed={String(filtered.value)}
75
+ data-action="recipe-action"
76
+ data-recipe-command="filter"
77
+ >
78
+ <LucideIcon icon={Filter} name="filter" />
79
+ </button>
80
+ <button
81
+ type="button"
82
+ aria-label="Refresh tasks"
83
+ data-action="recipe-action"
84
+ data-recipe-command="refresh"
85
+ >
86
+ <LucideIcon icon={RefreshCw} name="refresh-cw" />
87
+ </button>
88
+ </ToolbarControlGroup>
89
+ <ToolbarControlGroup label="More task actions" single>
90
+ <button
91
+ type="button"
92
+ aria-label="More task actions"
93
+ data-action="recipe-action"
94
+ data-recipe-command="more"
95
+ >
96
+ <LucideIcon icon={MoreHorizontal} name="more-horizontal" />
97
+ </button>
98
+ </ToolbarControlGroup>
99
+ </>
100
+ }
101
+ />
102
+ <div class="recipe-compact-toolbar__guide kui-content-item">
103
+ <div>
104
+ <strong>ToolbarControlGroup</strong>
105
+ <RecipeMutedText>
106
+ Related commands or pressed tools.
107
+ </RecipeMutedText>
108
+ </div>
109
+ <div>
110
+ <strong>SegmentedControl</strong>
111
+ <RecipeMutedText>A few visible exclusive choices.</RecipeMutedText>
112
+ </div>
113
+ <div>
114
+ <strong>Select</strong>
115
+ <RecipeMutedText>A longer controlled value list.</RecipeMutedText>
116
+ </div>
117
+ <div>
118
+ <strong>Ordinary button</strong>
119
+ <RecipeMutedText>One independent command.</RecipeMutedText>
94
120
  </div>
95
- }
96
- />
97
- <div class="recipe-compact-toolbar__guide kui-content-item">
98
- <div>
99
- <strong>ToolbarControlGroup</strong>
100
- <span class="kui-recipe__muted">
101
- Related commands or pressed tools.
102
- </span>
103
- </div>
104
- <div>
105
- <strong>SegmentedControl</strong>
106
- <span class="kui-recipe__muted">
107
- A few visible exclusive choices.
108
- </span>
109
- </div>
110
- <div>
111
- <strong>Select</strong>
112
- <span class="kui-recipe__muted">A longer controlled value list.</span>
113
- </div>
114
- <div>
115
- <strong>Ordinary button</strong>
116
- <span class="kui-recipe__muted">One independent command.</span>
117
121
  </div>
122
+ <RecipeOwnershipNote>
123
+ The recipe owns control semantics and wrapping. The app owns values,
124
+ actions, persistence, and responsive priority.
125
+ </RecipeOwnershipNote>
118
126
  </div>
119
- <Text class="kui-recipe__ownership kui-content-item">
120
- The recipe owns control semantics and wrapping. The app owns values,
121
- actions, persistence, and responsive priority.
122
- </Text>
123
- </section>
127
+ </RecipeRoot>
124
128
  );
125
129
  return {
126
130
  render,
@@ -0,0 +1,19 @@
1
+ .recipe-form__section {
2
+ display: grid;
3
+ min-width: 0;
4
+ margin-inline: var(--kui-layout-inline-margin, remify(8px));
5
+ gap: var(--kui-layout-item-gap);
6
+ }
7
+
8
+ .recipe-form__heading-summary {
9
+ min-width: 0;
10
+ overflow: hidden;
11
+ color: var(--kui-color-text-quiet);
12
+ text-overflow: ellipsis;
13
+ white-space: nowrap;
14
+ }
15
+
16
+ .recipe-form__fields,
17
+ .recipe-form__footer {
18
+ align-content: start;
19
+ }
@@ -4,16 +4,16 @@ import '@kerfjs/ui/webawesome.css';
4
4
  import '@awesome.me/webawesome/dist/components/button/button.js';
5
5
  import '@awesome.me/webawesome/dist/components/input/input.js';
6
6
  import '@awesome.me/webawesome/dist/components/textarea/textarea.js';
7
- import './recipes.css';
7
+ import './composer-form.css';
8
8
 
9
9
  import { ListInsetText } from '@kerfjs/ui/list-inset-text';
10
10
  import { Select } from '@kerfjs/ui/select';
11
11
  import { StateBanner } from '@kerfjs/ui/state-banner';
12
- import { Text } from '@kerfjs/ui/text';
13
12
  import { Toolbar } from '@kerfjs/ui/toolbar';
14
13
  import { ToolbarText } from '@kerfjs/ui/toolbar-text';
15
14
  import { signal } from 'kerfjs';
16
15
 
16
+ import { RecipeOwnershipNote, RecipeRoot } from './recipe-root.js';
17
17
  import type { RecipeFactory } from './types.js';
18
18
 
19
19
  type ValueField = HTMLElement & { value: string };
@@ -40,11 +40,13 @@ export const createRecipe: RecipeFactory = (announce) => {
40
40
  const audience = signal('team');
41
41
  const status = signal<'idle' | 'error' | 'saved'>('idle');
42
42
  const render = () => (
43
- <form
44
- class="kui-recipe recipe-form kui-recipe__surface kui-content"
45
- data-recipe="recipe-composer-form"
46
- aria-labelledby="recipe-composer-title"
47
- aria-describedby="recipe-composer-summary"
43
+ <RecipeRoot
44
+ element="form"
45
+ recipe="recipe-composer-form"
46
+ measure="form"
47
+ contentLayout
48
+ ariaLabelledBy="recipe-composer-title"
49
+ ariaDescribedBy="recipe-composer-summary"
48
50
  noValidate
49
51
  >
50
52
  <Toolbar
@@ -58,11 +60,13 @@ export const createRecipe: RecipeFactory = (announce) => {
58
60
  />
59
61
  }
60
62
  />
61
- <ListInsetText className="kui-recipe__heading-summary">
62
- <span id="recipe-composer-summary">
63
- Share a concise, actionable update with collaborators.
64
- </span>
65
- </ListInsetText>
63
+ <div class="recipe-form__heading-summary">
64
+ <ListInsetText sides="rl">
65
+ <span id="recipe-composer-summary">
66
+ Share a concise, actionable update with collaborators.
67
+ </span>
68
+ </ListInsetText>
69
+ </div>
66
70
  {status.value === 'error' && (
67
71
  <StateBanner
68
72
  title="Add a title before publishing"
@@ -124,12 +128,12 @@ export const createRecipe: RecipeFactory = (announce) => {
124
128
  Publish update
125
129
  </wa-button>
126
130
  </div>
127
- <Text class="kui-recipe__ownership">
131
+ <RecipeOwnershipNote contentItem={false}>
128
132
  The recipe owns field, message, and action rhythm. The app owns
129
133
  validation rules, draft persistence, permissions, and transport.
130
- </Text>
134
+ </RecipeOwnershipNote>
131
135
  </footer>
132
- </form>
136
+ </RecipeRoot>
133
137
  );
134
138
  return {
135
139
  render,