@kerfjs/ui 5.0.0-beta.8 → 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 (72) hide show
  1. package/README.md +20 -20
  2. package/ai/component-catalog.json +123 -80
  3. package/ai/public-api-signatures-v1.md +181 -26
  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-BNQ2YDV4.js → chunk-2TS7ZSYX.js} +2 -2
  13. package/dist/{chunk-BNQ2YDV4.js.map → chunk-2TS7ZSYX.js.map} +1 -1
  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-YKUUZPED.js → chunk-L4OKFDJY.js} +7 -7
  19. package/dist/{chunk-YKUUZPED.js.map → chunk-L4OKFDJY.js.map} +1 -1
  20. package/dist/{chunk-H6URQGJI.js → chunk-W5EQ3HYQ.js} +11 -11
  21. package/dist/{chunk-H6URQGJI.js.map → chunk-W5EQ3HYQ.js.map} +1 -1
  22. package/dist/index.d.ts +3 -3
  23. package/dist/index.js +10 -10
  24. package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +7 -7
  25. package/dist/list-action-row.js +5 -0
  26. package/dist/list-action-row.js.map +1 -0
  27. package/dist/{menu-header.d.ts → list-header.d.ts} +9 -9
  28. package/dist/list-header.js +7 -0
  29. package/dist/list-header.js.map +1 -0
  30. package/dist/{menu-item.d.ts → list-item.d.ts} +5 -5
  31. package/dist/list-item.js +5 -0
  32. package/dist/list-item.js.map +1 -0
  33. package/dist/panel-header.js +1 -1
  34. package/dist/split-view.d.ts +1 -1
  35. package/dist/split-view.js.map +1 -1
  36. package/dist/styles/catalog.css +482 -0
  37. package/dist/styles/list-action-row.css +160 -0
  38. package/dist/styles/{menu-header.css → list-header.css} +33 -33
  39. package/dist/styles/list-item.css +123 -0
  40. package/dist/styles/styles.css +4 -3
  41. package/dist/value-table.js +1 -1
  42. package/dist/wire-catalog.d.ts +26 -0
  43. package/dist/wire-catalog.js +50 -0
  44. package/dist/wire-catalog.js.map +1 -0
  45. package/docs/accessibility.md +5 -5
  46. package/docs/catalog.md +154 -0
  47. package/docs/component-contract.md +12 -12
  48. package/docs/component-selection.md +12 -11
  49. package/docs/design-philosophy.md +4 -4
  50. package/docs/layout.md +8 -8
  51. package/docs/recipes.md +17 -5
  52. package/docs/split-view.md +1 -1
  53. package/docs/ux-demo.md +5 -5
  54. package/llms.txt +12 -8
  55. package/package.json +25 -16
  56. package/ux-demo/recipes/app-shell.tsx +3 -3
  57. package/ux-demo/recipes/list-detail-dialog.tsx +45 -0
  58. package/ux-demo/recipes/loaders.ts +4 -2
  59. package/ux-demo/recipes/loading-inspector.tsx +79 -0
  60. package/ux-demo/recipes/navigation-sidebar.tsx +5 -5
  61. package/ux-demo/recipes/navigation-stack.tsx +4 -4
  62. package/ux-demo/recipes/recipes.css +13 -7
  63. package/dist/browser/menu-action-row.js +0 -4
  64. package/dist/menu-action-row.js +0 -5
  65. package/dist/menu-action-row.js.map +0 -1
  66. package/dist/menu-header.js +0 -7
  67. package/dist/menu-header.js.map +0 -1
  68. package/dist/menu-item.js +0 -5
  69. package/dist/menu-item.js.map +0 -1
  70. package/dist/styles/menu-action-row.css +0 -160
  71. package/dist/styles/menu-item.css +0 -123
  72. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
@@ -1,20 +1,20 @@
1
1
  import { SafeHtml } from 'kerfjs';
2
2
 
3
- type MenuHeaderRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
3
+ type ListHeaderRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
4
4
  'data-component'?: never;
5
5
  'data-action'?: never;
6
6
  'data-has-badge'?: never;
7
7
  'data-has-count'?: never;
8
8
  'data-toggle'?: never;
9
9
  }>;
10
- type MenuHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
10
+ type ListHeaderTriggerAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
11
11
  'data-action'?: never;
12
12
  popoverTarget?: string;
13
13
  popoverTargetAction?: 'toggle' | 'show' | 'hide';
14
14
  'aria-controls'?: string;
15
15
  'aria-haspopup'?: 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid' | 'true';
16
16
  }>;
