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

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 (212) hide show
  1. package/README.md +29 -5
  2. package/ai/compile-time-contracts-v1.json +5 -2
  3. package/ai/component-catalog-v2.d.ts +19 -1
  4. package/ai/component-catalog-v2.json +842 -127
  5. package/ai/component-catalog-v2.schema.json +33 -1
  6. package/ai/component-catalog.json +603 -126
  7. package/ai/component-catalog.schema.json +21 -1
  8. package/ai/public-api-signatures-v1.md +218 -31
  9. package/ai/skill.md +27 -24
  10. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  11. package/analyzer/index.mjs +105 -35
  12. package/dist/app-tab.d.ts +10 -1
  13. package/dist/app-tab.js +1 -1
  14. package/dist/catalog-resources.d.ts +1 -1
  15. package/dist/catalog.d.ts +28 -5
  16. package/dist/catalog.js +66 -25
  17. package/dist/catalog.js.map +1 -1
  18. package/dist/{chunk-OL4TC3U2.js → chunk-2CE22Q2K.js} +9 -2
  19. package/dist/chunk-2CE22Q2K.js.map +1 -0
  20. package/dist/{chunk-SIU2AC5W.js → chunk-2S7OWAEV.js} +3 -3
  21. package/dist/{chunk-SIU2AC5W.js.map → chunk-2S7OWAEV.js.map} +1 -1
  22. package/dist/{chunk-2YOUUEWR.js → chunk-3S7OUHUN.js} +4 -4
  23. package/dist/{chunk-2YOUUEWR.js.map → chunk-3S7OUHUN.js.map} +1 -1
  24. package/dist/{chunk-2WMSE4MK.js → chunk-4XE2CDYB.js} +2 -2
  25. package/dist/{chunk-2WMSE4MK.js.map → chunk-4XE2CDYB.js.map} +1 -1
  26. package/dist/{chunk-V6RZWDII.js → chunk-7GUQYHKA.js} +19 -2
  27. package/dist/chunk-7GUQYHKA.js.map +1 -0
  28. package/dist/{chunk-ZNZ2JDBW.js → chunk-7IUSV4XG.js} +1 -1
  29. package/dist/chunk-7IUSV4XG.js.map +1 -0
  30. package/dist/{chunk-UMGY5JSP.js → chunk-7XJO4D6A.js} +22 -2
  31. package/dist/chunk-7XJO4D6A.js.map +1 -0
  32. package/dist/{chunk-KKNK3JUT.js → chunk-ABAXNUXU.js} +5 -5
  33. package/dist/chunk-ABAXNUXU.js.map +1 -0
  34. package/dist/{chunk-AQSKJ6U6.js → chunk-DNCOFEUL.js} +21 -2
  35. package/dist/chunk-DNCOFEUL.js.map +1 -0
  36. package/dist/chunk-EKYZCRCX.js +256 -0
  37. package/dist/chunk-EKYZCRCX.js.map +1 -0
  38. package/dist/{chunk-COUPLRZG.js → chunk-IR53BQUJ.js} +16 -12
  39. package/dist/chunk-IR53BQUJ.js.map +1 -0
  40. package/dist/{chunk-3V75EQIP.js → chunk-JGFZ42ZF.js} +3 -3
  41. package/dist/{chunk-3V75EQIP.js.map → chunk-JGFZ42ZF.js.map} +1 -1
  42. package/dist/{chunk-54HM36IQ.js → chunk-KAQUUTZR.js} +4 -2
  43. package/dist/chunk-KAQUUTZR.js.map +1 -0
  44. package/dist/{chunk-ZSYLBVWG.js → chunk-LIOXPQ7N.js} +4 -4
  45. package/dist/chunk-LIOXPQ7N.js.map +1 -0
  46. package/dist/{chunk-S6ZTTTV7.js → chunk-M6GLI6A6.js} +3 -3
  47. package/dist/{chunk-S6ZTTTV7.js.map → chunk-M6GLI6A6.js.map} +1 -1
  48. package/dist/{chunk-OQI7RCRP.js → chunk-MTM43SJO.js} +7 -1
  49. package/dist/chunk-MTM43SJO.js.map +1 -0
  50. package/dist/{chunk-5CE64WGC.js → chunk-OSCL5TKR.js} +3 -3
  51. package/dist/{chunk-5CE64WGC.js.map → chunk-OSCL5TKR.js.map} +1 -1
  52. package/dist/{chunk-DARTMI5Z.js → chunk-SV22UIW4.js} +2 -2
  53. package/dist/{chunk-DARTMI5Z.js.map → chunk-SV22UIW4.js.map} +1 -1
  54. package/dist/{chunk-ELRMBQEC.js → chunk-T7C2VZUB.js} +17 -2
  55. package/dist/chunk-T7C2VZUB.js.map +1 -0
  56. package/dist/{chunk-DJKWFYAA.js → chunk-YZV2PTY4.js} +3 -3
  57. package/dist/{chunk-DJKWFYAA.js.map → chunk-YZV2PTY4.js.map} +1 -1
  58. package/dist/collapsible-panel.d.ts +1 -1
  59. package/dist/collapsible-panel.js.map +1 -1
  60. package/dist/css-values.d.ts +41 -3
  61. package/dist/css-values.js +1 -1
  62. package/dist/grid.d.ts +15 -1
  63. package/dist/grid.js +2 -1
  64. package/dist/index.d.ts +1 -1
  65. package/dist/index.js +19 -19
  66. package/dist/list-action-row.js +1 -1
  67. package/dist/list-header.js +1 -1
  68. package/dist/list-item.js +1 -1
  69. package/dist/list.d.ts +21 -2
  70. package/dist/list.js +2 -1
  71. package/dist/nav-stack.js +1 -1
  72. package/dist/pane.d.ts +17 -2
  73. package/dist/pane.js +1 -1
  74. package/dist/row.d.ts +19 -1
  75. package/dist/row.js +2 -1
  76. package/dist/segmented-control.js +1 -1
  77. package/dist/select.d.ts +13 -3
  78. package/dist/select.js +1 -1
  79. package/dist/skeleton.d.ts +5 -1
  80. package/dist/skeleton.js +1 -1
  81. package/dist/spacer.js +1 -1
  82. package/dist/split-view.js +1 -1
  83. package/dist/state-banner.js +1 -1
  84. package/dist/styles/app-tab.css +9 -0
  85. package/dist/styles/catalog/components/catalog-example-stack.css +4 -1
  86. package/dist/styles/catalog/components/catalog-example.css +145 -0
  87. package/dist/styles/collapsible-panel.css +132 -4
  88. package/dist/styles/document.css +23 -0
  89. package/dist/styles/foundation.css +21 -7
  90. package/dist/styles/grid.css +10 -0
  91. package/dist/styles/list-header.css +45 -0
  92. package/dist/styles/list-inset-control.css +8 -1
  93. package/dist/styles/list-item.css +6 -0
  94. package/dist/styles/list.css +11 -0
  95. package/dist/styles/nav-stack.css +89 -2
  96. package/dist/styles/pane.css +86 -1
  97. package/dist/styles/resizable-region.css +93 -5
  98. package/dist/styles/row.css +10 -0
  99. package/dist/styles/select.css +39 -4
  100. package/dist/styles/skeleton.css +4 -1
  101. package/dist/styles/split-view.css +107 -1
  102. package/dist/styles/tab-scaffold.css +64 -1
  103. package/dist/styles/text.css +6 -0
  104. package/dist/styles/toolbar-control-group.css +90 -11
  105. package/dist/styles/toolbar-text.css +4 -1
  106. package/dist/styles/toolbar.css +130 -38
  107. package/dist/styles/webawesome.css +48 -2
  108. package/dist/styles/workbench.css +136 -4
  109. package/dist/text.d.ts +4 -3
  110. package/dist/text.js +1 -1
  111. package/dist/toolbar-text.js +1 -1
  112. package/dist/toolbar.d.ts +24 -3
  113. package/dist/toolbar.js +1 -1
  114. package/dist/{types-CzgSrFXP.d.ts → types-CEsieBZi.d.ts} +6 -1
  115. package/dist/value-table.js +1 -1
  116. package/dist/wire-resizable-regions.js +1 -1
  117. package/dist/wire-sidebar.d.ts +18 -3
  118. package/dist/wire-sidebar.js +96 -15
  119. package/dist/wire-sidebar.js.map +1 -1
  120. package/docs/accessibility.md +49 -9
  121. package/docs/app-layouts.md +73 -0
  122. package/docs/catalog.md +23 -2
  123. package/docs/collapsible-panel.md +83 -9
  124. package/docs/component-contract.md +36 -12
  125. package/docs/component-integration.md +9 -4
  126. package/docs/component-selection.md +25 -6
  127. package/docs/design/templates/list/stack-dark.svg +1 -1
  128. package/docs/design/templates/list/stack.svg +1 -1
  129. package/docs/design/templates/list-dark.svg +1 -1
  130. package/docs/design/templates/list-header/action-dark.svg +1 -1
  131. package/docs/design/templates/list-header/action.svg +1 -1
  132. package/docs/design/templates/list-header/count-dark.svg +1 -1
  133. package/docs/design/templates/list-header/count.svg +1 -1
  134. package/docs/design/templates/list-header/inline-action-dark.svg +1 -1
  135. package/docs/design/templates/list-header/inline-action.svg +1 -1
  136. package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -1
  137. package/docs/design/templates/list-header/toggle-expanded.svg +1 -1
  138. package/docs/design/templates/list-header-dark.svg +1 -1
  139. package/docs/design/templates/list-header.svg +1 -1
  140. package/docs/design/templates/list.svg +1 -1
  141. package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
  142. package/docs/design/templates/state-banner/info-badge.svg +1 -1
  143. package/docs/design/templates/state-banner-dark.svg +1 -1
  144. package/docs/design/templates/state-banner.svg +1 -1
  145. package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
  146. package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
  147. package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
  148. package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
  149. package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
  150. package/docs/design/templates/toolbar-control-group.svg +1 -1
  151. package/docs/design/templates/toolbar-dark.svg +1 -1
  152. package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
  153. package/docs/design/templates/toolbar-text/default.svg +1 -1
  154. package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
  155. package/docs/design/templates/toolbar-text/large.svg +1 -1
  156. package/docs/design/templates/toolbar-text/small-dark.svg +1 -1
  157. package/docs/design/templates/toolbar-text/small.svg +1 -1
  158. package/docs/design/templates/toolbar-text-dark.svg +1 -1
  159. package/docs/design/templates/toolbar-text.svg +1 -1
  160. package/docs/design/templates/toolbar.svg +1 -1
  161. package/docs/document-baseline.md +31 -0
  162. package/docs/layout.md +86 -21
  163. package/docs/nav-stack.md +8 -0
  164. package/docs/recipes.md +81 -44
  165. package/docs/split-view.md +8 -0
  166. package/docs/tab-scaffold.md +4 -2
  167. package/docs/type-contracts.md +21 -17
  168. package/docs/ui-analyzer.md +19 -0
  169. package/docs/ui-evaluator.md +17 -0
  170. package/docs/ux-demo.md +5 -5
  171. package/docs/webawesome-theme.md +20 -2
  172. package/docs/workbench.md +20 -1
  173. package/evaluator/index.mjs +73 -8
  174. package/llms.txt +32 -14
  175. package/package.json +4 -2
  176. package/ux-demo/recipes/app-shell.tsx +209 -178
  177. package/ux-demo/recipes/collapsible-sidebar.tsx +189 -159
  178. package/ux-demo/recipes/compact-toolbar.tsx +48 -35
  179. package/ux-demo/recipes/composer-form.tsx +87 -79
  180. package/ux-demo/recipes/list-detail-dialog.tsx +124 -142
  181. package/ux-demo/recipes/list-workspace-states.tsx +83 -83
  182. package/ux-demo/recipes/loaders.ts +5 -2
  183. package/ux-demo/recipes/loading-inspector.tsx +157 -160
  184. package/ux-demo/recipes/navigation-sidebar.tsx +111 -109
  185. package/ux-demo/recipes/navigation-stack.tsx +50 -49
  186. package/ux-demo/recipes/types.ts +14 -0
  187. package/ux-demo/recipes/workspace-header.tsx +73 -56
  188. package/dist/chunk-54HM36IQ.js.map +0 -1
  189. package/dist/chunk-AQSKJ6U6.js.map +0 -1
  190. package/dist/chunk-COUPLRZG.js.map +0 -1
  191. package/dist/chunk-ELRMBQEC.js.map +0 -1
  192. package/dist/chunk-J3EE6XWD.js +0 -115
  193. package/dist/chunk-J3EE6XWD.js.map +0 -1
  194. package/dist/chunk-KKNK3JUT.js.map +0 -1
  195. package/dist/chunk-OL4TC3U2.js.map +0 -1
  196. package/dist/chunk-OQI7RCRP.js.map +0 -1
  197. package/dist/chunk-UMGY5JSP.js.map +0 -1
  198. package/dist/chunk-V6RZWDII.js.map +0 -1
  199. package/dist/chunk-ZNZ2JDBW.js.map +0 -1
  200. package/dist/chunk-ZSYLBVWG.js.map +0 -1
  201. package/ux-demo/recipes/app-shell.css +0 -87
  202. package/ux-demo/recipes/collapsible-sidebar.css +0 -83
  203. package/ux-demo/recipes/compact-toolbar.css +0 -35
  204. package/ux-demo/recipes/composer-form.css +0 -19
  205. package/ux-demo/recipes/list-detail-dialog.css +0 -42
  206. package/ux-demo/recipes/list-workspace-states.css +0 -30
  207. package/ux-demo/recipes/loading-inspector.css +0 -7
  208. package/ux-demo/recipes/navigation-sidebar.css +0 -12
  209. package/ux-demo/recipes/navigation-stack.css +0 -12
  210. package/ux-demo/recipes/recipe-root.css +0 -67
  211. package/ux-demo/recipes/recipe-root.tsx +0 -87
  212. package/ux-demo/recipes/workspace-header.css +0 -7
