@kerfjs/ui 5.0.0-beta.7 → 5.0.0-beta.9

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 (83) hide show
  1. package/README.md +20 -20
  2. package/ai/component-catalog.json +130 -83
  3. package/ai/public-api-signatures-v1.md +200 -29
  4. package/ai/skill.md +12 -12
  5. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  6. package/dist/browser/list-action-row.js +4 -0
  7. package/dist/browser/{menu-header.js → list-header.js} +2 -2
  8. package/dist/browser/{menu-item.js → list-item.js} +2 -2
  9. package/dist/catalog.d.ts +119 -0
  10. package/dist/catalog.js +128 -0
  11. package/dist/catalog.js.map +1 -0
  12. package/dist/{chunk-TZNCCM5H.js → chunk-2TS7ZSYX.js} +4 -4
  13. package/dist/chunk-2TS7ZSYX.js.map +1 -0
  14. package/dist/{chunk-PONXLV5U.js → chunk-37T3VNIZ.js} +1 -1
  15. package/dist/{chunk-PONXLV5U.js.map → chunk-37T3VNIZ.js.map} +1 -1
  16. package/dist/{chunk-3YD6ZNRA.js → chunk-7JB7VPRI.js} +8 -8
  17. package/dist/{chunk-3YD6ZNRA.js.map → chunk-7JB7VPRI.js.map} +1 -1
  18. package/dist/{chunk-N3SUXLLS.js → chunk-BLK3UEEI.js} +2 -2
  19. package/dist/{chunk-N3SUXLLS.js.map → chunk-BLK3UEEI.js.map} +1 -1
  20. package/dist/{chunk-YKUUZPED.js → chunk-L4OKFDJY.js} +7 -7
  21. package/dist/{chunk-YKUUZPED.js.map → chunk-L4OKFDJY.js.map} +1 -1
  22. package/dist/{chunk-KJYXOTG3.js → chunk-RUNWEPR4.js} +3 -3
  23. package/dist/chunk-RUNWEPR4.js.map +1 -0
  24. package/dist/{chunk-H6URQGJI.js → chunk-W5EQ3HYQ.js} +11 -11
  25. package/dist/{chunk-H6URQGJI.js.map → chunk-W5EQ3HYQ.js.map} +1 -1
  26. package/dist/index.d.ts +4 -4
  27. package/dist/index.js +11 -11
  28. package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +7 -7
  29. package/dist/list-action-row.js +5 -0
  30. package/dist/list-action-row.js.map +1 -0
  31. package/dist/{menu-header.d.ts → list-header.d.ts} +9 -9
  32. package/dist/list-header.js +7 -0
  33. package/dist/list-header.js.map +1 -0
  34. package/dist/{menu-item.d.ts → list-item.d.ts} +5 -5
  35. package/dist/list-item.js +5 -0
  36. package/dist/list-item.js.map +1 -0
  37. package/dist/nav-stack.js +1 -1
  38. package/dist/panel-header.d.ts +9 -1
  39. package/dist/panel-header.js +1 -1
  40. package/dist/split-view.d.ts +1 -1
  41. package/dist/split-view.js +1 -1
  42. package/dist/split-view.js.map +1 -1
  43. package/dist/styles/catalog.css +482 -0
  44. package/dist/styles/list-action-row.css +160 -0
  45. package/dist/styles/{menu-header.css → list-header.css} +33 -33
  46. package/dist/styles/list-item.css +123 -0
  47. package/dist/styles/styles.css +4 -3
  48. package/dist/toolbar-text.d.ts +10 -2
  49. package/dist/toolbar-text.js +1 -1
  50. package/dist/value-table.js +1 -1
  51. package/dist/wire-catalog.d.ts +26 -0
  52. package/dist/wire-catalog.js +50 -0
  53. package/dist/wire-catalog.js.map +1 -0
  54. package/docs/accessibility.md +15 -10
  55. package/docs/catalog.md +154 -0
  56. package/docs/component-contract.md +12 -12
  57. package/docs/component-selection.md +14 -13
  58. package/docs/design-philosophy.md +4 -4
  59. package/docs/layout.md +8 -8
  60. package/docs/recipes.md +17 -5
  61. package/docs/split-view.md +1 -1
  62. package/docs/ux-demo.md +5 -5
  63. package/llms.txt +12 -8
  64. package/package.json +25 -16
  65. package/ux-demo/recipes/app-shell.tsx +3 -3
  66. package/ux-demo/recipes/list-detail-dialog.tsx +45 -0
  67. package/ux-demo/recipes/loaders.ts +4 -2
  68. package/ux-demo/recipes/loading-inspector.tsx +79 -0
  69. package/ux-demo/recipes/navigation-sidebar.tsx +5 -5
  70. package/ux-demo/recipes/navigation-stack.tsx +4 -4
  71. package/ux-demo/recipes/recipes.css +13 -7
  72. package/dist/browser/menu-action-row.js +0 -4
  73. package/dist/chunk-KJYXOTG3.js.map +0 -1
  74. package/dist/chunk-TZNCCM5H.js.map +0 -1
  75. package/dist/menu-action-row.js +0 -5
  76. package/dist/menu-action-row.js.map +0 -1
  77. package/dist/menu-header.js +0 -7
  78. package/dist/menu-header.js.map +0 -1
  79. package/dist/menu-item.js +0 -5
  80. package/dist/menu-item.js.map +0 -1
  81. package/dist/styles/menu-action-row.css +0 -160
  82. package/dist/styles/menu-item.css +0 -123
  83. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
@@ -1,6 +1,6 @@
1
1
  # Public API signatures for the UI authoring corpus
2
2
 
3
- Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.7` and `kerfjs@5.0.0-beta.7`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
3
+ Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.9` and `kerfjs@5.0.0-beta.9`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
4
4
 
5
5
  ## `@kerfjs/ui/disclosure-arrow`
6
6
 
@@ -45,18 +45,26 @@ export { Toolbar, type ToolbarProps };
45
45
  import * as kerfjs from 'kerfjs';
46
46
 
47
47
  type ToolbarTextSize = 'xlarge' | 'large' | 'default' | 'small';