17
- interface MenuHeaderBaseProps {
17
+ interface ListHeaderBaseProps {
18
18
  label: string;
19
19
  action?: string;
20
20
  actionLabel?: string;
@@ -25,10 +25,10 @@ interface MenuHeaderBaseProps {
25
25
  toggle?: boolean;
26
26
  /** Render as an unanimated loading skeleton: keep the label and action affordance, disable interaction. */
27
27
  placeholder?: boolean;
28
- rootAttributes?: MenuHeaderRootAttributes;
29
- triggerAttributes?: MenuHeaderTriggerAttributes;
28
+ rootAttributes?: ListHeaderRootAttributes;
29
+ triggerAttributes?: ListHeaderTriggerAttributes;
30
30
  }
31
- type MenuHeaderIndicatorProps = {
31
+ type ListHeaderIndicatorProps = {
32
32
  count: number;
33
33
  countLabel: string;
34
34
  badge?: never;
@@ -37,7 +37,7 @@ type MenuHeaderIndicatorProps = {
37
37
  countLabel?: never;
38
38
  badge?: SafeHtml;
39
39
  };
40
- type MenuHeaderProps = MenuHeaderBaseProps & MenuHeaderIndicatorProps;
41
- declare function MenuHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes }: MenuHeaderProps): SafeHtml;
40
+ type ListHeaderProps = ListHeaderBaseProps & ListHeaderIndicatorProps;
41
+ declare function ListHeader({ label, count, countLabel, badge, action, actionLabel, actionIcon, actionDisabled, disabledReason, expanded, toggle, placeholder, rootAttributes, triggerAttributes }: ListHeaderProps): SafeHtml;
42
42
 
43
- export { MenuHeader, type MenuHeaderProps };
43
+ export { ListHeader, type ListHeaderProps };
@@ -0,0 +1,7 @@
1
+ export { ListHeader } from './chunk-W5EQ3HYQ.js';
2
+
3
+
4
+
5
+
6
+
7
+ //# sourceMappingURL=list-header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"list-header.js"}
@@ -1,6 +1,6 @@
1
1
  import { SafeHtml } from 'kerfjs';
2
2
 
3
- type MenuItemRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
3
+ type ListItemRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
4
4
  'data-component'?: never;
5
5
  'data-action'?: never;
6
6
  'data-item-id'?: never;
@@ -8,7 +8,7 @@ type MenuItemRootAttributes = Readonly<Record<`data-${string}`, string | undefin
8
8
  'data-multiline'?: never;
9
9
  'data-state'?: never;
10
10
  }>;
11
- interface MenuItemProps {
11
+ interface ListItemProps {
12
12
  label: string | SafeHtml;
13
13
  icon?: SafeHtml;
14
14
  trailing?: SafeHtml;
@@ -26,8 +26,8 @@ interface MenuItemProps {
26
26
  tabIndex?: number;
27
27
  /** Render the row as an unanimated loading skeleton, disabling its action. */
28
28
  placeholder?: boolean;
29
- rootAttributes?: MenuItemRootAttributes;
29
+ rootAttributes?: ListItemRootAttributes;
30
30
  }
31
- declare function MenuItem({ label, icon, trailing, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, rootAttributes }: MenuItemProps): SafeHtml;
31
+ declare function ListItem({ label, icon, trailing, selected, action, itemId, className, style, pressed, accessibleLabel, title, multiline, state, disabled, tabIndex, placeholder, rootAttributes }: ListItemProps): SafeHtml;
32
32
 
33
- export { MenuItem, type MenuItemProps };
33
+ export { ListItem, type ListItemProps };
@@ -0,0 +1,5 @@
1
+ export { ListItem } from './chunk-L4OKFDJY.js';
2
+
3
+
4
+
5
+ //# sourceMappingURL=list-item.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"list-item.js"}
@@ -1,4 +1,4 @@
1
- export { PanelHeader } from './chunk-BNQ2YDV4.js';
1
+ export { PanelHeader } from './chunk-2TS7ZSYX.js';
2
2
 
3
3
 
4
4
 
@@ -31,7 +31,7 @@ interface SplitViewProps {
31
31
  className?: string;
32
32
  }
33
33
  /**
34
- * A list-detail (master-detail) split. On roomy classes it shows both panes side
34
+ * A list-detail split. On roomy classes it shows both panes side
35
35
  * by side with an optional resizable separator; on compact classes it collapses
36
36
  * to a `NavStack` (list → detail). See `docs/23-app-layouts.md` §3.2. Compose the
37
37
  * resizable wiring with `wireResizableRegions` and the compact back with
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/split-view.tsx"],"names":[],"mappings":";;;;;;AA4CO,SAAS,UAAU,EAAE,EAAA,EAAI,OAAO,IAAA,EAAM,MAAA,EAAQ,UAAU,KAAA,EAAO,YAAA,GAAe,OAAO,SAAA,GAAY,EAAA,EAAI,cAAc,EAAA,EAAI,SAAA,GAAY,QAAQ,SAAA,EAAW,SAAA,GAAY,IAAG,EAAmB;AAC7L,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,KAAA,GAAQ,YAAA,GACV,CAAC,EAAE,GAAA,EAAK,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,IAAA,EAAK,EAAG,EAAE,GAAA,EAAK,QAAA,EAAU,KAAA,EAAO,WAAA,EAAa,OAAA,EAAS,MAAA,EAAQ,CAAA,GACzG,CAAC,EAAE,GAAA,EAAK,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,IAAA,EAAM,CAAA;AACrD,IAAA,uBAAO,GAAA,CAAC,SAAI,KAAA,EAAO,CAAA,uCAAA,EAA0C,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,EAAG,EAAA,EAAQ,gBAAA,EAAe,YAAA,EAAa,mBAAgB,SAAA,EACnI,QAAA,kBAAA,GAAA,CAAC,YAAS,EAAA,EAAI,CAAA,EAAG,EAAE,CAAA,MAAA,CAAA,EAAU,KAAA,EAAc,KAAA,EAAc,SAAA,EAAsB,CAAA,EACjF,CAAA;AAAA,EACF;AACA,EAAA,MAAM,QAAA,GAAW,SAAA,mBACb,GAAA,CAAC,eAAA,EAAA,EAAgB,IAAI,CAAA,EAAG,EAAE,CAAA,KAAA,CAAA,EAAS,KAAA,EAAO,aAAa,MAAA,EAAQ,IAAA,EAAM,SAAA,CAAU,IAAA,EAAM,KAAK,SAAA,CAAU,GAAA,EAAK,GAAA,EAAK,SAAA,CAAU,KACtH,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,+BAAA,EAAgC,mBAAe,IAAA,EAAE,QAAA,EAAA,IAAA,EAAK,CAAA,EACnE,CAAA,uBACC,KAAA,EAAA,EAAI,KAAA,EAAM,+BAAA,EAAgC,iBAAA,EAAe,MAAE,QAAA,EAAA,IAAA,EAAK,CAAA;AACrE,EAAA,uBAAO,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,CAAA,eAAA,EAAkB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,EAAG,EAAA,EAAQ,gBAAA,EAAe,YAAA,EAAa,iBAAA,EAAgB,OAAA,EAAQ,cAAY,KAAA,EAC9H,QAAA,EAAA;AAAA,IAAA,QAAA;AAAA,oBACD,GAAA,CAAC,SAAI,KAAA,EAAM,iCAAA,EAAkC,qBAAiB,IAAA,EAAC,YAAA,EAAY,WAAA,IAAe,MAAA,EAAY,QAAA,EAAA,MAAA,EAAO;AAAA,GAAA,EAC/G,CAAA;AACF","file":"split-view.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { NavStack } from './nav-stack.js';\nimport { ResizableRegion } from './resizable-region.js';\n\nexport interface SplitViewResizable {\n size: number;\n min: number;\n max: number;\n}\n\nexport interface SplitViewProps {\n id: string;\n label: string;\n /** The list (primary) pane. */\n list: SafeHtml;\n /** The detail (secondary) pane. */\n detail: SafeHtml;\n /**\n * Compact (\"one pane at a time\") classes — a handset or portrait tablet.\n * Derive from `deviceClass().value.compact`. When true the split collapses to\n * a `NavStack`: the list is the root and the detail is pushed over it.\n */\n compact?: boolean;\n /** In compact mode, whether the detail is currently pushed over the list. */\n detailActive?: boolean;\n /** Title/label for the list (compact NavStack root + region label). */\n listTitle?: string;\n /** Title/label for the detail (compact NavStack pushed view + region label). */\n detailTitle?: string;\n /** Back label for the compact NavStack (default \"Back\"). */\n backLabel?: string;\n /** A resizable separator on roomy classes (min/max px). Omit for a fixed split. */\n resizable?: SplitViewResizable;\n className?: string;\n}\n\n/**\n * A list-detail (master-detail) split. On roomy classes it shows both panes side\n * by side with an optional resizable separator; on compact classes it collapses\n * to a `NavStack` (list → detail). See `docs/23-app-layouts.md` §3.2. Compose the\n * resizable wiring with `wireResizableRegions` and the compact back with\n * `wireNavStack`.\n */\nexport function SplitView({ id, label, list, detail, compact = false, detailActive = false, listTitle = '', detailTitle = '', backLabel = 'Back', resizable, className = '' }: SplitViewProps) {\n if (compact) {\n const views = detailActive\n ? [{ key: 'list', title: listTitle, content: list }, { key: 'detail', title: detailTitle, content: detail }]\n : [{ key: 'list', title: listTitle, content: list }];\n return <div class={`kui-split-view kui-split-view--compact ${className}`.trim()} id={id} data-component=\"split-view\" data-split-mode=\"compact\">\n <NavStack id={`${id}-stack`} label={label} views={views} backLabel={backLabel} />\n </div>;\n }\n const listPane = resizable\n ? <ResizableRegion id={`${id}-list`} label={listTitle || 'List'} size={resizable.size} min={resizable.min} max={resizable.max}>\n <div class=\"kui-split-view__list kui-pane\" data-split-list>{list}</div>\n </ResizableRegion>\n : <div class=\"kui-split-view__list kui-pane\" data-split-list>{list}</div>;\n return <div class={`kui-split-view ${className}`.trim()} id={id} data-component=\"split-view\" data-split-mode=\"split\" aria-label={label}>\n {listPane}\n <div class=\"kui-split-view__detail kui-pane\" data-split-detail aria-label={detailTitle || undefined}>{detail}</div>\n </div>;\n}\n"]}
1
+ {"version":3,"sources":["../src/split-view.tsx"],"names":[],"mappings":";;;;;;AA4CO,SAAS,UAAU,EAAE,EAAA,EAAI,OAAO,IAAA,EAAM,MAAA,EAAQ,UAAU,KAAA,EAAO,YAAA,GAAe,OAAO,SAAA,GAAY,EAAA,EAAI,cAAc,EAAA,EAAI,SAAA,GAAY,QAAQ,SAAA,EAAW,SAAA,GAAY,IAAG,EAAmB;AAC7L,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,MAAM,KAAA,GAAQ,YAAA,GACV,CAAC,EAAE,GAAA,EAAK,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,IAAA,EAAK,EAAG,EAAE,GAAA,EAAK,QAAA,EAAU,KAAA,EAAO,WAAA,EAAa,OAAA,EAAS,MAAA,EAAQ,CAAA,GACzG,CAAC,EAAE,GAAA,EAAK,MAAA,EAAQ,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,IAAA,EAAM,CAAA;AACrD,IAAA,uBAAO,GAAA,CAAC,SAAI,KAAA,EAAO,CAAA,uCAAA,EAA0C,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,EAAG,EAAA,EAAQ,gBAAA,EAAe,YAAA,EAAa,mBAAgB,SAAA,EACnI,QAAA,kBAAA,GAAA,CAAC,YAAS,EAAA,EAAI,CAAA,EAAG,EAAE,CAAA,MAAA,CAAA,EAAU,KAAA,EAAc,KAAA,EAAc,SAAA,EAAsB,CAAA,EACjF,CAAA;AAAA,EACF;AACA,EAAA,MAAM,QAAA,GAAW,SAAA,mBACb,GAAA,CAAC,eAAA,EAAA,EAAgB,IAAI,CAAA,EAAG,EAAE,CAAA,KAAA,CAAA,EAAS,KAAA,EAAO,aAAa,MAAA,EAAQ,IAAA,EAAM,SAAA,CAAU,IAAA,EAAM,KAAK,SAAA,CAAU,GAAA,EAAK,GAAA,EAAK,SAAA,CAAU,KACtH,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAM,+BAAA,EAAgC,mBAAe,IAAA,EAAE,QAAA,EAAA,IAAA,EAAK,CAAA,EACnE,CAAA,uBACC,KAAA,EAAA,EAAI,KAAA,EAAM,+BAAA,EAAgC,iBAAA,EAAe,MAAE,QAAA,EAAA,IAAA,EAAK,CAAA;AACrE,EAAA,uBAAO,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,CAAA,eAAA,EAAkB,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK,EAAG,EAAA,EAAQ,gBAAA,EAAe,YAAA,EAAa,iBAAA,EAAgB,OAAA,EAAQ,cAAY,KAAA,EAC9H,QAAA,EAAA;AAAA,IAAA,QAAA;AAAA,oBACD,GAAA,CAAC,SAAI,KAAA,EAAM,iCAAA,EAAkC,qBAAiB,IAAA,EAAC,YAAA,EAAY,WAAA,IAAe,MAAA,EAAY,QAAA,EAAA,MAAA,EAAO;AAAA,GAAA,EAC/G,CAAA;AACF","file":"split-view.js","sourcesContent":["import type { SafeHtml } from 'kerfjs';\n\nimport { NavStack } from './nav-stack.js';\nimport { ResizableRegion } from './resizable-region.js';\n\nexport interface SplitViewResizable {\n size: number;\n min: number;\n max: number;\n}\n\nexport interface SplitViewProps {\n id: string;\n label: string;\n /** The list (primary) pane. */\n list: SafeHtml;\n /** The detail (secondary) pane. */\n detail: SafeHtml;\n /**\n * Compact (\"one pane at a time\") classes — a handset or portrait tablet.\n * Derive from `deviceClass().value.compact`. When true the split collapses to\n * a `NavStack`: the list is the root and the detail is pushed over it.\n */\n compact?: boolean;\n /** In compact mode, whether the detail is currently pushed over the list. */\n detailActive?: boolean;\n /** Title/label for the list (compact NavStack root + region label). */\n listTitle?: string;\n /** Title/label for the detail (compact NavStack pushed view + region label). */\n detailTitle?: string;\n /** Back label for the compact NavStack (default \"Back\"). */\n backLabel?: string;\n /** A resizable separator on roomy classes (min/max px). Omit for a fixed split. */\n resizable?: SplitViewResizable;\n className?: string;\n}\n\n/**\n * A list-detail split. On roomy classes it shows both panes side\n * by side with an optional resizable separator; on compact classes it collapses\n * to a `NavStack` (list → detail). See `docs/23-app-layouts.md` §3.2. Compose the\n * resizable wiring with `wireResizableRegions` and the compact back with\n * `wireNavStack`.\n */\nexport function SplitView({ id, label, list, detail, compact = false, detailActive = false, listTitle = '', detailTitle = '', backLabel = 'Back', resizable, className = '' }: SplitViewProps) {\n if (compact) {\n const views = detailActive\n ? [{ key: 'list', title: listTitle, content: list }, { key: 'detail', title: detailTitle, content: detail }]\n : [{ key: 'list', title: listTitle, content: list }];\n return <div class={`kui-split-view kui-split-view--compact ${className}`.trim()} id={id} data-component=\"split-view\" data-split-mode=\"compact\">\n <NavStack id={`${id}-stack`} label={label} views={views} backLabel={backLabel} />\n </div>;\n }\n const listPane = resizable\n ? <ResizableRegion id={`${id}-list`} label={listTitle || 'List'} size={resizable.size} min={resizable.min} max={resizable.max}>\n <div class=\"kui-split-view__list kui-pane\" data-split-list>{list}</div>\n </ResizableRegion>\n : <div class=\"kui-split-view__list kui-pane\" data-split-list>{list}</div>;\n return <div class={`kui-split-view ${className}`.trim()} id={id} data-component=\"split-view\" data-split-mode=\"split\" aria-label={label}>\n {listPane}\n <div class=\"kui-split-view__detail kui-pane\" data-split-detail aria-label={detailTitle || undefined}>{detail}</div>\n </div>;\n}\n"]}
@@ -0,0 +1,482 @@
1
+ .kui-catalog {
2
+ /* Collapsing the sidebar changes its grid column, which reflows the detail
3
+ pane. Animating that width relayouts the whole shell per frame, so the column
4
+ snaps instantly (one reflow) and the fixed-width sidebar slides via transform
5
+ only (composited, clipped by this shell's overflow). */
6
+ --kui-catalog-sidebar-width: 18rem;
7
+ --kui-catalog-content-measure: 70rem;
8
+
9
+ display: grid;
10
+ height: 100vh;
11
+ grid-template-columns: var(--kui-catalog-sidebar-width) minmax(0, 1fr);
12
+ overflow: hidden;
13
+ }
14
+
15
+ .kui-catalog[data-sidebar-collapsed="true"] {
16
+ grid-template-columns: 0 minmax(0, 1fr);
17
+ }
18
+
19
+ .kui-catalog__sidebar {
20
+ width: var(--kui-catalog-sidebar-width);
21
+ min-width: var(--kui-catalog-sidebar-width);
22
+ height: 100%;
23
+ padding: 0;
24
+ overflow: hidden;
25
+ justify-self: start;
26
+ border-inline-end: 1px solid var(--kui-color-border);
27
+ background: var(--kui-color-surface);
28
+ transition:
29
+ transform 200ms ease,
30
+ visibility 0s linear 0s;
31
+ }
32
+
33
+ .kui-catalog[data-sidebar-collapsed="true"] .kui-catalog__sidebar {
34
+ border-inline-end: 0;
35
+ /* Slide out first, then hide, so the panel animates offscreen and only then
36
+ leaves the tab order rather than snapping invisible mid-slide. */
37
+ transform: translateX(-100%);
38
+ visibility: hidden;
39
+ transition:
40
+ transform 200ms ease,
41
+ visibility 0s linear 200ms;
42
+ }
43
+
44
+ .kui-catalog__brand {
45
+ border-block-end: 1px solid var(--kui-color-border);
46
+ }
47
+
48
+ .kui-catalog__brand .kui-toolbar {
49
+ padding: var(--kui-space-xs);
50
+ }
51
+
52
+ /* Scoped to the brand so it outranks the ToolbarControlGroup's own gap rule; a
53
+ single-class selector would lose that specificity and drop the logo→title gap,
54
+ which then misaligns the subtitle (whose padding assumes the gap is present). */
55
+ .kui-catalog__brand .kui-catalog__identity {
56
+ min-width: 0;
57
+ gap: var(--kui-layout-control-gap, 0.5rem);
58
+ }
59
+
60
+ .kui-catalog__identity h1,
61
+ .kui-catalog__subtitle {
62
+ margin: 0;
63
+ }
64
+
65
+ .kui-catalog__identity h1 {
66
+ overflow: hidden;
67
+ font-size: var(--kui-font-m);
68
+ letter-spacing: 0.07em;
69
+ line-height: 1.15;
70
+ text-overflow: ellipsis;
71
+ text-transform: uppercase;
72
+ white-space: nowrap;
73
+ }
74
+
75
+ .kui-catalog__subtitle {
76
+ overflow: hidden;
77
+ margin-block-start: calc(-1 * var(--kui-space-xs));
78
+ padding-block-end: var(--kui-space-xs);
79
+ padding-inline: calc(
80
+ var(--kui-space-xs) + 0.125rem + 2.125rem +
81
+ var(--kui-layout-control-gap, 0.5rem)
82
+ )
83
+ var(--kui-space-xs);
84
+ color: var(--kui-color-text-quiet);
85
+ font-size: var(--kui-font-xs);
86
+ text-overflow: ellipsis;
87
+ white-space: nowrap;
88
+ }
89
+
90
+ .kui-catalog__mark {
91
+ display: block;
92
+ width: 2.125rem;
93
+ height: 2.125rem;
94
+ flex: 0 0 auto;
95
+ }
96
+
97
+ .kui-catalog__group {
98
+ margin-block-start: var(--kui-space-m);
99
+ }
100
+
101
+ .kui-catalog__group:first-child {
102
+ margin-block-start: var(--kui-space-2xs);
103
+ }
104
+
105
+ .kui-catalog__items {
106
+ display: grid;
107
+ gap: var(--kui-space-2xs);
108
+ }
109
+
110
+ .kui-catalog__items .kui-list-item {
111
+ grid-template-columns: minmax(0, 1fr);
112
+ }
113
+
114
+ .kui-catalog__group--secondary {
115
+ padding-block-start: var(--kui-space-s);
116
+ border-block-start: 1px solid var(--kui-color-border-quiet);
117
+ }
118
+
119
+ .kui-catalog__secondary {
120
+ display: grid;
121
+ padding-block: var(--kui-space-xs);
122
+ gap: var(--kui-space-s);
123
+ }
124
+
125
+ .kui-catalog__secondary-group {
126
+ display: grid;
127
+ min-width: 0;
128
+ gap: var(--kui-space-2xs);
129
+ }
130
+
131
+ .kui-catalog__secondary-heading {
132
+ margin: 0;
133
+ padding: 0.1rem 0.55rem;
134
+ color: var(--kui-color-text-quiet);
135
+ font-size: var(--kui-font-2xs);
136
+ font-weight: 650;
137
+ letter-spacing: 0.04em;
138
+ text-transform: uppercase;
139
+ }
140
+
141
+ .kui-catalog__secondary-group .kui-list-item {
142
+ padding-inline-start: 1rem;
143
+ }
144
+
145
+ /* Labeled, aligned preview examples (CatalogExample / CatalogExampleStack). */
146
+ .kui-catalog-example-stack {
147
+ display: flex;
148
+ flex-direction: column;
149
+ gap: 1rem;
150
+ }
151
+
152
+ .kui-catalog-example {
153
+ display: flex;
154
+ width: 100%;
155
+ min-width: 0;
156
+ max-width: 100%;
157
+ flex-direction: column;
158
+ align-items: flex-start;
159
+ gap: 0.5rem;
160
+ }
161
+
162
+ /* A ListHeader used as an example label shrinks to its title; one that is itself
163
+ the demoed specimen keeps its default full width. */
164
+ .kui-catalog-example > .kui-list-header:first-child {
165
+ width: auto;
166
+ }
167
+
168
+ .kui-catalog-example__note {
169
+ /* Align the note text with the label above and a content-item specimen below:
170
+ all land at 8px margin + 1px border + 8px padding from the example's edge. */
171
+ margin: 0;
172
+ padding-inline: calc(
173
+ var(--kui-layout-inline-margin, 0.5rem) + 1px +
174
+ var(--kui-layout-item-padding, 0.5rem)
175
+ );
176
+ color: var(--kui-color-text-quiet);
177
+ font-size: var(--kui-font-s);
178
+ }
179
+
180
+ /* The inset is published as --kui-catalog-example-align and applied as the
181
+ specimen's leading margin, so a debug overlay can exclude it from the measured
182
+ margin and never paint the alignment device as intrinsic geometry. */
183
+ .kui-catalog-example[data-align="glyph"]
184
+ > :not(.kui-list-header):not(.kui-catalog-example__note) {
185
+ --kui-catalog-example-align: 1rem;
186
+ margin-inline-start: var(--kui-catalog-example-align);
187
+ }
188
+
189
+ .kui-catalog-example[data-align="inline-control"]
190
+ > :not(.kui-list-header):not(.kui-catalog-example__note) {
191
+ --kui-catalog-example-align: 0.5rem;
192
+ margin-inline-start: var(--kui-catalog-example-align);
193
+ }
194
+
195
+ .kui-catalog__detail {
196
+ min-width: 0;
197
+ height: 100%;
198
+ grid-column: 2;
199
+ }
200
+
201
+ .kui-catalog__header {
202
+ border-block-end: 1px solid var(--kui-color-border);
203
+ background: var(--kui-color-surface);
204
+ }
205
+
206
+ .kui-catalog__header .kui-toolbar {
207
+ padding-block-end: 0;
208
+ }
209
+
210
+ .kui-catalog__title {
211
+ min-width: 0;
212
+ }
213
+
214
+ .kui-catalog__title h2,
215
+ .kui-catalog__description {
216
+ margin: 0;
217
+ }
218
+
219
+ .kui-catalog__title h2 {
220
+ overflow: hidden;
221
+ font-size: var(--kui-font-l);
222
+ letter-spacing: -0.025em;
223
+ line-height: 1.2;
224
+ text-overflow: ellipsis;
225
+ white-space: nowrap;
226
+ }
227
+
228
+ .kui-catalog__description {
229
+ max-width: var(--kui-catalog-content-measure, 70rem);
230
+ margin-block: 0 var(--kui-space-xs);
231
+ color: var(--kui-color-text-quiet);
232
+ background: transparent;
233
+ line-height: 1.45;
234
+ }
235
+
236
+ .kui-catalog__header-actions {
237
+ display: flex;
238
+ min-width: 0;
239
+ align-items: center;
240
+ gap: var(--kui-layout-control-gap);
241
+ }
242
+
243
+ .kui-catalog__settings {
244
+ box-shadow: none;
245
+ }
246
+
247
+ .kui-catalog__settings > button {
248
+ display: inline-flex;
249
+ align-items: center;
250
+ gap: 0.375rem;
251
+ }
252
+
253
+ .kui-catalog__settings > button:focus-visible {
254
+ outline: var(--kui-focus-ring);
255
+ outline-offset: 2px;
256
+ }
257
+
258
+ .kui-catalog__settings svg {
259
+ width: 1rem;
260
+ height: 1rem;
261
+ }
262
+
263
+ .kui-catalog__stage {
264
+ width: 100%;
265
+ background-color: var(--kui-color-surface-lowered);
266
+ background-image:
267
+ linear-gradient(
268
+ 45deg,
269
+ color-mix(in srgb, var(--kui-color-border-quiet) 42%, transparent) 25%,
270
+ transparent 25%
271
+ ),
272
+ linear-gradient(
273
+ -45deg,
274
+ color-mix(in srgb, var(--kui-color-border-quiet) 42%, transparent) 25%,
275
+ transparent 25%
276
+ ),
277
+ linear-gradient(
278
+ 45deg,
279
+ transparent 75%,
280
+ color-mix(in srgb, var(--kui-color-border-quiet) 42%, transparent) 75%
281
+ ),
282
+ linear-gradient(
283
+ -45deg,
284
+ transparent 75%,
285
+ color-mix(in srgb, var(--kui-color-border-quiet) 42%, transparent) 75%
286
+ );
287
+ background-position:
288
+ 0 0,
289
+ 0 0.75rem,
290
+ 0.75rem -0.75rem,
291
+ -0.75rem 0;
292
+ background-size: 1.5rem 1.5rem;
293
+ }
294
+
295
+ .kui-catalog__canvas {
296
+ position: relative;
297
+ display: grid;
298
+ width: min(100%, var(--kui-catalog-content-measure, 70rem));
299
+ min-width: 0;
300
+ min-height: 100%;
301
+ margin-inline: auto;
302
+ padding: var(--kui-layout-content-gap);
303
+ box-sizing: border-box;
304
+ place-items: center;
305
+ }
306
+
307
+ .kui-catalog__footer {
308
+ --kui-toolbar-gap: 0.25rem;
309
+ --kui-toolbar-group-size: 2rem;
310
+
311
+ border-block-start: 1px solid var(--kui-color-border);
312
+ background: color-mix(
313
+ in srgb,
314
+ var(--kui-color-surface-lowered) 72%,
315
+ var(--kui-color-surface)
316
+ );
317
+ }
318
+
319
+ .kui-catalog__status {
320
+ display: flex;
321
+ min-height: 1.625rem;
322
+ padding: 0.375rem 0.75rem 0;
323
+ color: var(--kui-color-text-quiet);
324
+ align-items: center;
325
+ justify-content: space-between;
326
+ gap: var(--kui-layout-control-gap);
327
+ font-size: var(--kui-font-xs);
328
+ }
329
+
330
+ .kui-catalog__footer .kui-toolbar {
331
+ min-height: 2.5rem;
332
+ padding: 0.25rem 0.5rem 0.5rem;
333
+ }
334
+
335
+ .kui-catalog__footer .kui-toolbar-control-group {
336
+ height: 2rem;
337
+ min-width: 2rem;
338
+ }
339
+
340
+ .kui-catalog__resources {
341
+ min-width: 0;
342
+ }
343
+
344
+ .kui-catalog__resource-group {
345
+ max-width: 100%;
346
+ overflow-x: auto;
347
+ }
348
+
349
+ .kui-catalog__resource {
350
+ position: relative;
351
+ display: inline-flex;
352
+ flex: 0 0 auto;
353
+ min-width: 0;
354
+ height: 1.875rem;
355
+ padding: 0 var(--kui-space-xs);
356
+ border: 1px solid transparent;
357
+ border-radius: var(--kui-radius-pill);
358
+ color: var(--kui-color-text);
359
+ background: transparent;
360
+ text-decoration: none;
361
+ white-space: nowrap;
362
+ align-items: center;
363
+ gap: var(--kui-space-2xs);
364
+ }
365
+
366
+ .kui-catalog__resource:hover {
367
+ border-color: var(--kui-color-border);
368
+ background: var(--kui-color-surface);
369
+ }
370
+
371
+ .kui-catalog__resource:focus-visible {
372
+ outline: var(--kui-focus-ring);
373
+ outline-offset: 2px;
374
+ }
375
+
376
+ .kui-catalog__resource svg {
377
+ width: 0.875rem;
378
+ height: 0.875rem;
379
+ }
380
+
381
+ /* The optional monospace detail is a visually-hidden accessible affordance. */
382
+ .kui-catalog__resource code {
383
+ position: absolute;
384
+ inset-block-start: 0;
385
+ inset-inline-start: 0;
386
+ width: 1px;
387
+ height: 1px;
388
+ padding: 0;
389
+ overflow: hidden;
390
+ border: 0;
391
+ clip-path: inset(50%);
392
+ white-space: nowrap;
393
+ }
394
+
395
+ .kui-catalog__related-group {
396
+ padding: 0;
397
+ }
398
+
399
+ .kui-catalog__related-select {
400
+ width: min(16.25rem, 100%);
401
+ }
402
+
403
+ @media (max-width: 64rem) {
404
+ .kui-catalog__settings button span {
405
+ position: absolute;
406
+ width: 1px;
407
+ height: 1px;
408
+ overflow: hidden;
409
+ clip-path: inset(50%);
410
+ white-space: nowrap;
411
+ }
412
+ }
413
+
414
+ @media (max-width: 52rem) {
415
+ .kui-catalog {
416
+ height: auto;
417
+ min-height: 100vh;
418
+ grid-template-columns: 1fr;
419
+ }
420
+ .kui-catalog[data-sidebar-collapsed="true"] {
421
+ grid-template-columns: 1fr;
422
+ }
423
+ .kui-catalog[data-sidebar-collapsed="true"] .kui-catalog__sidebar {
424
+ display: none;
425
+ }
426
+ .kui-catalog__sidebar {
427
+ width: 100%;
428
+ min-width: 0;
429
+ height: auto;
430
+ justify-self: stretch;
431
+ /* Narrow layout collapses via display:none, not the desktop slide. */
432
+ transform: none;
433
+ border-inline-end: 0;
434
+ border-block-end: 1px solid var(--kui-color-border);
435
+ }
436
+ .kui-catalog__detail {
437
+ height: auto;
438
+ min-height: auto;
439
+ grid-column: 1;
440
+ }
441
+ .kui-catalog__sidebar nav {
442
+ display: grid;
443
+ grid-template-columns: repeat(2, minmax(0, 1fr));
444
+ align-items: start;
445
+ gap: 0.5rem 1rem;
446
+ }
447
+ .kui-catalog__group,
448
+ .kui-catalog__group:first-child {
449
+ min-width: 0;
450
+ margin: 0;
451
+ }
452
+ .kui-catalog__stage {
453
+ min-height: 24rem;
454
+ }
455
+ .kui-catalog__canvas {
456
+ min-height: 16rem;
457
+ }
458
+ .kui-catalog__status {
459
+ align-items: flex-start;
460
+ flex-direction: column;
461
+ gap: 0.25rem;
462
+ }
463
+ /* Stack the footer's resource links above the related selector at narrow widths
464
+ instead of keeping them in one row. */
465
+ .kui-catalog__footer .kui-toolbar[data-has-center="false"] {
466
+ grid-template-columns: minmax(0, 1fr);
467
+ }
468
+ .kui-catalog__footer .kui-toolbar__leading,
469
+ .kui-catalog__resource-group {
470
+ width: 100%;
471
+ }
472
+ .kui-catalog__footer .kui-toolbar__trailing {
473
+ width: 100%;
474
+ justify-content: flex-start;
475
+ }
476
+ }
477
+
478
+ @media (prefers-reduced-motion: reduce) {
479
+ .kui-catalog__sidebar {
480
+ transition-duration: 0s;
481
+ }
482
+ }