@@ -1,137 +1,139 @@
1
1
  import '@kerfjs/ui/layout.css';
2
- import './navigation-sidebar.css';
3
2
 
3
+ import { List } from '@kerfjs/ui/list';
4
4
  import { ListHeader } from '@kerfjs/ui/list-header';
5
5
  import { ListItem } from '@kerfjs/ui/list-item';
6
6
  import { LucideIcon } from '@kerfjs/ui/lucide-icon';
7
7
  import { Pane } from '@kerfjs/ui/pane';
8
+ import { Text } from '@kerfjs/ui/text';
8
9
  import { Toolbar } from '@kerfjs/ui/toolbar';
9
10
  import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
10
11
  import { signal } from 'kerfjs';
11
12
  import { Folder, Inbox, Plus, Settings, Users } from 'lucide';
12
13
 
13
- import {
14
- RecipeMutedText,
15
- RecipeOwnershipNote,
16
- RecipeRoot,
17
- } from './recipe-root.js';
18
- import type { RecipeFactory } from './types.js';
14
+ import type { RecipeFactory, RecipePresentation } from './types.js';
15
+
16
+ export const presentation: RecipePresentation = {
17
+ viewport: {
18
+ layout: 'grid',
19
+ width: 'compact',
20
+ minHeight: 'medium',
21
+ frame: 'solid',
22
+ surface: 'default',
23
+ overflow: 'hidden',
24
+ shadow: true,
25
+ },
26
+ note: 'The recipe owns pane, content-item, and toolbar geometry. The app owns routes, permissions, labels, and disclosure state.',
27
+ };
19
28
 
