@kerfjs/ui 5.0.0-beta.48 → 5.0.0-beta.50
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 +49 -13
- package/ai/catalog-authoring.json +12 -1
- package/ai/catalog-authoring.schema.json +49 -1
- package/ai/compile-time-contracts-v1.json +14 -0
- package/ai/compile-time-contracts-v1.schema.json +1 -0
- package/ai/component-catalog-v2-overrides.json +7 -0
- package/ai/component-catalog-v2.json +164 -23
- package/ai/component-catalog.json +184 -27
- package/ai/public-api-signatures-v1.md +239 -168
- package/ai/skill.md +27 -12
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/app-tab.d.ts +3 -1
- package/dist/app-tab.js +1 -1
- package/dist/badge.d.ts +24 -0
- package/dist/badge.js +3 -0
- package/dist/badge.js.map +1 -0
- package/dist/browser/badge.js +3 -0
- package/dist/browser/grid.js +3 -0
- package/dist/browser/list-header.js +1 -0
- package/dist/browser/state-banner.js +1 -0
- package/dist/catalog-resources.d.ts +2 -2
- package/dist/catalog.d.ts +24 -125
- package/dist/catalog.js +387 -261
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-OGTJNHRG.js → chunk-2WMSE4MK.js} +5 -3
- package/dist/chunk-2WMSE4MK.js.map +1 -0
- package/dist/{chunk-GYGM363P.js → chunk-2YOUUEWR.js} +16 -6
- package/dist/chunk-2YOUUEWR.js.map +1 -0
- package/dist/{chunk-UDHHKZWO.js → chunk-3V75EQIP.js} +8 -4
- package/dist/chunk-3V75EQIP.js.map +1 -0
- package/dist/{chunk-W2FK7ZNY.js → chunk-4R777DUZ.js} +10 -2
- package/dist/chunk-4R777DUZ.js.map +1 -0
- package/dist/{chunk-T7DOWS6T.js → chunk-54HM36IQ.js} +6 -3
- package/dist/chunk-54HM36IQ.js.map +1 -0
- package/dist/{chunk-DAO2VFTT.js → chunk-5CE64WGC.js} +6 -4
- package/dist/chunk-5CE64WGC.js.map +1 -0
- package/dist/{chunk-SOK5EUF7.js → chunk-6ZXOQEAN.js} +4 -2
- package/dist/chunk-6ZXOQEAN.js.map +1 -0
- package/dist/{chunk-OGNE3OFT.js → chunk-AQSKJ6U6.js} +4 -2
- package/dist/chunk-AQSKJ6U6.js.map +1 -0
- package/dist/{chunk-VDUJZF5G.js → chunk-CMUIXJZM.js} +2 -2
- package/dist/{chunk-VDUJZF5G.js.map → chunk-CMUIXJZM.js.map} +1 -1
- package/dist/{chunk-OMSRSL3B.js → chunk-COUPLRZG.js} +5 -3
- package/dist/chunk-COUPLRZG.js.map +1 -0
- package/dist/{chunk-2TYUFZUC.js → chunk-D7GWXIAS.js} +4 -2
- package/dist/chunk-D7GWXIAS.js.map +1 -0
- package/dist/{chunk-AQFWBG23.js → chunk-DARTMI5Z.js} +5 -3
- package/dist/chunk-DARTMI5Z.js.map +1 -0
- package/dist/{chunk-V6GQKOX2.js → chunk-DJKWFYAA.js} +5 -3
- package/dist/chunk-DJKWFYAA.js.map +1 -0
- package/dist/{chunk-6NDBE2OK.js → chunk-E36EVCKG.js} +5 -3
- package/dist/chunk-E36EVCKG.js.map +1 -0
- package/dist/chunk-ELRMBQEC.js +47 -0
- package/dist/chunk-ELRMBQEC.js.map +1 -0
- package/dist/{chunk-TIGSG6BX.js → chunk-HJKRCKDR.js} +7 -3
- package/dist/chunk-HJKRCKDR.js.map +1 -0
- package/dist/{chunk-Y6LYKFXY.js → chunk-HMQ2ZDGG.js} +5 -3
- package/dist/chunk-HMQ2ZDGG.js.map +1 -0
- package/dist/{chunk-WN52USHV.js → chunk-J3EE6XWD.js} +2 -2
- package/dist/{chunk-WN52USHV.js.map → chunk-J3EE6XWD.js.map} +1 -1
- package/dist/{chunk-CK6P5AUK.js → chunk-KKNK3JUT.js} +7 -4
- package/dist/chunk-KKNK3JUT.js.map +1 -0
- package/dist/{chunk-ZPWZIVI6.js → chunk-N3GKMPDW.js} +7 -3
- package/dist/chunk-N3GKMPDW.js.map +1 -0
- package/dist/{chunk-DYBPI5O2.js → chunk-NDUGJ4TU.js} +4 -2
- package/dist/chunk-NDUGJ4TU.js.map +1 -0
- package/dist/{chunk-WSXUPSID.js → chunk-NN6GYOA2.js} +7 -3
- package/dist/chunk-NN6GYOA2.js.map +1 -0
- package/dist/{chunk-VXFGMDA7.js → chunk-OQI7RCRP.js} +10 -2
- package/dist/chunk-OQI7RCRP.js.map +1 -0
- package/dist/{chunk-E34TE2ZI.js → chunk-S6ZTTTV7.js} +5 -3
- package/dist/chunk-S6ZTTTV7.js.map +1 -0
- package/dist/{chunk-V6HCTCVD.js → chunk-SIU2AC5W.js} +8 -4
- package/dist/chunk-SIU2AC5W.js.map +1 -0
- package/dist/{chunk-WVLMZ5SG.js → chunk-UMGY5JSP.js} +4 -2
- package/dist/chunk-UMGY5JSP.js.map +1 -0
- package/dist/{chunk-I4V3UDSA.js → chunk-V6RZWDII.js} +7 -5
- package/dist/chunk-V6RZWDII.js.map +1 -0
- package/dist/{chunk-CAB6NQ2T.js → chunk-VOCPXGQN.js} +3 -1
- package/dist/chunk-VOCPXGQN.js.map +1 -0
- package/dist/chunk-XD7DWYXG.js +34 -0
- package/dist/chunk-XD7DWYXG.js.map +1 -0
- package/dist/{chunk-B6JYRX3P.js → chunk-ZIYOJFZD.js} +43 -4
- package/dist/chunk-ZIYOJFZD.js.map +1 -0
- package/dist/{chunk-EYALJQQD.js → chunk-ZK5EGVFP.js} +4 -2
- package/dist/chunk-ZK5EGVFP.js.map +1 -0
- package/dist/{chunk-6WTETUZ6.js → chunk-ZLWQZUGF.js} +8 -2
- package/dist/chunk-ZLWQZUGF.js.map +1 -0
- package/dist/{chunk-XKFRMGMW.js → chunk-ZNZ2JDBW.js} +3 -1
- package/dist/chunk-ZNZ2JDBW.js.map +1 -0
- package/dist/{chunk-I45QO2IZ.js → chunk-ZSYLBVWG.js} +6 -4
- package/dist/chunk-ZSYLBVWG.js.map +1 -0
- package/dist/collapsible-panel.d.ts +3 -1
- package/dist/collapsible-panel.js +4 -2
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/disclosure-arrow.d.ts +3 -1
- package/dist/disclosure-arrow.js +1 -1
- package/dist/empty-state.d.ts +3 -1
- package/dist/empty-state.js +1 -1
- package/dist/floating-toolbar.d.ts +3 -1
- package/dist/floating-toolbar.js +1 -1
- package/dist/grid.d.ts +20 -0
- package/dist/grid.js +4 -0
- package/dist/grid.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +33 -31
- package/dist/list-action-row.d.ts +3 -1
- package/dist/list-action-row.js +1 -1
- package/dist/list-header.d.ts +6 -1
- package/dist/list-header.js +2 -1
- package/dist/list-inset-control.d.ts +3 -1
- package/dist/list-inset-control.js +1 -1
- package/dist/list-inset-text.d.ts +3 -1
- package/dist/list-inset-text.js +1 -1
- package/dist/list-item.d.ts +3 -1
- package/dist/list-item.js +1 -1
- package/dist/list.d.ts +3 -1
- package/dist/list.js +1 -1
- package/dist/loading-spinner.d.ts +3 -1
- package/dist/loading-spinner.js +1 -1
- package/dist/lucide-icon.d.ts +3 -1
- package/dist/lucide-icon.js +1 -1
- package/dist/nav-stack.d.ts +3 -1
- package/dist/nav-stack.js +1 -1
- package/dist/pane.d.ts +3 -1
- package/dist/pane.js +1 -1
- package/dist/resizable-region.d.ts +5 -2
- package/dist/resizable-region.js +1 -1
- package/dist/row.d.ts +3 -1
- package/dist/row.js +1 -1
- package/dist/segmented-control.d.ts +3 -1
- package/dist/segmented-control.js +1 -1
- package/dist/select.d.ts +3 -1
- package/dist/select.js +1 -1
- package/dist/skeleton.d.ts +3 -1
- package/dist/skeleton.js +1 -1
- package/dist/spacer.d.ts +3 -1
- package/dist/spacer.js +1 -1
- package/dist/split-view.d.ts +3 -1
- package/dist/split-view.js +24 -5
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.d.ts +3 -1
- package/dist/state-banner.js +2 -1
- package/dist/styles/badge.css +86 -0
- package/dist/styles/catalog/components/catalog-detail.css +35 -0
- package/dist/styles/catalog/components/catalog-example-stack.css +6 -0
- package/dist/styles/catalog/components/catalog-example.css +35 -0
- package/dist/styles/catalog/components/catalog-resource-footer.css +37 -0
- package/dist/styles/catalog/components/catalog-secondary-sections.css +38 -0
- package/dist/styles/catalog/components/catalog-section-list.css +22 -0
- package/dist/styles/catalog/components/catalog-sidebar.css +82 -0
- package/dist/styles/catalog/components/catalog-stage.css +60 -0
- package/dist/styles/catalog/components/catalog.css +22 -0
- package/dist/styles/catalog.css +10 -587
- package/dist/styles/grid.css +11 -0
- package/dist/styles/list-header.css +4 -60
- package/dist/styles/resizable-region.css +24 -0
- package/dist/styles/select.css +0 -3
- package/dist/styles/split-view.css +1 -1
- package/dist/styles/state-banner.css +0 -27
- package/dist/styles/styles.css +2 -0
- package/dist/styles/tab-bar.css +6 -1
- package/dist/styles/text.css +4 -0
- package/dist/styles/token-search-field.css +5 -40
- package/dist/styles/toolbar-control-group.css +110 -16
- package/dist/styles/toolbar-text.css +4 -0
- package/dist/styles/toolbar.css +72 -0
- package/dist/styles/webawesome.css +31 -0
- package/dist/sunken-panel.d.ts +3 -1
- package/dist/sunken-panel.js +1 -1
- package/dist/surface-scaffold.d.ts +6 -2
- package/dist/surface-scaffold.js +1 -1
- package/dist/tab-bar.d.ts +8 -1
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +3 -1
- package/dist/tab-scaffold.js +3 -1
- package/dist/tab-scaffold.js.map +1 -1
- package/dist/text.d.ts +5 -2
- package/dist/text.js +1 -1
- package/dist/token-search-field.d.ts +5 -1
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-control-group.d.ts +33 -3
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.d.ts +3 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/toolbar.d.ts +9 -1
- package/dist/toolbar.js +1 -1
- package/dist/types-CzgSrFXP.d.ts +64 -0
- package/dist/value-table.d.ts +6 -2
- package/dist/value-table.js +1 -1
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +3 -1
- package/dist/workbench.js +3 -1
- package/dist/workbench.js.map +1 -1
- package/docs/accessibility.md +11 -1
- package/docs/catalog.md +25 -7
- package/docs/component-contract.md +71 -22
- package/docs/component-selection.md +19 -14
- package/docs/design-philosophy.md +6 -5
- package/docs/layout.md +24 -1
- package/docs/recipes.md +5 -5
- package/docs/type-contracts.md +1 -0
- package/docs/ux-demo.md +4 -4
- package/docs/webawesome-theme.md +8 -4
- package/llms.txt +35 -8
- package/package.json +15 -2
- package/ux-demo/recipes/app-shell.css +87 -0
- package/ux-demo/recipes/app-shell.tsx +207 -178
- package/ux-demo/recipes/collapsible-sidebar.css +83 -0
- package/ux-demo/recipes/collapsible-sidebar.tsx +152 -122
- package/ux-demo/recipes/compact-toolbar.css +35 -0
- package/ux-demo/recipes/compact-toolbar.tsx +104 -100
- package/ux-demo/recipes/composer-form.css +19 -0
- package/ux-demo/recipes/composer-form.tsx +19 -15
- package/ux-demo/recipes/list-detail-dialog.css +42 -0
- package/ux-demo/recipes/list-detail-dialog.tsx +127 -125
- package/ux-demo/recipes/list-workspace-states.css +30 -0
- package/ux-demo/recipes/list-workspace-states.tsx +66 -60
- package/ux-demo/recipes/loading-inspector.css +7 -0
- package/ux-demo/recipes/loading-inspector.tsx +151 -131
- package/ux-demo/recipes/navigation-sidebar.css +12 -0
- package/ux-demo/recipes/navigation-sidebar.tsx +116 -105
- package/ux-demo/recipes/navigation-stack.css +12 -0
- package/ux-demo/recipes/navigation-stack.tsx +47 -37
- package/ux-demo/recipes/recipe-root.css +67 -0
- package/ux-demo/recipes/recipe-root.tsx +87 -0
- package/ux-demo/recipes/workspace-header.css +7 -0
- package/ux-demo/recipes/workspace-header.tsx +60 -61
- package/dist/chunk-2TYUFZUC.js.map +0 -1
- package/dist/chunk-6NDBE2OK.js.map +0 -1
- package/dist/chunk-6WTETUZ6.js.map +0 -1
- package/dist/chunk-AQFWBG23.js.map +0 -1
- package/dist/chunk-B6JYRX3P.js.map +0 -1
- package/dist/chunk-CAB6NQ2T.js.map +0 -1
- package/dist/chunk-CK6P5AUK.js.map +0 -1
- package/dist/chunk-DAO2VFTT.js.map +0 -1
- package/dist/chunk-DYBPI5O2.js.map +0 -1
- package/dist/chunk-E34TE2ZI.js.map +0 -1
- package/dist/chunk-EYALJQQD.js.map +0 -1
- package/dist/chunk-GYGM363P.js.map +0 -1
- package/dist/chunk-I45QO2IZ.js.map +0 -1
- package/dist/chunk-I4V3UDSA.js.map +0 -1
- package/dist/chunk-OGNE3OFT.js.map +0 -1
- package/dist/chunk-OGTJNHRG.js.map +0 -1
- package/dist/chunk-OMSRSL3B.js.map +0 -1
- package/dist/chunk-SOK5EUF7.js.map +0 -1
- package/dist/chunk-T7DOWS6T.js.map +0 -1
- package/dist/chunk-TIGSG6BX.js.map +0 -1
- package/dist/chunk-UDHHKZWO.js.map +0 -1
- package/dist/chunk-V6GQKOX2.js.map +0 -1
- package/dist/chunk-V6HCTCVD.js.map +0 -1
- package/dist/chunk-VXFGMDA7.js.map +0 -1
- package/dist/chunk-W2FK7ZNY.js.map +0 -1
- package/dist/chunk-WSXUPSID.js.map +0 -1
- package/dist/chunk-WVLMZ5SG.js.map +0 -1
- package/dist/chunk-XKFRMGMW.js.map +0 -1
- package/dist/chunk-Y6LYKFXY.js.map +0 -1
- package/dist/chunk-ZPWZIVI6.js.map +0 -1
- package/ux-demo/recipes/recipes.css +0 -453
|
@@ -7,7 +7,7 @@ decision procedure.
|
|
|
7
7
|
|
|
8
8
|
1. Search the [Kerf UX catalog](./ux-demo.md) and the supported Web Awesome set.
|
|
9
9
|
2. Reuse a primitive when its purpose, anatomy, state, and interaction match.
|
|
10
|
-
3. Compose primitives for recurring layout
|
|
10
|
+
3. Compose primitives for recurring layout through public props and tokens. Do not select one component from another component's stylesheet.
|
|
11
11
|
4. Add a thin application adapter for product copy, domain mapping, actions, routing, persistence, permissions, and transport.
|
|
12
12
|
5. Use custom markup only when the semantic contract differs. If the pattern recurs across products, open an upstream component or recipe request.
|
|
13
13
|
|
|
@@ -15,12 +15,13 @@ The application adapter is usually a plain function that maps domain state to
|
|
|
15
15
|
component props plus stable `data-action` values. It is not a fork of package
|
|
16
16
|
markup or CSS.
|
|
17
17
|
|
|
18
|
-
**Don't fight the components.** The package is built to look right
|
|
19
|
-
|
|
20
|
-
`border`, `background`, a wrapper card, or a decoration, check whether
|
|
21
|
-
component, the pane, or the content-item already owns it — it almost always
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
**Don't fight the components.** The package is built to look right without an
|
|
19
|
+
application customization layer. Before adding `padding`, `margin`, `width`,
|
|
20
|
+
`height`, `border`, `background`, a wrapper card, or a decoration, check whether
|
|
21
|
+
the component, the pane, or the content-item already owns it — it almost always
|
|
22
|
+
does. Express presentation through props and tokens. Encapsulate genuinely new
|
|
23
|
+
structure in a self-styled application component, and never reach into a nested
|
|
24
|
+
Kerf or Web Awesome component from that stylesheet. See
|
|
24
25
|
[`design-philosophy.md`](./design-philosophy.md) "Reach for the primitive, not for
|
|
25
26
|
CSS".
|
|
26
27
|
|
|
@@ -38,7 +39,7 @@ foreground/background contrast relationships and visible focus ring.
|
|
|
38
39
|
|
|
39
40
|
### Typed runtime dimensions
|
|
40
41
|
|
|
41
|
-
Use `Row` or `List` spacing shorthands for repeated sibling spacing: `gap="xs"`,
|
|
42
|
+
Use `Row`, `Grid`, or `List` spacing shorthands for repeated sibling spacing: `gap="xs"`,
|
|
42
43
|
`gap="m"`, and the other `UiSpaceName` values resolve to their matching
|
|
43
44
|
`--kui-space-*` token. Use `Spacer` for one deliberate empty width or height, or
|
|
44
45
|
pass `flex` to consume the remaining space along a flex parent's main axis.
|
|
@@ -53,23 +54,25 @@ application-owned length tokens. `plus` creates a non-standalone
|
|
|
53
54
|
|
|
54
55
|
```tsx
|
|
55
56
|
import { calc, pct, plus, rem, space } from "@kerfjs/ui/css-values";
|
|
57
|
+
import { Grid } from "@kerfjs/ui/grid";
|
|
56
58
|
import { List } from "@kerfjs/ui/list";
|
|
57
59
|
import { Row } from "@kerfjs/ui/row";
|
|
58
60
|
|
|
59
61
|
<List gap="xs">...</List>;
|
|
62
|
+
<Grid columns={3} gap="m">...</Grid>;
|
|
60
63
|
<Row hAlign="full" vAlign="middle" gap="m">...</Row>;
|
|
61
64
|
<List gap={space("m")}>...</List>;
|
|
62
65
|
<List gap={calc(plus(rem(0.25), pct(10)))}>...</List>;
|
|
63
66
|
```
|
|
64
67
|
|
|
65
68
|
`CssValue` is the common branded base, not an arbitrary-string constructor.
|
|
66
|
-
Raw strings such as `"0.25rem"`
|
|
69
|
+
Raw strings such as `"0.25rem"` do not satisfy `Row.gap`, `Grid.gap`, or `List.gap`; this
|
|
67
70
|
contract lands before the 5.0 stable release so misspelled tokens and incomplete
|
|
68
71
|
expressions fail during typechecking rather than in the browser.
|
|
69
72
|
|
|
70
73
|
Other CSS-valued props use distinct contracts: `Spacer.width` and
|
|
71
74
|
`Spacer.height` take `UiSpaceName | CssLength`; `flex()` creates `CssFlex` for
|
|
72
|
-
`Row.flex` and `List.flex`; `Skeleton` dimensions take typed lengths (plus finite intrinsic
|
|
75
|
+
`Row.flex`, `Grid.flex`, and `List.flex`; `Skeleton` dimensions take typed lengths (plus finite intrinsic
|
|
73
76
|
keywords for width/height); and `uiColor()`/`colorVar()` create `CssColor` for
|
|
74
77
|
`SelectChoice.color`. Do not substitute one brand for another. Row components
|
|
75
78
|
have no declaration-string `style` prop; use `className`, documented public
|
|
@@ -119,13 +122,15 @@ an upstream component or recipe request.
|
|
|
119
122
|
| Interface need | Use when | Do not use when; nearest alternative | Required wiring | Application owns | Import | Recipe |
|
|
120
123
|
| ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
121
124
|
| Icon — `LucideIcon` | A decorative or explicitly labeled Lucide-compatible icon belongs in app UI. | Do not use an icon as the only name of an unfamiliar action; add visible or accessible text. Prefer it over Web Awesome `wa-icon`. | None. | Icon choice and meaningful label. | `@kerfjs/ui/lucide-icon` | [Accessibility](./accessibility.md#shared-rules) |
|
|
125
|
+
| Compact metadata — `Badge` | A short status, count, category, or metadata label needs self-owned visual treatment configured by semantic props. | Do not use it as a button, link, or removable chip. Do not restyle consuming-component descendants to create a badge. | Choose `tone`, `appearance`, `shape`, and `size`; supply `label` for abbreviated content or `ariaHidden` when a surrounding component already owns the same accessible text. | Metadata text, semantic tone mapping, and abbreviated-content labeling. | `@kerfjs/ui/badge` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
122
126
|
| Disclosure indicator — `DisclosureArrow` | A control needs one animated 18px root-scaled visual for open and closed state, including configurable directions or a replacement icon. | Do not use it as the interactive control or accessible name; place it inside the button or control that exposes expanded state. | Pass the controlled `open` state, render it inside the owning control, author replacement icon content facing right before transforms, and override `--kui-disclosure-arrow-size` only when another visual size is required. Direction changes use the shortest rotation path, with counterclockwise chosen for a 180-degree closed-to-open tie. | Open state, interaction, accessible name, size override, replacement glyph, direction choices, and shortest-path rotation. | `@kerfjs/ui/disclosure-arrow` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
123
127
|
| Application toolbar or heading — `Toolbar` | Leading identity, optional centered content, and trailing controls form one horizontal app bar or a page/panel/dialog heading. | Do not add bespoke heading geometry or a private wrapper; use the same zone contract everywhere. | Put identity/title text directly in a zone as `ToolbarText`; group controls and icon tiles in `ToolbarControlGroup`. | Actions, command availability, responsive relocation, state, and supporting copy below a heading. | `@kerfjs/ui/toolbar` | [Toolbar composition](../README.md#component-subpaths) |
|
|
124
|
-
| Toolbar control cluster — `ToolbarControlGroup`
|
|
128
|
+
| Toolbar control cluster — `ToolbarControlGroup`, `ToolbarActionLink` | Related toolbar buttons, semantic links, compact/tight controls, avatar actions, or a dropdown trigger/menu surface need one configured chrome contract. | Do not use it merely to align unrelated controls. Do not restyle anchors or dropdown parts in application CSS; use `ToolbarActionLink`, `overflow`, and `menuInset`. Web Awesome `wa-button-group` is only for an exceptional grouped-action contract. | Delegate child actions; use `ToolbarActionLink` for native navigation, `overflow="scroll"` for bounded action rows, and `menuInset` for dropdown surface padding. Use `avatarImage` instead of inserting an `img`; use `SegmentedControl` for an exclusive choice. | Actions, destinations, pressed/expanded state, content, and policy. | `@kerfjs/ui/toolbar-control-group` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
125
129
|
| Floating controls over content — `FloatingToolbar` | A small cluster of controls (e.g. a drawer restore) must float over scrolling content — distinct and forced-dark — inside a positioned container. | Do not use it for a primary page or panel toolbar (use `Toolbar`), or for anything that must sit above dialogs/overlays — it is not top-layer. | Compose `ToolbarControlGroup`s and delegate their actions; the app owns visibility and position. | Controls, visibility, and position. | `@kerfjs/ui/floating-toolbar` | [Component subpaths](../README.md#component-subpaths) |
|
|
126
130
|
| 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`. | Optional `headingLevel` for `role="heading"` + `aria-level`. Defaults to one line, ellipsized when it does not fit; `wrap` flows onto multiple lines, `ellipsis` toggles the trailing …, and `maxLines` caps a wrap. | Text, heading level, overflow behavior, and responsive priority. | `@kerfjs/ui/toolbar-text` | [Toolbar composition](../README.md#component-subpaths) |
|
|
127
131
|
| Semantic heading or paragraph — `Text` | Ordinary application copy needs native `h1`–`h6` or `p` semantics plus standard geometry, or a semantic quiet/danger tone, compact size, or monospace font. | Do not choose heading levels for appearance alone or recreate muted, error, small, or code utility classes. Use `ToolbarText` for toolbar identity/title content and `ListInsetText` when list-aligned prose needs full margin/border/padding geometry. | Omit `variant` for the default `p`, or choose `h1`–`h6` from the document outline. Independently compose `tone="quiet | danger"`, `size="compact"`, and `font="monospace"`; ordinary native global, `data-_`, and `aria-_` attributes pass through. | Copy, localization, semantic variant, presentation roles, and document heading hierarchy. | `@kerfjs/ui/text` | [Text](./layout.md#text) |
|
|
128
132
|
| Vertical stack — `List` | Rows, sections, or arbitrary components need a consistent vertical stretch layout, optional standard/custom gap, flex growth, one vertical scroll owner, or edge dividers. | It is layout-only and does not provide `ul`/`ol` semantics. Do not nest scroll owners or use it when a horizontal layout is required. | Choose `gap`, `flex`, `scrollable`, and canonical `dividerSides` (`t`, `r`, `b`, `l` combinations in that order); bound the block size when scrolling. | Child semantics/content, containing height, and which optional layout behaviors apply. | `@kerfjs/ui/list` | [Pane geometry](../README.md#pane-and-content-geometry) |
|
|
133
|
+
| Equal-width columns — `Grid` | A fixed positive number of sibling columns must divide the available width evenly, including when child content has different intrinsic widths. | Do not use it for intrinsic, asymmetric, spanning, auto-fit, or masonry tracks; use application-owned CSS grid. Do not use it for a user-operable separator; use `ResizableRegion`. | Choose `columns`, a typed `gap`, and optional typed `flex`; preserve source order when the app changes the count responsively. | Child semantics/content, the column count and responsive policy, and typed gap/flex choices. | `@kerfjs/ui/grid` | [Grid](./layout.md#grid) |
|
|
129
134
|
| 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. The rounded root clips overflowing descendants; set `multiline` when the primary label should wrap. | Routing, selection, permissions, copy, action handling, and domain `data-*` values. | `@kerfjs/ui/list-item` | [Pane geometry](../README.md#pane-and-content-geometry) |
|
|
130
135
|
| 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) |
|
|
131
136
|
| 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 a heading `Toolbar`. | 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) |
|
|
@@ -139,9 +144,9 @@ an upstream component or recipe request.
|
|
|
139
144
|
| Standalone rail or drawer — `CollapsiblePanel` | One left/right rail or bottom drawer needs controlled collapse without adopting the complete `Workbench` shell. | Do not use it for several coordinated workspace panels or retain an empty icon-only rail while collapsed. | Pair it with `CollapsiblePanelToggle`; call `wireSidebar` for focus transfer, Escape/backdrop dismissal, compact overlay trapping, and persistence integration. | Panel content/size, collapsed state, toggle placement, persistence, and compact policy. | `@kerfjs/ui/collapsible-panel` | [App-layout decision matrix](./app-layouts.md#decision-matrix) |
|
|
140
145
|
| Lowered content surface — `SunkenPanel` | A main work area or nested content group needs one visually lowered surface with a compact inset and vertical stack. | Do not use it merely to add padding, as a replacement for `Pane` anatomy, or around children that already own the same outer surface. | Compose the application-owned content as children, choose the default `rounded` shape or `square` for a flush edge-to-edge area, and pass `ariaLabel` only when the surface is a distinct named region. | Region semantics, accessible name, shape, scroll ownership, child ordering, and content. | `@kerfjs/ui/sunken-panel` | [SunkenPanel](./sunken-panel.md) |
|
|
141
146
|
| 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) |
|
|
142
|
-
| 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
|
|
147
|
+
| 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 static equal columns; use `Grid`. Use application CSS grid for static asymmetric or intrinsic tracks. 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) |
|
|
143
148
|
| One application tab — `AppTab` | A controlled app tab needs selection, close, drag, leading/trailing anatomy, compact/segmented/icon-only presentation, label truncation, 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`; use typed appearance props instead of descendant CSS. | Tab identity, order, selection, close policy, content, domain metadata values, and outer placement. | `@kerfjs/ui/app-tab` | [Tabs contract](./accessibility.md#tabs) |
|
|
144
|
-
| Application tab strip — `TabBar`, `wireTabBars`, `reorderTabs` | Tabs switch page regions and may overflow, close, reorder, fill a segmented inspector strip,
|
|
149
|
+
| Application tab strip — `TabBar`, `wireTabBars`, `reorderTabs` | Tabs switch page regions and may overflow, close, reorder, fill a segmented inspector strip, sit beside a trailing action, or keep that adjacent action distinct from a far-edge end action. | 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. Combine `trailingPlacement="adjacent"` with `end` for split actions; TabBar owns the shrinking/scrolling tab geometry and keeps both actions visible. Choose typed presentation/allocation props while the parent owns outer placement. | Ordered tabs, selection, panels, routing, closing, persistence, action content, and outer placement. | `@kerfjs/ui/tab-bar` plus `@kerfjs/ui/wire-tab-bars` | [Tabs contract](./accessibility.md#tabs) |
|
|
145
150
|
| Panel, dialog, or page heading — `Toolbar` + `ToolbarText` | A panel, dialog, or page needs an extra-large title, optional icon, grouped trailing actions, and optional supporting copy. | Do not add a private heading wrapper or bespoke layout. The toolbar supplies structure, not modal behavior; use an application overlay or Web Awesome `wa-dialog` for that behavior. | Connect title/supporting-copy ids to the host, group controls, and delegate actions. **For a page or view title set `headingLevel` (usually `1`)** on `ToolbarText`; omit it for a dialog title referenced by `aria-labelledby`. | Open state, focus lifecycle, dismissal, controls, ids, copy, and heading level. | `@kerfjs/ui/toolbar` + `@kerfjs/ui/toolbar-text` | [Toolbar headings](./accessibility.md#toolbar-headings) |
|
|
146
151
|
| Dialog or popup geometry — `DialogSurface`, `PopupSurface` | Recurring Web Awesome dialogs need standard sizes/presentations/body/footer insets, or dropdown menus need list-compatible inset ownership. | Do not use these wrappers as generic layout or to replace native dialog/dropdown behavior. Avoid consumer `::part()` overrides and do not stack a dialog body inset around list-owned child geometry. | Wrap one `wa-dialog` or `wa-dropdown`; choose the typed geometry while retaining native labeling, open state, focus, dismissal, and menu semantics. Compose a dialog body as a `List` by default, use `bodyInset="none"` when its children own list geometry, and wrap bare prose in `ListInsetText`. | State, focus lifecycle, dismissal, labels, content, and outer placement. | `@kerfjs/ui/surface-scaffold` | [Surface scaffolds](./surface-scaffold.md) |
|
|
147
152
|
| 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) |
|
|
@@ -158,7 +163,7 @@ an upstream component or recipe request.
|
|
|
158
163
|
- `Toolbar` serves persistent app chrome and headings; the zone contents and accessible naming distinguish the purpose.
|
|
159
164
|
- `TabBar` changes tabpanels and supports overflow/reorder; `SegmentedControl` chooses among a few compact views; `Select` handles a longer value list.
|
|
160
165
|
- `StateBanner` persists beside affected work; `EmptyState` replaces absent content; `wa-callout` is contextual ecosystem content; `wa-toast` and `wa-toast-item` are transient and must not carry the only copy of important state.
|
|
161
|
-
- `ResizableRegion` is an interactive controlled pane.
|
|
166
|
+
- `ResizableRegion` is an interactive controlled pane. `Grid` is the right answer for static equal-width columns; application CSS grid remains the answer for asymmetric or intrinsic tracks.
|
|
162
167
|
- A `ResizableRegion` overlay automatically clamps both its track and fixed-size
|
|
163
168
|
animated content to the responsive overlay maximum. Set the policy and maximum
|
|
164
169
|
on the component instead of adding application descendant width/height fixes.
|
|
@@ -28,16 +28,17 @@ Establish one reading order and one dominant action per decision point. Group fi
|
|
|
28
28
|
|
|
29
29
|
The package is designed to look right _unstyled_. A screen built from the
|
|
30
30
|
primitives, their props, and the semantic tokens should already read well, so
|
|
31
|
-
|
|
31
|
+
applications do not carry a component-customization stylesheet. Before adding any `padding`, `margin`, `width`,
|
|
32
32
|
`height`, `border`, `background`, wrapper card, or decoration, check whether the
|
|
33
33
|
component, the pane, or the content-item already owns it — it almost always does,
|
|
34
34
|
and adding more usually **double-insets** or fights the component (the most common
|
|
35
35
|
mistake). Trust component defaults: render a control at its natural size and color
|
|
36
36
|
and fix the surrounding layout rather than overriding the control. Legitimate
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
never build a heading, toolbar, card,
|
|
37
|
+
CSS for genuinely new structure belongs to the application-owned component that
|
|
38
|
+
renders that structure. It must not select a nested Kerf or Web Awesome component;
|
|
39
|
+
configure those children through props and documented tokens instead. Never size,
|
|
40
|
+
space, or re-skin a primitive by hand, and never build a heading, toolbar, card,
|
|
41
|
+
or pane geometry that a primitive provides.
|
|
41
42
|
|
|
42
43
|
### Panes share one child-owned geometry
|
|
43
44
|
|
package/docs/layout.md
CHANGED
|
@@ -112,7 +112,7 @@ The two relationships that get confused most are 8px vs 24px: 8px is _inside_ a
|
|
|
112
112
|
group, 24px is _between_ major, differing regions. 16px sits between them for
|
|
113
113
|
same-kind groups.
|
|
114
114
|
|
|
115
|
-
For `Row.gap` and `List.gap`, pass these token names directly (`gap="xs"`, `gap="m"`) or use
|
|
115
|
+
For `Row.gap`, `Grid.gap`, and `List.gap`, pass these token names directly (`gap="xs"`, `gap="m"`) or use
|
|
116
116
|
`space('xs')` from `@kerfjs/ui/css-values` when composing a value in JavaScript.
|
|
117
117
|
Use `rem`, `em`, `px`, `pct`, `lengthVar`, and `calc(plus(...))` only when a
|
|
118
118
|
named spacing relationship does not express the requirement; do not pass raw
|
|
@@ -169,6 +169,29 @@ uses physical width and height. Prefer `Row.gap` or `List.gap` for uniform
|
|
|
169
169
|
sibling rhythm, and never use Spacer to cancel or duplicate component-owned
|
|
170
170
|
insets.
|
|
171
171
|
|
|
172
|
+
## Grid
|
|
173
|
+
|
|
174
|
+
Use `Grid` from `@kerfjs/ui/grid` when a fixed positive number of columns must
|
|
175
|
+
share the available width evenly. Every track is `minmax(0, 1fr)`, so a child
|
|
176
|
+
with wider intrinsic content cannot make its column wider than its peers. Grid
|
|
177
|
+
defaults to the `xs` gap and accepts the same finite spacing names, complete
|
|
178
|
+
typed `CssLength` values, and typed flex-participation contract as Row.
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { Grid } from "@kerfjs/ui/grid";
|
|
182
|
+
|
|
183
|
+
<Grid columns={2} gap="m">
|
|
184
|
+
<label>Quantity <input /></label>
|
|
185
|
+
<label>Unit <input /></label>
|
|
186
|
+
</Grid>;
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
`columns` must be a positive safe integer; invalid counts throw instead of
|
|
190
|
+
silently producing invalid CSS. Grid is layout-only and adds no ARIA grid role.
|
|
191
|
+
The application owns child semantics and responsive count changes. Use
|
|
192
|
+
application-owned CSS grid for intrinsic, asymmetric, spanning, auto-fit, or
|
|
193
|
+
masonry tracks, and use `ResizableRegion` when people must adjust a boundary.
|
|
194
|
+
|
|
172
195
|
## Row and List alignment
|
|
173
196
|
|
|
174
197
|
Use `Row` for a horizontal flex layout and `List` for a vertical one. `Row`
|
package/docs/recipes.md
CHANGED
|
@@ -28,7 +28,7 @@ specific dispatch; either way, wire once at a stable root and retain disposal.
|
|
|
28
28
|
|
|
29
29
|
## Desktop application shell
|
|
30
30
|
|
|
31
|
-
[Open the recipe](../ux-demo/?component=recipe-app-shell) · [TSX source](../ux-demo/recipes/app-shell.tsx) · [shared CSS](../ux-demo/recipes/
|
|
31
|
+
[Open the recipe](../ux-demo/?component=recipe-app-shell) · [TSX source](../ux-demo/recipes/app-shell.tsx) · [shared composition CSS](../ux-demo/recipes/recipe-components.css)
|
|
32
32
|
|
|
33
33
|
Use `Toolbar`, controlled `ResizableRegion` panes, and one
|
|
34
34
|
`.kui-pane__content` scroll owner per pane. The recipe owns the shell topology;
|
|
@@ -164,9 +164,9 @@ Chromium, Firefox, and WebKit by `tests/browser/collapsible-sidebar-recipe.spec.
|
|
|
164
164
|
- For direct `wa-*` JSX, import types from `@kerfjs/ui/webawesome`. Import only
|
|
165
165
|
individual Web Awesome registration modules and theme them with
|
|
166
166
|
`@kerfjs/ui/webawesome.css`.
|
|
167
|
-
- Prefer public props and variables at the composition boundary.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
167
|
+
- Prefer public props and variables at the composition boundary. A recipe's own
|
|
168
|
+
component may style the structure it renders, but its stylesheet must not
|
|
169
|
+
select nested Kerf or Web Awesome components. Add missing configuration to the
|
|
170
|
+
owning component instead of copying markup or reaching through its boundary.
|
|
171
171
|
- Start from the copyable mount adapter, or reproduce its complete boundary:
|
|
172
172
|
wire stable `data-action` hooks once and retain every disposer.
|
package/docs/type-contracts.md
CHANGED
|
@@ -26,6 +26,7 @@ public-signature artifact.
|
|
|
26
26
|
| `KUI-T011` | Semantic component zones use the recursive `KerfUiContent` type: `SafeHtml`, runtime-empty booleans/nullish values, and readonly nested arrays are valid; arbitrary strings, numbers, and signals are rejected. Explicit text positions such as `ListInsetText` retain their text exception. |
|
|
27
27
|
| `KUI-T012` | `List.gap` accepts finite `UiSpaceName` shorthands or a complete branded `CssLength`; raw strings and non-standalone `CssLengthExpression` arithmetic are rejected. `calc(plus(...))` promotes a typed expression to a complete value. |
|
|
28
28
|
| `KUI-T013` | CSS-valued props preserve property grammar: `List.flex` uses `CssFlex`, `Skeleton` dimensions use `CssSize`/`CssLength`, and choice icons use `CssColor`; row declaration strings are absent. |
|
|
29
|
+
| `KUI-T014` | Stable single-root visual components accept the explicit native `slot` attribute. Multi-root `ResizableRegion` and `CollapsiblePanel` remain excluded because no single returned element can own the assignment, and other arbitrary native attributes remain rejected. |
|
|
29
30
|
|
|
30
31
|
`KerfUiContent` lets conditionals and mapped component collections be direct
|
|
31
32
|
siblings without an otherwise-unnecessary `Fragment`:
|
package/docs/ux-demo.md
CHANGED
|
@@ -12,18 +12,18 @@ of the current `src/` and `ux-demo/` inputs, so a stale `dist-demo` cannot pass
|
|
|
12
12
|
after a route or component edit. `npm run demo:serve` remains a preview-only
|
|
13
13
|
command for an already-built catalog.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Ten lazy production-composition routes live under the `Recipes` category.
|
|
16
16
|
Each `?component=recipe-*` route uses public package primitives, real wiring,
|
|
17
17
|
deterministic state, and semantic layout owners from the [recipe guide](./recipes.md).
|
|
18
18
|
|
|
19
19
|
- It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
|
|
20
20
|
- 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.
|
|
21
|
-
- The `AppTab` and `TabBar` routes are focused component specimens: AppTab shows its selected, compact, segmented, icon-only, truncation, placeholder, metadata, and close-icon axes inside the required tablist context; TabBar shows its rail, segmented, inspector, allocation, placement, and accessory-zone axes without application readouts. The separate `Application tabs composition
|
|
21
|
+
- The `AppTab` and `TabBar` routes are focused component specimens: AppTab shows its selected, compact, segmented, icon-only, truncation, placeholder, metadata, and close-icon axes inside the required tablist context; TabBar shows its rail, segmented, inspector, allocation, placement, and accessory-zone axes, including an adjacent action plus a separately pinned end action whose tabs overflow at narrow widths, without application readouts. The separate `Application tabs` composition route demonstrates controlled selection, matching panel content, overflow, adding, closing, keyboard/pointer reordering, and application-owned ordering. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
|
|
22
22
|
- 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, and feedback scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component. The former aggregate Web Awesome theme specimen is intentionally omitted because its component families are covered by those focused routes.
|
|
23
|
-
- `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, recommendation, 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. Non-supported Web Awesome recommendations project to a visible `Discouraged`
|
|
23
|
+
- `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, recommendation, 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. Functional groups follow their authored importance order; components precede compositions inside each group, equal-importance entries use alphabetical order, and the Recipes group comes last. Composition names stay concise and project a visible `Composition` sidebar tag. Non-supported Web Awesome recommendations project to a visible `Discouraged` tag, with Popup as the deliberate conditional exception. Demo paths resolve to the matching `ui/ux-demo/demos/<id>.tsx` or `ui/ux-demo/recipes/*.tsx` file, or to `ui/ux-demo/webawesome-demos.tsx` for Web Awesome entries. `ui/ux-demo/main.tsx` owns only the catalog shell, shared state, routing, and interaction wiring. First-party component entries additionally derive `ui/src/<public-subpath>.tsx` from their canonical `delivery.browserImport` or opt-in `delivery.moduleImport`.
|
|
24
24
|
- 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.
|
|
25
25
|
- The master/detail shell groups first-party routes by category with production `ListHeader` and `ListItem` components. Catalog entries can supply short reusable trailing tags; the Web Awesome projection uses them to label exactly the 15 discouraged choices. 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.
|
|
26
|
-
- 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 supporting copy in its own aligned row; 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
|
|
26
|
+
- 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 supporting copy in its own aligned row; 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 preview scroll owner rather than a viewport-height stage or nested card, and one explicitly repeated SVG tile keeps the pattern continuous through below-fold content. 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.
|
|
27
27
|
- The composer-form recipe demonstrates one coherent visible form surface with toolbar title/supporting-copy 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.
|
|
28
28
|
- 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.
|
|
29
29
|
- 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.
|
package/docs/webawesome-theme.md
CHANGED
|
@@ -97,6 +97,11 @@ tiers:
|
|
|
97
97
|
complete surface and the inset within it. Accordion applies the outer margin
|
|
98
98
|
once to the group rather than separating connected items; Card's header,
|
|
99
99
|
body, and footer all receive the full inset.
|
|
100
|
+
- Plain Details use the 8px `--kui-wa-surface-inset` on the trigger and keep the
|
|
101
|
+
body flush; plain Accordion items use that 8px inset on both trigger and body.
|
|
102
|
+
Outlined and sunken Details use the 16px `--kui-wa-container-inset` on the
|
|
103
|
+
trigger and the 8px surface inset on the body, while framed Accordion items
|
|
104
|
+
use the 16px container inset for both regions.
|
|
100
105
|
- `appearance="sunken"` is a Kerf theme extension for Accordion, Card, and
|
|
101
106
|
Details. It removes raised chrome, paints the lowered semantic surface, and
|
|
102
107
|
uses the shared rounded-rectangle radius. Override
|
|
@@ -177,10 +182,9 @@ component should declare the no-arrow choice independent of theme context.
|
|
|
177
182
|
The UX catalog includes a focused, addressable specimen for every component in
|
|
178
183
|
this list. Because these are themed ecosystem components rather than
|
|
179
184
|
first-class `@kerfjs/ui` exports, the sidebar keeps them under a collapsible
|
|
180
|
-
`Web Awesome` heading with category subgroups.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
state at once.
|
|
185
|
+
`Web Awesome` heading with category subgroups. There is no aggregate theme
|
|
186
|
+
route: the focused modal, drawer, toast, media, and helper routes make each
|
|
187
|
+
component's behavior inspectable without opening every state at once.
|
|
184
188
|
|
|
185
189
|
## Badge and tag guidance
|
|
186
190
|
|
package/llms.txt
CHANGED
|
@@ -12,6 +12,10 @@ npm install kerfjs @kerfjs/ui # kerfjs is a peer; Web Awesome is an optional pee
|
|
|
12
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
|
+
For a public subpath containing several visual components, use a folder with one
|
|
16
|
+
component per source file and one owned stylesheet per visual component. Keep its
|
|
17
|
+
compatibility entrypoints implementation-free: JavaScript/TypeScript only re-exports
|
|
18
|
+
the folder modules, and CSS only imports their owner stylesheets.
|
|
15
19
|
|
|
16
20
|
## How to build a screen (retrieval order)
|
|
17
21
|
|
|
@@ -56,9 +60,17 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
|
56
60
|
`ListInsetText`. These are typed props, not native `<slot>` elements,
|
|
57
61
|
wrapper slot components, or a generic `slots` object. Leave unresolved
|
|
58
62
|
dynamic expressions unknown.
|
|
63
|
+
Stable single-root Kerf UI components separately accept the explicit native
|
|
64
|
+
`slot="name"` prop for light-DOM composition inside a custom element and
|
|
65
|
+
preserve it across rerenders. Do not infer arbitrary native-attribute
|
|
66
|
+
spreading from this narrow interop contract. `ResizableRegion` and
|
|
67
|
+
`CollapsiblePanel` remain excluded because their optional restore controls
|
|
68
|
+
make their output multi-root; `CollapsiblePanelToggle` remains slot-capable.
|
|
59
69
|
4. **When authoring a component gallery**, load
|
|
60
70
|
[catalog-authoring.json](./ai/catalog-authoring.json), then follow its
|
|
61
71
|
authoritative [Catalog demo authoring contract](./docs/catalog.md#catalog-demo-authoring-contract).
|
|
72
|
+
Use its component/composition/recipe taxonomy and functional-group,
|
|
73
|
+
importance, alphabetical-tie, and recipes-last ordering policy.
|
|
62
74
|
5. **Apply the rules below and in** [skill.md](./ai/skill.md).
|
|
63
75
|
6. **Before calling an integration complete**, run
|
|
64
76
|
[`kerf-ui-doctor`](./docs/ui-doctor.md). Its portable JSON report merges
|
|
@@ -81,12 +93,12 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
|
81
93
|
## Core rules (do the right thing)
|
|
82
94
|
|
|
83
95
|
- **Reach for the primitive, not for CSS.** The package is built to look right
|
|
84
|
-
|
|
85
|
-
`background`, a wrapper card, or a decoration,
|
|
86
|
-
pane, or
|
|
87
|
-
component prop or
|
|
88
|
-
|
|
89
|
-
|
|
96
|
+
without an application customization layer. Before adding `padding`, `margin`,
|
|
97
|
+
`width`, `height`, `border`, `background`, a wrapper card, or a decoration,
|
|
98
|
+
check whether the component, pane, or content-item already owns it. Prefer a
|
|
99
|
+
component prop or documented `--kui-*` token. CSS for genuinely new structure
|
|
100
|
+
belongs to the application-owned component that renders it and must not select
|
|
101
|
+
nested Kerf or Web Awesome components.
|
|
90
102
|
- **Never double-inset.** A `.kui-pane` has no padding; its `.kui-content` children
|
|
91
103
|
own the geometry (8px inline margin + 1px border + 8px padding). Do not add a
|
|
92
104
|
container padding on top of a content-item's own margin — pick one owner. Use
|
|
@@ -107,10 +119,18 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
|
107
119
|
for List flex, typed lengths for Skeleton geometry, and
|
|
108
120
|
`uiColor()`/`colorVar()` for choice-icon colors. These brands are intentionally
|
|
109
121
|
not interchangeable; rows use classes/tokens/props rather than raw `style`.
|
|
122
|
+
- **Use `Grid` for a fixed positive count of equal-width columns.** It uses
|
|
123
|
+
zero-minimum fractional tracks, so intrinsic content cannot make one column
|
|
124
|
+
wider than its peers. The app owns responsive count changes; use custom CSS
|
|
125
|
+
grid for asymmetric/intrinsic tracks and `ResizableRegion` for adjustable
|
|
126
|
+
boundaries. `Grid.gap` and `Grid.flex` use the same typed grammars as Row.
|
|
110
127
|
- **Keep Web Awesome on the shared inset tiers.** `webawesome.css` uses
|
|
111
128
|
`--kui-wa-control-inset` (8px controls/items) and
|
|
112
129
|
`--kui-wa-surface-margin` / `--kui-wa-surface-inset` (8px around/inside
|
|
113
130
|
Accordion, Card, Details, Callout, and Include), while
|
|
131
|
+
plain Details keep their body flush while plain Accordion items inset both
|
|
132
|
+
trigger and body, framed Details use the 16px container inset only on the
|
|
133
|
+
trigger, and framed Accordion items use it on both regions;
|
|
114
134
|
`--kui-wa-container-inset` keeps Tab Panel at 16px. Dialog body and footer use
|
|
115
135
|
the surface (8px) and container (16px) tiers respectively, independent of the
|
|
116
136
|
dialog's shared `--spacing`. Override the tier at the narrowest useful scope
|
|
@@ -200,7 +220,11 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
|
200
220
|
overflow autoscroll, drag preview, and
|
|
201
221
|
replacement focus after controlled automatic tab activation,
|
|
202
222
|
`wireResizableRegions` the live drag preview; they report committed changes via
|
|
203
|
-
callbacks.
|
|
223
|
+
callbacks. For a tab-local action plus a separate far-edge action, set
|
|
224
|
+
`TabBar trailingPlacement="adjacent"`, put the local action in `trailing`, and
|
|
225
|
+
the workspace action in `end`; the component keeps both visible while its tab
|
|
226
|
+
list shrinks and scrolls, without application descendant CSS. Managed
|
|
227
|
+
TokenSearchField clear also preserves open state and replacement
|
|
204
228
|
editor focus before the next input task, so shortcut-letter typing stays in search.
|
|
205
229
|
Select All deletion does the same; deletion
|
|
206
230
|
restoration finishes before the next keystroke rather than on a delayed frame.
|
|
@@ -237,10 +261,13 @@ maintain a central style list. Configure JSX with `"jsxImportSource": "kerfjs"`.
|
|
|
237
261
|
optional string `badge` for a terse count or status beside the title. It
|
|
238
262
|
inherits the banner tone and is announced with the banner, so keep its text
|
|
239
263
|
meaningful and never rely on color alone.
|
|
264
|
+
- **Use `Badge` for compact non-interactive metadata.** Configure its semantic
|
|
265
|
+
tone, appearance, shape, and size instead of creating badge CSS in an app or
|
|
266
|
+
consuming component. Use a real button or link for interactive content.
|
|
240
267
|
|
|
241
268
|
## Public exports
|
|
242
269
|
|
|
243
|
-
`LucideIcon`, `DisclosureArrow`, `Toolbar`, `ToolbarControlGroup`, `FloatingToolbar`, `ToolbarText`, `Text`,
|
|
270
|
+
`Badge`, `LucideIcon`, `DisclosureArrow`, `Toolbar`, `ToolbarControlGroup`, `ToolbarActionLink`, `FloatingToolbar`, `ToolbarText`, `Text`,
|
|
244
271
|
`ListItem`, `ListActionRow`, `ListHeader`, `ListInsetControl`,
|
|
245
272
|
`ListInsetText`, `SegmentedControl`,
|
|
246
273
|
`Select`, `TokenSearchField` (+ `readTokenSearchField`, `placeTokenSearchCaret`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kerfjs/ui",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.50",
|
|
4
4
|
"description": "Accessible, composable UI primitives for kerf applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -58,6 +58,11 @@
|
|
|
58
58
|
"browser": "./dist/browser/disclosure-arrow.js",
|
|
59
59
|
"import": "./dist/disclosure-arrow.js"
|
|
60
60
|
},
|
|
61
|
+
"./badge": {
|
|
62
|
+
"types": "./dist/badge.d.ts",
|
|
63
|
+
"browser": "./dist/browser/badge.js",
|
|
64
|
+
"import": "./dist/badge.js"
|
|
65
|
+
},
|
|
61
66
|
"./toolbar": {
|
|
62
67
|
"types": "./dist/toolbar.d.ts",
|
|
63
68
|
"browser": "./dist/browser/toolbar.js",
|
|
@@ -98,6 +103,11 @@
|
|
|
98
103
|
"browser": "./dist/browser/row.js",
|
|
99
104
|
"import": "./dist/row.js"
|
|
100
105
|
},
|
|
106
|
+
"./grid": {
|
|
107
|
+
"types": "./dist/grid.d.ts",
|
|
108
|
+
"browser": "./dist/browser/grid.js",
|
|
109
|
+
"import": "./dist/grid.js"
|
|
110
|
+
},
|
|
101
111
|
"./spacer": {
|
|
102
112
|
"types": "./dist/spacer.d.ts",
|
|
103
113
|
"browser": "./dist/browser/spacer.js",
|
|
@@ -286,6 +296,7 @@
|
|
|
286
296
|
"./webawesome.css": "./dist/styles/webawesome.css",
|
|
287
297
|
"./styles.css": "./dist/styles/styles.css",
|
|
288
298
|
"./lucide-icon.css": "./dist/styles/lucide-icon.css",
|
|
299
|
+
"./badge.css": "./dist/styles/badge.css",
|
|
289
300
|
"./disclosure-arrow.css": "./dist/styles/disclosure-arrow.css",
|
|
290
301
|
"./toolbar.css": "./dist/styles/toolbar.css",
|
|
291
302
|
"./toolbar-control-group.css": "./dist/styles/toolbar-control-group.css",
|
|
@@ -295,6 +306,7 @@
|
|
|
295
306
|
"./text.css": "./dist/styles/text.css",
|
|
296
307
|
"./list.css": "./dist/styles/list.css",
|
|
297
308
|
"./row.css": "./dist/styles/row.css",
|
|
309
|
+
"./grid.css": "./dist/styles/grid.css",
|
|
298
310
|
"./spacer.css": "./dist/styles/spacer.css",
|
|
299
311
|
"./list-action-row.css": "./dist/styles/list-action-row.css",
|
|
300
312
|
"./list-item.css": "./dist/styles/list-item.css",
|
|
@@ -354,6 +366,7 @@
|
|
|
354
366
|
"check:profile": "node scripts/check-application-ui-profile.mjs",
|
|
355
367
|
"check:demo-conformance": "node scripts/check-catalog-demo-conformance.mjs",
|
|
356
368
|
"check:recipes": "node scripts/check-recipes.mjs",
|
|
369
|
+
"check:css-ownership": "node scripts/check-css-ownership.mjs",
|
|
357
370
|
"check:ai-regressions": "node scripts/check-ai-regressions.mjs && node scripts/audit-ai-regression-results.mjs && node scripts/audit-ai-regression-results-v3.mjs",
|
|
358
371
|
"check:ai-signatures": "node scripts/sync-ai-public-signatures.mjs --check",
|
|
359
372
|
"ai:signatures:sync": "npm run build && node scripts/sync-ai-public-signatures.mjs",
|
|
@@ -379,7 +392,7 @@
|
|
|
379
392
|
"test:contract-types:packed": "node scripts/check-packed-type-contracts.mjs",
|
|
380
393
|
"test:consumer-types": "npm run test:contract-types:source && npm run build && node node_modules/typescript7/bin/tsc -p tests/consumer-types/webawesome/tsconfig.json && npm run test:contract-types:packed",
|
|
381
394
|
"test:e2e": "playwright test",
|
|
382
|
-
"check": "npm run check:catalog && npm run check:component-integrations && npm run check:profile && npm run check:demo-conformance && npm run check:recipes && npm run check:guidance && npm run check:ai-compatibility && npm run check:ai-regressions && npm run lint && npm run typecheck && npm test && npm run test:consumer-types && npm run check:ai-signatures && npm run demo:build && npm run check:design-templates && npm pack --dry-run",
|
|
395
|
+
"check": "npm run check:catalog && npm run check:component-integrations && npm run check:profile && npm run check:demo-conformance && npm run check:recipes && npm run check:css-ownership && npm run check:guidance && npm run check:ai-compatibility && npm run check:ai-regressions && npm run lint && npm run typecheck && npm test && npm run test:consumer-types && npm run check:ai-signatures && npm run demo:build && npm run check:design-templates && npm pack --dry-run",
|
|
383
396
|
"prepublishOnly": "npm run check",
|
|
384
397
|
"check:design-templates": "node scripts/check-design-templates.mjs",
|
|
385
398
|
"check:design-templates:drift": "node scripts/check-design-templates-drift.mjs",
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
.recipe-shell {
|
|
2
|
+
display: grid;
|
|
3
|
+
height: min(37rem, 70vh);
|
|
4
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.recipe-shell__body {
|
|
8
|
+
display: flex;
|
|
9
|
+
min-width: 0;
|
|
10
|
+
min-height: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.recipe-shell__nav,
|
|
14
|
+
.recipe-shell__inspector {
|
|
15
|
+
display: grid;
|
|
16
|
+
min-height: 0;
|
|
17
|
+
background: var(--kui-color-surface);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.recipe-shell__nav-list,
|
|
21
|
+
.recipe-shell__inspector-body,
|
|
22
|
+
.recipe-shell__main-body {
|
|
23
|
+
min-height: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.recipe-shell__main {
|
|
27
|
+
display: grid;
|
|
28
|
+
min-width: 16rem;
|
|
29
|
+
min-height: 0;
|
|
30
|
+
flex: 1;
|
|
31
|
+
background: var(--kui-color-surface-lowered);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.recipe-shell__cards {
|
|
35
|
+
display: grid;
|
|
36
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
37
|
+
gap: var(--kui-layout-content-gap);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.recipe-shell__card {
|
|
41
|
+
--kui-content-item-border: var(--kui-color-border-quiet);
|
|
42
|
+
--kui-content-item-background: var(--kui-color-surface);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@container (max-width: 44rem) {
|
|
46
|
+
.recipe-shell__inspector-region {
|
|
47
|
+
display: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.recipe-shell[data-responsive-pane="inspector"] .recipe-shell__nav-region,
|
|
51
|
+
.recipe-shell[data-responsive-pane="inspector"] .recipe-shell__main {
|
|
52
|
+
display: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.recipe-shell[data-responsive-pane="inspector"]
|
|
56
|
+
.recipe-shell__inspector-region {
|
|
57
|
+
display: flex;
|
|
58
|
+
min-width: 0;
|
|
59
|
+
flex: 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.recipe-shell__cards {
|
|
63
|
+
grid-template-columns: 1fr 1fr;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@container (max-width: 28rem) {
|
|
68
|
+
.recipe-shell__nav-region {
|
|
69
|
+
display: none;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.recipe-shell[data-responsive-pane="navigation"] .recipe-shell__main,
|
|
73
|
+
.recipe-shell[data-responsive-pane="navigation"]
|
|
74
|
+
.recipe-shell__inspector-region {
|
|
75
|
+
display: none;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.recipe-shell[data-responsive-pane="navigation"] .recipe-shell__nav-region {
|
|
79
|
+
display: flex;
|
|
80
|
+
min-width: 0;
|
|
81
|
+
flex: 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.recipe-shell__cards {
|
|
85
|
+
grid-template-columns: 1fr;
|
|
86
|
+
}
|
|
87
|
+
}
|