@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.
- package/README.md +20 -20
- package/ai/component-catalog.json +123 -80
- package/ai/public-api-signatures-v1.md +181 -26
- package/ai/skill.md +12 -12
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/browser/list-action-row.js +4 -0
- package/dist/browser/{menu-header.js → list-header.js} +2 -2
- package/dist/browser/{menu-item.js → list-item.js} +2 -2
- package/dist/catalog.d.ts +119 -0
- package/dist/catalog.js +128 -0
- package/dist/catalog.js.map +1 -0
- package/dist/{chunk-BNQ2YDV4.js → chunk-2TS7ZSYX.js} +2 -2
- package/dist/{chunk-BNQ2YDV4.js.map → chunk-2TS7ZSYX.js.map} +1 -1
- package/dist/{chunk-PONXLV5U.js → chunk-37T3VNIZ.js} +1 -1
- package/dist/{chunk-PONXLV5U.js.map → chunk-37T3VNIZ.js.map} +1 -1
- package/dist/{chunk-3YD6ZNRA.js → chunk-7JB7VPRI.js} +8 -8
- package/dist/{chunk-3YD6ZNRA.js.map → chunk-7JB7VPRI.js.map} +1 -1
- package/dist/{chunk-YKUUZPED.js → chunk-L4OKFDJY.js} +7 -7
- package/dist/{chunk-YKUUZPED.js.map → chunk-L4OKFDJY.js.map} +1 -1
- package/dist/{chunk-H6URQGJI.js → chunk-W5EQ3HYQ.js} +11 -11
- package/dist/{chunk-H6URQGJI.js.map → chunk-W5EQ3HYQ.js.map} +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +10 -10
- package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +7 -7
- package/dist/list-action-row.js +5 -0
- package/dist/list-action-row.js.map +1 -0
- package/dist/{menu-header.d.ts → list-header.d.ts} +9 -9
- package/dist/list-header.js +7 -0
- package/dist/list-header.js.map +1 -0
- package/dist/{menu-item.d.ts → list-item.d.ts} +5 -5
- package/dist/list-item.js +5 -0
- package/dist/list-item.js.map +1 -0
- package/dist/panel-header.js +1 -1
- package/dist/split-view.d.ts +1 -1
- package/dist/split-view.js.map +1 -1
- package/dist/styles/catalog.css +482 -0
- package/dist/styles/list-action-row.css +160 -0
- package/dist/styles/{menu-header.css → list-header.css} +33 -33
- package/dist/styles/list-item.css +123 -0
- package/dist/styles/styles.css +4 -3
- package/dist/value-table.js +1 -1
- package/dist/wire-catalog.d.ts +26 -0
- package/dist/wire-catalog.js +50 -0
- package/dist/wire-catalog.js.map +1 -0
- package/docs/accessibility.md +5 -5
- package/docs/catalog.md +154 -0
- package/docs/component-contract.md +12 -12
- package/docs/component-selection.md +12 -11
- package/docs/design-philosophy.md +4 -4
- package/docs/layout.md +8 -8
- package/docs/recipes.md +17 -5
- package/docs/split-view.md +1 -1
- package/docs/ux-demo.md +5 -5
- package/llms.txt +12 -8
- package/package.json +25 -16
- package/ux-demo/recipes/app-shell.tsx +3 -3
- package/ux-demo/recipes/list-detail-dialog.tsx +45 -0
- package/ux-demo/recipes/loaders.ts +4 -2
- package/ux-demo/recipes/loading-inspector.tsx +79 -0
- package/ux-demo/recipes/navigation-sidebar.tsx +5 -5
- package/ux-demo/recipes/navigation-stack.tsx +4 -4
- package/ux-demo/recipes/recipes.css +13 -7
- package/dist/browser/menu-action-row.js +0 -4
- package/dist/menu-action-row.js +0 -5
- package/dist/menu-action-row.js.map +0 -1
- package/dist/menu-header.js +0 -7
- package/dist/menu-header.js.map +0 -1
- package/dist/menu-item.js +0 -5
- package/dist/menu-item.js.map +0 -1
- package/dist/styles/menu-action-row.css +0 -160
- package/dist/styles/menu-item.css +0 -123
- package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
package/docs/catalog.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Catalog — a reusable component-gallery shell
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui/catalog` is an opt-in, whole-screen shell for building a **component
|
|
4
|
+
catalog** — the collapsible category sidebar + titled preview stage + resources
|
|
5
|
+
footer that the kerf UI catalog itself uses. Point it at your own components and
|
|
6
|
+
you get the same shell without rebuilding it. Like the app layouts, it is a
|
|
7
|
+
subpath-only, tree-shakeable module that adds nothing to the main barrel.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @kerfjs/ui # kerfjs is a peer; @kerfjs/ui/select/register is needed only if entries use `related`
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- `Catalog(props)` returns the shell as `SafeHtml` (a `<main class="kui-catalog">`).
|
|
14
|
+
It is **controlled and stateless**: your app owns the `active`, `collapsed`, and
|
|
15
|
+
`theme` signals and computes the preview `content` from `active` in its own
|
|
16
|
+
`mount()` render.
|
|
17
|
+
- `wireCatalog(root, options)` wires the interactions (sidebar selection, the
|
|
18
|
+
related-entry selector, and the collapse/theme toggles) with one delegated
|
|
19
|
+
listener set and returns a disposer; it can also mirror the active id into the
|
|
20
|
+
URL.
|
|
21
|
+
|
|
22
|
+
## What you supply
|
|
23
|
+
|
|
24
|
+
- **`sections`** — category-grouped entries: `{ category, entries: [{ id, name,
|
|
25
|
+
description?, resources?, related? }] }`. Each entry becomes a sidebar `ListItem`
|
|
26
|
+
under a `ListHeader` for its category.
|
|
27
|
+
- **`content`** — the rendered preview for the active entry. Keep a map of `id →
|
|
28
|
+
() => SafeHtml` in your app and call `renderers[active]()` in your render.
|
|
29
|
+
- **`brand`** — `{ title, subtitle?, logoUrl? }` for the sidebar header.
|
|
30
|
+
- **`secondarySections`** — an optional secondary "ecosystem" group shown below the
|
|
31
|
+
primary sections with a quieter treatment: `{ label, sections, collapsible?,
|
|
32
|
+
expanded? }`. When `collapsible`, the label is a disclosure toggle controlling
|
|
33
|
+
`expanded` (the app owns it; wire it with `wireCatalog`'s `onToggleSecondary`).
|
|
34
|
+
- Optional slots: `headerActions` (extra header controls), `sidebarFooter` (extra
|
|
35
|
+
sidebar content), and `status` (a footer status line).
|
|
36
|
+
|
|
37
|
+
Per-entry `resources` render as "open in new tab" links in the footer, and
|
|
38
|
+
`related` renders a "Related entries" selector (a `Select`, so register it with
|
|
39
|
+
`@kerfjs/ui/select/register` when you use it).
|
|
40
|
+
|
|
41
|
+
## Preview examples
|
|
42
|
+
|
|
43
|
+
Compose each entry's `content` from `CatalogExample` (and `CatalogExampleStack`)
|
|
44
|
+
instead of hand-rolled example markup, so labels, notes, and left-edge alignment
|
|
45
|
+
stay consistent:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { CatalogExample, CatalogExampleStack } from '@kerfjs/ui/catalog';
|
|
49
|
+
|
|
50
|
+
const buttonPreview = (
|
|
51
|
+
<CatalogExampleStack label="Button variants">
|
|
52
|
+
<CatalogExample label="Icon" note="A bare glyph." align="glyph">
|
|
53
|
+
<LucideIcon icon={Plus} name="plus" />
|
|
54
|
+
</CatalogExample>
|
|
55
|
+
<CatalogExample label="Control" align="inline-control">
|
|
56
|
+
<SegmentedControl id="view" label="View" value="list" choices={choices} />
|
|
57
|
+
</CatalogExample>
|
|
58
|
+
<CatalogExample label="In composition">
|
|
59
|
+
<ValueTable label="Metadata">{rows}</ValueTable>
|
|
60
|
+
</CatalogExample>
|
|
61
|
+
</CatalogExampleStack>
|
|
62
|
+
);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`label` is optional — omit it for a bare specimen with no `ListHeader`. `align`
|
|
66
|
+
lines a specimen's visible left edge up with its label text: `'glyph'`
|
|
67
|
+
(16px) for a bare glyph/text specimen, `'inline-control'` (8px) for a control that
|
|
68
|
+
already carries ~8px of its own inline padding, and `'none'` (the default) for a
|
|
69
|
+
content-item/composition that already owns its geometry. The inset is published as
|
|
70
|
+
the `--kui-catalog-example-align` custom property so a debug overlay can exclude it
|
|
71
|
+
from a specimen's measured margin.
|
|
72
|
+
|
|
73
|
+
## Complete example
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { mount, signal } from 'kerfjs';
|
|
77
|
+
import { Catalog, type CatalogSection } from '@kerfjs/ui/catalog';
|
|
78
|
+
import { wireCatalog } from '@kerfjs/ui/wire-catalog';
|
|
79
|
+
import '@kerfjs/ui/styles.css'; // or import each primitive's CSS + @kerfjs/ui/catalog.css
|
|
80
|
+
|
|
81
|
+
// 1. Describe your components once.
|
|
82
|
+
const sections: CatalogSection[] = [
|
|
83
|
+
{
|
|
84
|
+
category: 'Controls',
|
|
85
|
+
entries: [
|
|
86
|
+
{ id: 'button', name: 'Button', description: 'A pressable control.',
|
|
87
|
+
resources: [{ label: 'Source', href: '/src/button.tsx', detail: 'src/button.tsx' }] },
|
|
88
|
+
{ id: 'field', name: 'Field', description: 'A labeled input.',
|
|
89
|
+
related: [{ id: 'button', name: 'Button', group: 'Used with' }] },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{ category: 'Feedback', entries: [{ id: 'toast', name: 'Toast', description: 'A transient message.' }] },
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
// 2. One preview render per entry id.
|
|
96
|
+
const renderers: Record<string, () => ReturnType<typeof Button>> = {
|
|
97
|
+
button: () => <Button label="Save" />,
|
|
98
|
+
field: () => <Field label="Name" />,
|
|
99
|
+
toast: () => <Toast>Saved</Toast>,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// 3. App-owned state (domain: which entry; transient: collapsed; global: theme).
|
|
103
|
+
const initial = new URLSearchParams(location.search).get('c') ?? sections[0].entries[0].id;
|
|
104
|
+
const active = signal(initial);
|
|
105
|
+
const collapsed = signal(false);
|
|
106
|
+
const theme = signal<'light' | 'dark'>('light');
|
|
107
|
+
|
|
108
|
+
const app = document.getElementById('app')!;
|
|
109
|
+
mount(app, () => (
|
|
110
|
+
<Catalog
|
|
111
|
+
brand={{ title: 'Acme UI', subtitle: 'Design system' }}
|
|
112
|
+
sections={sections}
|
|
113
|
+
active={active.value}
|
|
114
|
+
content={renderers[active.value]?.() ?? <></>}
|
|
115
|
+
collapsed={collapsed.value}
|
|
116
|
+
theme={theme.value}
|
|
117
|
+
/>
|
|
118
|
+
));
|
|
119
|
+
|
|
120
|
+
wireCatalog(app, {
|
|
121
|
+
onSelect: (id) => { active.value = id; },
|
|
122
|
+
onToggleSidebar: () => { collapsed.value = !collapsed.value; },
|
|
123
|
+
onToggleTheme: () => {
|
|
124
|
+
theme.value = theme.value === 'dark' ? 'light' : 'dark';
|
|
125
|
+
document.documentElement.dataset.theme = theme.value; // apply your theme however you like
|
|
126
|
+
},
|
|
127
|
+
urlParam: 'c', // mirror the active id into ?c=<id>
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Ownership boundary
|
|
132
|
+
|
|
133
|
+
`Catalog` renders the shell; the app owns everything stateful:
|
|
134
|
+
|
|
135
|
+
- **`active`** is domain state (which entry is shown) — the app's signal, updated in
|
|
136
|
+
`onSelect`, read to compute `content`.
|
|
137
|
+
- **`collapsed`** is transient UI — the app's signal, flipped in `onToggleSidebar`.
|
|
138
|
+
- **`theme`** is a global preference — the app's signal; `wireCatalog` only reports
|
|
139
|
+
the toggle, the app applies the theme (the shell reads `theme` to show the toggle's
|
|
140
|
+
opposite-state label). Omit `theme` to hide the toggle entirely.
|
|
141
|
+
|
|
142
|
+
## Custom action names
|
|
143
|
+
|
|
144
|
+
The shell emits `data-action="catalog-select"` (sidebar items),
|
|
145
|
+
`catalog-toggle-sidebar`, and `catalog-toggle-theme`. Override them with
|
|
146
|
+
`selectAction` / `toggleSidebarAction` / `toggleThemeAction` on `Catalog` (and the
|
|
147
|
+
matching options on `wireCatalog`) if they collide with your own action table.
|
|
148
|
+
|
|
149
|
+
## CSS
|
|
150
|
+
|
|
151
|
+
`Catalog` composes public primitives (`Toolbar`, `ListHeader`, `ListItem`, `Select`,
|
|
152
|
+
…). Import `@kerfjs/ui/styles.css` for the whole layer, or `@kerfjs/ui/catalog.css`
|
|
153
|
+
plus each composed primitive's CSS. The shell is theme-aware and responsive: it
|
|
154
|
+
stacks the sidebar above the detail below ~832px and hides it when collapsed.
|
|
@@ -26,11 +26,11 @@ remains authored rather than generated.
|
|
|
26
26
|
- A reusable component never owns per-instance mutable module state.
|
|
27
27
|
- Consumers style through `--kui-*` semantic tokens and public component classes. Foundation tokens provide opinionated neutral, brand/info, success, warning, and danger fill/border/foreground roles. Stateful components expose local override variables; prefer an equivalent prop or token before writing a selector.
|
|
28
28
|
|
|
29
|
-
`
|
|
30
|
-
`
|
|
29
|
+
`ListItem.rootAttributes`, `ListActionRow.rootAttributes`,
|
|
30
|
+
`ListHeader.rootAttributes`, and `AppTab.rootAttributes` accept
|
|
31
31
|
application-owned `data-*` metadata without
|
|
32
|
-
adding product fields to the shared API. A `
|
|
33
|
-
`trailingActionAttributes`, and a `
|
|
32
|
+
adding product fields to the shared API. A `ListActionRow` trailing action uses
|
|
33
|
+
`trailingActionAttributes`, and a `ListHeader` action/disclosure uses
|
|
34
34
|
`triggerAttributes`, for `data-*`, native popover target/action attributes, and
|
|
35
35
|
the corresponding `aria-controls`/`aria-haspopup` relationship. These slots do
|
|
36
36
|
not accept roles or component-owned action, selection, disclosure, accessible
|
|
@@ -46,10 +46,10 @@ component continues to own the named close button or focusable separator, and
|
|
|
46
46
|
`wireTabBars()` / `wireResizableRegions()` continue to own transient behavior
|
|
47
47
|
and disposal.
|
|
48
48
|
|
|
49
|
-
`
|
|
50
|
-
`
|
|
49
|
+
`ListItem.trailing`, `ListActionRow.label`, `ListActionRow.icon`, and
|
|
50
|
+
`ListActionRow.trailingActionIcon` are dormant SafeHtml slots. They must not
|
|
51
51
|
contain controls. When a row needs an independently interactive trailing
|
|
52
|
-
region, `
|
|
52
|
+
region, `ListActionRow` owns the noninteractive root and the two sibling native
|
|
53
53
|
buttons; the application owns their delegated behavior and controlled state.
|
|
54
54
|
Both row components align a leading icon with the first label line when
|
|
55
55
|
`multiline` allows the label to wrap; additional lines extend below that fixed
|
|
@@ -67,7 +67,7 @@ ownership to the application.
|
|
|
67
67
|
|
|
68
68
|
Do not select a component's descendant by element name, id, attribute alone, or
|
|
69
69
|
an unlisted implementation class. Selectors such as `.kui-state-banner span`,
|
|
70
|
-
`.kui-
|
|
70
|
+
`.kui-list-item [data-state]`, and `.kui-list-item .local-label` depend on
|
|
71
71
|
private structure. If no prop, token, or cataloged class expresses a recurring
|
|
72
72
|
need, request a supported hook instead of inferring one from rendered markup.
|
|
73
73
|
|
|
@@ -78,13 +78,13 @@ glyph contract, `--kui-disclosure-icon-scale: .5`; changing one contract does
|
|
|
78
78
|
not implicitly change the other. Direction changes take the shortest rotation
|
|
79
79
|
path; a 180-degree closed-to-open tie uses counterclockwise rotation.
|
|
80
80
|
|
|
81
|
-
In `
|
|
81
|
+
In `ListHeader` toggle mode, omitting `actionIcon` composes the production
|
|
82
82
|
`DisclosureArrow` and derives its visual direction from `expanded`. The app
|
|
83
83
|
must update that controlled state and reveal or hide real content. Passing a
|
|
84
84
|
custom `actionIcon` replaces the default rather than layering or rotating both.
|
|
85
85
|
The root fills the available inline width after its standard margins. A
|
|
86
86
|
separate action stays at the logical end in its 44px target while the visible
|
|
87
|
-
glyph defaults to 18px through `--kui-
|
|
87
|
+
glyph defaults to 18px through `--kui-list-header-action-icon-size`.
|
|
88
88
|
|
|
89
89
|
`StateBanner` exposes instance-level `--kui-state-banner-background`,
|
|
90
90
|
`--kui-state-banner-border`, `--kui-state-banner-foreground`,
|
|
@@ -113,7 +113,7 @@ changing geometry. `.kui-content-item--pill` selects the 22px radius.
|
|
|
113
113
|
|
|
114
114
|
`ToolbarControlGroup` is the unit of toolbar organization, even for dormant
|
|
115
115
|
text. Each group reserves `calc(2px + remify(42px))`, or 44px, with 8px between
|
|
116
|
-
groups and inside items. `
|
|
116
|
+
groups and inside items. `ListHeader` similarly separates its dormant title and
|
|
117
117
|
optional count or badge from its optional 44px action. Use the mutually
|
|
118
118
|
exclusive `count`/`countLabel` pair for non-negative safe-integer section
|
|
119
119
|
quantities; reserve `badge` for non-count `SafeHtml`. Do not concatenate counts
|
|
@@ -172,7 +172,7 @@ and tab order are data, a `ResizableRegion`'s committed size and a
|
|
|
172
172
|
Each helper already owns only the *ephemeral mechanics* around that state —
|
|
173
173
|
`wireNavStack` the push/pop animation, `wireTabBars` the overflow autoscroll and
|
|
174
174
|
drag preview, `wireResizableRegions` the live drag preview — and reports committed
|
|
175
|
-
changes through callbacks. A `
|
|
175
|
+
changes through callbacks. A `ListHeader` `toggle` disclosure's `expanded` is
|
|
176
176
|
likewise app-owned: it is a one-line boolean the app already tracks and must read to
|
|
177
177
|
render the section body, so a managed helper would remove no real complexity. Reach
|
|
178
178
|
for a managed default only when the transient behavior is substantial enough that
|
|
@@ -34,11 +34,12 @@ application boundary:
|
|
|
34
34
|
| Desktop application shell | [Catalog](../ux-demo/) · `?component=recipe-app-shell` |
|
|
35
35
|
| Navigation sidebar | [Catalog](../ux-demo/) · `?component=recipe-navigation-sidebar` |
|
|
36
36
|
| Workspace header | [Catalog](../ux-demo/) · `?component=recipe-workspace-header` |
|
|
37
|
-
|
|
|
37
|
+
| List-detail dialog | [Catalog](../ux-demo/) · `?component=recipe-list-detail-dialog` |
|
|
38
38
|
| Composer form | [Catalog](../ux-demo/) · `?component=recipe-composer-form` |
|
|
39
39
|
| List workspace states | [Catalog](../ux-demo/) · `?component=recipe-list-workspace-states` |
|
|
40
40
|
| Compact toolbar choices and actions | [Catalog](../ux-demo/) · `?component=recipe-compact-toolbar` |
|
|
41
41
|
| Navigation stack | [Catalog](../ux-demo/) · `?component=recipe-navigation-stack` |
|
|
42
|
+
| Loading inspector | [Catalog](../ux-demo/) · `?component=recipe-loading-inspector` |
|
|
42
43
|
|
|
43
44
|
Recipes use public production exports and show ownership boundaries; they are
|
|
44
45
|
copyable reference compositions, not new monolithic components.
|
|
@@ -63,18 +64,18 @@ an upstream component or recipe request.
|
|
|
63
64
|
| Application toolbar — `Toolbar` | Leading identity, optional centered content, and trailing controls form one horizontal app bar. | Do not use it for a page, panel, or dialog heading; use `PanelHeader`. | Compose `ToolbarText` and `ToolbarControlGroup` where their contracts fit. | Actions, command availability, responsive relocation, and state. | `@kerfjs/ui/toolbar` | [Toolbar composition](../README.md#component-subpaths) |
|
|
64
65
|
| Toolbar control cluster — `ToolbarControlGroup` | Related toolbar controls need contained, borderless, pressed, or single-control treatment. | Do not use it merely to align unrelated buttons; use toolbar slots or ordinary layout. Web Awesome `wa-button-group` is only for an exceptional grouped-action contract. | Delegate child actions; use `SegmentedControl` for an exclusive choice. | Actions, pressed/expanded state, and policy. | `@kerfjs/ui/toolbar-control-group` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
65
66
|
| Toolbar identity text — `ToolbarText` | A toolbar needs extra-large (page/panel title), large, default, or compact textual identity. | Plain text is not a heading by default; when a title needs heading semantics pass `headingLevel` (or prefer `PanelHeader`, which forwards it). | Optional `headingLevel` for `role="heading"` + `aria-level`. | Text, heading level, and responsive priority. | `@kerfjs/ui/toolbar-text` | [Toolbar composition](../README.md#component-subpaths) |
|
|
66
|
-
| Navigation row — `
|
|
67
|
-
| Navigation row with trailing action — `
|
|
68
|
-
| Navigation section heading — `
|
|
67
|
+
| Navigation row — `ListItem` | A pane or navigation area needs a selectable, disabled, dormant-trailing, or multiline action row. | Do not put a control in `trailing`; use `ListActionRow` when the trailing region must be independently interactive. Use an `<a>` for navigation that must retain link behavior, a native `<button>` for an ordinary action, or implement the complete ARIA menu widget. | Delegate its `data-action`; compose inside a `.kui-content` section. Put domain event/drop metadata in `rootAttributes` rather than adding wrapper markup. | Routing, selection, permissions, copy, action handling, and domain `data-*` values. | `@kerfjs/ui/list-item` | [Pane geometry](../README.md#pane-and-content-geometry) |
|
|
68
|
+
| Navigation row with trailing action — `ListActionRow` | A full-width row needs a selectable primary action and an independently focusable trailing action. | Use `ListItem` when trailing content is dormant metadata. Do not put controls inside the row's `label`, `icon`, or `trailingActionIcon` SafeHtml slots. Do not use `AppTab` outside tablist semantics or `ToolbarControlGroup` outside a toolbar. | Delegate both action strings; update controlled selection and any popover/context-menu state in the app. | Routing, selection, both action policies, domain metadata, and popover/context-menu behavior. | `@kerfjs/ui/list-action-row` | [Accessibility](./accessibility.md#listactionrow) |
|
|
69
|
+
| Navigation section heading — `ListHeader` | A menu section needs a full-width label, semantic count, non-count badge, logical-end action, or real disclosure state. | Do not concatenate counts into `label` or put numeric content in `badge`; use `count` with the localized full phrase in `countLabel`. Do not add a disclosure arrow to navigation that reveals nothing. Do not shrink its 44px action target to the 18px visual. Do not use it as a page, panel, or dialog title; use `PanelHeader`. | Delegate its optional action; the app controls expanded state and revealed content. Toggle mode supplies `DisclosureArrow` unless `actionIcon` replaces it. Use `triggerAttributes` only for domain `data-*` or a native popover relationship. | Section organization, valid count and localized count label, disclosure state and content, non-count badge content, popover target behavior, and policy. | `@kerfjs/ui/list-header` | [Pane geometry](../README.md#pane-and-content-geometry) |
|
|
69
70
|
| Application layout composition | A sidebar, main area, inspector, or dialog needs shared toolbar/content/footer and child geometry. | Do not pad the pane shell, wrap child-owned geometry in competing insets, invent unrelated centered measures, or leave an icon-only rail for a hidden pane. | Use `.kui-pane` and one `.kui-pane__content`; add `.kui-content` and `.kui-content-item` as needed. A visible pane owns collapse in its toolbar; move a hidden inline-start pane's restore control to the main toolbar leading edge and an inline-end pane's restore control to its trailing edge. | Layout hierarchy, reading width, scroll ownership, responsive relocation, and pane visibility state. | `@kerfjs/ui/layout.css` | [Pane anatomy](./layout.md#anatomy) |
|
|
70
|
-
| Menu composition | Navigation sections need full-size rows and the same content-item geometry as every other pane. | Do not add sidebar-specific wrapper padding, shrink targets to icon size, nest an interactive trailing control in `
|
|
71
|
+
| Menu composition | Navigation sections need full-size rows and the same content-item geometry as every other pane. | Do not add sidebar-specific wrapper padding, shrink targets to icon size, nest an interactive trailing control in `ListItem`, or use a chevron on a row that does not disclose content. Use ordinary links for a different navigation contract. | Compose `ListHeader`, `ListItem`, and `ListActionRow` in `.kui-content`; use `ListHeader` toggle mode with real controlled content, and use `.kui-content-item` for other surfaces plus a pane footer for toolbar actions. | Information architecture, disclosure content and state, responsive drawer/shell behavior, and token overrides. | `@kerfjs/ui/layout.css` | [Pane geometry](../README.md#pane-and-content-geometry) |
|
|
71
72
|
| Resizable application pane — `ResizableRegion`, `clampRegionSize`, `resizeRegionFromPointer` | A controlled split pane needs the Kerf separator, collapse state, pointer plus keyboard resizing, or a product-specific decorative grip. | Do not use it for a static two-column layout; use CSS grid. Prefer it over Web Awesome `wa-split-panel` unless that component's distinct API is required. Keep `handleIcon` noninteractive. | Call `wireResizableRegions` from `@kerfjs/ui/wire-resizable-regions` once and retain its disposer. | Size signal, min/max policy, collapse policy, persistence, and optional decorative handle icon. | `@kerfjs/ui/resizable-region` | [ResizableRegion contract](./accessibility.md#resizableregion) |
|
|
72
73
|
| One application tab — `AppTab` | A controlled app tab needs selection, close, drag, leading/trailing anatomy, safe domain metadata, or a product-specific close glyph. | Do not render it alone or use it for a small settings choice; compose in `TabBar`, or use `SegmentedControl`. Keep `closeIcon` noninteractive. | Compose in `TabBar`; let `wireTabBars` manage interaction. Put only domain `data-*` values in `rootAttributes`. | Tab identity, order, selection, close policy, content, and domain metadata values. | `@kerfjs/ui/app-tab` | [Tabs contract](./accessibility.md#tabs) |
|
|
73
74
|
| Application tab strip — `TabBar`, `wireTabBars`, `reorderTabs` | Tabs switch page regions and may overflow, close, or reorder. | Do not use it for a compact local view toggle; use `SegmentedControl`. Do not use it for a long choice list; use `Select`. Prefer it over Web Awesome `wa-tab-group`, `wa-tab`, and `wa-tab-panel` for Kerf app tabs. | Call `wireTabBars` once, retain the disposer, and apply `onReorder` synchronously; `reorderTabs` is the default array helper. | Ordered tabs, selection, panels, routing, closing, and persistence. | `@kerfjs/ui/tab-bar` plus `@kerfjs/ui/wire-tab-bars` | [Tabs contract](./accessibility.md#tabs) |
|
|
74
75
|
| Panel, dialog, or page heading — `PanelHeader` | A panel, dialog, or page needs a heading with an extra-large title, an optional icon and subtitle, and trailing actions. | Do not use it as persistent app chrome; use `Toolbar`. It supplies header structure, not modal behavior; use an application overlay or Web Awesome `wa-dialog` for that behavior. | Connect the title id and any provided summary id to the dialog or panel host, pass the trailing controls (typically a `ToolbarControlGroup`), and delegate their actions. **For a page or view title set `headingLevel` (usually `1`)** so the title is a real heading landmark (`role="heading"` + `aria-level`) for screen-reader heading navigation; omit it for a dialog title, which is referenced via `aria-labelledby` to `titleId` and needs no heading. | Open state, focus lifecycle, dismissal, the trailing controls, labels, copy, and the heading level for page use. | `@kerfjs/ui/panel-header` | [Header ownership](./component-contract.md#extracted-versus-application-specific) |
|
|
75
76
|
| Key/value facts — `ValueTable`, `ValueTableRow` | Read-only labels and values form a semantic definition list, optionally with a leading icon. | Do not use it for editable form fields or a row/column data grid; use native form or table semantics. | Compose typed `ValueTableRow` entries; pass `icon` when a 24px leading icon adds useful context. | Values, formatting, icon meaning, and empty/loading policy. | `@kerfjs/ui/value-table` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
76
77
|
| Indeterminate activity — `LoadingSpinner` | A Kerf surface needs compact, labeled or decorative indeterminate progress. | Do not use it for known progress; use Web Awesome `wa-progress-bar` or `wa-progress-ring`. Direct Web Awesome UI may use `wa-spinner`; do not mix spinner systems within one surface. | None; pass a label when the spinner conveys status. | Loading lifecycle and adjacent status copy. | `@kerfjs/ui/loading-spinner` | [Accessibility](./accessibility.md#shared-rules) |
|
|
77
|
-
| Loading placeholder — `Skeleton` + a component's `placeholder` prop | A value or a whole component is still loading and should hold its space as a subtle, unanimated block, keeping the layout stable — an inspector or detail view rendering its real chrome with per-record values absent. | Do not use it for known progress (use `LoadingSpinner`), do not animate it, and do not hand-rebuild a component's empty state — set `placeholder` on the component instead. Prefer it over `wa-skeleton`, which the pure-Kerf primitives avoid to stay Web-Awesome-free. | Set `placeholder` on a value-bearing component (`Select`, `
|
|
78
|
+
| Loading placeholder — `Skeleton` + a component's `placeholder` prop | A value or a whole component is still loading and should hold its space as a subtle, unanimated block, keeping the layout stable — an inspector or detail view rendering its real chrome with per-record values absent. | Do not use it for known progress (use `LoadingSpinner`), do not animate it, and do not hand-rebuild a component's empty state — set `placeholder` on the component instead. Prefer it over `wa-skeleton`, which the pure-Kerf primitives avoid to stay Web-Awesome-free. | Set `placeholder` on a value-bearing component (`Select`, `ListHeader`, `ListItem`, `ValueTableRow`, `PanelHeader`, `SegmentedControl`, `StateBanner`, `AppTab`, `ToolbarText`, `ListActionRow`) to render skeletons in its value slots with interactivity disabled; use the standalone `Skeleton` for a custom slot. | Loading lifecycle, which slots are unknown, and announcing the loading region. | `@kerfjs/ui/skeleton` | [Accessibility](./accessibility.md#shared-rules) |
|
|
78
79
|
| Value selection — `Select` | A controlled form value comes from a moderate or long choice list, possibly grouped or icon-bearing. | Do not use it for commands; use a real action menu. Do not use it for a small visible choice set; use `SegmentedControl`. Prefer it over direct `wa-select`, `wa-option`, or value-like `wa-dropdown`/`wa-dropdown-item` composition. | Import `@kerfjs/ui/select/register` once; listen for standard input/change events. | Controlled value, validation, choices, and domain mapping. | `@kerfjs/ui/select` | [Web Awesome integration](../README.md#web-awesome-theme) |
|
|
79
80
|
| Small exclusive choice — `SegmentedControl` | A few visible choices switch a compact view or setting, with toolbar, rounded, or pill presentation. | Do not use it for tabpanel semantics; use `TabBar`. Do not use it for many choices; use `Select`. Prefer it over `wa-button-group` when the controls select one value. | Delegate its action, read `data-segment-value`, update `value`, and rerender. | Controlled value, labels, action, and persistence. | `@kerfjs/ui/segmented-control` | [SegmentedControl contract](./accessibility.md#segmentedcontrol) |
|
|
80
81
|
| Structured search editor — `TokenSearchField`, `readTokenSearchField`, `placeTokenSearchCaret`, `wireTokenSearchFields` | Free text and ordered, editable, removable filter tokens share one searchbox; enable `collapsible` when an empty, unfocused field should reduce to one iconic action, standalone or in a toolbar group. | Do not use it for ordinary text entry; use a native input or Web Awesome `wa-input`. Do not use it when filters belong in separate form controls. | Read DOM-owned text on input, empty `textContent` on clear, and use `placeTokenSearchCaret` after explicit controlled focus changes. Call `wireTokenSearchFields` from `@kerfjs/ui/wire-token-search-fields` once so Enter submits without adding a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. In `collapsible` mode it also manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse); bind the field's `expanded` to the signal on the returned handle (`handle.expanded(id)`) or adopt your own via `collapsible.signals`, and opt out per behavior only when the app must own it. | Parsing, suggestions, tokens, query execution, results, announcements, and — only if overriding the default — the collapsible `expanded` signal. | `@kerfjs/ui/token-search-field` | [TokenSearchField contract](./accessibility.md#tokensearchfield) |
|
|
@@ -138,9 +139,9 @@ geometry and 44px targets.
|
|
|
138
139
|
<aside class="kui-pane">
|
|
139
140
|
<nav class="kui-pane__content kui-content">
|
|
140
141
|
<section>
|
|
141
|
-
<
|
|
142
|
-
<
|
|
143
|
-
<
|
|
142
|
+
<ListHeader label="Workspace" />
|
|
143
|
+
<ListItem action="open" label="Inbox" icon={inboxIcon} />
|
|
144
|
+
<ListActionRow action="open-file" label="main.ts" trailingAction="file-actions" trailingActionLabel="Actions for main.ts" trailingActionIcon={moreIcon} />
|
|
144
145
|
</section>
|
|
145
146
|
<div class="kui-content-item">Workspace details</div>
|
|
146
147
|
</nav>
|
|
@@ -152,7 +153,7 @@ forks the package anatomy and spacing contract.
|
|
|
152
153
|
|
|
153
154
|
```tsx
|
|
154
155
|
<aside class="sidebar padded">
|
|
155
|
-
<h2 class="
|
|
156
|
+
<h2 class="list-header-copy">Workspace</h2>
|
|
156
157
|
<button class="menu-row-copy padded">Inbox</button>
|
|
157
158
|
<div class="panel indented-with-negative-margin">Workspace details</div>
|
|
158
159
|
</aside>
|
|
@@ -166,7 +167,7 @@ registration, and the CSS-only `@kerfjs/ui/webawesome.css` theme.
|
|
|
166
167
|
|
|
167
168
|
| Web Awesome choice | Kerf decision |
|
|
168
169
|
| --- | --- |
|
|
169
|
-
| `wa-button`, `wa-dropdown`, `wa-dropdown-item` | Use buttons and command menus for actions. Use `
|
|
170
|
+
| `wa-button`, `wa-dropdown`, `wa-dropdown-item` | Use buttons and command menus for actions. Use `ListItem` for a navigation row and `Select` when the user chooses a value. |
|
|
170
171
|
| `wa-button-group` | Use only for exceptional grouped actions; use `SegmentedControl` for one-of-many selection. |
|
|
171
172
|
| `wa-input`, `wa-tag` | Use for ordinary text and tags; use `TokenSearchField` only when text and ordered filter tokens form one editor. |
|
|
172
173
|
| `wa-select`, `wa-option` | Use `Select`, which owns Kerf spacing, controlled rendering, icon stability, and explicit registration. |
|
|
@@ -53,9 +53,9 @@ Use the package composition so the geometry has one owner:
|
|
|
53
53
|
<aside class="kui-pane">
|
|
54
54
|
<nav class="kui-pane__content kui-content">
|
|
55
55
|
<section>
|
|
56
|
-
<
|
|
57
|
-
<
|
|
58
|
-
<
|
|
56
|
+
<ListHeader label="Workspace" />
|
|
57
|
+
<ListItem action="open" label="Inbox" icon={inboxIcon} />
|
|
58
|
+
<ListItem action="open" label="Drafts" />
|
|
59
59
|
</section>
|
|
60
60
|
<div class="kui-content-item">Panel contents</div>
|
|
61
61
|
</nav>
|
|
@@ -68,7 +68,7 @@ and makes transparent borders behave differently from visible ones:
|
|
|
68
68
|
```tsx
|
|
69
69
|
<aside class="pane padded">
|
|
70
70
|
<section class="padded">
|
|
71
|
-
<
|
|
71
|
+
<ListHeader label="Workspace" />
|
|
72
72
|
<div class="panel padded-again">Panel contents</div>
|
|
73
73
|
</section>
|
|
74
74
|
</aside>
|
package/docs/layout.md
CHANGED
|
@@ -23,7 +23,7 @@ omits the footer; a navigation pane commonly uses all three. Fixed chrome stays
|
|
|
23
23
|
outside `.kui-pane__content`, which is the pane's scroll owner.
|
|
24
24
|
|
|
25
25
|
`.kui-content` is a vertical stack with a 24px gap between major children.
|
|
26
|
-
Sections may contain adjacent `
|
|
26
|
+
Sections may contain adjacent `ListItem` rows without adding another major gap.
|
|
27
27
|
Ordinary surface-like children use `.kui-content-item` and own their complete
|
|
28
28
|
geometry:
|
|
29
29
|
|
|
@@ -57,8 +57,8 @@ semantic status.
|
|
|
57
57
|
| Inline metadata | `.kui-inline-metadata` | `--kui-layout-metadata-gap: 4px` |
|
|
58
58
|
| Explicit scroll owner outside a pane | `.kui-scroll-owner` | `overflow: auto` |
|
|
59
59
|
|
|
60
|
-
The component layer applies the same contract to `Toolbar`, `
|
|
61
|
-
`
|
|
60
|
+
The component layer applies the same contract to `Toolbar`, `ListHeader`,
|
|
61
|
+
`ListItem`, `PanelHeader`, `StateBanner`, `ValueTable`,
|
|
62
62
|
`ValueTableRow`, tabs, and form controls. A value-table row separator starts at
|
|
63
63
|
the row's 8px content inset, or at 40px when the row contains its 24px leading
|
|
64
64
|
icon and 8px gap, and always ends 8px from the right edge. Each value-table row
|
|
@@ -83,7 +83,7 @@ fixed 16px baseline, so it delivers as `rem`.
|
|
|
83
83
|
|
|
84
84
|
| Value | Token | Relationship — when to use |
|
|
85
85
|
| --- | --- | --- |
|
|
86
|
-
| 0px | `--kui-space-none` | No separation. The elements read as a single unit (a control and its own affordance, adjacent `
|
|
86
|
+
| 0px | `--kui-space-none` | No separation. The elements read as a single unit (a control and its own affordance, adjacent `ListItem` rows). |
|
|
87
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
88
|
| 8px | `--kui-space-xs` | Standard. Between elements **within a group** — the content-item padding/gutter, gaps between toolbar controls in a group. |
|
|
89
89
|
| 16px | `--kui-space-m` | Minor. Between **homogeneous groups** — two lists, two sibling sections of the same kind. |
|
|
@@ -109,14 +109,14 @@ consistent across every surface.
|
|
|
109
109
|
4. Keep one scrolling content owner per pane. Toolbar and footer siblings stay
|
|
110
110
|
fixed while the content scrolls.
|
|
111
111
|
5. A split item keeps dormant and interactive regions separate. For example,
|
|
112
|
-
`
|
|
112
|
+
`ListHeader` renders its title/count-or-badge cluster separately from its optional
|
|
113
113
|
logical-end 44px action. The header fills the available inline width and its
|
|
114
114
|
action glyph defaults to 18px; disclosure mode makes the title cluster itself
|
|
115
115
|
the button and supplies the production `DisclosureArrow` unless `actionIcon`
|
|
116
116
|
replaces it.
|
|
117
|
-
`
|
|
118
|
-
and trailing buttons. `
|
|
119
|
-
In multiline `
|
|
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
120
|
centered on the label's first line rather than the full wrapped label.
|
|
121
121
|
6. Reading width, column placement, and responsive relocation remain application
|
|
122
122
|
decisions. The shared classes define local geometry, not the whole shell.
|
package/docs/recipes.md
CHANGED
|
@@ -44,12 +44,12 @@ Adapt only public `--kui-layout-*` and component variables.
|
|
|
44
44
|
[Open the recipe](../ux-demo/?component=recipe-navigation-sidebar) · [TSX source](../ux-demo/recipes/navigation-sidebar.tsx)
|
|
45
45
|
|
|
46
46
|
One unpadded `.kui-pane` owns toolbar/content/footer structure. Its
|
|
47
|
-
`.kui-content` uses 24px major gaps; `
|
|
47
|
+
`.kui-content` uses 24px major gaps; `ListHeader`, `ListItem`, and other
|
|
48
48
|
`.kui-content-item` children own their 8px margin, 1px border, and 8px padding.
|
|
49
49
|
Rows and footer toolbar groups remain 44px tall. The app owns routes,
|
|
50
50
|
permissions, labels, selection, valid section counts and their localized
|
|
51
51
|
`countLabel` phrases, non-count badge content, disclosure state, and revealed
|
|
52
|
-
content. A toggled `
|
|
52
|
+
content. A toggled `ListHeader` supplies the production `DisclosureArrow` when
|
|
53
53
|
no custom `actionIcon` is needed; ordinary navigation rows stay chevron-free.
|
|
54
54
|
|
|
55
55
|
## Workspace header
|
|
@@ -60,13 +60,13 @@ no custom `actionIcon` is needed; ordinary navigation rows stay chevron-free.
|
|
|
60
60
|
overflow, and primary actions. The app owns authorization and command policy;
|
|
61
61
|
controls relocate without changing focus order.
|
|
62
62
|
|
|
63
|
-
##
|
|
63
|
+
## List-detail dialog
|
|
64
64
|
|
|
65
|
-
[Open the recipe](../ux-demo/?component=recipe-
|
|
65
|
+
[Open the recipe](../ux-demo/?component=recipe-list-detail-dialog) · [TSX source](../ux-demo/recipes/list-detail-dialog.tsx)
|
|
66
66
|
|
|
67
67
|
The production Web Awesome dialog owns modal focus and Escape; the thin recipe
|
|
68
68
|
adapter restores the invoking control consistently after the hide event.
|
|
69
|
-
`PanelHeader`, `
|
|
69
|
+
`PanelHeader`, `ListHeader`, `ListItem`, and `ValueTable` own their included
|
|
70
70
|
anatomy. The application owns open state, selection, dismissal policy, and
|
|
71
71
|
record actions. `PanelHeader` places the trailing controls the app passes
|
|
72
72
|
directly into its top toolbar's trailing zone.
|
|
@@ -121,6 +121,18 @@ settles the chrome (reduced motion collapses the slide to instant). A live
|
|
|
121
121
|
[`app-layouts.md`](app-layouts.md) for choosing among `NavStack`, `SplitView`,
|
|
122
122
|
`Workbench`, and `TabScaffold`.
|
|
123
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
|
+
|
|
124
136
|
## Rules shared by every recipe
|
|
125
137
|
|
|
126
138
|
- Import `@kerfjs/ui/layout.css`; keep every pane unpadded and use exactly one
|
package/docs/split-view.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Split view (list-detail)
|
|
2
2
|
|
|
3
|
-
`@kerfjs/ui/split-view` is a list-detail
|
|
3
|
+
`@kerfjs/ui/split-view` is a list-detail layout: a list and a
|
|
4
4
|
detail side by side on roomy screens, collapsing to a `NavStack` (list → detail)
|
|
5
5
|
on compact ones. One of the opt-in app layouts (see
|
|
6
6
|
[`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
|
package/docs/ux-demo.md
CHANGED
|
@@ -7,21 +7,21 @@ Each `?component=recipe-*` route uses public package primitives, real wiring,
|
|
|
7
7
|
deterministic state, and semantic layout owners from the [recipe guide](./recipes.md).
|
|
8
8
|
|
|
9
9
|
- It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
|
|
10
|
-
- The `
|
|
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
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.
|
|
12
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.
|
|
13
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
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 `
|
|
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
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
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
|
|
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
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 `
|
|
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
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
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.
|
|
23
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.
|
|
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;
|
|
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
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
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
27
|
- The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
|
package/llms.txt
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
npm install kerfjs @kerfjs/ui # kerfjs is a peer; Web Awesome is an optional peer
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
Import each component from its own subpath (`@kerfjs/ui/
|
|
12
|
+
Import each component from its own subpath (`@kerfjs/ui/list-item`, `@kerfjs/ui/toolbar`,
|
|
13
13
|
…); the browser build pulls that component's reachable CSS automatically, so do not
|
|
14
14
|
maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
15
15
|
|
|
@@ -82,9 +82,9 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
|
82
82
|
provides; but do keep a plain disclosure toggle (a one-line boolean the app already
|
|
83
83
|
renders from) in the app.
|
|
84
84
|
- **Placeholder loading state, don't rebuild it.** For a value or component that is
|
|
85
|
-
still loading, set `placeholder` on the component (`Select`, `
|
|
86
|
-
`
|
|
87
|
-
`AppTab`, `ToolbarText`, `
|
|
85
|
+
still loading, set `placeholder` on the component (`Select`, `ListHeader`,
|
|
86
|
+
`ListItem`, `ValueTableRow`, `PanelHeader`, `SegmentedControl`, `StateBanner`,
|
|
87
|
+
`AppTab`, `ToolbarText`, `ListActionRow`): it renders its real chrome with value
|
|
88
88
|
slots as subtle **unanimated** skeleton blocks and interactivity disabled, so a
|
|
89
89
|
parent composes a faithful loading view (e.g. an inspector) without hand-rebuilding
|
|
90
90
|
markup. Use the standalone `Skeleton` for a custom slot. Prefer it over `wa-skeleton`
|
|
@@ -94,15 +94,19 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
|
94
94
|
## Public exports
|
|
95
95
|
|
|
96
96
|
`LucideIcon`, `DisclosureArrow`, `Toolbar`, `ToolbarControlGroup`, `ToolbarText`,
|
|
97
|
-
`PanelHeader`, `
|
|
97
|
+
`PanelHeader`, `ListItem`, `ListActionRow`, `ListHeader`, `SegmentedControl`,
|
|
98
98
|
`Select`, `TokenSearchField` (+ `readTokenSearchField`, `placeTokenSearchCaret`,
|
|
99
99
|
`wireTokenSearchFields`), `AppTab`, `TabBar` (+ `wireTabBars`, `reorderTabs`),
|
|
100
100
|
`ResizableRegion` (+ `wireResizableRegions`), `StateBanner`, `EmptyState`,
|
|
101
101
|
`LoadingSpinner`, `Skeleton`, `ValueTable`, `ValueTableRow`. Whole-screen layouts (opt-in,
|
|
102
102
|
tree-shakeable, declarative with `wire…` helpers, each with a companion CSS import):
|
|
103
103
|
`NavStack`, `SplitView`, `Workbench`, `TabScaffold`, driven by `deviceClass()` from
|
|
104
|
-
`@kerfjs/ui/device-class`. `@kerfjs/ui/
|
|
105
|
-
`
|
|
104
|
+
`@kerfjs/ui/device-class`. `@kerfjs/ui/catalog` is another opt-in shell — a
|
|
105
|
+
`Catalog` component + `wireCatalog` helper for building a component-gallery catalog
|
|
106
|
+
(collapsible category sidebar + preview stage + resources footer) from your own
|
|
107
|
+
entries; controlled/stateless like the layouts. `@kerfjs/ui/webawesome` is a
|
|
108
|
+
type-only boundary for `wa-*` JSX; `@kerfjs/ui/select/register` and the CSS are the
|
|
109
|
+
declared side effects.
|
|
106
110
|
|
|
107
111
|
## Reference
|
|
108
112
|
|
|
@@ -111,7 +115,7 @@ tree-shakeable, declarative with `wire…` helpers, each with a companion CSS im
|
|
|
111
115
|
- [layout.md](./docs/layout.md): the pane/content-item geometry, public roles/tokens, and the spacing scale.
|
|
112
116
|
- [component-contract.md](./docs/component-contract.md): what a component owns vs. what the app owns.
|
|
113
117
|
- [accessibility.md](./docs/accessibility.md): keyboard and ARIA contracts per component.
|
|
114
|
-
- [recipes.md](./docs/recipes.md): production compositions (app shell,
|
|
118
|
+
- [recipes.md](./docs/recipes.md): production compositions (app shell, list-detail dialog, composer form, …) as copyable references.
|
|
115
119
|
- [app-layouts.md](./docs/app-layouts.md): choosing `NavStack`/`SplitView`/`Workbench`/`TabScaffold` by data + interaction + device, and dialog presentation.
|
|
116
120
|
- [webawesome-theme.md](./docs/webawesome-theme.md): the optional Web Awesome theme; catalog coverage means themed-and-supported, not preferred (prefer the Kerf primitive when one exists).
|
|
117
121
|
- [component-catalog.json](./ai/component-catalog.json) + [public-api-signatures-v1.md](./ai/public-api-signatures-v1.md) + [webawesome-jsx-signatures-v1.md](./ai/webawesome-jsx-signatures-v1.md): machine-readable facts and exact signatures.
|