20
29
  export const createRecipe: RecipeFactory = (announce) => {
21
30
  const selected = signal('inbox');
22
31
  const expanded = signal(true);
23
32
  const render = () => (
24
- <RecipeRoot
33
+ <Pane
25
34
  element="aside"
26
- recipe="recipe-navigation-sidebar"
27
- measure="sidebar"
28
- >
29
- <div class="recipe-sidebar">
30
- <Pane
31
- element="div"
32
- contentClassName="recipe-sidebar__body"
33
- footer={
34
- <Toolbar
35
- label="Sidebar actions"
36
- dividerSides=""
37
- leading={
38
- <ToolbarControlGroup appearance="borderless" single>
39
- <button
40
- type="button"
41
- aria-label="Add project"
42
- data-action="recipe-action"
43
- data-recipe-command="add"
44
- >
45
- <LucideIcon icon={Plus} name="plus" />
46
- </button>
47
- </ToolbarControlGroup>
48
- }
49
- trailing={
50
- <ToolbarControlGroup appearance="borderless" single>
51
- <button
52
- type="button"
53
- aria-label="Workspace settings"
54
- data-action="recipe-action"
55
- data-recipe-command="settings"
56
- >
57
- <LucideIcon icon={Settings} name="settings" />
58
- </button>
59
- </ToolbarControlGroup>
60
- }
61
- />
35
+ label="Workspace navigation"
36
+ rootAttributes={{ 'data-recipe': 'recipe-navigation-sidebar' }}
37
+ footer={
38
+ <Toolbar
39
+ label="Sidebar actions"
40
+ dividerSides="t"
41
+ leading={
42
+ <ToolbarControlGroup appearance="borderless" single>
43
+ <button
44
+ type="button"
45
+ aria-label="Add project"
46
+ data-action="recipe-action"
47
+ data-recipe-command="add"
48
+ >
49
+ <LucideIcon icon={Plus} name="plus" />
50
+ </button>
51
+ </ToolbarControlGroup>
62
52
  }
63
- >
64
- <section>
65
- <ListHeader
66
- label="Workspace"
67
- count={3}
68
- countLabel="3 workspaces"
69
- action="recipe-action"
70
- actionLabel="Add workspace item"
71
- actionIcon={<LucideIcon icon={Plus} name="plus" />}
72
- />
73
- <ListItem
74
- action="recipe-action"
75
- itemId="inbox"
76
- label="Inbox"
77
- icon={<LucideIcon icon={Inbox} name="inbox" />}
78
- trailing={<span>12</span>}
79
- selected={selected.value === 'inbox'}
80
- />
53
+ trailing={
54
+ <ToolbarControlGroup appearance="borderless" single>
55
+ <button
56
+ type="button"
57
+ aria-label="Workspace settings"
58
+ data-action="recipe-action"
59
+ data-recipe-command="settings"
60
+ >
61
+ <LucideIcon icon={Settings} name="settings" />
62
+ </button>
63
+ </ToolbarControlGroup>
64
+ }
65
+ />
66
+ }
67
+ >
68
+ <section>
69
+ <ListHeader
70
+ label="Workspace"
71
+ count={3}
72
+ countLabel="3 workspaces"
73
+ action="recipe-action"
74
+ actionLabel="Add workspace item"
75
+ actionIcon={<LucideIcon icon={Plus} name="plus" />}
76
+ />
77
+ <ListItem
78
+ action="recipe-action"
79
+ itemId="inbox"
80
+ label="Inbox"
81
+ icon={<LucideIcon icon={Inbox} name="inbox" />}
82
+ trailing={<span>12</span>}
83
+ selected={selected.value === 'inbox'}
84
+ />
85
+ <ListItem
86
+ action="recipe-action"
87
+ itemId="shared"
88
+ label="Shared with me"
89
+ icon={<LucideIcon icon={Users} name="users" />}
90
+ selected={selected.value === 'shared'}
91
+ />
92
+ <ListItem
93
+ action="recipe-action"
94
+ itemId="drafts"
95
+ label="Drafts without an icon"
96
+ selected={selected.value === 'drafts'}
97
+ />
98
+ </section>
99
+ <section>
100
+ <ListHeader
101
+ label="Projects"
102
+ toggle
103
+ action="recipe-action"
104
+ expanded={expanded.value}
105
+ />
106
+ {expanded.value && (
107
+ <>
81
108
  <ListItem
82
109
  action="recipe-action"
83
- itemId="shared"
84
- label="Shared with me"
85
- icon={<LucideIcon icon={Users} name="users" />}
86
- selected={selected.value === 'shared'}
110
+ itemId="design"
111
+ label="Design system rollout across desktop and tablet"
112
+ icon={<LucideIcon icon={Folder} name="folder" />}
113
+ selected={selected.value === 'design'}
114
+ multiline
87
115
  />
88
116
  <ListItem
89
117
  action="recipe-action"
90
- itemId="drafts"
91
- label="Drafts without an icon"
92
- selected={selected.value === 'drafts'}
93
- />
94
- </section>
95
- <section>
96
- <ListHeader
97
- label="Projects"
98
- toggle
99
- action="recipe-action"
100
- expanded={expanded.value}
118
+ itemId="archive"
119
+ label="Archived"
120
+ disabled
121
+ title="Available to administrators"
101
122
  />
102
- {expanded.value && (
103
- <>
104
- <ListItem
105
- action="recipe-action"
106
- itemId="design"
107
- label="Design system rollout across desktop and tablet"
108
- icon={<LucideIcon icon={Folder} name="folder" />}
109
- selected={selected.value === 'design'}
110
- multiline
111
- />
112
- <ListItem
113
- action="recipe-action"
114
- itemId="archive"
115
- label="Archived"
116
- disabled
117
- title="Available to administrators"
118
- />
119
- <div class="kui-content-item">
123
+ <div class="kui-content-item">
124
+ <List gap="2xs">
125
+ <Text variant="span">
120
126
  <strong>Quarterly goal</strong>
121
- <RecipeMutedText>
122
- Ship accessible navigation patterns to every workspace.
123
- </RecipeMutedText>
124
- </div>
125
- </>
126
- )}
127
- </section>
128
- <RecipeOwnershipNote>
129
- The recipe owns pane, content-item, and toolbar geometry. The app
130
- owns routes, permissions, labels, and disclosure state.
131
- </RecipeOwnershipNote>
132
- </Pane>
133
- </div>
134
- </RecipeRoot>
127
+ </Text>
128
+ <Text variant="span" tone="quiet" size="compact">
129
+ Ship accessible navigation patterns to every workspace.
130
+ </Text>
131
+ </List>
132
+ </div>
133
+ </>
134
+ )}
135
+ </section>
136
+ </Pane>
135
137
  );
136
138
  return {
137
139
  render,
@@ -1,22 +1,31 @@
1
1
  import '@kerfjs/ui/layout.css';
2
2
  import '@kerfjs/ui/nav-stack.css';
3
3
  import '@kerfjs/ui/toolbar-text.css';
4
- import './navigation-stack.css';
5
4
 
6
5
  import { List } from '@kerfjs/ui/list';
7
6
  import { ListHeader } from '@kerfjs/ui/list-header';
8
7
  import { ListItem } from '@kerfjs/ui/list-item';
9
8
  import { LucideIcon } from '@kerfjs/ui/lucide-icon';
10
9
  import { NavStack, type NavStackView } from '@kerfjs/ui/nav-stack';
10
+ import { Row } from '@kerfjs/ui/row';
11
+ import { Text } from '@kerfjs/ui/text';
11
12
  import { signal } from 'kerfjs';
12
13
  import { ChevronRight, FileText, Folder } from 'lucide';
13
14
 
14
- import {
15
- RecipeMutedText,
16
- RecipeOwnershipNote,
17
- RecipeRoot,
18
- } from './recipe-root.js';
19
- import type { RecipeFactory } from './types.js';
15
+ import type { RecipeFactory, RecipePresentation } from './types.js';
16
+
17
+ export const presentation: RecipePresentation = {
18
+ viewport: {
19
+ layout: 'grid',
20
+ width: 'compact',
21
+ height: 'tall',
22
+ frame: 'solid',
23
+ surface: 'default',
24
+ overflow: 'hidden',
25
+ shadow: true,
26
+ },
27
+ note: 'The recipe owns the stack as a signal of views and pushes/pops it; NavStack renders the stack and wireNavStack slides the content and settles the chrome. The app owns selection, data, and routing.',
28
+ };
20
29
 
21
30
  interface LibraryItem {
22
31
  id: string;
@@ -50,29 +59,20 @@ export const createRecipe: RecipeFactory = (announce) => {
50
59
  key: 'library',
51
60
  title: 'Library',
52
61
  content: (
53
- <div class="recipe-navstack__view">
54
- <List gap="m">
55
- <section>
56
- <ListHeader label="Components" />
57
- {ITEMS.map((item) => (
58
- <ListItem
59
- action="recipe-action"
60
- itemId={item.id}
61
- label={item.label}
62
- icon={<LucideIcon icon={Folder} name="folder" />}
63
- trailing={
64
- <LucideIcon icon={ChevronRight} name="chevron-right" />
65
- }
66
- />
67
- ))}
68
- </section>
69
- <RecipeOwnershipNote>
70
- The recipe owns the stack as a signal of views and pushes/pops it;
71
- `NavStack` renders the stack and `wireNavStack` slides the content
72
- and settles the chrome. The app owns selection, data, and routing.
73
- </RecipeOwnershipNote>
74
- </List>
75
- </div>
62
+ <List>
63
+ <section>
64
+ <ListHeader label="Components" />
65
+ {ITEMS.map((item) => (
66
+ <ListItem
67
+ action="recipe-action"
68
+ itemId={item.id}
69
+ label={item.label}
70
+ icon={<LucideIcon icon={Folder} name="folder" />}
71
+ trailing={<LucideIcon icon={ChevronRight} name="chevron-right" />}
72
+ />
73
+ ))}
74
+ </section>
75
+ </List>
76
76
  ),
77
77
  });
78
78
 
@@ -80,32 +80,33 @@ export const createRecipe: RecipeFactory = (announce) => {
80
80
  key: item.id,
81
81
  title: item.label,
82
82
  content: (
83
- <div class="recipe-navstack__view">
84
- <List gap="m">
85
- <div class="kui-content-item">
83
+ <List controlInsets="t">
84
+ <div class="kui-content-item">
85
+ <Row gap="xs">
86
86
  <LucideIcon icon={FileText} name="file-text" />
87
- <strong>{item.label}</strong>
88
- <RecipeMutedText>{item.detail}</RecipeMutedText>
89
- </div>
90
- </List>
91
- </div>
87
+ <List gap="2xs">
88
+ <Text variant="span">
89
+ <strong>{item.label}</strong>
90
+ </Text>
91
+ <Text variant="span" tone="quiet" size="compact">
92
+ {item.detail}
93
+ </Text>
94
+ </List>
95
+ </Row>
96
+ </div>
97
+ </List>
92
98
  ),
93
99
  });
94
100
 
95
101
  const views = signal<NavStackView[]>([listView()]);
96
102
 
97
103
  const render = () => (
98
- <RecipeRoot recipe="recipe-navigation-stack" measure="stack">
99
- <div class="recipe-navstack">
100
- <div class="recipe-navstack__stage">
101
- <NavStack
102
- id="recipe-nav"
103
- label="Component library"
104
- views={views.value}
105
- />
106
- </div>
107
- </div>
108
- </RecipeRoot>
104
+ <section
105
+ data-recipe="recipe-navigation-stack"
106
+ aria-label="Component library"
107
+ >
108
+ <NavStack id="recipe-nav" label="Component library" views={views.value} />
109
+ </section>
109
110
  );
110
111
 
111
112
  return {
@@ -1,3 +1,4 @@
1
+ import type { CatalogExampleViewport } from '@kerfjs/ui/catalog';
1
2
  import type { SafeHtml } from 'kerfjs';
2
3
 
3
4
  export interface RecipeController {
@@ -17,3 +18,16 @@ export interface RecipeController {
17
18
  export type RecipeFactory = (
18
19
  announce: (message: string) => void,
19
20
  ) => RecipeController;
21
+
22
+ /**
23
+ * Catalog-only presentation for one recipe. The UX catalog frames the recipe in
24
+ * a `CatalogExample` viewport and shows `note` when recipe notes are toggled on;
25
+ * an application that copies the recipe ignores this export and mounts the
26
+ * controller into its own container.
27
+ */
28
+ export interface RecipePresentation {
29
+ /** Catalog-owned specimen frame: width, height, surface, and border. */
30
+ viewport?: CatalogExampleViewport;
31
+ /** Ownership note: what the recipe owns versus what the application owns. */
32
+ note: string;
33
+ }
@@ -1,75 +1,92 @@
1
1
  import '@kerfjs/ui/layout.css';
2
- import './workspace-header.css';
3
2
 
3
+ import { List } from '@kerfjs/ui/list';
4
+ import { ListInsetText } from '@kerfjs/ui/list-inset-text';
4
5
  import { StateBanner } from '@kerfjs/ui/state-banner';
6
+ import { Text } from '@kerfjs/ui/text';
5
7
  import { Toolbar } from '@kerfjs/ui/toolbar';
6
8
  import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
7
9
  import { ToolbarText } from '@kerfjs/ui/toolbar-text';
8
10
 
9
- import { RecipeOwnershipNote, RecipeRoot } from './recipe-root.js';
10
- import type { RecipeFactory } from './types.js';
11
+ import type { RecipeFactory, RecipePresentation } from './types.js';
12
+
13
+ export const presentation: RecipePresentation = {
14
+ viewport: {
15
+ width: 'full',
16
+ frame: 'solid',
17
+ surface: 'default',
18
+ overflow: 'hidden',
19
+ shadow: true,
20
+ },
21
+ note: 'The recipe owns page hierarchy and action relocation. The app owns authorization, command behavior, breadcrumbs, and product copy.',
22
+ };
11
23
 
12
24
  export const createRecipe: RecipeFactory = (announce) => ({
13
25
  render: () => (
14
- <RecipeRoot recipe="recipe-workspace-header">
15
- <div class="recipe-header kui-content">
16
- <p class="recipe-header__context kui-inline-metadata kui-content-item">
17
- <span>Northstar workspace</span>
18
- <span>·</span>
19
- <span>Product planning</span>
20
- </p>
21
- <Toolbar
22
- label="Workspace heading"
23
- dividerSides=""
24
- leading={
25
- <ToolbarText
26
- text="Accessibility readiness and responsive navigation rollout"
27
- size="xlarge"
28
- id="recipe-workspace-title"
29
- headingLevel={1}
30
- />
31
- }
32
- trailing={
33
- <ToolbarControlGroup
34
- appearance="borderless"
35
- buttonAppearance="push"
36
- content="text"
37
- >
38
- <button
39
- type="button"
40
- data-action="recipe-action"
41
- data-recipe-command="share"
42
- >
43
- Share
44
- </button>
45
- <button
46
- type="button"
47
- data-action="recipe-action"
48
- data-recipe-command="more"
26
+ <section
27
+ data-recipe="recipe-workspace-header"
28
+ aria-labelledby="recipe-workspace-title"
29
+ >
30
+ <List gap="l" controlInsets="tb">
31
+ <List>
32
+ <ListInsetText sides="rl">
33
+ <Text variant="span" tone="quiet" size="compact">
34
+ Northstar workspace · Product planning
35
+ </Text>
36
+ </ListInsetText>
37
+ <Toolbar
38
+ label="Workspace heading"
39
+ dividerSides=""
40
+ responsive="stack"
41
+ responsiveAt="compact"
42
+ leading={
43
+ <ToolbarText
44
+ text="Accessibility readiness and responsive navigation rollout"
45
+ size="xlarge"
46
+ id="recipe-workspace-title"
47
+ headingLevel={1}
48
+ wrap
49
+ maxLines={2}
50
+ />
51
+ }
52
+ trailing={
53
+ <ToolbarControlGroup
54
+ appearance="borderless"
55
+ buttonAppearance="push"
56
+ content="text"
49
57
  >
50
- More actions
51
- </button>
52
- <button
53
- type="button"
54
- data-action="recipe-action"
55
- data-recipe-command="publish"
56
- >
57
- Publish update
58
- </button>
59
- </ToolbarControlGroup>
60
- }
61
- />
58
+ <button
59
+ type="button"
60
+ data-action="recipe-action"
61
+ data-recipe-command="share"
62
+ >
63
+ Share
64
+ </button>
65
+ <button
66
+ type="button"
67
+ data-action="recipe-action"
68
+ data-recipe-command="more"
69
+ >
70
+ More actions
71
+ </button>
72
+ <button
73
+ type="button"
74
+ data-action="recipe-action"
75
+ data-recipe-command="publish"
76
+ >
77
+ Publish update
78
+ </button>
79
+ </ToolbarControlGroup>
80
+ }
81
+ />
82
+ </List>
62
83
  <StateBanner
63
84
  title="Ready for review"
64
85
  detail="All required checks passed 18 minutes ago."
65
86
  tone="success"
66
87
  />
67
- <RecipeOwnershipNote>
68
- The recipe owns page hierarchy and action relocation. The app owns
69
- authorization, command behavior, breadcrumbs, and product copy.
70
- </RecipeOwnershipNote>
71
- </div>
72
- </RecipeRoot>
88
+ </List>
89
+ </section>
73
90
  ),
74
91
  action(command) {
75
92
  announce(`${command} requested`);
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/skeleton.tsx"],"names":[],"mappings":";;;AAkBA,SAAS,UAAA,CACP,KAAA,EACA,MAAA,EACA,MAAA,EACoB;AACpB,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,KAAA,IAAS,SAAS,KAAK,CAAA,CAAA;AAAA,IACvB,MAAA,IAAU,UAAU,MAAM,CAAA,CAAA;AAAA,IAC1B,MAAA,IAAU,yBAAyB,MAAM,CAAA;AAAA,GAC3C,CAAE,OAAO,OAAO,CAAA;AAChB,EAAA,OAAO,KAAA,CAAM,MAAA,GAAS,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA,GAAI,MAAA;AAC1C;AAQO,SAAS,QAAA,CAAS;AAAA,EACvB,KAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ;AACF,CAAA,EAAkB;AAChB,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,IAAA,EAAM,QAAQ,KAAA,GAAQ,MAAA;AAAA,IACtB,YAAA,EAAc,KAAA;AAAA,IACd,aAAA,EAAe,QAAQ,MAAA,GAAa;AAAA,GACtC;AACA,EAAA,IAAI,KAAA,IAAS,QAAQ,CAAA,EAAG;AACtB,IAAA,uBACE,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,KAAA,EAAO,CAAA,mBAAA,EAAsB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,QAC9C,gBAAA,EAAe,UAAA;AAAA,QACf,KAAA,EAAO,KAAA,GAAQ,CAAA,MAAA,EAAS,KAAK,CAAA,CAAA,GAAK,MAAA;AAAA,QACjC,GAAG,IAAA;AAAA,QACJ,IAAA;AAAA,QAEC,QAAA,EAAA,KAAA,CAAM,KAAK,EAAE,MAAA,EAAQ,OAAM,EAAG,CAAC,GAAG,KAAA,qBACjC,GAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACC,KAAA,EAAM,cAAA;AAAA,YACN,aAAA,EAAY,MAAA;AAAA,YACZ,KAAA,EAAO,UAAA;AAAA,cACL,KAAA,KAAU,KAAA,GAAQ,CAAA,GAAI,GAAA,CAAI,EAAE,CAAA,GAAI,MAAA;AAAA,cAChC,MAAA;AAAA,cACA;AAAA;AACF;AAAA,SAEH;AAAA;AAAA,KACH;AAAA,EAEJ;AACA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,aAAA,EAAgB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACxC,gBAAA,EAAe,UAAA;AAAA,MACf,KAAA,EAAO,UAAA,CAAW,KAAA,EAAO,MAAA,EAAQ,MAAM,CAAA;AAAA,MACtC,GAAG,IAAA;AAAA,MACJ;AAAA;AAAA,GACD;AAEL","file":"chunk-54HM36IQ.js","sourcesContent":["import { type CssLength, type CssSize, pct } from './css-values.js';\n\nexport interface SkeletonProps {\n /** Typed width or intrinsic sizing keyword. Defaults to filling its slot. */\n width?: CssSize;\n /** Typed height or intrinsic sizing keyword. Defaults to a single text line. */\n height?: CssSize;\n /** Typed corner-radius override. Defaults to the small radius token. */\n radius?: CssLength;\n /** Render this many stacked lines (the last one shorter), for multi-line text. */\n lines?: number;\n /** Accessible label. Omit to keep the block decorative (`aria-hidden`). */\n label?: string;\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\nfunction blockStyle(\n width?: CssSize,\n height?: CssSize,\n radius?: CssLength,\n): string | undefined {\n const parts = [\n width && `width:${width}`,\n height && `height:${height}`,\n radius && `--kui-skeleton-radius:${radius}`,\n ].filter(Boolean);\n return parts.length ? parts.join(';') : undefined;\n}\n\n/**\n * A subtle, deliberately **unanimated** loading placeholder block. Use it for a\n * value slot whose content is not yet known, on its own or via a component's\n * `placeholder` prop. Decorative by default (`aria-hidden`); pass `label` to\n * announce it. Sizes to its slot unless `width`/`height` are given.\n */\nexport function Skeleton({\n width,\n height,\n radius,\n lines,\n label,\n className = '',\n slot,\n}: SkeletonProps) {\n const a11y = {\n role: label ? 'img' : undefined,\n 'aria-label': label,\n 'aria-hidden': label ? undefined : ('true' as const),\n };\n if (lines && lines > 1) {\n return (\n <span\n class={`kui-skeleton-lines ${className}`.trim()}\n data-component=\"skeleton\"\n style={width ? `width:${width}` : undefined}\n {...a11y}\n slot={slot}\n >\n {Array.from({ length: lines }, (_, index) => (\n <span\n class=\"kui-skeleton\"\n aria-hidden=\"true\"\n style={blockStyle(\n index === lines - 1 ? pct(60) : undefined,\n height,\n radius,\n )}\n ></span>\n ))}\n </span>\n );\n }\n return (\n <span\n class={`kui-skeleton ${className}`.trim()}\n data-component=\"skeleton\"\n style={blockStyle(width, height, radius)}\n {...a11y}\n slot={slot}\n ></span>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/row.tsx"],"names":[],"mappings":";;;;AAgBA,IAAM,UAAA,GAAqC;AAAA,EACzC,MAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA;AACF,CAAA;AAwBO,SAAS,GAAA,CAAI;AAAA,EAClB,QAAA;AAAA,EACA,MAAA,GAAS,MAAA;AAAA,EACT,MAAA,GAAS,MAAA;AAAA,EACT,GAAA,GAAM,IAAA;AAAA,EACN,IAAA,GAAO,KAAA;AAAA,EACP,IAAA,GAAO,KAAA;AAAA,EACP,UAAA,GAAa,EAAA;AAAA,EACb,aAAA,GAAgB,EAAA;AAAA,EAChB,SAAA,GAAY,EAAA;AAAA,EACZ;AACF,CAAA,EAAa;AACX,EAAA,MAAM,WAAW,UAAA,CAAW,QAAA,CAAS,GAAkB,CAAA,GACnD,KAAA,CAAM,GAAkB,CAAA,GACxB,GAAA;AACJ,EAAA,MAAM,SAAA,GAAY,IAAA,KAAS,IAAA,GAAO,UAAA,GAAa,IAAA,IAAQ,MAAA;AACvD,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,kBAAkB,QAAQ,CAAA,CAAA;AAAA,IAC1B,SAAA,GAAY,CAAA,gBAAA,EAAmB,SAAS,CAAA,CAAA,GAAK;AAAA,GAC/C,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAEX,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACnC,gBAAA,EAAe,KAAA;AAAA,MACf,cAAA,EAAc,oBAAoB,MAAM,CAAA;AAAA,MACxC,cAAA,EAAc,kBAAkB,MAAM,CAAA;AAAA,MACtC,WAAA,EAAW,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,MAC/B,WAAA,EAAW,OAAO,IAAI,CAAA;AAAA,MACtB,oBAAkB,UAAA,IAAc,MAAA;AAAA,MAChC,uBAAqB,aAAA,IAAiB,MAAA;AAAA,MACtC,KAAA;AAAA,MACA,IAAA;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ","file":"chunk-AQSKJ6U6.js","sourcesContent":["import {\n type CssFlex,\n type CssFlexKeyword,\n type CssLength,\n space,\n type UiSpaceName,\n} from './css-values.js';\nimport type { Sides } from './divider-sides.js';\nimport {\n type HorizontalAlignment,\n horizontalAlignment,\n type VerticalAlignment,\n verticalAlignment,\n} from './flex-alignment.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\nconst spaceNames: readonly UiSpaceName[] = [\n 'none',\n '2xs',\n 'xs',\n 's',\n 'm',\n 'l',\n 'xl',\n];\n\nexport interface RowProps {\n children?: KerfUiContent;\n /** Horizontal distribution. Defaults to left. */\n hAlign?: HorizontalAlignment;\n /** Vertical alignment and wrapped-line distribution. Defaults to full. */\n vAlign?: VerticalAlignment;\n /** A named UI spacing token or typed CSS length. Defaults to xs. */\n gap?: UiSpaceName | CssLength;\n /** Allow this row to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */\n flex?: boolean | CssFlexKeyword | CssFlex;\n /** Allow children to wrap onto additional lines. */\n wrap?: boolean;\n /** Physical sides that receive the standard 17px text inset. */\n textInsets?: Sides;\n /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */\n controlInsets?: Sides;\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/** A horizontal flex row with explicit physical-axis alignment and spacing. */\nexport function Row({\n children,\n hAlign = 'left',\n vAlign = 'full',\n gap = 'xs',\n flex = false,\n wrap = false,\n textInsets = '',\n controlInsets = '',\n className = '',\n slot,\n}: RowProps) {\n const gapValue = spaceNames.includes(gap as UiSpaceName)\n ? space(gap as UiSpaceName)\n : gap;\n const flexValue = flex === true ? '1 1 auto' : flex || undefined;\n const style = [\n `--_kui-row-gap:${gapValue}`,\n flexValue ? `--_kui-row-flex:${flexValue}` : '',\n ]\n .filter(Boolean)\n .join(';');\n\n return (\n <div\n class={`kui-row ${className}`.trim()}\n data-component=\"row\"\n data-h-align={horizontalAlignment(hAlign)}\n data-v-align={verticalAlignment(vAlign)}\n data-flex={String(Boolean(flex))}\n data-wrap={String(wrap)}\n data-text-insets={textInsets || undefined}\n data-control-insets={controlInsets || undefined}\n style={style}\n slot={slot}\n >\n {children}\n </div>\n );\n}\n\nexport type {\n CssFlex,\n CssFlexKeyword,\n CssLength,\n UiSpaceName,\n} from './css-values.js';\nexport type { Sides } from './divider-sides.js';\nexport type {\n HorizontalAlignment,\n VerticalAlignment,\n} from './flex-alignment.js';\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/app-tab.tsx"],"names":[],"mappings":"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","file":"chunk-COUPLRZG.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { em } from './css-values.js';\nimport { filterDataAttributes } from './extension-attributes.js';\nimport type { KerfUiContent } from './semantic-content.js';\nimport { Skeleton } from './skeleton.js';\n\nconst PROTECTED_ROOT_DATA_ATTRIBUTES = new Set([\n 'data-component',\n 'data-action',\n 'data-tab-id',\n 'data-selected',\n 'data-tab-dragging',\n 'data-tab-drop-position',\n]);\n\ntype AppTabRootAttributes = Readonly<\n Record<`data-${string}`, string | undefined> & {\n 'data-component'?: never;\n 'data-action'?: never;\n 'data-tab-id'?: never;\n 'data-selected'?: never;\n 'data-tab-dragging'?: never;\n 'data-tab-drop-position'?: never;\n }\n>;\n\nexport type AppTabPresentation = 'pill' | 'segmented' | 'icon-only';\nexport type AppTabSize = 'default' | 'compact';\n\nexport interface AppTabProps {\n id: string;\n name: string;\n selected?: boolean;\n closable?: boolean;\n draggable?: boolean;\n leading?: KerfUiContent;\n trailing?: KerfUiContent;\n /** Visual treatment within a TabBar. Icon-only tabs retain `name` as their accessible name. */\n presentation?: AppTabPresentation;\n /** Compact tabs use the 32px application-rail height. */\n size?: AppTabSize;\n /** Maximum visible label width in CSS pixels before ellipsis. */\n labelMaxWidth?: number;\n /** Decorative dormant content for the close button. Must not contain interactive descendants. */\n closeIcon?: SafeHtml;\n selectAction?: string;\n closeAction?: string;\n className?: string;\n /** Render as an unanimated loading skeleton, disabling select/close and dragging. */\n placeholder?: boolean;\n rootAttributes?: AppTabRootAttributes;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\nfunction CloseIcon() {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2.25\"\n stroke-linecap=\"round\"\n >\n <path d=\"m7 7 10 10\"></path>\n <path d=\"M17 7 7 17\"></path>\n </svg>\n );\n}\n\nexport function AppTab({\n id,\n name,\n selected = false,\n closable = true,\n draggable = false,\n leading,\n trailing,\n presentation = 'pill',\n size = 'default',\n labelMaxWidth,\n closeIcon,\n selectAction = 'select-tab',\n closeAction = 'close-tab',\n className = '',\n placeholder = false,\n rootAttributes = {},\n slot,\n}: AppTabProps) {\n const keyshortcuts = [\n closable ? 'Delete Backspace' : '',\n draggable ? 'Alt+Shift+ArrowLeft Alt+Shift+ArrowRight' : '',\n ]\n .filter(Boolean)\n .join(' ');\n const extensionAttributes = filterDataAttributes(\n rootAttributes,\n PROTECTED_ROOT_DATA_ATTRIBUTES,\n );\n return (\n <div\n {...extensionAttributes}\n class={`kui-app-tab ${className}`.trim()}\n data-component=\"app-tab\"\n data-tab-id={id}\n data-selected={String(selected)}\n data-presentation={presentation}\n data-size={size}\n data-placeholder={placeholder ? 'true' : undefined}\n draggable={placeholder ? 'false' : draggable ? 'true' : 'false'}\n aria-busy={placeholder ? 'true' : undefined}\n style={\n labelMaxWidth === undefined\n ? undefined\n : `--kui-app-tab-label-max-width:${labelMaxWidth}px`\n }\n slot={slot}\n >\n {closable && (\n <button\n type=\"button\"\n tabindex=\"-1\"\n class=\"kui-app-tab__close\"\n data-action={placeholder ? undefined : closeAction}\n data-tab-id={id}\n disabled={placeholder || undefined}\n aria-label={`Close ${name}`}\n title={`Close ${name}`}\n >\n <span class=\"kui-app-tab__close-icon\" aria-hidden=\"true\">\n {closeIcon ?? <CloseIcon />}\n </span>\n </button>\n )}\n <button\n type=\"button\"\n class=\"kui-app-tab__select\"\n role=\"tab\"\n aria-selected={String(selected)}\n aria-label={presentation === 'icon-only' ? name : undefined}\n aria-keyshortcuts={placeholder ? undefined : keyshortcuts || undefined}\n data-action={placeholder ? undefined : selectAction}\n data-tab-id={id}\n disabled={placeholder || undefined}\n tabindex={placeholder ? '-1' : selected ? '0' : '-1'}\n >\n {leading}\n <span\n class=\"kui-app-tab__name\"\n aria-hidden={presentation === 'icon-only' ? 'true' : undefined}\n >\n {placeholder ? <Skeleton width={em(7)} /> : name}\n </span>\n {closable || trailing ? (\n <span class=\"kui-app-tab__trailing\">{trailing}</span>\n ) : undefined}\n </button>\n </div>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/grid.tsx"],"names":[],"mappings":";;;AASA,IAAM,UAAA,GAAqC;AAAA,EACzC,MAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA;AACF,CAAA;AAgBO,SAAS,IAAA,CAAK;AAAA,EACnB,QAAA;AAAA,EACA,OAAA;AAAA,EACA,GAAA,GAAM,IAAA;AAAA,EACN,IAAA,GAAO,KAAA;AAAA,EACP,SAAA,GAAY,EAAA;AAAA,EACZ;AACF,CAAA,EAAc;AACZ,EAAA,IAAI,CAAC,MAAA,CAAO,aAAA,CAAc,OAAO,CAAA,IAAK,UAAU,CAAA,EAAG;AACjD,IAAA,MAAM,IAAI,WAAW,8CAA8C,CAAA;AAAA,EACrE;AAEA,EAAA,MAAM,WAAW,UAAA,CAAW,QAAA,CAAS,GAAkB,CAAA,GACnD,KAAA,CAAM,GAAkB,CAAA,GACxB,GAAA;AACJ,EAAA,MAAM,SAAA,GAAY,IAAA,KAAS,IAAA,GAAO,UAAA,GAAa,IAAA,IAAQ,MAAA;AACvD,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,uBAAuB,OAAO,CAAA,CAAA;AAAA,IAC9B,mBAAmB,QAAQ,CAAA,CAAA;AAAA,IAC3B,SAAA,GAAY,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAA,GAAK;AAAA,GAChD,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAEX,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,KAAA,EAAO,CAAA,SAAA,EAAY,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACpC,gBAAA,EAAe,MAAA;AAAA,MACf,cAAA,EAAc,OAAO,OAAO,CAAA;AAAA,MAC5B,WAAA,EAAW,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,MAC/B,KAAA;AAAA,MACA,IAAA;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ","file":"chunk-ELRMBQEC.js","sourcesContent":["import {\n type CssFlex,\n type CssFlexKeyword,\n type CssLength,\n space,\n type UiSpaceName,\n} from './css-values.js';\nimport type { KerfUiContent } from './semantic-content.js';\n\nconst spaceNames: readonly UiSpaceName[] = [\n 'none',\n '2xs',\n 'xs',\n 's',\n 'm',\n 'l',\n 'xl',\n];\n\nexport interface GridProps {\n children?: KerfUiContent;\n /** Number of equal-width columns. Must be a positive safe integer. */\n columns: number;\n /** A named UI spacing token or typed CSS length. Defaults to xs. */\n gap?: UiSpaceName | CssLength;\n /** Allow this grid to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */\n flex?: boolean | CssFlexKeyword | CssFlex;\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/** A fixed-count grid whose columns share the available width equally. */\nexport function Grid({\n children,\n columns,\n gap = 'xs',\n flex = false,\n className = '',\n slot,\n}: GridProps) {\n if (!Number.isSafeInteger(columns) || columns < 1) {\n throw new RangeError('Grid columns must be a positive safe integer');\n }\n\n const gapValue = spaceNames.includes(gap as UiSpaceName)\n ? space(gap as UiSpaceName)\n : gap;\n const flexValue = flex === true ? '1 1 auto' : flex || undefined;\n const style = [\n `--_kui-grid-columns:${columns}`,\n `--_kui-grid-gap:${gapValue}`,\n flexValue ? `--_kui-grid-flex:${flexValue}` : '',\n ]\n .filter(Boolean)\n .join(';');\n\n return (\n <div\n class={`kui-grid ${className}`.trim()}\n data-component=\"grid\"\n data-columns={String(columns)}\n data-flex={String(Boolean(flex))}\n style={style}\n slot={slot}\n >\n {children}\n </div>\n );\n}\n\nexport type {\n CssFlex,\n CssFlexKeyword,\n CssLength,\n UiSpaceName,\n} from './css-values.js';\n"]}