48
+ /** ARIA heading level for a title exposed as a heading landmark. */
49
+ type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
48
50
  interface ToolbarTextProps {
49
51
  text: string;
50
52
  size?: ToolbarTextSize;
51
53
  className?: string;
52
54
  /** Optional id, e.g. so a dialog can reference the title via aria-labelledby. */
53
55
  id?: string;
56
+ /**
57
+ * Expose heading semantics (`role="heading"` + `aria-level`) so the text acts as
58
+ * a heading landmark — e.g. a page's primary title. Omit to keep the plain span
59
+ * (the default), which suits a dialog title referenced via `aria-labelledby`.
60
+ */
61
+ headingLevel?: HeadingLevel;
54
62
  /** Render the text as an unanimated loading skeleton instead of its value. */
55
63
  placeholder?: boolean;
56
64
  }
57
- declare function ToolbarText({ text, size, className, id, placeholder }: ToolbarTextProps): kerfjs.SafeHtml;
65
+ declare function ToolbarText({ text, size, className, id, headingLevel, placeholder }: ToolbarTextProps): kerfjs.SafeHtml;
58
66
 
59
- export { ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
67
+ export { type HeadingLevel, ToolbarText, type ToolbarTextProps, type ToolbarTextSize };
60
68
  ```
61
69
 
62
70
  ## `@kerfjs/ui/toolbar-control-group`
@@ -79,26 +87,26 @@ declare function ToolbarControlGroup({ children, label, className, expanded, sin
79
87
  export { ToolbarControlGroup, type ToolbarControlGroupProps };
80
88
  ```
81
89
 
82
- ## `@kerfjs/ui/menu-header`
90
+ ## `@kerfjs/ui/list-header`
83
91
 
84
92
  ```ts
85
93
  import { SafeHtml } from 'kerfjs';
86
94
 
87
- type MenuHeaderRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
95
+ type ListHeaderRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
88
96
  'data-component'?: never;
89
97
  'data-action'?: never;
90
98
  'data-has-badge'?: never;
91
99
  'data-has-count'?: never;
92
100
  'data-toggle'?: never;
93
101
  }>;
94
- type MenuHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
102
+ type ListHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
95
103
  'data-action'?: never;
96
104
  popoverTarget?: string;
97
105
  popoverTargetAction?: 'toggle' | 'show' | 'hide';
98
106
  'aria-controls'?: string;
99
107
  'aria-haspopup'?: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | 'true';
100
108
  }>;
101
- interface MenuHeaderBaseProps {
109
+ interface ListHeaderBaseProps {
102
110
  label: string;
103
111
  action?: string;
104
112
  actionLabel?: string;
@@ -109,10 +117,10 @@ interface MenuHeaderBaseProps {
109
117
  toggle?: boolean;
110
118
  /** Render as an unanimated loading skeleton: keep the label and action affordance, disable interaction. */
111
119
  placeholder?: boolean;
112
- rootAttributes?: MenuHeaderRootAttributes;
113
- triggerAttributes?: MenuHeaderTriggerAttributes;
120
+ rootAttributes?: ListHeaderRootAttributes;
121
+ triggerAttributes?: ListHeaderTriggerAttributes;
114
122
  }
115
- type MenuHeaderIndicatorProps = {
123
+ type ListHeaderIndicatorProps = {
116
124
  count: number;
117
125
  countLabel: string;
118
126
  badge?: never;
@@ -121,18 +129,18 @@ type MenuHeaderIndicatorProps = {
121
129
  countLabel?: never;
122
130
  badge?: SafeHtml;
123
131
  };
124
- type MenuHeaderProps = MenuHeaderBaseProps & MenuHeaderIndicatorProps;
125
- declare function MenuHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes }: MenuHeaderProps): SafeHtml;
132
+ type ListHeaderProps = ListHeaderBaseProps & ListHeaderIndicatorProps;
133
+ declare function ListHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes }: ListHeaderProps): SafeHtml;
126
134
 
127
- export { MenuHeader, type MenuHeaderProps };
135
+ export { ListHeader, type ListHeaderProps };
128
136
  ```
129
137
 
130
- ## `@kerfjs/ui/menu-action-row`
138
+ ## `@kerfjs/ui/list-action-row`
131
139
 
132
140
  ```ts
133
141
  import { SafeHtml } from 'kerfjs';
134
142
 
135
- type MenuActionRowRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
143
+ type ListActionRowRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
136
144
  'data-component'?: never;
137
145
  'data-action'?: never;
138
146
  'data-item-id'?: never;
@@ -142,7 +150,7 @@ type MenuActionRowRootAttributes = Readonly<Record<`data-${string}`, string | un
142
150
  'data-selected'?: never;
143
151
  'data-pressed'?: never;
144
152
  }>;
145
- type MenuActionRowTrailingAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
153
+ type ListActionRowTrailingAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
146
154
  'data-component'?: never;
147
155
  'data-action'?: never;
148
156
  'data-item-id'?: never;
@@ -151,7 +159,7 @@ type MenuActionRowTrailingAttributes = Readonly<Record<`data-${string}`, string
151
159
  'aria-controls'?: string;
152
160
  'aria-haspopup'?: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | 'true';
153
161
  }>;
154
- interface MenuActionRowProps {
162
+ interface ListActionRowProps {
155
163
  /** Visible dormant content for the primary button. Must not contain interactive descendants. */
156
164
  label: string | SafeHtml;
157
165
  /** Decorative dormant content for the primary button. Must not contain interactive descendants. */
@@ -176,20 +184,20 @@ interface MenuActionRowProps {
176
184
  trailingActionTitle?: string;
177
185
  className?: string;
178
186
  style?: string;
179
- rootAttributes?: MenuActionRowRootAttributes;
180
- trailingActionAttributes?: MenuActionRowTrailingAttributes;
187
+ rootAttributes?: ListActionRowRootAttributes;
188
+ trailingActionAttributes?: ListActionRowTrailingAttributes;
181
189
  }
182
- declare function MenuActionRow({ label, icon, action, itemId, selected, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, trailingAction, trailingActionLabel, trailingActionIcon, trailingActionDisabled, trailingActionTitle, className, style, rootAttributes, trailingActionAttributes }: MenuActionRowProps): SafeHtml;
190
+ declare function ListActionRow({ label, icon, action, itemId, selected, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, trailingAction, trailingActionLabel, trailingActionIcon, trailingActionDisabled, trailingActionTitle, className, style, rootAttributes, trailingActionAttributes }: ListActionRowProps): SafeHtml;
183
191
 
184
- export { MenuActionRow, type MenuActionRowProps };
192
+ export { ListActionRow, type ListActionRowProps };
185
193
  ```
186
194
 
187
- ## `@kerfjs/ui/menu-item`
195
+ ## `@kerfjs/ui/list-item`
188
196
 
189
197
  ```ts
190
198
  import { SafeHtml } from 'kerfjs';
191
199
 
192
- type MenuItemRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
200
+ type ListItemRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
193
201
  'data-component'?: never;
194
202
  'data-action'?: never;
195
203
  'data-item-id'?: never;
@@ -197,7 +205,7 @@ type MenuItemRootAttributes = Readonly<Record<`data-${string}`, string | undefin
197
205
  'data-multiline'?: never;
198
206
  'data-state'?: never;
199
207
  }>;
200
- interface MenuItemProps {
208
+ interface ListItemProps {
201
209
  label: string | SafeHtml;
202
210
  icon?: SafeHtml;
203
211
  trailing?: SafeHtml;
@@ -215,17 +223,18 @@ interface MenuItemProps {
215
223
  tabIndex?: number;
216
224
  /** Render the row as an unanimated loading skeleton, disabling its action. */
217
225
  placeholder?: boolean;
218
- rootAttributes?: MenuItemRootAttributes;
226
+ rootAttributes?: ListItemRootAttributes;
219
227
  }
220
- declare function MenuItem({ label, icon, trailing, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, rootAttributes }: MenuItemProps): SafeHtml;
228
+ declare function ListItem({ label, icon, trailing, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, rootAttributes }: ListItemProps): SafeHtml;
221
229
 
222
- export { MenuItem, type MenuItemProps };
230
+ export { ListItem, type ListItemProps };
223
231
  ```
224
232
 
225
233
  ## `@kerfjs/ui/panel-header`
226
234
 
227
235
  ```ts
228
236
  import { SafeHtml } from 'kerfjs';
237
+ import { HeadingLevel } from './toolbar-text.js';
229
238
 
230
239
  interface PanelHeaderProps {
231
240
  title: string;
@@ -235,6 +244,13 @@ interface PanelHeaderProps {
235
244
  icon?: SafeHtml;
236
245
  iconClassName?: string;
237
246
  actions?: SafeHtml;
247
+ /**
248
+ * Expose the title as a heading landmark (`role="heading"` + `aria-level`). Set it
249
+ * for a PAGE or view heading so screen-reader heading navigation works and the view
250
+ * has a primary heading; omit it (the default) for a dialog title, which is instead
251
+ * referenced via `aria-labelledby={titleId}` and needs no heading landmark.
252
+ */
253
+ headingLevel?: HeadingLevel;
238
254
  /** Render the title and summary as unanimated loading skeletons, keeping the icon and actions. */
239
255
  placeholder?: boolean;
240
256
  }
@@ -250,7 +266,7 @@ interface PanelHeaderProps {
250
266
  * is passed straight into the toolbar's trailing zone; the app supplies whatever
251
267
  * trailing controls it needs (typically a `ToolbarControlGroup`).
252
268
  */
253
- declare function PanelHeader({ title, titleId, summary, summaryId, icon, iconClassName, actions, placeholder }: PanelHeaderProps): SafeHtml;
269
+ declare function PanelHeader({ title, titleId, summary, summaryId, icon, iconClassName, actions, headingLevel, placeholder }: PanelHeaderProps): SafeHtml;
254
270
 
255
271
  export { PanelHeader, type PanelHeaderProps };
256
272
  ```
@@ -455,7 +471,7 @@ interface SplitViewProps {
455
471
  className?: string;
456
472
  }
457
473
  /**
458
- * A list-detail (master-detail) split. On roomy classes it shows both panes side
474
+ * A list-detail split. On roomy classes it shows both panes side
459
475
  * by side with an optional resizable separator; on compact classes it collapses
460
476
  * to a `NavStack` (list → detail). See `docs/23-app-layouts.md` §3.2. Compose the
461
477
  * resizable wiring with `wireResizableRegions` and the compact back with
@@ -670,6 +686,161 @@ declare function deviceClass(options?: DeviceClassOptions): ReadonlySignal<Devic
670
686
  export { DEFAULT_BREAKPOINTS, type DeviceBreakpoints, type DeviceClass, type DeviceClassOptions, type DeviceOrientation, type DeviceSize, type Viewport, classifyViewport, deviceClass };
671
687
  ```
672
688
 
689
+ ## `@kerfjs/ui/catalog`
690
+
691
+ ```ts
692
+ import { SafeHtml } from 'kerfjs';
693
+
694
+ /** A reference link shown in the detail footer for the active entry. */
695
+ interface CatalogResource {
696
+ label: string;
697
+ href: string;
698
+ /** Optional monospace detail (e.g. a file path) shown after the label. */
699
+ detail?: string;
700
+ }
701
+ /** A related entry offered in the detail footer's "Related" selector. */
702
+ interface CatalogRelated {
703
+ id: string;
704
+ name: string;
705
+ /** Group heading in the selector, e.g. "Uses" / "Used by". */
706
+ group: string;
707
+ }
708
+ interface CatalogEntry {
709
+ id: string;
710
+ name: string;
711
+ description?: string;
712
+ resources?: readonly CatalogResource[];
713
+ related?: readonly CatalogRelated[];
714
+ }
715
+ interface CatalogSection {
716
+ category: string;
717
+ entries: readonly CatalogEntry[];
718
+ }
719
+ /**
720
+ * A secondary group of sections shown below the primary sidebar sections with a
721
+ * quieter "ecosystem" treatment (e.g. third-party components). Optionally
722
+ * collapsible — the app owns `expanded` and toggles it from `wireCatalog`'s
723
+ * `onToggleSecondary`.
724
+ */
725
+ interface CatalogSecondaryGroup {
726
+ label: string;
727
+ sections: readonly CatalogSection[];
728
+ /** When true, the group's label is a disclosure toggle controlling `expanded`. */
729
+ collapsible?: boolean;
730
+ /** Whether the group is expanded (controlled). Ignored unless `collapsible`. */
731
+ expanded?: boolean;
732
+ }
733
+ interface CatalogBrand {
734
+ title: string;
735
+ subtitle?: string;
736
+ /** Logo image URL (rendered decorative). Omit for a text-only brand. */
737
+ logoUrl?: string;
738
+ }
739
+ interface CatalogProps {
740
+ brand: CatalogBrand;
741
+ sections: readonly CatalogSection[];
742
+ /** The controlled active entry id — the app owns this signal. */
743
+ active: string;
744
+ /** The rendered preview for the active entry; the app computes it from `active`. */
745
+ content: SafeHtml;
746
+ /** Whether the sidebar is collapsed (controlled). */
747
+ collapsed?: boolean;
748
+ /** Current theme; when set, a theme toggle is shown that switches to the opposite. Omit to hide it. */
749
+ theme?: 'light' | 'dark';
750
+ /** Extra header controls placed before the theme toggle (each a `ToolbarControlGroup`). */
751
+ headerActions?: SafeHtml;
752
+ /** A secondary "ecosystem" group of sections below the primary category groups. */
753
+ secondarySections?: CatalogSecondaryGroup;
754
+ /** Extra sidebar content below the category groups (and the secondary group). */
755
+ sidebarFooter?: SafeHtml;
756
+ /** Status line content shown at the start of the detail footer. */
757
+ status?: SafeHtml;
758
+ selectAction?: string;
759
+ toggleSidebarAction?: string;
760
+ toggleThemeAction?: string;
761
+ /** Action fired by the secondary group's disclosure toggle (when collapsible). */
762
+ toggleSecondaryAction?: string;
763
+ className?: string;
764
+ }
765
+ /**
766
+ * A reusable component-catalog shell: a collapsible category sidebar, a titled
767
+ * detail stage that renders the active entry's preview, and a footer with
768
+ * reference links and a related-entry selector. Built entirely from public
769
+ * `@kerfjs/ui` primitives. Controlled and stateless — the app owns the `active`,
770
+ * `collapsed`, and `theme` signals and computes `content` from `active` in its own
771
+ * render; wire the sidebar/collapse/theme actions with `wireCatalog`.
772
+ */
773
+ declare function Catalog({ brand, sections, active, content, collapsed, theme, headerActions, secondarySections, sidebarFooter, status, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, className, }: CatalogProps): SafeHtml;
774
+ /**
775
+ * How a {@link CatalogExample}'s content aligns its visible left edge with the
776
+ * example's `ListHeader` label (which sits 16px in — 8px title + 8px label):
777
+ * - `'glyph'` — a bare glyph/text specimen with no inline geometry insets the full 16px.
778
+ * - `'inline-control'` — a control that already carries ~8px of its own inline padding insets 8px so its content lands on the same line.
779
+ * - `'none'` — a content-item / composition that owns its geometry and already aligns; no inset (default).
780
+ */
781
+ type CatalogExampleAlign = 'glyph' | 'inline-control' | 'none';
782
+ interface CatalogExampleProps {
783
+ /** The example's label, shown as a `ListHeader` above the specimen. Omit for a bare specimen. */
784
+ label?: string;
785
+ /** Optional explanatory note between the label and the specimen. */
786
+ note?: SafeHtml | string;
787
+ /** Alignment inset for the specimen — see {@link CatalogExampleAlign}. Default `'none'`. */
788
+ align?: CatalogExampleAlign;
789
+ className?: string;
790
+ children?: SafeHtml | readonly SafeHtml[];
791
+ }
792
+ /**
793
+ * One labeled example in a catalog preview: a `ListHeader` label, an optional
794
+ * note, and the specimen. `align` insets the specimen so its visible left edge
795
+ * lines up with the label text, encoding the catalog's alignment rules as a
796
+ * first-class prop instead of per-demo CSS. The inset is published as the
797
+ * `--kui-catalog-example-align` custom property so a debug overlay can exclude it
798
+ * from a specimen's measured margin.
799
+ */
800
+ declare function CatalogExample({ label, note, align, className, children }: CatalogExampleProps): SafeHtml;
801
+ interface CatalogExampleStackProps {
802
+ /** Accessible label for the stack region. */
803
+ label?: string;
804
+ className?: string;
805
+ children?: SafeHtml | readonly SafeHtml[];
806
+ }
807
+ /** A vertically-stacked group of {@link CatalogExample}s with the catalog's example rhythm. */
808
+ declare function CatalogExampleStack({ label, className, children }: CatalogExampleStackProps): SafeHtml;
809
+
810
+ export { Catalog, type CatalogBrand, type CatalogEntry, CatalogExample, type CatalogExampleAlign, type CatalogExampleProps, CatalogExampleStack, type CatalogExampleStackProps, type CatalogProps, type CatalogRelated, type CatalogResource, type CatalogSecondaryGroup, type CatalogSection };
811
+ ```
812
+
813
+ ## `@kerfjs/ui/wire-catalog`
814
+
815
+ ```ts
816
+ interface WireCatalogOptions {
817
+ /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */
818
+ onSelect: (id: string) => void;
819
+ /** Invoked when the sidebar collapse/expand control is activated. */
820
+ onToggleSidebar?: () => void;
821
+ /** Invoked when the theme toggle is activated. */
822
+ onToggleTheme?: () => void;
823
+ /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */
824
+ onToggleSecondary?: () => void;
825
+ /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */
826
+ urlParam?: string;
827
+ selectAction?: string;
828
+ toggleSidebarAction?: string;
829
+ toggleThemeAction?: string;
830
+ toggleSecondaryAction?: string;
831
+ }
832
+ /**
833
+ * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar
834
+ * item selection (and the related-entry selector), the sidebar collapse toggle, and
835
+ * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates
836
+ * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.
837
+ * Returns a disposer.
838
+ */
839
+ declare function wireCatalog(root: HTMLElement, { onSelect, onToggleSidebar, onToggleTheme, onToggleSecondary, urlParam, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, }: WireCatalogOptions): () => void;
840
+
841
+ export { type WireCatalogOptions, wireCatalog };
842
+ ```
843
+
673
844
  ## `@kerfjs/ui/segmented-control`
674
845
 
675
846
  ```ts
package/ai/skill.md CHANGED
@@ -27,18 +27,18 @@ Quick routing:
27
27
 
28
28
  | Need | Choose | Nearest alternatives / boundary |
29
29
  | --- | --- | --- |
30
- | Navigation row | `MenuItem` | Use an ordinary link or button when sidebar/menu anatomy and state do not apply. Multiline leading icons stay aligned with the first text line. |
31
- | Navigation row with a trailing action | `MenuActionRow` | Use `MenuItem` when the trailing region is dormant; never put controls inside either component's SafeHtml slots. Multiline leading icons stay aligned with the first text line. |
30
+ | Navigation row | `ListItem` | Use an ordinary link or button when sidebar/menu anatomy and state do not apply. Multiline leading icons stay aligned with the first text line. |
31
+ | Navigation row with a trailing action | `ListActionRow` | Use `ListItem` when the trailing region is dormant; never put controls inside either component's SafeHtml slots. Multiline leading icons stay aligned with the first text line. |
32
32
  | Page, panel, or dialog heading | `Toolbar` or `PanelHeader` | Toolbar is persistent chrome; `PanelHeader` heads a panel, dialog, or page. |
33
33
  | Exclusive choice | `TabBar`, `SegmentedControl`, or `Select` | Tabs switch tabpanels; segments expose a few choices; Select handles a longer value list. |
34
34
  | Status or absent content | `StateBanner` or `EmptyState` | Web Awesome callouts suit ecosystem content; toasts are transient and never the only record of important state. |
35
- | Loading placeholder | a component's `placeholder` prop, or `Skeleton` | Set `placeholder` on a value-bearing component (`Select`, `MenuHeader`, `MenuItem`, `ValueTableRow`, `PanelHeader`, `SegmentedControl`, `StateBanner`, `AppTab`, `ToolbarText`, `MenuActionRow`) to render its real chrome with value slots as subtle **unanimated** skeletons and interactivity disabled — do not hand-rebuild an empty state. `Skeleton` is the standalone block for a custom slot. Use `LoadingSpinner` for known busy activity, not a skeleton; never animate a skeleton; prefer it over `wa-skeleton`. |
35
+ | Loading placeholder | a component's `placeholder` prop, or `Skeleton` | Set `placeholder` on a value-bearing component (`Select`, `ListHeader`, `ListItem`, `ValueTableRow`, `PanelHeader`, `SegmentedControl`, `StateBanner`, `AppTab`, `ToolbarText`, `ListActionRow`) to render its real chrome with value slots as subtle **unanimated** skeletons and interactivity disabled — do not hand-rebuild an empty state. `Skeleton` is the standalone block for a custom slot. Use `LoadingSpinner` for known busy activity, not a skeleton; never animate a skeleton; prefer it over `wa-skeleton`. |
36
36
  | Adjustable or static columns | `ResizableRegion` or CSS grid | Use the component only for a user-operable controlled separator. |
37
37
  | Structured or ordinary search | `TokenSearchField` or an input | Use token search only when text and ordered filter chips form one editor. |
38
38
  | Key/value facts | `ValueTable` with `ValueTableRow` | Use rows with `icon` only when the optional 24px leading visual adds context. |
39
39
  | Command search | Application-local semantics using shared layout vocabulary | No runtime component or production recipe is exported; the app owns behavior, policy, and copy. |
40
40
 
41
- For a complete shell, sidebar, workspace header, master-detail dialog, composer
41
+ For a complete shell, sidebar, workspace header, list-detail dialog, composer
42
42
  form, list-state lifecycle, or mixed-control toolbar, start from the runnable
43
43
  reference in [`docs/recipes.md`](../docs/recipes.md). Preserve its production
44
44
  primitives and semantic ownership; replace application state, policy, and copy.
@@ -49,11 +49,11 @@ Hard rules:
49
49
 
50
50
  1. Import visual components from explicit JavaScript subpaths. In CSS-aware browser builds each subpath brings in its own reachable CSS, including UI subcomponents, while unrelated CSS remains out. The root barrel and `@kerfjs/ui/unstyled` are CSS-free; pair the root barrel with `styles.css` only when the complete layer is intentional. Manual CSS subpaths remain available for custom pipelines.
51
51
  2. Components return Kerf `SafeHtml`. Never pass DOM nodes as children or use inline JSX event handlers.
52
- 3. Keep state, product copy, persistence, and domain mappings in the application. Do not add product-specific actions or fields to a generic component. Put MenuItem/MenuActionRow/MenuHeader/AppTab domain `data-*` metadata in `rootAttributes`; use MenuActionRow `trailingActionAttributes` and MenuHeader `triggerAttributes` only for domain `data-*` or native popover target/action and `aria-controls`/`aria-haspopup`. These slots do not replace component-owned action, item/tab identity, selection, disclosure, naming, disabled, icon, or role semantics.
52
+ 3. Keep state, product copy, persistence, and domain mappings in the application. Do not add product-specific actions or fields to a generic component. Put ListItem/ListActionRow/ListHeader/AppTab domain `data-*` metadata in `rootAttributes`; use ListActionRow `trailingActionAttributes` and ListHeader `triggerAttributes` only for domain `data-*` or native popover target/action and `aria-controls`/`aria-haspopup`. These slots do not replace component-owned action, item/tab identity, selection, disclosure, naming, disabled, icon, or role semantics.
53
53
  4. Wire `data-action` hooks from one stable root with `delegate()` or `delegateActions()` and retain disposers.
54
54
  5. Use the opinionated `--kui-color-*` semantic ramps and component-level override properties. Override tokens at the narrowest useful scope and prefer equivalent props/tokens. Public-class-to-public-class selectors are supported when every Kerf class appears in the catalog entry's `publicClasses`; never target descendant tags, ids, attribute-only anatomy, or unlisted implementation classes.
55
55
  6. `Select` is pure until the app explicitly imports `@kerfjs/ui/select/register`; do not import Web Awesome's full registration bundle. When an app writes direct `wa-*` JSX, add `import type {} from '@kerfjs/ui/webawesome'` for the catalog-supported intrinsic-element declarations, import the CSS-only `@kerfjs/ui/webawesome.css` theme once, and keep importing individual Web Awesome component modules so their JavaScript remains tree-shakeable. The type boundary emits no code and registers nothing. Pass icon-bearing `choices` and `renderSelected` content normally: `Select` preserves its slotted option icons across Kerf rerenders and keys selected content by the controlled value, so app wrappers must not add competing morph-control attributes.
56
- 7. Decorative icons are hidden; controls are named; focus is visible; state never relies on color alone; reduced motion and increased contrast remain usable. `DisclosureArrow` defaults to an 18px root-scaled visual and exposes `--kui-disclosure-arrow-size` for consumer sizing; it never becomes the interaction or accessible-name owner. Put it in an owning native control with a stable accessible name and controlled `aria-expanded`. `MenuHeader` toggle mode supplies it when `actionIcon` is omitted, but the app must reveal matching content; a custom icon replaces it. Author replacement icon content facing right before its configured direction transform is applied. Direction changes take the shortest rotation path, with counterclockwise chosen for a 180-degree closed-to-open tie. Kerf `Select` separately keeps its intrinsic Web Awesome expand glyph at `--kui-disclosure-icon-scale: .5`.
56
+ 7. Decorative icons are hidden; controls are named; focus is visible; state never relies on color alone; reduced motion and increased contrast remain usable. `DisclosureArrow` defaults to an 18px root-scaled visual and exposes `--kui-disclosure-arrow-size` for consumer sizing; it never becomes the interaction or accessible-name owner. Put it in an owning native control with a stable accessible name and controlled `aria-expanded`. `ListHeader` toggle mode supplies it when `actionIcon` is omitted, but the app must reveal matching content; a custom icon replaces it. Author replacement icon content facing right before its configured direction transform is applied. Direction changes take the shortest rotation path, with counterclockwise chosen for a 180-degree closed-to-open tie. Kerf `Select` separately keeps its intrinsic Web Awesome expand glyph at `--kui-disclosure-icon-scale: .5`.
57
57
  8. `ResizableRegion` uses `wireResizableRegions()` for Arrow, Shift+Arrow, Home/End, and pointer behavior. The app owns size persistence. `handleIcon` replaces decorative dormant glyph content only.
58
58
  9. Use controlled `SegmentedControl` for a small exclusive choice set. Select `appearance="toolbar"` when nesting it inside `ToolbarControlGroup`; use rounded or pill shapes for standalone contexts. Handle its action, update `value`, keep meaningful choice labels, and preserve every enabled native button in sequential Tab order.
59
59
  10. Compose `AppTab` inside controlled `TabBar`; call `wireTabBars()` once and retain its disposer. It owns same-bar drag mechanics, including proximity-based horizontal edge autoscroll, while the app applies `onReorder` and owns order, selection, panels, close policy, routing, and persistence. Use runtime-filtered `rootAttributes` for domain metadata and keep an optional `closeIcon` decorative and noninteractive.
@@ -63,7 +63,7 @@ Hard rules:
63
63
  14. Treat the complete Web Awesome catalog as support coverage, not a recommendation list. Consider Popup when it replaces custom anchored positioning. Prefer Kerf `Select` over direct Dropdown/Dropdown Item/Select/Option composition, `SegmentedControl` over Button Group, `TabBar` or `SegmentedControl` over Web Awesome Tabs, `LucideIcon` over Web Awesome Icon, and `ResizableRegion` over Split Panel. Use Tree/Tree Item, Animated Image, and Comparison only for a specific required behavior; avoid Zoomable Frame.
64
64
  15. Build sidebars, main areas, inspectors, and dialogs from `@kerfjs/ui/layout.css`: an unpadded `.kui-pane`, optional `.kui-pane__toolbar`, one scrolling `.kui-pane__content`, and optional `.kui-pane__footer`. Add `.kui-content` for 24px major vertical separation and `.kui-content-item` for a child-owned 8px inline margin, 1px transparent-or-visible border, 8px padding, and 12px radius. Use the pill modifier for 22px. Do not pad pane shells or duplicate item geometry in wrappers.
65
65
  16. Keep a visible collapsible pane's collapse control in its own toolbar. When hidden, move its restore control into the adjacent main toolbar on the corresponding logical edge: leading for an inline-start sidebar and trailing for an inline-end inspector. Collapse the pane completely; do not preserve an empty icon-only rail.
66
- 16. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a `ToolbarControlGroup`. A popup menu in a toolbar is a `single` `ToolbarControlGroup` wrapping a `wa-dropdown` whose `slot="trigger"` `wa-button` is the toolbar button and whose `wa-dropdown-item`s are the menu, with the dropdown kept under `data-morph-skip-children`. `PanelHeader` composes these for you as a panel/dialog/page heading. Wrap every toolbar item, including dormant text, in `ToolbarControlGroup`. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Split dormant and interactive regions: `MenuHeader` fills the available inline width and keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end 44px action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-menu-header-action-icon-size`; never shrink the target to match it. Pass every non-negative safe-integer section quantity through `count` with a localized full spoken `countLabel`; never concatenate it into `label` or put a number in `badge`. `MenuItem.trailing` is dormant; use `MenuActionRow` when primary and trailing actions need sibling 44px native buttons. Its `label`, `icon`, and `trailingActionIcon` slots are also dormant and cannot contain controls. Let panes relocate at narrow widths instead of shrinking targets.
66
+ 16. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a `ToolbarControlGroup`. A popup menu in a toolbar is a `single` `ToolbarControlGroup` wrapping a `wa-dropdown` whose `slot="trigger"` `wa-button` is the toolbar button and whose `wa-dropdown-item`s are the menu, with the dropdown kept under `data-morph-skip-children`. `PanelHeader` composes these for you as a panel/dialog/page heading. Wrap every toolbar item, including dormant text, in `ToolbarControlGroup`. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Split dormant and interactive regions: `ListHeader` fills the available inline width and keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end 44px action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-list-header-action-icon-size`; never shrink the target to match it. Pass every non-negative safe-integer section quantity through `count` with a localized full spoken `countLabel`; never concatenate it into `label` or put a number in `badge`. `ListItem.trailing` is dormant; use `ListActionRow` when primary and trailing actions need sibling 44px native buttons. Its `label`, `icon`, and `trailingActionIcon` slots are also dormant and cannot contain controls. Let panes relocate at narrow widths instead of shrinking targets.
67
67
  17. When a recurring concept has no matching export or production recipe, keep its semantics in a thin application adapter while reusing the public layout vocabulary. The composer recipe uses one visible form surface, `PanelHeader` title/summary ids, shared 8px field/action gutters, 24px major rhythm, and a conditional StateBanner as its only nested semantic surface; do not turn every section into a card or double-inset intrinsically bordered controls. The application-local `../docs/examples/command-palette-adapter.tsx` is reference source for one such missing concept, not an `@kerfjs/ui` runtime export or catalog recipe. The application owns its registration, ranking, history, permissions, availability, shortcut policy, focus policy, dispatch, and copy. If a missing concept recurs across products, open an upstream component or recipe request.
68
68
  18. Compose `ValueTable` from typed `ValueTableRow` entries instead of handwritten `dt`/`dd` wrappers. Pass `icon` for the optional 24px leading visual; the row owns 8px of root-scaled top and bottom padding, the 8px iconless or 40px icon-bearing separator start, and the common 8px right inset.
69
69
  19. Use `PanelHeader` for a panel, dialog, or page heading, not a custom heading row. It is a plain top `Toolbar` (overriding no Toolbar styles): the leading zone holds an optional icon (a normal bordered `ToolbarControlGroup` given a brand fill with a matching border) and the title as extra-large `ToolbarText`; the app's trailing controls (typically a `ToolbarControlGroup`) go straight in the trailing zone, and the icon group is omitted when no icon is passed. Its optional `summary`/`summaryId` row stays below the title. The app owns modal behavior, focus, dismissal, command policy, and action handling.
@@ -77,17 +77,17 @@ Common mistakes:
77
77
  | Hard-coded project/transport action in a component | Pass a semantic `data-action` string from an application adapter |
78
78
  | Per-instance signal at module scope | Create state in the application or a factory and pass it in |
79
79
  | `role="menuitem"` on one button | Use the native button, or implement the complete ARIA menu widget |
80
- | Put a button/link/interactive role in `MenuItem.trailing` or a `MenuActionRow` SafeHtml slot | Use `MenuActionRow` for the sibling controls and keep its label/icon slots dormant |
81
- | Copy MenuItem/MenuHeader markup to add product data or a popover target | Use the typed `rootAttributes`/`triggerAttributes` slots and retain the component's protected semantics |
82
- | Concatenate a section count into a MenuHeader label or numeric badge | Pass `count={value}` and a localized full phrase such as `countLabel="3 notes"`; reserve `badge` for non-count SafeHtml |
83
- | Put a chevron on navigation that reveals nothing | Remove it, or model a real controlled `MenuHeader` disclosure; omit `actionIcon` to use the production `DisclosureArrow` |
80
+ | Put a button/link/interactive role in `ListItem.trailing` or a `ListActionRow` SafeHtml slot | Use `ListActionRow` for the sibling controls and keep its label/icon slots dormant |
81
+ | Copy ListItem/ListHeader markup to add product data or a popover target | Use the typed `rootAttributes`/`triggerAttributes` slots and retain the component's protected semantics |
82
+ | Concatenate a section count into a ListHeader label or numeric badge | Pass `count={value}` and a localized full phrase such as `countLabel="3 notes"`; reserve `badge` for non-count SafeHtml |
83
+ | Put a chevron on navigation that reveals nothing | Remove it, or model a real controlled `ListHeader` disclosure; omit `actionIcon` to use the production `DisclosureArrow` |
84
84
  | Test only a custom-element attribute | Assert live property, emitted event, focus, and rendered output |
85
85
  | Import all Web Awesome components | Import only `@kerfjs/ui/select/register` for `Select`, or individual Web Awesome modules for other controls; `webawesome.css` registers no JavaScript |
86
86
  | Write `wa-*` JSX without activating its types | Add `import type {} from '@kerfjs/ui/webawesome'`; registration and theme imports remain separate |
87
87
  | Maintain a root list of component styles | Import each visual component from its JS subpath; its reachable CSS follows automatically |
88
88
  | Add demo-only markup for a production state | Add the state to the production component, then render that export in the catalog |
89
89
  | Choose a listed ecosystem component by default | Apply the component-selection guidance above; catalog coverage means supported and themed, not preferred |
90
- | Add sidebar-specific wrapper padding | Use the unpadded `.kui-pane`; let `MenuHeader`, `MenuItem`, and `.kui-content-item` own their 8/1/8 geometry |
90
+ | Add sidebar-specific wrapper padding | Use the unpadded `.kui-pane`; let `ListHeader`, `ListItem`, and `.kui-content-item` own their 8/1/8 geometry |
91
91
  | Put bare text or controls directly in a toolbar slot | Wrap every item in `ToolbarControlGroup`, using `appearance="borderless"` for transparent chrome |
92
92
  | Let the document, pane, and list all scroll | Keep fixed chrome outside one `.kui-pane__content` scroll owner per pane |
93
93
  | Invent an `@kerfjs/ui` command-palette export | Keep the semantics application-local, reuse public layout vocabulary, and propose recurring behavior upstream |
@@ -1,6 +1,6 @@
1
1
  # Web Awesome JSX signatures for the UI authoring corpus
2
2
 
3
- Generated from the emitted `@kerfjs/ui@5.0.0-beta.7` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
3
+ Generated from the emitted `@kerfjs/ui@5.0.0-beta.9` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
4
4
 
5
5
  ```ts
6
6
  import { KerfCustomElement } from 'kerfjs/jsx-runtime';
@@ -0,0 +1,4 @@
1
+ import '../styles/foundation.css';
2
+ import '../styles/skeleton.css';
3
+ import '../styles/list-action-row.css';
4
+ export * from '../list-action-row.js';
@@ -2,5 +2,5 @@ import '../styles/foundation.css';
2
2
  import '../styles/lucide-icon.css';
3
3
  import '../styles/disclosure-arrow.css';
4
4
  import '../styles/skeleton.css';
5
- import '../styles/menu-header.css';
6
- export * from '../menu-header.js';
5
+ import '../styles/list-header.css';
6
+ export * from '../list-header.js';
@@ -1,4 +1,4 @@
1
1
  import '../styles/foundation.css';
2
2
  import '../styles/skeleton.css';
3
- import '../styles/menu-item.css';
4
- export * from '../menu-item.js';
3
+ import '../styles/list-item.css';
4
+ export * from '../list-item.js';
@@ -0,0 +1,119 @@
1
+ import { SafeHtml } from 'kerfjs';
2
+
3
+ /** A reference link shown in the detail footer for the active entry. */
4
+ interface CatalogResource {
5
+ label: string;
6
+ href: string;
7
+ /** Optional monospace detail (e.g. a file path) shown after the label. */
8
+ detail?: string;
9
+ }
10
+ /** A related entry offered in the detail footer's "Related" selector. */
11
+ interface CatalogRelated {
12
+ id: string;
13
+ name: string;
14
+ /** Group heading in the selector, e.g. "Uses" / "Used by". */
15
+ group: string;
16
+ }
17
+ interface CatalogEntry {
18
+ id: string;
19
+ name: string;
20
+ description?: string;
21
+ resources?: readonly CatalogResource[];
22
+ related?: readonly CatalogRelated[];
23
+ }
24
+ interface CatalogSection {
25
+ category: string;
26
+ entries: readonly CatalogEntry[];
27
+ }
28
+ /**
29
+ * A secondary group of sections shown below the primary sidebar sections with a
30
+ * quieter "ecosystem" treatment (e.g. third-party components). Optionally
31
+ * collapsible — the app owns `expanded` and toggles it from `wireCatalog`'s
32
+ * `onToggleSecondary`.
33
+ */
34
+ interface CatalogSecondaryGroup {
35
+ label: string;
36
+ sections: readonly CatalogSection[];
37
+ /** When true, the group's label is a disclosure toggle controlling `expanded`. */
38
+ collapsible?: boolean;
39
+ /** Whether the group is expanded (controlled). Ignored unless `collapsible`. */
40
+ expanded?: boolean;
41
+ }
42
+ interface CatalogBrand {
43
+ title: string;
44
+ subtitle?: string;
45
+ /** Logo image URL (rendered decorative). Omit for a text-only brand. */
46
+ logoUrl?: string;
47
+ }
48
+ interface CatalogProps {
49
+ brand: CatalogBrand;
50
+ sections: readonly CatalogSection[];
51
+ /** The controlled active entry id — the app owns this signal. */
52
+ active: string;
53
+ /** The rendered preview for the active entry; the app computes it from `active`. */
54
+ content: SafeHtml;
55
+ /** Whether the sidebar is collapsed (controlled). */
56
+ collapsed?: boolean;
57
+ /** Current theme; when set, a theme toggle is shown that switches to the opposite. Omit to hide it. */
58
+ theme?: 'light' | 'dark';
59
+ /** Extra header controls placed before the theme toggle (each a `ToolbarControlGroup`). */
60
+ headerActions?: SafeHtml;
61
+ /** A secondary "ecosystem" group of sections below the primary category groups. */
62
+ secondarySections?: CatalogSecondaryGroup;
63
+ /** Extra sidebar content below the category groups (and the secondary group). */
64
+ sidebarFooter?: SafeHtml;
65
+ /** Status line content shown at the start of the detail footer. */
66
+ status?: SafeHtml;
67
+ selectAction?: string;
68
+ toggleSidebarAction?: string;
69
+ toggleThemeAction?: string;
70
+ /** Action fired by the secondary group's disclosure toggle (when collapsible). */
71
+ toggleSecondaryAction?: string;
72
+ className?: string;
73
+ }
74
+ /**
75
+ * A reusable component-catalog shell: a collapsible category sidebar, a titled
76
+ * detail stage that renders the active entry's preview, and a footer with
77
+ * reference links and a related-entry selector. Built entirely from public
78
+ * `@kerfjs/ui` primitives. Controlled and stateless — the app owns the `active`,
79
+ * `collapsed`, and `theme` signals and computes `content` from `active` in its own
80
+ * render; wire the sidebar/collapse/theme actions with `wireCatalog`.
81
+ */
82
+ declare function Catalog({ brand, sections, active, content, collapsed, theme, headerActions, secondarySections, sidebarFooter, status, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, className, }: CatalogProps): SafeHtml;
83
+ /**
84
+ * How a {@link CatalogExample}'s content aligns its visible left edge with the
85
+ * example's `ListHeader` label (which sits 16px in — 8px title + 8px label):
86
+ * - `'glyph'` — a bare glyph/text specimen with no inline geometry insets the full 16px.
87
+ * - `'inline-control'` — a control that already carries ~8px of its own inline padding insets 8px so its content lands on the same line.
88
+ * - `'none'` — a content-item / composition that owns its geometry and already aligns; no inset (default).
89
+ */
90
+ type CatalogExampleAlign = 'glyph' | 'inline-control' | 'none';
91
+ interface CatalogExampleProps {
92
+ /** The example's label, shown as a `ListHeader` above the specimen. Omit for a bare specimen. */
93
+ label?: string;
94
+ /** Optional explanatory note between the label and the specimen. */
95
+ note?: SafeHtml | string;
96
+ /** Alignment inset for the specimen — see {@link CatalogExampleAlign}. Default `'none'`. */
97
+ align?: CatalogExampleAlign;
98
+ className?: string;
99
+ children?: SafeHtml | readonly SafeHtml[];
100
+ }
101
+ /**
102
+ * One labeled example in a catalog preview: a `ListHeader` label, an optional
103
+ * note, and the specimen. `align` insets the specimen so its visible left edge
104
+ * lines up with the label text, encoding the catalog's alignment rules as a
105
+ * first-class prop instead of per-demo CSS. The inset is published as the
106
+ * `--kui-catalog-example-align` custom property so a debug overlay can exclude it
107
+ * from a specimen's measured margin.
108
+ */
109
+ declare function CatalogExample({ label, note, align, className, children }: CatalogExampleProps): SafeHtml;
110
+ interface CatalogExampleStackProps {
111
+ /** Accessible label for the stack region. */
112
+ label?: string;
113
+ className?: string;
114
+ children?: SafeHtml | readonly SafeHtml[];
115
+ }
116
+ /** A vertically-stacked group of {@link CatalogExample}s with the catalog's example rhythm. */
117
+ declare function CatalogExampleStack({ label, className, children }: CatalogExampleStackProps): SafeHtml;
118
+
119
+ export { Catalog, type CatalogBrand, type CatalogEntry, CatalogExample, type CatalogExampleAlign, type CatalogExampleProps, CatalogExampleStack, type CatalogExampleStackProps, type CatalogProps, type CatalogRelated, type CatalogResource, type CatalogSecondaryGroup, type CatalogSection };