@kerfjs/ui 4.4.1 → 5.0.0-beta.10
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.
- package/README.md +240 -10
- package/ai/component-catalog.json +4766 -0
- package/ai/component-catalog.schema.json +79 -0
- package/ai/public-api-signatures-v1.md +1327 -0
- package/ai/skill.md +73 -11
- package/ai/webawesome-jsx-signatures-v1.md +106 -0
- package/dist/app-tab.d.ts +14 -2
- package/dist/app-tab.js +3 -1
- package/dist/browser/app-tab.js +3 -2
- package/dist/browser/disclosure-arrow.js +4 -0
- package/dist/browser/empty-state.js +3 -3
- package/dist/browser/list-action-row.js +4 -0
- package/dist/browser/list-header.js +6 -0
- package/dist/browser/list-item.js +4 -0
- package/dist/browser/loading-spinner.js +2 -2
- package/dist/browser/lucide-icon.js +2 -2
- package/dist/browser/panel-header.js +7 -0
- package/dist/browser/resizable-region.js +2 -2
- package/dist/browser/segmented-control.js +3 -2
- package/dist/browser/select.js +4 -3
- package/dist/browser/skeleton.js +3 -0
- package/dist/browser/state-banner.js +3 -2
- package/dist/browser/tab-bar.js +2 -2
- package/dist/browser/token-search-field.js +4 -0
- package/dist/browser/toolbar-control-group.js +2 -2
- package/dist/browser/toolbar-text.js +3 -2
- package/dist/browser/toolbar.js +2 -2
- package/dist/browser/value-table.js +3 -2
- package/dist/catalog.d.ts +119 -0
- package/dist/catalog.js +143 -0
- package/dist/catalog.js.map +1 -0
- package/dist/chunk-2Z5TWHKQ.js +34 -0
- package/dist/chunk-2Z5TWHKQ.js.map +1 -0
- package/dist/chunk-37T3VNIZ.js +19 -0
- package/dist/chunk-37T3VNIZ.js.map +1 -0
- package/dist/chunk-56RJXPHG.js +43 -0
- package/dist/chunk-56RJXPHG.js.map +1 -0
- package/dist/chunk-7BR3DO2A.js +31 -0
- package/dist/chunk-7BR3DO2A.js.map +1 -0
- package/dist/chunk-7JB7VPRI.js +34 -0
- package/dist/chunk-7JB7VPRI.js.map +1 -0
- package/dist/chunk-7LDCTU7D.js +134 -0
- package/dist/chunk-7LDCTU7D.js.map +1 -0
- package/dist/{chunk-4HHCYSGT.js → chunk-AQNKJRFH.js} +2 -2
- package/dist/{chunk-4HHCYSGT.js.map → chunk-AQNKJRFH.js.map} +1 -1
- package/dist/{chunk-MBR3TYNL.js → chunk-AVZHJN6C.js} +3 -3
- package/dist/chunk-AVZHJN6C.js.map +1 -0
- package/dist/chunk-B24C4A6T.js +22 -0
- package/dist/chunk-B24C4A6T.js.map +1 -0
- package/dist/chunk-BLK3UEEI.js +23 -0
- package/dist/chunk-BLK3UEEI.js.map +1 -0
- package/dist/chunk-CVZYNJJ5.js +265 -0
- package/dist/chunk-CVZYNJJ5.js.map +1 -0
- package/dist/chunk-I7INRJO4.js +17 -0
- package/dist/chunk-I7INRJO4.js.map +1 -0
- package/dist/chunk-L4OKFDJY.js +24 -0
- package/dist/chunk-L4OKFDJY.js.map +1 -0
- package/dist/chunk-LC3YSAZC.js +39 -0
- package/dist/chunk-LC3YSAZC.js.map +1 -0
- package/dist/{chunk-3O7ZEUW2.js → chunk-M5JRI6MN.js} +3 -3
- package/dist/chunk-M5JRI6MN.js.map +1 -0
- package/dist/chunk-RUNWEPR4.js +10 -0
- package/dist/chunk-RUNWEPR4.js.map +1 -0
- package/dist/chunk-W5EQ3HYQ.js +42 -0
- package/dist/chunk-W5EQ3HYQ.js.map +1 -0
- package/dist/{chunk-MQIZAD4A.js → chunk-XXMOARTH.js} +5 -3
- package/dist/chunk-XXMOARTH.js.map +1 -0
- package/dist/chunk-Y5MRDTLC.js +20 -0
- package/dist/chunk-Y5MRDTLC.js.map +1 -0
- package/dist/{chunk-TRNBHPNW.js → chunk-YGN7OMLT.js} +11 -8
- package/dist/chunk-YGN7OMLT.js.map +1 -0
- package/dist/collapsible-panel.d.ts +66 -0
- package/dist/collapsible-panel.js +22 -0
- package/dist/collapsible-panel.js.map +1 -0
- package/dist/device-class.d.ts +62 -0
- package/dist/device-class.js +75 -0
- package/dist/device-class.js.map +1 -0
- package/dist/disclosure-arrow.d.ts +14 -0
- package/dist/disclosure-arrow.js +4 -0
- package/dist/disclosure-arrow.js.map +1 -0
- package/dist/index.d.ts +10 -6
- package/dist/index.js +21 -16
- package/dist/list-action-row.d.ts +52 -0
- package/dist/list-action-row.js +5 -0
- package/dist/list-action-row.js.map +1 -0
- package/dist/list-header.d.ts +43 -0
- package/dist/list-header.js +7 -0
- package/dist/list-header.js.map +1 -0
- package/dist/list-item.d.ts +33 -0
- package/dist/list-item.js +5 -0
- package/dist/list-item.js.map +1 -0
- package/dist/nav-stack.d.ts +38 -0
- package/dist/nav-stack.js +5 -0
- package/dist/nav-stack.js.map +1 -0
- package/dist/panel-header.d.ts +36 -0
- package/dist/panel-header.js +7 -0
- package/dist/panel-header.js.map +1 -0
- package/dist/resizable-region.d.ts +3 -1
- package/dist/resizable-region.js +1 -1
- package/dist/segmented-control.d.ts +3 -1
- package/dist/segmented-control.js +2 -1
- package/dist/select-register.d.ts +5 -1
- package/dist/select-register.js +3 -0
- package/dist/select-register.js.map +1 -1
- package/dist/select.d.ts +5 -2
- package/dist/select.js +2 -1
- package/dist/skeleton.d.ts +24 -0
- package/dist/skeleton.js +3 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/split-view.d.ts +42 -0
- package/dist/split-view.js +21 -0
- package/dist/split-view.js.map +1 -0
- package/dist/state-banner.d.ts +3 -1
- package/dist/state-banner.js +2 -1
- package/dist/styles/app-tab.css +152 -0
- package/dist/styles/catalog.css +498 -0
- package/dist/styles/collapsible-panel.css +146 -0
- package/dist/styles/disclosure-arrow.css +23 -0
- package/dist/styles/empty-state.css +47 -0
- package/dist/styles/foundation.css +347 -0
- package/dist/styles/layout.css +73 -0
- package/dist/styles/list-action-row.css +160 -0
- package/dist/styles/list-header.css +160 -0
- package/dist/styles/list-item.css +123 -0
- package/dist/styles/loading-spinner.css +21 -0
- package/dist/styles/lucide-icon.css +6 -0
- package/dist/styles/nav-stack.css +111 -0
- package/dist/styles/panel-header.css +64 -0
- package/dist/styles/resizable-region.css +111 -0
- package/{src → dist/styles}/segmented-control.css +62 -31
- package/dist/styles/select.css +138 -0
- package/dist/styles/skeleton.css +31 -0
- package/dist/styles/split-view.css +35 -0
- package/dist/styles/state-banner.css +173 -0
- package/dist/styles/styles.css +23 -0
- package/dist/styles/tab-bar.css +45 -0
- package/dist/styles/tab-scaffold.css +84 -0
- package/dist/styles/token-search-field.css +289 -0
- package/dist/styles/toolbar-control-group.css +211 -0
- package/dist/styles/toolbar-text.css +40 -0
- package/dist/styles/toolbar.css +61 -0
- package/dist/styles/value-table.css +75 -0
- package/{src → dist/styles}/webawesome.css +119 -29
- package/dist/styles/workbench.css +94 -0
- package/dist/tab-bar.d.ts +8 -1
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +30 -0
- package/dist/tab-scaffold.js +16 -0
- package/dist/tab-scaffold.js.map +1 -0
- package/dist/token-search-field.d.ts +43 -0
- package/dist/token-search-field.js +4 -0
- package/dist/token-search-field.js.map +1 -0
- package/dist/toolbar-text.d.ts +15 -3
- package/dist/toolbar-text.js +2 -1
- package/dist/value-table.d.ts +12 -2
- package/dist/value-table.js +2 -1
- package/dist/webawesome.d.ts +100 -0
- package/dist/webawesome.js +3 -0
- package/dist/webawesome.js.map +1 -0
- package/dist/wire-catalog.d.ts +26 -0
- package/dist/wire-catalog.js +48 -0
- package/dist/wire-catalog.js.map +1 -0
- package/dist/wire-nav-stack.d.ts +15 -0
- package/dist/wire-nav-stack.js +88 -0
- package/dist/wire-nav-stack.js.map +1 -0
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-sidebar.d.ts +43 -0
- package/dist/wire-sidebar.js +117 -0
- package/dist/wire-sidebar.js.map +1 -0
- package/dist/wire-tab-bars.d.ts +15 -2
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-tab-scaffold.d.ts +11 -0
- package/dist/wire-tab-scaffold.js +16 -0
- package/dist/wire-tab-scaffold.js.map +1 -0
- package/dist/wire-token-search-fields.d.ts +97 -0
- package/dist/wire-token-search-fields.js +5 -0
- package/dist/wire-token-search-fields.js.map +1 -0
- package/dist/workbench.d.ts +33 -0
- package/dist/workbench.js +17 -0
- package/dist/workbench.js.map +1 -0
- package/docs/accessibility.md +101 -3
- package/docs/app-layouts.md +61 -0
- package/docs/catalog.md +155 -0
- package/docs/collapsible-panel.md +102 -0
- package/docs/component-contract.md +169 -4
- package/docs/component-selection.md +185 -0
- package/docs/design/templates/panel-header/icon-actions-dark.svg +1 -0
- package/docs/design/templates/panel-header/icon-actions.svg +1 -0
- package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +1 -0
- package/docs/design/templates/panel-header/icon-summary-actions.svg +1 -0
- package/docs/design/templates/panel-header/no-icon-dark.svg +1 -0
- package/docs/design/templates/panel-header/no-icon.svg +1 -0
- package/docs/design/templates/panel-header/page-heading-dark.svg +1 -0
- package/docs/design/templates/panel-header/page-heading.svg +1 -0
- package/docs/design/templates/panel-header/title-only-dark.svg +1 -0
- package/docs/design/templates/panel-header/title-only.svg +1 -0
- package/docs/design/templates/panel-header-dark.svg +13 -0
- package/docs/design/templates/panel-header.svg +13 -0
- package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
- package/docs/design/templates/toolbar-control-group-dark.svg +11 -0
- package/docs/design/templates/toolbar-control-group.svg +11 -0
- package/docs/design/templates.md +83 -0
- package/docs/design-philosophy.md +61 -1
- package/docs/device-class.md +54 -0
- package/docs/examples/command-palette-adapter.tsx +70 -0
- package/docs/layout.md +131 -0
- package/docs/nav-stack.md +47 -0
- package/docs/recipes.md +167 -0
- package/docs/split-view.md +49 -0
- package/docs/tab-scaffold.md +41 -0
- package/docs/ux-demo.md +23 -9
- package/docs/webawesome-theme.md +15 -0
- package/docs/workbench.md +47 -0
- package/llms.txt +123 -14
- package/package.json +139 -44
- package/ux-demo/recipes/app-shell.tsx +32 -0
- package/ux-demo/recipes/collapsible-sidebar.tsx +100 -0
- package/ux-demo/recipes/compact-toolbar.tsx +43 -0
- package/ux-demo/recipes/composer-form.tsx +63 -0
- package/ux-demo/recipes/list-detail-dialog.tsx +45 -0
- package/ux-demo/recipes/list-workspace-states.tsx +26 -0
- package/ux-demo/recipes/loaders.ts +33 -0
- package/ux-demo/recipes/loading-inspector.tsx +81 -0
- package/ux-demo/recipes/mount-recipe.ts +43 -0
- package/ux-demo/recipes/navigation-sidebar.tsx +26 -0
- package/ux-demo/recipes/navigation-stack.tsx +76 -0
- package/ux-demo/recipes/recipes.css +448 -0
- package/ux-demo/recipes/types.ts +17 -0
- package/ux-demo/recipes/workspace-header.tsx +12 -0
- package/dist/browser/dialog-header.js +0 -3
- package/dist/browser/menu-header.js +0 -3
- package/dist/browser/menu-item.js +0 -3
- package/dist/browser/page-header.js +0 -3
- package/dist/chunk-2PES33HS.js +0 -13
- package/dist/chunk-2PES33HS.js.map +0 -1
- package/dist/chunk-3O7ZEUW2.js.map +0 -1
- package/dist/chunk-723YNJBH.js +0 -17
- package/dist/chunk-723YNJBH.js.map +0 -1
- package/dist/chunk-GY5WH7TO.js +0 -10
- package/dist/chunk-GY5WH7TO.js.map +0 -1
- package/dist/chunk-MBR3TYNL.js.map +0 -1
- package/dist/chunk-MQIZAD4A.js.map +0 -1
- package/dist/chunk-NHXX4JI5.js +0 -28
- package/dist/chunk-NHXX4JI5.js.map +0 -1
- package/dist/chunk-OPCJRCYO.js +0 -19
- package/dist/chunk-OPCJRCYO.js.map +0 -1
- package/dist/chunk-T5XNMVOI.js +0 -24
- package/dist/chunk-T5XNMVOI.js.map +0 -1
- package/dist/chunk-TRNBHPNW.js.map +0 -1
- package/dist/chunk-VHW5YNIB.js +0 -17
- package/dist/chunk-VHW5YNIB.js.map +0 -1
- package/dist/chunk-VKH2JLDZ.js +0 -10
- package/dist/chunk-VKH2JLDZ.js.map +0 -1
- package/dist/chunk-WY4CAEPG.js +0 -14
- package/dist/chunk-WY4CAEPG.js.map +0 -1
- package/dist/dialog-header.d.ts +0 -14
- package/dist/dialog-header.js +0 -3
- package/dist/dialog-header.js.map +0 -1
- package/dist/menu-header.d.ts +0 -15
- package/dist/menu-header.js +0 -3
- package/dist/menu-header.js.map +0 -1
- package/dist/menu-item.d.ts +0 -22
- package/dist/menu-item.js +0 -3
- package/dist/menu-item.js.map +0 -1
- package/dist/page-header.d.ts +0 -9
- package/dist/page-header.js +0 -3
- package/dist/page-header.js.map +0 -1
- package/src/app-tab.css +0 -20
- package/src/dialog-header.css +0 -9
- package/src/empty-state.css +0 -6
- package/src/foundation.css +0 -106
- package/src/loading-spinner.css +0 -4
- package/src/lucide-icon.css +0 -1
- package/src/menu-header.css +0 -13
- package/src/menu-item.css +0 -12
- package/src/page-header.css +0 -3
- package/src/resizable-region.css +0 -19
- package/src/select.css +0 -18
- package/src/state-banner.css +0 -31
- package/src/styles.css +0 -18
- package/src/tab-bar.css +0 -4
- package/src/toolbar-control-group.css +0 -25
- package/src/toolbar-text.css +0 -4
- package/src/toolbar.css +0 -9
- package/src/value-table.css +0 -5
package/docs/layout.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Pane and content layout
|
|
2
|
+
|
|
3
|
+
Import `@kerfjs/ui/layout.css` for the shared geometry used by sidebars, main
|
|
4
|
+
areas, inspectors, and dialogs. The vocabulary deliberately describes structure
|
|
5
|
+
rather than location: the same pane can be placed anywhere.
|
|
6
|
+
|
|
7
|
+
## Anatomy
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
<aside class="kui-pane">
|
|
11
|
+
<div class="kui-pane__toolbar"><Toolbar label="Workspace" ... /></div>
|
|
12
|
+
<div class="kui-pane__content kui-content">
|
|
13
|
+
<section>...</section>
|
|
14
|
+
<section>...</section>
|
|
15
|
+
</div>
|
|
16
|
+
<footer class="kui-pane__footer"><Toolbar label="Actions" ... /></footer>
|
|
17
|
+
</aside>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`.kui-pane` has no padding. It reserves rows for an optional toolbar, one
|
|
21
|
+
scrolling content area, and an optional footer. A main area or dialog often
|
|
22
|
+
omits the footer; a navigation pane commonly uses all three. Fixed chrome stays
|
|
23
|
+
outside `.kui-pane__content`, which is the pane's scroll owner.
|
|
24
|
+
|
|
25
|
+
`.kui-content` is a vertical stack with a 24px gap between major children.
|
|
26
|
+
Sections may contain adjacent `ListItem` rows without adding another major gap.
|
|
27
|
+
Ordinary surface-like children use `.kui-content-item` and own their complete
|
|
28
|
+
geometry:
|
|
29
|
+
|
|
30
|
+
- 8px inline margin from the pane edge
|
|
31
|
+
- 1px border, transparent by default
|
|
32
|
+
- 8px internal padding and 8px internal gap
|
|
33
|
+
- `calc(1px + remify(11px))`, or 12px, rounded corners
|
|
34
|
+
|
|
35
|
+
Use `.kui-content-item--pill` for the 22px pill radius expressed as
|
|
36
|
+
`calc(1px + remify(21px))`. Consumers can make a content item visible without
|
|
37
|
+
changing its geometry by setting `--kui-content-item-border` and
|
|
38
|
+
`--kui-content-item-background`.
|
|
39
|
+
|
|
40
|
+
A visible parent surface does not make every child another visible card. The
|
|
41
|
+
composer recipe keeps its form as the single surface, uses `PanelHeader` for
|
|
42
|
+
its task hierarchy, and places field and action control edges on the shared 8px
|
|
43
|
+
inline gutter instead of nesting them inside another padded content item. A
|
|
44
|
+
conditional `StateBanner` remains visibly distinct because it communicates
|
|
45
|
+
semantic status.
|
|
46
|
+
|
|
47
|
+
## Public roles and tokens
|
|
48
|
+
|
|
49
|
+
| Need | Class | Token / default |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| Unpadded toolbar/content/footer structure | `.kui-pane` | — |
|
|
52
|
+
| Scrolling pane content | `.kui-pane__content` | — |
|
|
53
|
+
| Major vertical rhythm | `.kui-content` | `--kui-layout-content-gap: 24px` |
|
|
54
|
+
| Self-contained child geometry | `.kui-content-item` | 8px margin + 1px border + 8px padding |
|
|
55
|
+
| Pill child | `.kui-content-item--pill` | `--kui-layout-pill-radius: 22px` |
|
|
56
|
+
| Related controls | `.kui-control-cluster` | `--kui-layout-control-gap: 8px` |
|
|
57
|
+
| Inline metadata | `.kui-inline-metadata` | `--kui-layout-metadata-gap: 4px` |
|
|
58
|
+
| Explicit scroll owner outside a pane | `.kui-scroll-owner` | `overflow: auto` |
|
|
59
|
+
|
|
60
|
+
The component layer applies the same contract to `Toolbar`, `ListHeader`,
|
|
61
|
+
`ListItem`, `PanelHeader`, `StateBanner`, `ValueTable`,
|
|
62
|
+
`ValueTableRow`, tabs, and form controls. A value-table row separator starts at
|
|
63
|
+
the row's 8px content inset, or at 40px when the row contains its 24px leading
|
|
64
|
+
icon and 8px gap, and always ends 8px from the right edge. Each value-table row
|
|
65
|
+
also keeps 8px of root-scaled block padding independently of its semantic inline
|
|
66
|
+
inset. Most interactive rows and toolbar groups are 44px tall.
|
|
67
|
+
Toolbar groups reserve a real 1px outer border around a 42px inner area, even
|
|
68
|
+
when their border and background are transparent.
|
|
69
|
+
`PanelHeader` is a plain top `Toolbar` used as a panel, dialog, or page heading
|
|
70
|
+
and overrides no Toolbar styles. The leading zone holds the optional icon (a
|
|
71
|
+
normal bordered `ToolbarControlGroup` given a brand fill with a matching border,
|
|
72
|
+
a 22px glyph) and the title as extra-large `ToolbarText`; the app's trailing
|
|
73
|
+
controls go straight into the trailing zone, and the icon group is omitted when
|
|
74
|
+
no icon is passed. The optional subtitle is a separate row below the toolbar, its
|
|
75
|
+
text left-aligned with the title text.
|
|
76
|
+
|
|
77
|
+
## Spacing scale
|
|
78
|
+
|
|
79
|
+
Spacing is not a free choice. The official scale is five canonical steps, each
|
|
80
|
+
expressing exactly one relationship — pick the step by **how connected two
|
|
81
|
+
elements are**, not by eye. Every scalable value is `remify`-authored against the
|
|
82
|
+
fixed 16px baseline, so it delivers as `rem`.
|
|
83
|
+
|
|
84
|
+
| Value | Token | Relationship — when to use |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| 0px | `--kui-space-none` | No separation. The elements read as a single unit (a control and its own affordance, adjacent `ListItem` rows). |
|
|
87
|
+
| 4px | `--kui-space-2xs` | Very minor. Still one connected cluster, but readability or aesthetics demand a hair of air (inline metadata, an icon beside its label). |
|
|
88
|
+
| 8px | `--kui-space-xs` | Standard. Between elements **within a group** — the content-item padding/gutter, gaps between toolbar controls in a group. |
|
|
89
|
+
| 16px | `--kui-space-m` | Minor. Between **homogeneous groups** — two lists, two sibling sections of the same kind. |
|
|
90
|
+
| 24px | `--kui-space-l` | Major. Between **heterogeneous groups** — the `.kui-content` rhythm between major, differing regions. |
|
|
91
|
+
|
|
92
|
+
The two relationships that get confused most are 8px vs 24px: 8px is *inside* a
|
|
93
|
+
group, 24px is *between* major, differing regions. 16px sits between them for
|
|
94
|
+
same-kind groups.
|
|
95
|
+
|
|
96
|
+
`--kui-space-s` (12px) and `--kui-space-xl` (32px) exist but are **off the
|
|
97
|
+
canonical rhythm** — reach for them only as a deliberate exception, never as a
|
|
98
|
+
default step. Prefer the five canonical tokens so spacing stays legible and
|
|
99
|
+
consistent across every surface.
|
|
100
|
+
|
|
101
|
+
## Ownership rules
|
|
102
|
+
|
|
103
|
+
1. Do not pad a sidebar, main area, dialog, or `.kui-pane` shell. Children own
|
|
104
|
+
their own margin, border, background, padding, and radius.
|
|
105
|
+
2. Use 24px gaps for major vertical separation and 8px gaps inside an item or
|
|
106
|
+
between toolbar groups. Do not confuse the two relationships.
|
|
107
|
+
3. Wrap toolbar content in `ToolbarControlGroup`, including dormant text. A
|
|
108
|
+
transparent group still reserves the same 44px geometry as a visible group.
|
|
109
|
+
4. Keep one scrolling content owner per pane. Toolbar and footer siblings stay
|
|
110
|
+
fixed while the content scrolls.
|
|
111
|
+
5. A split item keeps dormant and interactive regions separate. For example,
|
|
112
|
+
`ListHeader` renders its title/count-or-badge cluster separately from its optional
|
|
113
|
+
logical-end 44px action. The header fills the available inline width and its
|
|
114
|
+
action glyph defaults to 18px; disclosure mode makes the title cluster itself
|
|
115
|
+
the button and supplies the production `DisclosureArrow` unless `actionIcon`
|
|
116
|
+
replaces it.
|
|
117
|
+
`ListActionRow` uses a noninteractive row root around sibling 44px primary
|
|
118
|
+
and trailing buttons. `ListItem.trailing` remains dormant content.
|
|
119
|
+
In multiline `ListItem` and `ListActionRow` rows, the leading icon stays
|
|
120
|
+
centered on the label's first line rather than the full wrapped label.
|
|
121
|
+
6. Reading width, column placement, and responsive relocation remain application
|
|
122
|
+
decisions. The shared classes define local geometry, not the whole shell.
|
|
123
|
+
7. A visible collapsible pane owns its collapse control in that pane's toolbar.
|
|
124
|
+
When the pane is hidden, move the restore control into the adjacent main
|
|
125
|
+
toolbar on the same logical edge: an inline-start pane restores from the
|
|
126
|
+
main toolbar's leading group, and an inline-end pane restores from its
|
|
127
|
+
trailing group. Do not leave an otherwise empty icon-only rail behind.
|
|
128
|
+
|
|
129
|
+
At narrow widths or 200% zoom, relocate or stack panes before shrinking targets.
|
|
130
|
+
The 8/1/8 item contract and 44px controls remain stable, so screenshots and
|
|
131
|
+
focus-order tests exercise the same model at every viewport.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Navigation stack
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui/nav-stack` is an iOS-style push/pop navigation stack: views slide in
|
|
4
|
+
and out over one another while the top chrome settles. A **single-pane layout is
|
|
5
|
+
a `NavStack` with one entry**. It is one of the opt-in app layouts (see
|
|
6
|
+
[`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
|
|
7
|
+
|
|
8
|
+
Import the component and its companion stylesheet:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { NavStack, type NavStackView } from '@kerfjs/ui/nav-stack';
|
|
12
|
+
import { wireNavStack } from '@kerfjs/ui/wire-nav-stack';
|
|
13
|
+
import '@kerfjs/ui/nav-stack.css';
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## State lives in the app
|
|
17
|
+
|
|
18
|
+
Like every `@kerfjs/ui` component, `NavStack` is declarative: the app owns the
|
|
19
|
+
stack as a `signal<NavStackView[]>`, `NavStack({ views })` renders it, and
|
|
20
|
+
`wireNavStack` animates the transitions.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
const views = signal<NavStackView[]>([{ key: 'inbox', title: 'Inbox', content: <InboxView /> }]);
|
|
24
|
+
|
|
25
|
+
// render inside mount():
|
|
26
|
+
<NavStack id="mail" label="Mail" views={views.value} />;
|
|
27
|
+
|
|
28
|
+
// once, after first render:
|
|
29
|
+
const dispose = wireNavStack(root, { onBack: () => { views.value = views.value.slice(0, -1); } });
|
|
30
|
+
|
|
31
|
+
// push / pop by editing the signal:
|
|
32
|
+
views.value = [...views.value, { key: id, title: 'Message', content: <MessageView id={id} /> }];
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`NavStack` renders every entry stacked, the last one active and the rest kept
|
|
36
|
+
mounted (so their DOM state and focus survive) but hidden. Each entry carries a
|
|
37
|
+
`key` (stable identity), `content`, an optional `title`, and optional per-view
|
|
38
|
+
`toolbar` actions. The back control appears automatically once the stack has more
|
|
39
|
+
than one entry; `wireNavStack`'s `onBack` is where the app pops its own signal.
|
|
40
|
+
|
|
41
|
+
## Transitions
|
|
42
|
+
|
|
43
|
+
`wireNavStack(root, { onBack, duration? })` observes the rendered stack and
|
|
44
|
+
animates each change: a pushed view slides in from the trailing edge; a popped
|
|
45
|
+
view slides back off it over the revealed view. It returns a disposer. The
|
|
46
|
+
animation honors `prefers-reduced-motion` (transitions collapse to instant) and
|
|
47
|
+
`duration: 0` disables it. Applicable at every device size and inside dialogs.
|
package/docs/recipes.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Production composition recipes
|
|
2
|
+
|
|
3
|
+
These seven reference compositions sit between individual primitives and product
|
|
4
|
+
code. Open each stable UX-catalog route to run it, then copy the linked TSX and
|
|
5
|
+
CSS. The examples import public package subpaths, use semantic layout owners,
|
|
6
|
+
and keep state in a per-instance application adapter. They are not new
|
|
7
|
+
monolithic components.
|
|
8
|
+
|
|
9
|
+
Copy the recipe source together with the catalog-independent
|
|
10
|
+
[`mount-recipe.ts`](../ux-demo/recipes/mount-recipe.ts) adapter. It mounts the
|
|
11
|
+
controller at one stable application root, uses `delegateActions()` for recipe
|
|
12
|
+
commands, forwards form and dialog lifecycle events, wires resize commits with
|
|
13
|
+
the public `onCommit` callback, retains every disposer, and returns one
|
|
14
|
+
idempotent disposer:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createRecipe } from './navigation-sidebar.js';
|
|
18
|
+
import { mountRecipe } from './mount-recipe.js';
|
|
19
|
+
|
|
20
|
+
const root = document.querySelector<HTMLElement>('#navigation')!;
|
|
21
|
+
const stopRecipe = mountRecipe(root, createRecipe(announce));
|
|
22
|
+
window.addEventListener('pagehide', stopRecipe, { once: true });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The adapter is delivered as reference source, not a new package runtime export.
|
|
26
|
+
`delegate()` remains a valid alternative when an application needs selector-
|
|
27
|
+
specific dispatch; either way, wire once at a stable root and retain disposal.
|
|
28
|
+
|
|
29
|
+
## Desktop application shell
|
|
30
|
+
|
|
31
|
+
[Open the recipe](../ux-demo/?component=recipe-app-shell) · [TSX source](../ux-demo/recipes/app-shell.tsx) · [shared CSS](../ux-demo/recipes/recipes.css)
|
|
32
|
+
|
|
33
|
+
Use `Toolbar`, controlled `ResizableRegion` panes, and one
|
|
34
|
+
`.kui-pane__content` scroll owner per pane. The recipe owns the shell topology;
|
|
35
|
+
the app owns routing, responsive pane visibility, sizes, persistence, and data.
|
|
36
|
+
Keep each visible pane's collapse action in its own toolbar. Once hidden, put
|
|
37
|
+
its restore action in the adjacent main toolbar on the same logical edge:
|
|
38
|
+
leading for an inline-start sidebar and trailing for an inline-end inspector.
|
|
39
|
+
Collapse the pane completely rather than preserving an empty icon rail.
|
|
40
|
+
Adapt only public `--kui-layout-*` and component variables.
|
|
41
|
+
|
|
42
|
+
## Navigation sidebar
|
|
43
|
+
|
|
44
|
+
[Open the recipe](../ux-demo/?component=recipe-navigation-sidebar) · [TSX source](../ux-demo/recipes/navigation-sidebar.tsx)
|
|
45
|
+
|
|
46
|
+
One unpadded `.kui-pane` owns toolbar/content/footer structure. Its
|
|
47
|
+
`.kui-content` uses 24px major gaps; `ListHeader`, `ListItem`, and other
|
|
48
|
+
`.kui-content-item` children own their 8px margin, 1px border, and 8px padding.
|
|
49
|
+
Rows and footer toolbar groups remain 44px tall. The app owns routes,
|
|
50
|
+
permissions, labels, selection, valid section counts and their localized
|
|
51
|
+
`countLabel` phrases, non-count badge content, disclosure state, and revealed
|
|
52
|
+
content. A toggled `ListHeader` supplies the production `DisclosureArrow` when
|
|
53
|
+
no custom `actionIcon` is needed; ordinary navigation rows stay chevron-free.
|
|
54
|
+
|
|
55
|
+
## Workspace header
|
|
56
|
+
|
|
57
|
+
[Open the recipe](../ux-demo/?component=recipe-workspace-header) · [TSX source](../ux-demo/recipes/workspace-header.tsx)
|
|
58
|
+
|
|
59
|
+
`PanelHeader` owns the page heading while one control cluster holds secondary,
|
|
60
|
+
overflow, and primary actions. The app owns authorization and command policy;
|
|
61
|
+
controls relocate without changing focus order.
|
|
62
|
+
|
|
63
|
+
## List-detail dialog
|
|
64
|
+
|
|
65
|
+
[Open the recipe](../ux-demo/?component=recipe-list-detail-dialog) · [TSX source](../ux-demo/recipes/list-detail-dialog.tsx)
|
|
66
|
+
|
|
67
|
+
The production Web Awesome dialog owns modal focus and Escape; the thin recipe
|
|
68
|
+
adapter restores the invoking control consistently after the hide event.
|
|
69
|
+
`PanelHeader`, `ListHeader`, `ListItem`, and `ValueTable` own their included
|
|
70
|
+
anatomy. The application owns open state, selection, dismissal policy, and
|
|
71
|
+
record actions. `PanelHeader` places the trailing controls the app passes
|
|
72
|
+
directly into its top toolbar's trailing zone.
|
|
73
|
+
The header sits on the dialog edge while retaining its internal control inset;
|
|
74
|
+
the selected title receives the full content gutter, the metadata table fills
|
|
75
|
+
the available detail width between the usual outer margins, and the action
|
|
76
|
+
cluster uses one outer gutter without a second content-item inset.
|
|
77
|
+
Do not rebuild the dialog or reach into private shadow parts.
|
|
78
|
+
|
|
79
|
+
## Composer form
|
|
80
|
+
|
|
81
|
+
[Open the recipe](../ux-demo/?component=recipe-composer-form) · [TSX source](../ux-demo/recipes/composer-form.tsx)
|
|
82
|
+
|
|
83
|
+
`PanelHeader` supplies the task title and summary, with their ids referenced
|
|
84
|
+
by the form. Production fields own labels, help, and native focus. The field
|
|
85
|
+
and footer control edges sit directly on the shared 8px inline gutter rather
|
|
86
|
+
than acquiring a second content-item padding inset; major children remain 24px
|
|
87
|
+
apart and related controls use 8px gaps. The app owns
|
|
88
|
+
validation, drafts, permissions, and transport. Persistent error or success
|
|
89
|
+
feedback is the only nested semantic surface and uses `StateBanner`, not a toast.
|
|
90
|
+
Because upgraded Web Awesome fields retain live value properties, controlled
|
|
91
|
+
resets synchronize both those properties and the rendered value attributes;
|
|
92
|
+
the Reset action also announces `Draft reset` through the catalog live region.
|
|
93
|
+
|
|
94
|
+
## List workspace states
|
|
95
|
+
|
|
96
|
+
[Open the recipe](../ux-demo/?component=recipe-list-workspace-states) · [TSX source](../ux-demo/recipes/list-workspace-states.tsx)
|
|
97
|
+
|
|
98
|
+
The same content region moves deterministically through loading, empty,
|
|
99
|
+
populated, stale/background refresh, and error/retry states. The recipe owns
|
|
100
|
+
feedback placement; the app owns fetching, cache age, retry policy, and domain
|
|
101
|
+
rows.
|
|
102
|
+
|
|
103
|
+
## Compact toolbar choices and actions
|
|
104
|
+
|
|
105
|
+
[Open the recipe](../ux-demo/?component=recipe-compact-toolbar) · [TSX source](../ux-demo/recipes/compact-toolbar.tsx)
|
|
106
|
+
|
|
107
|
+
Use `ToolbarControlGroup` for related commands, `SegmentedControl` for a few
|
|
108
|
+
visible exclusive choices, `Select` for a longer value list, and an ordinary
|
|
109
|
+
button for an independent command. The app owns values, actions, persistence,
|
|
110
|
+
and responsive priority.
|
|
111
|
+
|
|
112
|
+
## Navigation stack
|
|
113
|
+
|
|
114
|
+
[Open the recipe](../ux-demo/?component=recipe-navigation-stack) · [TSX source](../ux-demo/recipes/navigation-stack.tsx)
|
|
115
|
+
|
|
116
|
+
Drill from a library list into a detail and back with `NavStack`
|
|
117
|
+
(`@kerfjs/ui/nav-stack`): the app owns the stack as a signal of views and
|
|
118
|
+
pushes/pops it, `NavStack` renders it, and `wireNavStack` slides the content and
|
|
119
|
+
settles the chrome (reduced motion collapses the slide to instant). A live
|
|
120
|
+
`deviceClass()` badge shows the current size/orientation. See the layout guide
|
|
121
|
+
[`app-layouts.md`](app-layouts.md) for choosing among `NavStack`, `SplitView`,
|
|
122
|
+
`Workbench`, and `TabScaffold`.
|
|
123
|
+
|
|
124
|
+
## Loading inspector
|
|
125
|
+
|
|
126
|
+
[Open the recipe](../ux-demo/?component=recipe-loading-inspector) · [TSX source](../ux-demo/recipes/loading-inspector.tsx)
|
|
127
|
+
|
|
128
|
+
A record inspector whose per-record values load asynchronously. Every
|
|
129
|
+
value-bearing component (`PanelHeader`, `ValueTable`/`ValueTableRow`, `Select`,
|
|
130
|
+
`SegmentedControl`, `ListItem`, `StateBanner`) takes its `placeholder` from one
|
|
131
|
+
loading flag, so the same real chrome renders a faithful loading state and then
|
|
132
|
+
the populated record — no separate skeleton markup. The composition is the point;
|
|
133
|
+
`Skeleton` is the primitive it builds on. The app owns the loading lifecycle and
|
|
134
|
+
which values are still unknown.
|
|
135
|
+
|
|
136
|
+
## Collapsible sidebar
|
|
137
|
+
|
|
138
|
+
[Open the recipe](../ux-demo/?component=recipe-collapsible-sidebar) · [TSX source](../ux-demo/recipes/collapsible-sidebar.tsx)
|
|
139
|
+
|
|
140
|
+
A mini app frame whose left navigation rail and bottom activity drawer are
|
|
141
|
+
standalone `CollapsiblePanel`s (`@kerfjs/ui/collapsible-panel`) driven by
|
|
142
|
+
`wireSidebar` (`@kerfjs/ui/wire-sidebar`). `CollapsiblePanelToggle` supplies the
|
|
143
|
+
standard per-side glyph: a collapse toggle lives inside each panel and an expand
|
|
144
|
+
toggle lives in the always-visible main header, so a collapsed panel is still
|
|
145
|
+
reachable. `wireSidebar` owns the toggle delegation, moves focus into a panel on
|
|
146
|
+
open and restores it to the trigger on close, and — when a `deviceClass()` reports
|
|
147
|
+
`compact` — switches the open panel to a dismissable **overlay** (backdrop, Escape
|
|
148
|
+
and backdrop-click collapse, and a trapped Tab ring, the ARIA dialog pattern). It
|
|
149
|
+
also persists each panel's collapsed state through a supplied storage hook. The app
|
|
150
|
+
owns each `collapsed` signal, the panel sizes, and the content; the wire owns the
|
|
151
|
+
ephemeral interaction. For a full three-pane shell use `Workbench` instead — see
|
|
152
|
+
[`app-layouts.md`](app-layouts.md). This recipe is covered end-to-end across
|
|
153
|
+
Chromium, Firefox, and WebKit by `tests/browser/collapsible-sidebar-recipe.spec.ts`.
|
|
154
|
+
|
|
155
|
+
## Rules shared by every recipe
|
|
156
|
+
|
|
157
|
+
- Import `@kerfjs/ui/layout.css`; keep every pane unpadded and use exactly one
|
|
158
|
+
`.kui-pane__content` scroll owner for each real boundary.
|
|
159
|
+
- For direct `wa-*` JSX, import types from `@kerfjs/ui/webawesome`. Import only
|
|
160
|
+
individual Web Awesome registration modules and theme them with
|
|
161
|
+
`@kerfjs/ui/webawesome.css`.
|
|
162
|
+
- Prefer public props and variables at the composition boundary. When a recipe
|
|
163
|
+
needs responsive topology, join only classes listed in the catalog's
|
|
164
|
+
`publicClasses`; do not copy component markup or select descendants by tag,
|
|
165
|
+
id, attribute alone, or an unlisted implementation class.
|
|
166
|
+
- Start from the copyable mount adapter, or reproduce its complete boundary:
|
|
167
|
+
wire stable `data-action` hooks once and retain every disposer.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Split view (list-detail)
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui/split-view` is a list-detail layout: a list and a
|
|
4
|
+
detail side by side on roomy screens, collapsing to a `NavStack` (list → detail)
|
|
5
|
+
on compact ones. One of the opt-in app layouts (see
|
|
6
|
+
[`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { SplitView } from '@kerfjs/ui/split-view';
|
|
10
|
+
import { deviceClass } from '@kerfjs/ui/device-class';
|
|
11
|
+
import '@kerfjs/ui/split-view.css';
|
|
12
|
+
// plus nav-stack.css when the compact path is reachable, and
|
|
13
|
+
// wireResizableRegions / wireNavStack for the interactive behavior.
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Responsive by device class
|
|
17
|
+
|
|
18
|
+
`SplitView` is declarative; the app derives `compact` from the device class and
|
|
19
|
+
tracks its own selection:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const device = deviceClass();
|
|
23
|
+
const selected = signal<string | null>(null);
|
|
24
|
+
|
|
25
|
+
<SplitView
|
|
26
|
+
id="mail"
|
|
27
|
+
label="Mail"
|
|
28
|
+
compact={device.value.compact}
|
|
29
|
+
detailActive={selected.value !== null}
|
|
30
|
+
list={<ThreadList />}
|
|
31
|
+
detail={<Message id={selected.value} />}
|
|
32
|
+
listTitle="Threads"
|
|
33
|
+
detailTitle="Message"
|
|
34
|
+
resizable={{ size: 320, min: 220, max: 480 }}
|
|
35
|
+
/>;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- **Roomy** (`compact: false`): both panes show. With `resizable`, the list sits
|
|
39
|
+
in a `ResizableRegion` (wire it with `wireResizableRegions`); without it the
|
|
40
|
+
list takes a fixed `--kui-split-view-list-width` (default 320px) and the detail
|
|
41
|
+
fills the rest.
|
|
42
|
+
- **Compact** (`compact: true`): the split collapses to a `NavStack`. The list is
|
|
43
|
+
the root; when `detailActive` is true the detail is pushed over it with an
|
|
44
|
+
automatic back control (wire it with `wireNavStack`, whose `onBack` clears the
|
|
45
|
+
app's selection). This is the portrait-tablet / handset presentation; as a
|
|
46
|
+
dialog the compact form is a full-screen or large partial-cover modal.
|
|
47
|
+
|
|
48
|
+
Compose the interactive wiring from the existing helpers — `SplitView` adds no
|
|
49
|
+
wire of its own.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Tab scaffold
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui/tab-scaffold` is a mobile-first, iOS-like bottom tab bar that switches
|
|
4
|
+
between major app sections, where **each tab keeps its own content mounted** —
|
|
5
|
+
usually a `NavStack`, so each tab's stack and scroll survive a switch. One of the
|
|
6
|
+
opt-in app layouts (see [`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
|
|
7
|
+
It is distinct from `TabBar` (document-oriented, reorderable strips).
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { TabScaffold } from '@kerfjs/ui/tab-scaffold';
|
|
11
|
+
import { wireTabScaffold } from '@kerfjs/ui/wire-tab-scaffold';
|
|
12
|
+
import '@kerfjs/ui/tab-scaffold.css';
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Controlled selection
|
|
16
|
+
|
|
17
|
+
The app owns the active tab (a signal); `TabScaffold` renders every tab's scene
|
|
18
|
+
(only the active one visible) plus the bottom bar, and `wireTabScaffold` reports
|
|
19
|
+
clicks.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const active = signal('home');
|
|
23
|
+
|
|
24
|
+
<TabScaffold
|
|
25
|
+
id="app"
|
|
26
|
+
label="Sections"
|
|
27
|
+
active={active.value}
|
|
28
|
+
tabs={[
|
|
29
|
+
{ id: 'home', label: 'Home', icon: <HomeIcon />, content: <HomeStack /> },
|
|
30
|
+
{ id: 'search', label: 'Search', icon: <SearchIcon />, content: <SearchStack /> },
|
|
31
|
+
]}
|
|
32
|
+
/>;
|
|
33
|
+
|
|
34
|
+
// once, after first render:
|
|
35
|
+
const dispose = wireTabScaffold(root, { onSelect: (id) => { active.value = id; } });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Each `TabScaffoldTab` has an `id`, `label`, optional `icon`, and `content`. The
|
|
39
|
+
bottom bar respects the home-indicator safe area (`env(safe-area-inset-bottom)`)
|
|
40
|
+
and keeps 44px targets. On larger device classes, promote the tab set to a
|
|
41
|
+
`Workbench` rail or a persistent sidebar instead of a bottom bar.
|
package/docs/ux-demo.md
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
# UX catalog contract
|
|
2
2
|
|
|
3
|
-
Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation.
|
|
3
|
+
Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation. The development server allows repo-owned assets used by the catalog, and its production bundle uses relative asset URLs so the complete catalog remains intact below preview and proxy paths.
|
|
4
|
+
|
|
5
|
+
Seven lazy production-composition routes live under the `Recipes` category.
|
|
6
|
+
Each `?component=recipe-*` route uses public package primitives, real wiring,
|
|
7
|
+
deterministic state, and semantic layout owners from the [recipe guide](./recipes.md).
|
|
4
8
|
|
|
5
9
|
- It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
|
|
10
|
+
- The `ListActionRow` route demonstrates sibling primary/trailing native controls, independent disabled states, controlled selection, first-line leading-icon alignment for wrapped labels, and an application-owned native popover relationship without nesting controls. The `ListItem` route demonstrates the same multiline leading-icon anchor alongside its single-control navigation contract.
|
|
11
|
+
- The `AppTab` route demonstrates a runtime-filtered domain metadata slot and a decorative replacement close icon without changing TabBar selection, close, reorder, or focus ownership. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
|
|
6
12
|
- Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, feedback, and theme scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component.
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
13
|
+
- `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Demo paths resolve to `ui/ux-demo/main.tsx`, the matching `ui/ux-demo/recipes/*.tsx` file, or `ui/ux-demo/webawesome-demos.tsx` according to the canonical entry. First-party component entries additionally derive `ui/src/<browser-subpath>.tsx` from their canonical `delivery.browserImport`.
|
|
14
|
+
- Every detail footer groups compact `Demo source` and `Guidance` actions; first-party component details also expose `Component source`. Web Awesome details say `Integration guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
|
|
15
|
+
- The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
|
|
16
|
+
- The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with the subtitle in its own aligned row like `PanelHeader`; the current-view heading is also a toolbar. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The checkerboard belongs to the complete scrollable preview region rather than a nested card. A lightly separated footer keeps live status above compact source, guidance, and relationship groups. The focused `ResizableRegion` specimen stretches its surface through the available stage height and reports committed width in that shared status footer; narrow layouts retain local horizontal access when the pane is resized wider than the stage, and the split remains readable at 200% root scaling. Geometry coverage checks wide, intermediate, narrow, and 200% zoom layouts.
|
|
17
|
+
- The composer-form recipe demonstrates one coherent visible form surface with `PanelHeader` title/summary hierarchy, fields and actions on the shared 8px control gutter, and 24px major rhythm. Its conditional error or success `StateBanner` is the only nested semantic surface. Reset synchronizes the upgraded Web Awesome fields' live value properties with their empty controlled attributes and announces `Draft reset`; browser coverage preserves behavior and verifies wide, narrow, dark, validation, success, reset, 200% zoom, and forced-colors states.
|
|
18
|
+
- The list-detail dialog recipe places its header directly on the dialog edge while preserving the header's internal inset, gives the selected project title the complete outer-plus-inner content gutter, lets the value table fill the available detail column between the shared outer margins, and keeps record actions on one outer gutter without an extra content-item inset. Browser geometry coverage protects those relationships at wide and narrow widths.
|
|
19
|
+
- Catalog-only brand marks, preview dimensions, checkerboard tiles, and the compound display-settings control retain local geometry because they are specimen chrome or intrinsic control anatomy rather than reusable application spacing roles.
|
|
20
|
+
- The menu composition route demonstrates toolbar/content/footer anatomy, 24px major gaps, 8/1/8 child geometry, the full-width `ListHeader` count-or-badge/action split, logical-end 44px action targets with 18px visuals, and 44px row/header/toolbar targets. Its one chevron is a production 18px `DisclosureArrow` inside a controlled Tools header that actually reveals and hides content; ordinary Projects navigation has no false disclosure affordance. Browser assertions cover stable naming, expanded/content transitions, visual rotation, node identity, reduced motion, RTL alignment and containment, and wide, narrow, dark, and 200% layouts. The focused ListHeader route separately shows the shared neutral count pill for Attachments, Notes (including zero), and Duplicates plus a non-count legacy badge, full-width logical-end actions, and no disclosure without content.
|
|
21
|
+
- The `DisclosureArrow` route renders the production 18px root-scaled default without a catalog-only size override. Its two native buttons independently control the default right-to-down chevron and a recognizable right-facing replacement arrow with left-to-up custom directions; both use shortest-path rotation and their names stay stable while `aria-expanded` communicates state. Browser coverage verifies the custom clockwise path, pointer and native keyboard activation, focus, narrow and 200%-zoom stacking with contained labels, consumer sizing, and the independent `.5` Web Awesome glyph scale retained by Kerf `Select`.
|
|
22
|
+
- A detail with relationships renders one grouped footer selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
|
|
10
23
|
- Demo-only CSS supplies only the catalog shell and stages; component appearance stays in package CSS. Decorative chrome and Web Awesome controls share the same semantic system palette instead of carrying a separate demo identity.
|
|
11
|
-
- Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; StateBanner exposes every built-in tone plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
|
|
12
|
-
-
|
|
13
|
-
- The action
|
|
24
|
+
- Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; ListItem and ListHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; PanelHeader demonstrates its plain top-toolbar identity, extra-large title, optional subtitle row, and bordered brand-filled icon group; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
|
|
25
|
+
- Recipe ownership notes are hidden by default. A page-toolbar toggle reveals them as floating warning-toned notes with a folded corner, keeping instructional copy visually distinct and removable from the specimen flow.
|
|
26
|
+
- Settings exercise light/dark, increased contrast, and reduced motion without changing component code. The theme action starts from the operating system's effective color scheme, always names the appearance it will switch to, and installs an explicit light or dark override when activated; contrast and reduced motion expose their independent preferences through `aria-pressed`.
|
|
27
|
+
- The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
|
|
14
28
|
- The focused Toast route calls Web Awesome's documented programmatic stack API and notes that Hot Sheet 2 still uses its own app-level toast implementation; the catalog demonstrates ecosystem support without implying current product adoption.
|
|
15
|
-
- Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup behavior, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
|
|
16
|
-
- The production build keeps the catalog shell and Kerf routes in the entry chunk, then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route
|
|
29
|
+
- Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, validates offline source/guidance hrefs and accessible wide/narrow/200%-zoom wrapping, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup and responsive find behavior, single/multiline search alignment, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
|
|
30
|
+
- The production build keeps the catalog shell and Kerf routes in the entry chunk, then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route, the broad Web Awesome theme route, or the ToolbarControlGroup popup specimen is selected. Directly addressed routes pass through the same loader.
|
|
17
31
|
|
|
18
32
|
Before handoff, inspect the actual captures for readability, context, alignment, clipping, spacing, responsiveness, focus, and obvious defects. Fix and recapture rather than treating the screenshot as proof by itself.
|
package/docs/webawesome-theme.md
CHANGED
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
free components and the Kerf/Hot Sheet 2 design system.
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
|
+
import type {} from '@kerfjs/ui/webawesome';
|
|
7
8
|
import '@kerfjs/ui/webawesome.css';
|
|
8
9
|
import '@awesome.me/webawesome/dist/components/button/button.js';
|
|
9
10
|
import '@awesome.me/webawesome/dist/components/input/input.js';
|
|
10
11
|
```
|
|
11
12
|
|
|
13
|
+
The type-only package import augments `kerfjs/jsx-runtime` with the 70
|
|
14
|
+
catalog-supported `wa-*` elements. It has no runtime side effects. The catalog
|
|
15
|
+
gate keeps the declaration tags aligned with Web Awesome's installed custom
|
|
16
|
+
elements manifest, while individual component imports remain the only
|
|
17
|
+
registration boundary.
|
|
18
|
+
|
|
12
19
|
The CSS entry includes Web Awesome's native, utility, palette, and default-theme
|
|
13
20
|
styles, then overrides the public semantic contract in
|
|
14
21
|
`@layer wa-theme-overrides`. It does not import or register component
|
|
@@ -53,6 +60,11 @@ chevrons keep the same visual weight. The Accordion and Details components
|
|
|
53
60
|
retain their own rotation behavior because scaling uses `transform` while
|
|
54
61
|
their open state uses the independent `rotate` property.
|
|
55
62
|
|
|
63
|
+
That scale applies to each Web Awesome component's intrinsic glyph and is
|
|
64
|
+
independent of Kerf `DisclosureArrow`, whose default box is 18px relative to
|
|
65
|
+
the root font size and whose consumer override is
|
|
66
|
+
`--kui-disclosure-arrow-size`.
|
|
67
|
+
|
|
56
68
|
Tooltip and Popover use arrowless floating surfaces by default, matching Hot
|
|
57
69
|
Sheet 2. The theme sets Web Awesome's public `--wa-tooltip-arrow-size` token to
|
|
58
70
|
`0px` and maps each popover's public `--arrow-size` property from
|
|
@@ -129,6 +141,9 @@ static content and labels that constraint next to the rendered output.
|
|
|
129
141
|
|
|
130
142
|
The catalog lists every free Web Awesome component so support can be inspected;
|
|
131
143
|
listing does not make each component the preferred Kerf application pattern.
|
|
144
|
+
The [need-first decision matrix](./component-selection.md#web-awesome-overlap-policy)
|
|
145
|
+
is the exhaustive Kerf/Web Awesome overlap index; the rules below explain the
|
|
146
|
+
highest-risk choices.
|
|
132
147
|
|
|
133
148
|
- Consider `wa-popup` for low-level anchored positioning when its flip, shift,
|
|
134
149
|
or placement behavior avoids custom positioning code. Prefer the higher-level
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Workbench
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui/workbench` is the Xcode-like multi-panel workspace: a collapsible
|
|
4
|
+
left rail, right rail, and bottom drawer around a central work area (any absent).
|
|
5
|
+
It generalizes the instant-width / sliding-content collapse used by the catalog
|
|
6
|
+
sidebar. One of the opt-in app layouts (see
|
|
7
|
+
[`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)); best on
|
|
8
|
+
desktop-class devices.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { Workbench } from '@kerfjs/ui/workbench';
|
|
12
|
+
import '@kerfjs/ui/workbench.css';
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## State lives in the app
|
|
16
|
+
|
|
17
|
+
`Workbench` is declarative and the collapse is **pure CSS** — no wire. The app
|
|
18
|
+
owns each panel's `collapsed` flag (usually a signal) and toggles it; the panel
|
|
19
|
+
animates itself.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const navCollapsed = signal(false);
|
|
23
|
+
|
|
24
|
+
<Workbench
|
|
25
|
+
id="studio"
|
|
26
|
+
label="Studio"
|
|
27
|
+
main={<Editor />}
|
|
28
|
+
leftRail={{ content: <Navigator />, label: 'Navigator', collapsed: navCollapsed.value, size: 280 }}
|
|
29
|
+
rightRail={{ content: <Inspector />, label: 'Inspector', collapsed: inspectorCollapsed.value }}
|
|
30
|
+
bottomDrawer={{ content: <Console />, label: 'Console', collapsed: consoleCollapsed.value }}
|
|
31
|
+
/>;
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Each `WorkbenchPanel` takes `content`, an optional `collapsed`, an optional
|
|
35
|
+
`size` (rail width or drawer height in px, overriding the CSS default —
|
|
36
|
+
`--kui-workbench-rail-width` 280px, `--kui-workbench-drawer-height` 220px), and
|
|
37
|
+
an optional `label`.
|
|
38
|
+
|
|
39
|
+
## How the collapse animates
|
|
40
|
+
|
|
41
|
+
Collapsing snaps the panel's flex track to zero in a single reflow (so the work
|
|
42
|
+
area relayouts once, not per frame) while the panel's fixed-size content slides
|
|
43
|
+
out via a composited `transform` — a rail slides horizontally, the drawer
|
|
44
|
+
vertically — clipped by the shell's overflow. It honors `prefers-reduced-motion`
|
|
45
|
+
(the slide collapses to instant). On smaller device classes, present the rails'
|
|
46
|
+
contents through a `NavStack` or overlay drawers rather than shrinking the
|
|
47
|
+
three-panel shell.
|