@kerfjs/ui 5.0.0-beta.50 → 5.0.0-beta.51
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 +29 -5
- package/ai/compile-time-contracts-v1.json +5 -2
- package/ai/component-catalog-v2.d.ts +19 -1
- package/ai/component-catalog-v2.json +842 -127
- package/ai/component-catalog-v2.schema.json +33 -1
- package/ai/component-catalog.json +603 -126
- package/ai/component-catalog.schema.json +21 -1
- package/ai/public-api-signatures-v1.md +201 -29
- package/ai/skill.md +12 -9
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/analyzer/index.mjs +105 -35
- package/dist/app-tab.js +1 -1
- package/dist/catalog-resources.d.ts +1 -1
- package/dist/catalog.d.ts +28 -5
- package/dist/catalog.js +66 -25
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-COUPLRZG.js → chunk-5FZZMIMD.js} +3 -3
- package/dist/{chunk-COUPLRZG.js.map → chunk-5FZZMIMD.js.map} +1 -1
- package/dist/{chunk-DARTMI5Z.js → chunk-5PMGQDG7.js} +2 -2
- package/dist/{chunk-DARTMI5Z.js.map → chunk-5PMGQDG7.js.map} +1 -1
- package/dist/{chunk-V6RZWDII.js → chunk-7GUQYHKA.js} +19 -2
- package/dist/chunk-7GUQYHKA.js.map +1 -0
- package/dist/{chunk-ZNZ2JDBW.js → chunk-7IUSV4XG.js} +1 -1
- package/dist/chunk-7IUSV4XG.js.map +1 -0
- package/dist/{chunk-ELRMBQEC.js → chunk-AEPXGCNF.js} +17 -2
- package/dist/chunk-AEPXGCNF.js.map +1 -0
- package/dist/{chunk-5CE64WGC.js → chunk-D547PH4L.js} +3 -3
- package/dist/{chunk-5CE64WGC.js.map → chunk-D547PH4L.js.map} +1 -1
- package/dist/{chunk-S6ZTTTV7.js → chunk-DVZCMMB3.js} +3 -3
- package/dist/{chunk-S6ZTTTV7.js.map → chunk-DVZCMMB3.js.map} +1 -1
- package/dist/chunk-EKYZCRCX.js +256 -0
- package/dist/chunk-EKYZCRCX.js.map +1 -0
- package/dist/{chunk-3V75EQIP.js → chunk-JM53SMVF.js} +3 -3
- package/dist/{chunk-3V75EQIP.js.map → chunk-JM53SMVF.js.map} +1 -1
- package/dist/{chunk-2WMSE4MK.js → chunk-JMGKM7RK.js} +2 -2
- package/dist/{chunk-2WMSE4MK.js.map → chunk-JMGKM7RK.js.map} +1 -1
- package/dist/{chunk-2YOUUEWR.js → chunk-KX7DP2GO.js} +4 -4
- package/dist/{chunk-2YOUUEWR.js.map → chunk-KX7DP2GO.js.map} +1 -1
- package/dist/{chunk-OL4TC3U2.js → chunk-MO56ZZLG.js} +6 -2
- package/dist/chunk-MO56ZZLG.js.map +1 -0
- package/dist/{chunk-OQI7RCRP.js → chunk-MTM43SJO.js} +7 -1
- package/dist/chunk-MTM43SJO.js.map +1 -0
- package/dist/{chunk-ZSYLBVWG.js → chunk-NMGTHYW3.js} +3 -3
- package/dist/{chunk-ZSYLBVWG.js.map → chunk-NMGTHYW3.js.map} +1 -1
- package/dist/{chunk-AQSKJ6U6.js → chunk-NZXWU4R5.js} +21 -2
- package/dist/chunk-NZXWU4R5.js.map +1 -0
- package/dist/{chunk-DJKWFYAA.js → chunk-O3RCCSQJ.js} +3 -3
- package/dist/{chunk-DJKWFYAA.js.map → chunk-O3RCCSQJ.js.map} +1 -1
- package/dist/{chunk-UMGY5JSP.js → chunk-S4QRSBFI.js} +22 -2
- package/dist/chunk-S4QRSBFI.js.map +1 -0
- package/dist/{chunk-KKNK3JUT.js → chunk-UBH743HV.js} +5 -5
- package/dist/chunk-UBH743HV.js.map +1 -0
- package/dist/{chunk-54HM36IQ.js → chunk-ULCDZTWL.js} +2 -2
- package/dist/{chunk-54HM36IQ.js.map → chunk-ULCDZTWL.js.map} +1 -1
- package/dist/{chunk-SIU2AC5W.js → chunk-UPZ6EEJN.js} +3 -3
- package/dist/{chunk-SIU2AC5W.js.map → chunk-UPZ6EEJN.js.map} +1 -1
- package/dist/collapsible-panel.d.ts +1 -1
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/css-values.d.ts +39 -3
- package/dist/css-values.js +1 -1
- package/dist/grid.d.ts +15 -1
- package/dist/grid.js +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +19 -19
- package/dist/list-action-row.js +1 -1
- package/dist/list-header.js +1 -1
- package/dist/list-item.js +1 -1
- package/dist/list.d.ts +21 -2
- package/dist/list.js +2 -1
- package/dist/nav-stack.js +1 -1
- package/dist/pane.d.ts +17 -2
- package/dist/pane.js +1 -1
- package/dist/row.d.ts +19 -1
- package/dist/row.js +2 -1
- package/dist/segmented-control.js +1 -1
- package/dist/select.d.ts +13 -3
- package/dist/select.js +1 -1
- package/dist/skeleton.js +1 -1
- package/dist/spacer.js +1 -1
- package/dist/split-view.js +1 -1
- package/dist/state-banner.js +1 -1
- package/dist/styles/catalog/components/catalog-example-stack.css +4 -1
- package/dist/styles/catalog/components/catalog-example.css +145 -0
- package/dist/styles/collapsible-panel.css +132 -4
- package/dist/styles/document.css +23 -0
- package/dist/styles/foundation.css +21 -7
- package/dist/styles/grid.css +10 -0
- package/dist/styles/list-header.css +45 -0
- package/dist/styles/list-inset-control.css +8 -1
- package/dist/styles/list-item.css +6 -0
- package/dist/styles/list.css +11 -0
- package/dist/styles/nav-stack.css +89 -2
- package/dist/styles/pane.css +86 -1
- package/dist/styles/resizable-region.css +93 -5
- package/dist/styles/row.css +10 -0
- package/dist/styles/select.css +39 -4
- package/dist/styles/split-view.css +107 -1
- package/dist/styles/tab-scaffold.css +64 -1
- package/dist/styles/text.css +6 -0
- package/dist/styles/toolbar-control-group.css +90 -11
- package/dist/styles/toolbar-text.css +4 -1
- package/dist/styles/toolbar.css +130 -38
- package/dist/styles/webawesome.css +48 -2
- package/dist/styles/workbench.css +136 -4
- package/dist/text.d.ts +4 -3
- package/dist/text.js +1 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/toolbar.d.ts +24 -3
- package/dist/toolbar.js +1 -1
- package/dist/{types-CzgSrFXP.d.ts → types-CEsieBZi.d.ts} +6 -1
- package/dist/value-table.js +1 -1
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-sidebar.d.ts +18 -3
- package/dist/wire-sidebar.js +95 -14
- package/dist/wire-sidebar.js.map +1 -1
- package/docs/accessibility.md +45 -8
- package/docs/app-layouts.md +73 -0
- package/docs/catalog.md +23 -2
- package/docs/collapsible-panel.md +81 -8
- package/docs/component-contract.md +28 -9
- package/docs/component-integration.md +9 -4
- package/docs/component-selection.md +25 -6
- package/docs/design/templates/list/stack-dark.svg +1 -1
- package/docs/design/templates/list/stack.svg +1 -1
- package/docs/design/templates/list-dark.svg +1 -1
- package/docs/design/templates/list-header/action-dark.svg +1 -1
- package/docs/design/templates/list-header/action.svg +1 -1
- package/docs/design/templates/list-header/count-dark.svg +1 -1
- package/docs/design/templates/list-header/count.svg +1 -1
- package/docs/design/templates/list-header/inline-action-dark.svg +1 -1
- package/docs/design/templates/list-header/inline-action.svg +1 -1
- package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -1
- package/docs/design/templates/list-header/toggle-expanded.svg +1 -1
- package/docs/design/templates/list-header-dark.svg +1 -1
- package/docs/design/templates/list-header.svg +1 -1
- package/docs/design/templates/list.svg +1 -1
- package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
- package/docs/design/templates/state-banner/info-badge.svg +1 -1
- package/docs/design/templates/state-banner-dark.svg +1 -1
- package/docs/design/templates/state-banner.svg +1 -1
- package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -1
- package/docs/design/templates/toolbar/title-and-actions.svg +1 -1
- package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
- package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
- package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
- package/docs/design/templates/toolbar-control-group.svg +1 -1
- package/docs/design/templates/toolbar-dark.svg +1 -1
- package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
- package/docs/design/templates/toolbar-text/default.svg +1 -1
- package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
- package/docs/design/templates/toolbar-text/large.svg +1 -1
- package/docs/design/templates/toolbar-text/small-dark.svg +1 -1
- package/docs/design/templates/toolbar-text/small.svg +1 -1
- package/docs/design/templates/toolbar-text-dark.svg +1 -1
- package/docs/design/templates/toolbar-text.svg +1 -1
- package/docs/design/templates/toolbar.svg +1 -1
- package/docs/document-baseline.md +31 -0
- package/docs/layout.md +86 -21
- package/docs/nav-stack.md +8 -0
- package/docs/recipes.md +78 -43
- package/docs/split-view.md +8 -0
- package/docs/tab-scaffold.md +4 -2
- package/docs/type-contracts.md +21 -17
- package/docs/ui-analyzer.md +19 -0
- package/docs/ui-evaluator.md +17 -0
- package/docs/ux-demo.md +4 -4
- package/docs/webawesome-theme.md +20 -2
- package/docs/workbench.md +20 -1
- package/evaluator/index.mjs +73 -8
- package/llms.txt +32 -14
- package/package.json +4 -2
- package/ux-demo/recipes/app-shell.tsx +209 -178
- package/ux-demo/recipes/collapsible-sidebar.tsx +188 -159
- package/ux-demo/recipes/compact-toolbar.tsx +48 -35
- package/ux-demo/recipes/composer-form.tsx +85 -79
- package/ux-demo/recipes/list-detail-dialog.tsx +124 -142
- package/ux-demo/recipes/list-workspace-states.tsx +83 -83
- package/ux-demo/recipes/loaders.ts +5 -2
- package/ux-demo/recipes/loading-inspector.tsx +157 -160
- package/ux-demo/recipes/navigation-sidebar.tsx +111 -109
- package/ux-demo/recipes/navigation-stack.tsx +50 -49
- package/ux-demo/recipes/types.ts +14 -0
- package/ux-demo/recipes/workspace-header.tsx +73 -56
- package/dist/chunk-AQSKJ6U6.js.map +0 -1
- package/dist/chunk-ELRMBQEC.js.map +0 -1
- package/dist/chunk-J3EE6XWD.js +0 -115
- package/dist/chunk-J3EE6XWD.js.map +0 -1
- package/dist/chunk-KKNK3JUT.js.map +0 -1
- package/dist/chunk-OL4TC3U2.js.map +0 -1
- package/dist/chunk-OQI7RCRP.js.map +0 -1
- package/dist/chunk-UMGY5JSP.js.map +0 -1
- package/dist/chunk-V6RZWDII.js.map +0 -1
- package/dist/chunk-ZNZ2JDBW.js.map +0 -1
- package/ux-demo/recipes/app-shell.css +0 -87
- package/ux-demo/recipes/collapsible-sidebar.css +0 -83
- package/ux-demo/recipes/compact-toolbar.css +0 -35
- package/ux-demo/recipes/composer-form.css +0 -19
- package/ux-demo/recipes/list-detail-dialog.css +0 -42
- package/ux-demo/recipes/list-workspace-states.css +0 -30
- package/ux-demo/recipes/loading-inspector.css +0 -7
- package/ux-demo/recipes/navigation-sidebar.css +0 -12
- package/ux-demo/recipes/navigation-stack.css +0 -12
- package/ux-demo/recipes/recipe-root.css +0 -67
- package/ux-demo/recipes/recipe-root.tsx +0 -87
- package/ux-demo/recipes/workspace-header.css +0 -7
|
@@ -92,7 +92,27 @@
|
|
|
92
92
|
"wiring": {
|
|
93
93
|
"type": "object",
|
|
94
94
|
"required": ["export", "import", "required"],
|
|
95
|
-
"properties": {
|
|
95
|
+
"properties": {
|
|
96
|
+
"export": { "type": "string" },
|
|
97
|
+
"import": { "type": "string" },
|
|
98
|
+
"required": { "type": "boolean" },
|
|
99
|
+
"reason": { "type": "string" },
|
|
100
|
+
"stateAttributes": {
|
|
101
|
+
"description": "Wiring-owned state attributes: data-* attributes this helper writes at runtime on the entry's elements. The application must not render, remove, or treat them as its own state.",
|
|
102
|
+
"type": "array",
|
|
103
|
+
"items": { "$ref": "#/$defs/stateAttribute" }
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"additionalProperties": false
|
|
107
|
+
},
|
|
108
|
+
"stateAttribute": {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"required": ["name", "on", "meaning"],
|
|
111
|
+
"properties": {
|
|
112
|
+
"name": { "type": "string", "minLength": 1, "pattern": "^data-[a-z0-9]+(-[a-z0-9]+)*$" },
|
|
113
|
+
"on": { "type": "string", "minLength": 1, "description": "The element that carries it: a public class, or the element passed to the helper." },
|
|
114
|
+
"meaning": { "type": "string", "minLength": 1, "description": "What its presence or value means." }
|
|
115
|
+
},
|
|
96
116
|
"additionalProperties": false
|
|
97
117
|
},
|
|
98
118
|
"delivery": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Public API signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from emitted declarations for `@kerfjs/ui@5.0.0-beta.51` and `kerfjs@5.0.0-beta.51`. This bounded reference covers only APIs used by the seven-task corpus. It is interface evidence, not an implementation or runtime guarantee.
|
|
4
4
|
|
|
5
5
|
## `@kerfjs/ui/css-values`
|
|
6
6
|
|
|
@@ -10,6 +10,7 @@ declare const cssLengthBrand: unique symbol;
|
|
|
10
10
|
declare const cssLengthExpressionBrand: unique symbol;
|
|
11
11
|
declare const cssFlexBrand: unique symbol;
|
|
12
12
|
declare const cssColorBrand: unique symbol;
|
|
13
|
+
declare const cssForegroundColorBrand: unique symbol;
|
|
13
14
|
/**
|
|
14
15
|
* A complete typed CSS value minted by a property-specific Kerf UI builder.
|
|
15
16
|
*
|
|
@@ -42,6 +43,18 @@ type CssFlex = CssValue & {
|
|
|
42
43
|
type CssColor = CssValue & {
|
|
43
44
|
readonly [cssColorBrand]: 'CssColor';
|
|
44
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* A complete CSS color meant to paint a foreground: text or an icon drawn over
|
|
48
|
+
* a surface. Mint it with {@link uiColor} and a foreground token name
|
|
49
|
+
* ({@link UiForegroundColorName}) or with {@link foregroundColorVar}. It is a
|
|
50
|
+
* {@link CssColor}, but a plain `CssColor` is not a foreground color: fill,
|
|
51
|
+
* border, and surface tokens (including the `success` / `warning` / `danger` /
|
|
52
|
+
* `pop` fill aliases) are pale backgrounds that leave a foreground nearly
|
|
53
|
+
* invisible.
|
|
54
|
+
*/
|
|
55
|
+
type CssForegroundColor = CssColor & {
|
|
56
|
+
readonly [cssForegroundColorBrand]: 'CssForegroundColor';
|
|
57
|
+
};
|
|
45
58
|
type CssFlexKeyword = 'none' | 'auto' | 'initial';
|
|
46
59
|
type CssFlexBasis = CssLength | 'auto' | 'content' | 'min-content' | 'max-content' | 'fit-content';
|
|
47
60
|
type CssSizeKeyword = 'auto' | 'min-content' | 'max-content' | 'fit-content';
|
|
@@ -50,6 +63,13 @@ type CssSize = CssLength | CssSizeKeyword;
|
|
|
50
63
|
declare const uiColorNames: readonly ["accent", "accent-text", "border", "border-quiet", "brand-border-loud", "brand-border-normal", "brand-border-quiet", "brand-fill-loud", "brand-fill-normal", "brand-fill-quiet", "brand-on-fill", "brand-on-loud", "brand-on-normal", "brand-on-quiet", "danger", "danger-border-loud", "danger-border-normal", "danger-border-quiet", "danger-fill-loud", "danger-fill-normal", "danger-fill-quiet", "danger-on-loud", "danger-on-normal", "danger-on-quiet", "danger-text", "neutral-border-loud", "neutral-border-normal", "neutral-border-quiet", "neutral-fill-loud", "neutral-fill-normal", "neutral-fill-quiet", "neutral-on-loud", "neutral-on-normal", "neutral-on-quiet", "pop", "pop-border-loud", "pop-border-normal", "pop-border-quiet", "pop-fill-loud", "pop-fill-normal", "pop-fill-quiet", "pop-on-fill", "pop-on-loud", "pop-on-normal", "pop-on-quiet", "pop-text", "success", "success-border-loud", "success-border-normal", "success-border-quiet", "success-fill-loud", "success-fill-normal", "success-fill-quiet", "success-on-fill", "success-on-loud", "success-on-normal", "success-on-quiet", "success-text", "surface", "surface-lowered", "surface-raised", "text", "text-link", "text-quiet", "warning", "warning-border-loud", "warning-border-normal", "warning-border-quiet", "warning-fill-loud", "warning-fill-normal", "warning-fill-quiet", "warning-on-fill", "warning-on-loud", "warning-on-normal", "warning-on-quiet", "warning-text"];
|
|
51
64
|
/** Names of the public `--kui-color-*` semantic tokens. */
|
|
52
65
|
type UiColorName = (typeof uiColorNames)[number];
|
|
66
|
+
/**
|
|
67
|
+
* The semantic tokens that paint a foreground: the `*-on-*` roles, the text
|
|
68
|
+
* roles, and their `*-text` compatibility aliases. The bare `success`,
|
|
69
|
+
* `warning`, `danger`, `pop`, and `accent` aliases are quiet fills, not
|
|
70
|
+
* foregrounds.
|
|
71
|
+
*/
|
|
72
|
+
type UiForegroundColorName = Extract<UiColorName, `${string}-on-${string}` | `${string}-text` | 'text' | 'text-quiet' | 'text-link'>;
|
|
53
73
|
/** Kerf UI's complete spacing-token vocabulary. `s` and `xl` are exceptions. */
|
|
54
74
|
type UiSpaceName = 'none' | '2xs' | 'xs' | 's' | 'm' | 'l' | 'xl';
|
|
55
75
|
/** Create a complete pixel length. */
|
|
@@ -74,12 +94,28 @@ declare function plus(first: CssLength, second: CssLength, ...rest: readonly Css
|
|
|
74
94
|
declare function calc(expression: CssLengthExpression): CssLength;
|
|
75
95
|
/** Build a complete, structured CSS flex shorthand. */
|
|
76
96
|
declare function flex(grow: number, shrink?: number, basis?: CssFlexBasis): CssFlex;
|
|
77
|
-
/**
|
|
78
|
-
|
|
97
|
+
/**
|
|
98
|
+
* The brand {@link uiColor} returns for a token name: a
|
|
99
|
+
* {@link CssForegroundColor} for a foreground token, otherwise a plain
|
|
100
|
+
* {@link CssColor}.
|
|
101
|
+
*/
|
|
102
|
+
type UiColor<Name extends UiColorName> = Name extends UiForegroundColorName ? CssForegroundColor : CssColor;
|
|
103
|
+
/**
|
|
104
|
+
* Resolve a public Kerf UI semantic color token. A foreground token name
|
|
105
|
+
* (`success-on-quiet`, `text-quiet`, …) returns a {@link CssForegroundColor};
|
|
106
|
+
* any other token returns a plain {@link CssColor}.
|
|
107
|
+
*/
|
|
108
|
+
declare function uiColor<Name extends UiColorName>(name: Name): UiColor<Name>;
|
|
79
109
|
/** Reference an application-owned custom property whose contract is a color. */
|
|
80
110
|
declare function colorVar(name: `--${string}`, fallback?: CssColor): CssColor;
|
|
111
|
+
/**
|
|
112
|
+
* Reference an application-owned custom property whose contract is a
|
|
113
|
+
* foreground color (text or an icon over a surface). Use it where a prop
|
|
114
|
+
* accepts only {@link CssForegroundColor}, such as `SelectChoice.color`.
|
|
115
|
+
*/
|
|
116
|
+
declare function foregroundColorVar(name: `--${string}`, fallback?: CssForegroundColor): CssForegroundColor;
|
|
81
117
|
|
|
82
|
-
export { type CssColor, type CssFlex, type CssFlexBasis, type CssFlexKeyword, type CssLength, type CssLengthExpression, type CssSize, type CssSizeKeyword, type CssValue, type UiColorName, type UiSpaceName, calc, colorVar, em, flex, lengthVar, pct, plus, px, rem, space, uiColor };
|
|
118
|
+
export { type CssColor, type CssFlex, type CssFlexBasis, type CssFlexKeyword, type CssForegroundColor, type CssLength, type CssLengthExpression, type CssSize, type CssSizeKeyword, type CssValue, type UiColor, type UiColorName, type UiForegroundColorName, type UiSpaceName, calc, colorVar, em, flex, foregroundColorVar, lengthVar, pct, plus, px, rem, space, uiColor };
|
|
83
119
|
```
|
|
84
120
|
|
|
85
121
|
## `@kerfjs/ui/disclosure-arrow`
|
|
@@ -108,6 +144,7 @@ export { DisclosureArrow, type DisclosureArrowProps, type DisclosureDirection };
|
|
|
108
144
|
```ts
|
|
109
145
|
import * as kerfjs from 'kerfjs';
|
|
110
146
|
import { D as DividerSides } from './divider-sides-BzB6rphT.js';
|
|
147
|
+
import { PaneSeparatorSide } from './pane.js';
|
|
111
148
|
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
112
149
|
|
|
113
150
|
interface ToolbarProps {
|
|
@@ -119,15 +156,35 @@ interface ToolbarProps {
|
|
|
119
156
|
dividerSides?: DividerSides;
|
|
120
157
|
/** Horizontal treatment of the center zone. Defaults to centered content. */
|
|
121
158
|
centerAlign?: 'center' | 'stretch';
|
|
122
|
-
/**
|
|
123
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Component-owned responsive layout; applications choose the policy rather
|
|
161
|
+
* than restyling toolbar internals. Under every policy the trailing zone
|
|
162
|
+
* wraps its groups instead of clipping an action.
|
|
163
|
+
* - `none` keeps one row; the leading identity truncates first.
|
|
164
|
+
* - `stack` stacks the zones at `responsiveAt`, wrapping stacked control
|
|
165
|
+
* groups onto further rows.
|
|
166
|
+
* - `wrap` keeps one row while every zone fits at its natural width, and
|
|
167
|
+
* otherwise moves the trailing zone below a whole leading identity.
|
|
168
|
+
* - `center-priority` gives an expanded center control the full row.
|
|
169
|
+
*/
|
|
170
|
+
responsive?: 'none' | 'stack' | 'wrap' | 'center-priority';
|
|
124
171
|
/** Container width at which `responsive="stack"` activates. */
|
|
125
172
|
responsiveAt?: 'compact' | 'narrow';
|
|
173
|
+
/**
|
|
174
|
+
* Screen edges this toolbar claims for device safe-area compensation, for an
|
|
175
|
+
* app bar or bottom bar that sits directly at a screen edge rather than in a
|
|
176
|
+
* Pane header or footer. Each listed side pads by the inset its surrounding
|
|
177
|
+
* layout reports through `--kui-edge-inset-*`, or the full device inset when
|
|
178
|
+
* nothing routes that edge, while the toolbar's box and dividers paint
|
|
179
|
+
* through. Omitted, the toolbar pads only the inline edges an owner such as
|
|
180
|
+
* a Pane header hands it, and never a block edge.
|
|
181
|
+
*/
|
|
182
|
+
safeAreaEdges?: readonly PaneSeparatorSide[];
|
|
126
183
|
className?: string;
|
|
127
184
|
/** Native named-slot assignment when composed inside a web component. */
|
|
128
185
|
slot?: string;
|
|
129
186
|
}
|
|
130
|
-
declare function Toolbar({ leading, center, trailing, label, dividerSides, centerAlign, responsive, responsiveAt, className, slot, }: ToolbarProps): kerfjs.SafeHtml;
|
|
187
|
+
declare function Toolbar({ leading, center, trailing, label, dividerSides, centerAlign, responsive, responsiveAt, safeAreaEdges, className, slot, }: ToolbarProps): kerfjs.SafeHtml;
|
|
131
188
|
|
|
132
189
|
export { DividerSides, Toolbar, type ToolbarProps };
|
|
133
190
|
```
|
|
@@ -389,12 +446,29 @@ import { H as HorizontalAlignment, L as ListVerticalAlignment } from './flex-ali
|
|
|
389
446
|
export { V as VerticalAlignment } from './flex-alignment-4ms8ZbV8.js';
|
|
390
447
|
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
391
448
|
|
|
449
|
+
type ListRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
|
|
450
|
+
'data-component'?: never;
|
|
451
|
+
'data-gap'?: never;
|
|
452
|
+
'data-flex'?: never;
|
|
453
|
+
'data-fill'?: never;
|
|
454
|
+
'data-h-align'?: never;
|
|
455
|
+
'data-v-align'?: never;
|
|
456
|
+
'data-scrollable'?: never;
|
|
457
|
+
'data-text-insets'?: never;
|
|
458
|
+
'data-control-insets'?: never;
|
|
459
|
+
}>;
|
|
392
460
|
interface ListProps {
|
|
393
461
|
children?: KerfUiContent;
|
|
394
462
|
/** Use the standard item gap, a named UI spacing token, or a typed CSS length. Defaults to no gap. */
|
|
395
463
|
gap?: boolean | UiSpaceName | CssLength;
|
|
396
464
|
/** Allow this list to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
|
|
397
465
|
flex?: boolean | CssFlexKeyword | CssFlex;
|
|
466
|
+
/**
|
|
467
|
+
* Fill the height of a parent with a definite height, such as an app root or
|
|
468
|
+
* a fixed-height frame, when this list is that parent's layout root. Inside a
|
|
469
|
+
* flex layout use `flex` instead. Defaults to false.
|
|
470
|
+
*/
|
|
471
|
+
fill?: boolean;
|
|
398
472
|
/** Horizontal alignment. Defaults to full to preserve stretch-aligned list children. */
|
|
399
473
|
hAlign?: HorizontalAlignment;
|
|
400
474
|
/** Vertical distribution. Defaults to top. */
|
|
@@ -408,11 +482,13 @@ interface ListProps {
|
|
|
408
482
|
/** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */
|
|
409
483
|
controlInsets?: Sides;
|
|
410
484
|
className?: string;
|
|
485
|
+
/** Safe `data-*` metadata; List-owned structural attributes remain protected. */
|
|
486
|
+
rootAttributes?: ListRootAttributes;
|
|
411
487
|
/** Native named-slot assignment when composed inside a web component. */
|
|
412
488
|
slot?: string;
|
|
413
489
|
}
|
|
414
|
-
/** A stretch-aligned vertical stack with optional gap, flex, scroll, and dividers. */
|
|
415
|
-
declare function List({ children, gap, flex, hAlign, vAlign, scrollable, dividerSides, textInsets, controlInsets, className, slot, }: ListProps): kerfjs.SafeHtml;
|
|
490
|
+
/** A stretch-aligned vertical stack with optional gap, flex, fill, scroll, and dividers. */
|
|
491
|
+
declare function List({ children, gap, flex, fill, hAlign, vAlign, scrollable, dividerSides, textInsets, controlInsets, className, rootAttributes, slot, }: ListProps): kerfjs.SafeHtml;
|
|
416
492
|
|
|
417
493
|
export { CssFlex, CssFlexKeyword, CssLength, DividerSides, HorizontalAlignment, List, type ListProps, ListVerticalAlignment, Sides, UiSpaceName };
|
|
418
494
|
```
|
|
@@ -887,7 +963,10 @@ export { SplitView, type SplitViewProps, type SplitViewResizable };
|
|
|
887
963
|
import * as kerfjs from 'kerfjs';
|
|
888
964
|
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
889
965
|
|
|
890
|
-
/**
|
|
966
|
+
/**
|
|
967
|
+
* Logical pane sides: where a {@link Pane} can show a separator and where it can
|
|
968
|
+
* compensate for a device safe-area inset.
|
|
969
|
+
*/
|
|
891
970
|
type PaneSeparatorSide = 'block-start' | 'block-end' | 'inline-start' | 'inline-end';
|
|
892
971
|
/** Semantic root elements supported by {@link Pane}. */
|
|
893
972
|
type PaneElement = 'article' | 'aside' | 'div' | 'main' | 'section';
|
|
@@ -899,6 +978,10 @@ type PaneRootAttributes = Readonly<Record<`data-${string}`, string | undefined>
|
|
|
899
978
|
'data-separator-block-end'?: never;
|
|
900
979
|
'data-separator-inline-start'?: never;
|
|
901
980
|
'data-separator-inline-end'?: never;
|
|
981
|
+
'data-safe-area-block-start'?: never;
|
|
982
|
+
'data-safe-area-block-end'?: never;
|
|
983
|
+
'data-safe-area-inline-start'?: never;
|
|
984
|
+
'data-safe-area-inline-end'?: never;
|
|
902
985
|
}>;
|
|
903
986
|
interface PaneProps {
|
|
904
987
|
/** Optional fixed chrome above the scrolling content, arranged vertically. */
|
|
@@ -913,6 +996,14 @@ interface PaneProps {
|
|
|
913
996
|
contentElement?: PaneContentElement;
|
|
914
997
|
/** Independent logical-edge separator lines. Defaults to none. */
|
|
915
998
|
separators?: readonly PaneSeparatorSide[];
|
|
999
|
+
/**
|
|
1000
|
+
* Sides on which the pane may compensate for a device safe-area inset.
|
|
1001
|
+
* Defaults to every side: the pane pads its slots for each side that still
|
|
1002
|
+
* reaches an unsafe screen edge, as its surrounding layout reports, while its
|
|
1003
|
+
* background and separators paint through. List only the sides an app-owned
|
|
1004
|
+
* layout places at a screen edge, or pass `[]` to opt out.
|
|
1005
|
+
*/
|
|
1006
|
+
safeAreaEdges?: readonly PaneSeparatorSide[];
|
|
916
1007
|
id?: string;
|
|
917
1008
|
/** Accessible name for a landmark root such as `aside` or `main`. */
|
|
918
1009
|
label?: string;
|
|
@@ -933,7 +1024,7 @@ interface PaneProps {
|
|
|
933
1024
|
* each logical edge, so the same component works as a sidebar, main area,
|
|
934
1025
|
* inspector, or dialog column.
|
|
935
1026
|
*/
|
|
936
|
-
declare function Pane({ header, children, footer, element, contentElement, separators, id, label, contentLabel, className, headerClassName, contentClassName, footerClassName, rootAttributes, slot, }: PaneProps): kerfjs.SafeHtml;
|
|
1027
|
+
declare function Pane({ header, children, footer, element, contentElement, separators, safeAreaEdges, id, label, contentLabel, className, headerClassName, contentClassName, footerClassName, rootAttributes, slot, }: PaneProps): kerfjs.SafeHtml;
|
|
937
1028
|
|
|
938
1029
|
export { Pane, type PaneContentElement, type PaneElement, type PaneProps, type PaneSeparatorSide };
|
|
939
1030
|
```
|
|
@@ -1066,7 +1157,7 @@ interface CollapsiblePanelProps {
|
|
|
1066
1157
|
* underneath the transform transition. The app owns the `collapsed` signal;
|
|
1067
1158
|
* pair it with `wireSidebar` for the toggle, focus, compact-overlay, keyboard,
|
|
1068
1159
|
* and persistence semantics, and with `CollapsiblePanelToggle` for the standard
|
|
1069
|
-
* affordance. See `docs/
|
|
1160
|
+
* affordance. See `ui/docs/collapsible-panel.md` and `docs/23-app-layouts.md`.
|
|
1070
1161
|
*/
|
|
1071
1162
|
declare function CollapsiblePanel({ id, side, collapsed, size, label, children, separator, collapseMotion, contentOverflow, presentation, restoreControl, restorePosition, className, }: CollapsiblePanelProps): SafeHtml;
|
|
1072
1163
|
|
|
@@ -1092,9 +1183,18 @@ interface WireSidebarPanel {
|
|
|
1092
1183
|
collapsed: Signal<boolean>;
|
|
1093
1184
|
/** `data-action` value the panel's toggle button(s) carry. */
|
|
1094
1185
|
toggleAction: string;
|
|
1095
|
-
/** When set, the collapsed state is loaded from and saved to
|
|
1096
|
-
* this key (a persistence hook), so the panel remembers
|
|
1186
|
+
/** When set, the panel's inline collapsed state is loaded from and saved to
|
|
1187
|
+
* `storage` under this key (a persistence hook), so the panel remembers the
|
|
1188
|
+
* user's inline choice. A compact overlay's open/closed state is transient
|
|
1189
|
+
* and is never persisted. */
|
|
1097
1190
|
storageKey?: string;
|
|
1191
|
+
/** The panel's inline (non-compact) collapsed state when `storage` holds no
|
|
1192
|
+
* choice for it; wire-up seeds the signal with it. Defaults to the signal's
|
|
1193
|
+
* own value. Set it when the app seeds the signal from the device class
|
|
1194
|
+
* (`signal(device.value.compact)`) so a compact first render already starts
|
|
1195
|
+
* collapsed while a later crossing to a wide class still restores this
|
|
1196
|
+
* inline default. */
|
|
1197
|
+
inlineCollapsed?: boolean;
|
|
1098
1198
|
}
|
|
1099
1199
|
interface WireSidebarOptions {
|
|
1100
1200
|
panels: readonly WireSidebarPanel[];
|
|
@@ -1104,6 +1204,12 @@ interface WireSidebarOptions {
|
|
|
1104
1204
|
* dismissable backdrop, Escape and backdrop-click collapse it, and focus is
|
|
1105
1205
|
* trapped within the open panel (the ARIA dialog pattern). Without it the panel
|
|
1106
1206
|
* is always inline.
|
|
1207
|
+
*
|
|
1208
|
+
* An overlay is transient and user-initiated: whenever the compact overlay
|
|
1209
|
+
* presentation begins (wire-up on a compact device, or a crossing from a wide
|
|
1210
|
+
* class), every panel starts collapsed so nothing blocks the page until the
|
|
1211
|
+
* user opens it. The inline state is remembered and restored when the device
|
|
1212
|
+
* crosses back to a wide class (and on disposal).
|
|
1107
1213
|
*/
|
|
1108
1214
|
deviceClass?: ReadonlySignal<DeviceClass>;
|
|
1109
1215
|
/** Compact devices either overlay the panels (default) or hide them in favor
|
|
@@ -1119,7 +1225,7 @@ interface WireSidebarOptions {
|
|
|
1119
1225
|
* delegation with focus restore, focus-into on open, an optional compact overlay
|
|
1120
1226
|
* (backdrop + Escape + focus trap) driven by {@link deviceClass}, and an optional
|
|
1121
1227
|
* persistence hook. The app owns each `collapsed` signal and the layout; this wire
|
|
1122
|
-
* owns the interaction. Returns a disposer. See `docs/
|
|
1228
|
+
* owns the interaction. Returns a disposer. See `ui/docs/collapsible-panel.md` and `docs/23-app-layouts.md`.
|
|
1123
1229
|
*/
|
|
1124
1230
|
declare function wireSidebar(root: HTMLElement, { panels, deviceClass, compactPresentation, exclusiveCompact, storage, }: WireSidebarOptions): () => void;
|
|
1125
1231
|
|
|
@@ -1321,15 +1427,34 @@ export { DEFAULT_BREAKPOINTS, type DeviceBreakpoints, type DeviceClass, type Dev
|
|
|
1321
1427
|
```ts
|
|
1322
1428
|
import * as kerfjs from 'kerfjs';
|
|
1323
1429
|
import { SafeHtml } from 'kerfjs';
|
|
1324
|
-
import { a as CatalogProps } from './types-
|
|
1325
|
-
export { b as CatalogBrand, c as CatalogEntry, d as CatalogRelated, C as CatalogResource, e as CatalogSecondaryGroup, f as CatalogSection } from './types-
|
|
1430
|
+
import { a as CatalogProps } from './types-CEsieBZi.js';
|
|
1431
|
+
export { b as CatalogBrand, c as CatalogEntry, d as CatalogRelated, C as CatalogResource, e as CatalogSecondaryGroup, f as CatalogSection, g as CatalogStageRootAttributes } from './types-CEsieBZi.js';
|
|
1326
1432
|
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
1327
1433
|
|
|
1328
1434
|
/** Controlled, stateless component-catalog shell. */
|
|
1329
|
-
declare function Catalog({ brand, sections, active, content, collapsed, theme, headerActions, secondarySections, sidebarFooter, status, geometryOverlay, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, className, slot, }: CatalogProps): kerfjs.SafeHtml;
|
|
1435
|
+
declare function Catalog({ brand, sections, active, content, collapsed, theme, headerActions, secondarySections, sidebarFooter, status, geometryOverlay, stageRootAttributes, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, className, slot, }: CatalogProps): kerfjs.SafeHtml;
|
|
1330
1436
|
|
|
1331
1437
|
/** How a specimen aligns its visible edge with its `ListHeader` label. */
|
|
1332
1438
|
type CatalogExampleAlign = 'glyph' | 'inline-control' | 'none';
|
|
1439
|
+
interface CatalogExampleViewport {
|
|
1440
|
+
layout?: 'grid' | 'flex' | 'flex-column';
|
|
1441
|
+
width?: 'full' | 'compact' | 'medium' | 'wide' | 'text' | 'control';
|
|
1442
|
+
/** Fixed specimen height; `app` frames an application-sized recipe or layout.
|
|
1443
|
+
* A fixed height is definite, so a child that fills with `height: 100%`
|
|
1444
|
+
* (a horizontal `ResizableRegion`, say) spans the whole frame. */
|
|
1445
|
+
height?: 'short' | 'reduced' | 'medium' | 'tall' | 'app';
|
|
1446
|
+
/** A minimum the frame may grow past. It is not a definite height: a child
|
|
1447
|
+
* sized with `height: 100%` falls back to its content height, so use
|
|
1448
|
+
* `height` when the specimen must fill the frame. */
|
|
1449
|
+
minHeight?: 'short' | 'medium';
|
|
1450
|
+
frame?: 'solid' | 'dashed';
|
|
1451
|
+
surface?: 'default' | 'lowered';
|
|
1452
|
+
overflow?: 'hidden' | 'auto-x';
|
|
1453
|
+
responsive?: 'roomy-only';
|
|
1454
|
+
shadow?: boolean;
|
|
1455
|
+
/** Public component custom properties applied to the specimen viewport. */
|
|
1456
|
+
tokens?: Readonly<Record<`--${string}`, string>>;
|
|
1457
|
+
}
|
|
1333
1458
|
type CatalogExampleRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
|
|
1334
1459
|
'data-catalog-example'?: never;
|
|
1335
1460
|
'data-catalog-example-stack'?: never;
|
|
@@ -1341,6 +1466,10 @@ interface CatalogExampleProps {
|
|
|
1341
1466
|
label?: string;
|
|
1342
1467
|
note?: SafeHtml | string;
|
|
1343
1468
|
align?: CatalogExampleAlign;
|
|
1469
|
+
/** Optional catalog-owned constraints for demonstrating layout components. */
|
|
1470
|
+
viewport?: CatalogExampleViewport;
|
|
1471
|
+
/** Replacement guidance shown when a roomy-only viewport is hidden. */
|
|
1472
|
+
compactFallback?: SafeHtml | string;
|
|
1344
1473
|
rootAttributes?: CatalogExampleRootAttributes;
|
|
1345
1474
|
className?: string;
|
|
1346
1475
|
children?: KerfUiContent;
|
|
@@ -1348,7 +1477,7 @@ interface CatalogExampleProps {
|
|
|
1348
1477
|
slot?: string;
|
|
1349
1478
|
}
|
|
1350
1479
|
/** A labeled catalog specimen with optional explanatory text and alignment. */
|
|
1351
|
-
declare function CatalogExample({ label, note, align, rootAttributes, className, children, slot, }: CatalogExampleProps): SafeHtml;
|
|
1480
|
+
declare function CatalogExample({ label, note, align, viewport, compactFallback, rootAttributes, className, children, slot, }: CatalogExampleProps): SafeHtml;
|
|
1352
1481
|
|
|
1353
1482
|
type CatalogExampleStackRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
|
|
1354
1483
|
'data-catalog-example'?: never;
|
|
@@ -1368,13 +1497,13 @@ interface CatalogExampleStackProps {
|
|
|
1368
1497
|
/** A vertically stacked group of catalog examples. */
|
|
1369
1498
|
declare function CatalogExampleStack({ label, rootAttributes, className, children, slot, }: CatalogExampleStackProps): kerfjs.SafeHtml;
|
|
1370
1499
|
|
|
1371
|
-
export { Catalog, CatalogExample, type CatalogExampleAlign, type CatalogExampleProps, CatalogExampleStack, type CatalogExampleStackProps, CatalogProps };
|
|
1500
|
+
export { Catalog, CatalogExample, type CatalogExampleAlign, type CatalogExampleProps, CatalogExampleStack, type CatalogExampleStackProps, type CatalogExampleViewport, CatalogProps };
|
|
1372
1501
|
```
|
|
1373
1502
|
|
|
1374
1503
|
## `@kerfjs/ui/catalog-resources`
|
|
1375
1504
|
|
|
1376
1505
|
```ts
|
|
1377
|
-
import { C as CatalogResource } from './types-
|
|
1506
|
+
import { C as CatalogResource } from './types-CEsieBZi.js';
|
|
1378
1507
|
import './semantic-content-BbzjvSu9.js';
|
|
1379
1508
|
import 'kerfjs';
|
|
1380
1509
|
|
|
@@ -1515,7 +1644,7 @@ export { SegmentedControl, type SegmentedControlAppearance, type SegmentedContro
|
|
|
1515
1644
|
|
|
1516
1645
|
```ts
|
|
1517
1646
|
import { SafeHtml } from 'kerfjs';
|
|
1518
|
-
import {
|
|
1647
|
+
import { CssForegroundColor } from './css-values.js';
|
|
1519
1648
|
import { LucideNode } from './lucide-icon.js';
|
|
1520
1649
|
import 'lucide';
|
|
1521
1650
|
|
|
@@ -1524,9 +1653,19 @@ interface SelectChoice<Value extends string = string> {
|
|
|
1524
1653
|
label: string;
|
|
1525
1654
|
icon?: LucideNode;
|
|
1526
1655
|
iconName?: string;
|
|
1527
|
-
/**
|
|
1528
|
-
|
|
1656
|
+
/**
|
|
1657
|
+
* Foreground color for the optional icon: a semantic foreground token such
|
|
1658
|
+
* as `uiColor('success-on-quiet')`, or an application-owned
|
|
1659
|
+
* `foregroundColorVar('--app-icon-color')`. Fill tokens such as
|
|
1660
|
+
* `uiColor('success')` are pale background tints and do not type-check.
|
|
1661
|
+
*/
|
|
1662
|
+
color?: CssForegroundColor;
|
|
1529
1663
|
group?: string;
|
|
1664
|
+
/**
|
|
1665
|
+
* Draw a divider between this choice and the previous choice in the same
|
|
1666
|
+
* list. A group boundary is already a separator, so the first choice of a
|
|
1667
|
+
* group (or of the whole menu) never draws a second one.
|
|
1668
|
+
*/
|
|
1530
1669
|
separatorBefore?: boolean;
|
|
1531
1670
|
}
|
|
1532
1671
|
type SelectAccessibleName = {
|
|
@@ -2051,14 +2190,14 @@ import * as kerfjs from 'kerfjs';
|
|
|
2051
2190
|
import { KerfBaseAttrs } from 'kerfjs/jsx-runtime';
|
|
2052
2191
|
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
2053
2192
|
|
|
2054
|
-
type TextVariant = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p';
|
|
2193
|
+
type TextVariant = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p' | 'span';
|
|
2055
2194
|
type TextTone = 'default' | 'quiet' | 'danger';
|
|
2056
2195
|
type TextSize = 'default' | 'compact';
|
|
2057
2196
|
type TextFont = 'default' | 'monospace';
|
|
2058
2197
|
type TextBorder = 'transparent' | 'none';
|
|
2059
2198
|
type TextContent = KerfUiContent | string | number | readonly TextContent[];
|
|
2060
2199
|
type TextProps = Omit<KerfBaseAttrs, 'children' | 'class' | 'className'> & {
|
|
2061
|
-
/** Native heading or
|
|
2200
|
+
/** Native heading, paragraph, or inline span element to render. Defaults to `p`. */
|
|
2062
2201
|
variant?: TextVariant;
|
|
2063
2202
|
/** Semantic foreground treatment. Defaults to the inherited foreground. */
|
|
2064
2203
|
tone?: TextTone;
|
|
@@ -2073,7 +2212,8 @@ type TextProps = Omit<KerfBaseAttrs, 'children' | 'class' | 'className'> & {
|
|
|
2073
2212
|
className?: string;
|
|
2074
2213
|
};
|
|
2075
2214
|
/**
|
|
2076
|
-
* Semantic heading or
|
|
2215
|
+
* Semantic heading, paragraph, or inline text. Block variants use the standard
|
|
2216
|
+
* content-item padding; `span` adds no box geometry.
|
|
2077
2217
|
* All ordinary native heading/paragraph attributes pass through to the element.
|
|
2078
2218
|
*/
|
|
2079
2219
|
declare function Text({ variant: Variant, tone, size, font, border, children, class: classValue, className, ...attributes }: TextProps): kerfjs.SafeHtml;
|
|
@@ -2090,6 +2230,16 @@ import { S as Sides } from './divider-sides-BzB6rphT.js';
|
|
|
2090
2230
|
import { H as HorizontalAlignment, V as VerticalAlignment } from './flex-alignment-4ms8ZbV8.js';
|
|
2091
2231
|
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
2092
2232
|
|
|
2233
|
+
type RowRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
|
|
2234
|
+
'data-component'?: never;
|
|
2235
|
+
'data-h-align'?: never;
|
|
2236
|
+
'data-v-align'?: never;
|
|
2237
|
+
'data-flex'?: never;
|
|
2238
|
+
'data-fill'?: never;
|
|
2239
|
+
'data-wrap'?: never;
|
|
2240
|
+
'data-text-insets'?: never;
|
|
2241
|
+
'data-control-insets'?: never;
|
|
2242
|
+
}>;
|
|
2093
2243
|
interface RowProps {
|
|
2094
2244
|
children?: KerfUiContent;
|
|
2095
2245
|
/** Horizontal distribution. Defaults to left. */
|
|
@@ -2100,6 +2250,12 @@ interface RowProps {
|
|
|
2100
2250
|
gap?: UiSpaceName | CssLength;
|
|
2101
2251
|
/** Allow this row to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
|
|
2102
2252
|
flex?: boolean | CssFlexKeyword | CssFlex;
|
|
2253
|
+
/**
|
|
2254
|
+
* Fill the height of a parent with a definite height, such as an app root or
|
|
2255
|
+
* a fixed-height frame, when this row is that parent's layout root. Inside a
|
|
2256
|
+
* flex layout use `flex` instead. Defaults to false.
|
|
2257
|
+
*/
|
|
2258
|
+
fill?: boolean;
|
|
2103
2259
|
/** Allow children to wrap onto additional lines. */
|
|
2104
2260
|
wrap?: boolean;
|
|
2105
2261
|
/** Physical sides that receive the standard 17px text inset. */
|
|
@@ -2107,11 +2263,13 @@ interface RowProps {
|
|
|
2107
2263
|
/** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */
|
|
2108
2264
|
controlInsets?: Sides;
|
|
2109
2265
|
className?: string;
|
|
2266
|
+
/** Safe `data-*` metadata; Row-owned structural attributes remain protected. */
|
|
2267
|
+
rootAttributes?: RowRootAttributes;
|
|
2110
2268
|
/** Native named-slot assignment when composed inside a web component. */
|
|
2111
2269
|
slot?: string;
|
|
2112
2270
|
}
|
|
2113
2271
|
/** A horizontal flex row with explicit physical-axis alignment and spacing. */
|
|
2114
|
-
declare function Row({ children, hAlign, vAlign, gap, flex, wrap, textInsets, controlInsets, className, slot, }: RowProps): kerfjs.SafeHtml;
|
|
2272
|
+
declare function Row({ children, hAlign, vAlign, gap, flex, fill, wrap, textInsets, controlInsets, className, rootAttributes, slot, }: RowProps): kerfjs.SafeHtml;
|
|
2115
2273
|
|
|
2116
2274
|
export { CssFlex, CssFlexKeyword, CssLength, HorizontalAlignment, Row, type RowProps, Sides, UiSpaceName, VerticalAlignment };
|
|
2117
2275
|
```
|
|
@@ -2123,6 +2281,12 @@ import * as kerfjs from 'kerfjs';
|
|
|
2123
2281
|
import { UiSpaceName, CssLength, CssFlexKeyword, CssFlex } from './css-values.js';
|
|
2124
2282
|
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
2125
2283
|
|
|
2284
|
+
type GridRootAttributes = Readonly<Record<`data-${string}`, string | undefined> & {
|
|
2285
|
+
'data-component'?: never;
|
|
2286
|
+
'data-columns'?: never;
|
|
2287
|
+
'data-flex'?: never;
|
|
2288
|
+
'data-fill'?: never;
|
|
2289
|
+
}>;
|
|
2126
2290
|
interface GridProps {
|
|
2127
2291
|
children?: KerfUiContent;
|
|
2128
2292
|
/** Number of equal-width columns. Must be a positive safe integer. */
|
|
@@ -2131,12 +2295,20 @@ interface GridProps {
|
|
|
2131
2295
|
gap?: UiSpaceName | CssLength;
|
|
2132
2296
|
/** Allow this grid to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */
|
|
2133
2297
|
flex?: boolean | CssFlexKeyword | CssFlex;
|
|
2298
|
+
/**
|
|
2299
|
+
* Fill the height of a parent with a definite height, such as an app root or
|
|
2300
|
+
* a fixed-height frame, when this grid is that parent's layout root. Inside a
|
|
2301
|
+
* flex layout use `flex` instead. Defaults to false.
|
|
2302
|
+
*/
|
|
2303
|
+
fill?: boolean;
|
|
2134
2304
|
className?: string;
|
|
2305
|
+
/** Safe `data-*` metadata; Grid-owned structural attributes remain protected. */
|
|
2306
|
+
rootAttributes?: GridRootAttributes;
|
|
2135
2307
|
/** Native named-slot assignment when composed inside a web component. */
|
|
2136
2308
|
slot?: string;
|
|
2137
2309
|
}
|
|
2138
2310
|
/** A fixed-count grid whose columns share the available width equally. */
|
|
2139
|
-
declare function Grid({ children, columns, gap, flex, className, slot, }: GridProps): kerfjs.SafeHtml;
|
|
2311
|
+
declare function Grid({ children, columns, gap, flex, fill, className, rootAttributes, slot, }: GridProps): kerfjs.SafeHtml;
|
|
2140
2312
|
|
|
2141
2313
|
export { CssFlex, CssFlexKeyword, CssLength, Grid, type GridProps, UiSpaceName };
|
|
2142
2314
|
```
|
package/ai/skill.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kerf-ui
|
|
3
3
|
description: Build interfaces with kerfjs and the @kerfjs/ui production component package. Use whenever code imports @kerfjs/ui or a task asks for Kerf UI components.
|
|
4
|
-
kerf-ui-skill-version: 1.42.
|
|
4
|
+
kerf-ui-skill-version: 1.42.4
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Building with @kerfjs/ui
|
|
@@ -31,7 +31,9 @@ scaffolded by `create-kerf-component` owns the decisions in
|
|
|
31
31
|
`kerf.components.json`; run `npm run catalog:check` before using its output. If
|
|
32
32
|
no generated extension exists, start from the checked examples and require the
|
|
33
33
|
author to supply missing semantic and geometry decisions; never infer them from
|
|
34
|
-
rendered appearance.
|
|
34
|
+
rendered appearance. A `data-*` attribute listed in an entry's
|
|
35
|
+
`wiring.stateAttributes` belongs to the named helper: never render, remove, or
|
|
36
|
+
branch on it as application state.
|
|
35
37
|
|
|
36
38
|
Use v1 for selection, delivery, and compatibility. Use v2 when evaluating
|
|
37
39
|
composition. Join catalogs by `key` (`package:id`), never bare `id`. Treat
|
|
@@ -136,22 +138,23 @@ Hard rules:
|
|
|
136
138
|
5. Use the opinionated `--kui-color-*` semantic ramps and component-level override properties. Use `pop` only for attractive non-status emphasis such as featured, novel, or celebratory content; never substitute it for info, success, warning, or danger. Override tokens at the narrowest useful scope and prefer equivalent props/tokens. `publicClasses` exist for diagnostics, tooling, tests, and package-owned composition, not application styling. Never target a Kerf or Web Awesome component from application CSS; add missing configuration to the owner.
|
|
137
139
|
6. `Select` is pure until the app explicitly imports `@kerfjs/ui/select/register`; do not import Web Awesome's full registration bundle. When an app writes direct `wa-*` JSX, add `import type {} from '@kerfjs/ui/webawesome'` for the catalog-supported intrinsic-element declarations, import the CSS-only `@kerfjs/ui/webawesome.css` theme once, and keep importing individual Web Awesome component modules so their JavaScript remains tree-shakeable. The type boundary emits no code and registers nothing. Pass icon-bearing `choices` and `renderSelected` content normally: `Select` preserves its slotted option icons across Kerf rerenders and keys selected content by the controlled value, so app wrappers must not add competing morph-control attributes. Give it a visible `label` or an `ariaLabel` for a visually hidden name; the nonempty visible label takes precedence and the package names the actual shadow combobox, including `renderSelected`, without adding label geometry. Use `hint` for persistent supporting text below the control and `placeholderText` only for the empty value inside the closed control; loading placeholders retain the hint. When testing name or description, target the element with the `combobox` role rather than the `wa-select` wrapper. Web Awesome's hint attribute and explicit hint slot both describe that shadow combobox across supported engines; do not add an `aria-description` mirror or application shadow-DOM patch.
|
|
138
140
|
7. Decorative icons are hidden; controls are named; focus is visible; state never relies on color alone; reduced motion and increased contrast remain usable. `DisclosureArrow` defaults to an 18px root-scaled visual and exposes `--kui-disclosure-arrow-size` for consumer sizing; it never becomes the interaction or accessible-name owner. Put it in an owning native control with a stable accessible name and controlled `aria-expanded`. `ListHeader` toggle mode supplies it when `actionIcon` is omitted, but the app must reveal matching content; a custom icon replaces it. Author replacement icon content facing right before its configured direction transform is applied. Direction changes take the shortest rotation path, with counterclockwise chosen for a 180-degree closed-to-open tie. Kerf `Select` separately keeps its intrinsic Web Awesome expand glyph at `--kui-disclosure-icon-scale: .5`.
|
|
139
|
-
8. `ResizableRegion` uses `wireResizableRegions()` for Arrow, Shift+Arrow, Home/End, and pointer behavior. The app owns size persistence. `handleIcon` replaces decorative dormant glyph content only. Set `responsiveFillAt="narrow"` or `"compact"` when a responsive composition exposes the pane as its only inline track; the component fills that track and hides its dormant separator. For compact overlays, set `presentation="overlay"` and the overlay maximum variables. Do not add descendant width/height overrides.
|
|
141
|
+
8. `ResizableRegion` uses `wireResizableRegions()` for Arrow, Shift+Arrow, Home/End, and pointer behavior. The app owns size persistence. `handleIcon` replaces decorative dormant glyph content only. Set `responsiveFillAt="narrow"` or `"compact"` when a responsive composition exposes the pane as its only inline track; the component fills that track and hides its dormant separator. For compact overlays, set `presentation="overlay"` and the overlay maximum variables. A lone child (normally a `Pane`) fills the region and owns scrolling; several children stack at natural height. Do not add descendant width/height overrides.
|
|
140
142
|
9. Use controlled `SegmentedControl` for a small exclusive choice set. Select `appearance="toolbar"` when nesting it inside `ToolbarControlGroup`; use rounded or pill shapes for standalone contexts. The package derives inset hover/selection radii from the owning outer shape through `--kui-layout-highlight-inset`; do not restyle an inner highlight to an independent radius. Handle its action, update `value`, keep meaningful choice labels, and preserve every enabled native button in sequential Tab order.
|
|
141
143
|
10. Compose `AppTab` inside controlled `TabBar`; call `wireTabBars()` once and retain its disposer. It owns same-bar drag mechanics, including proximity-based horizontal edge autoscroll, while the app applies `onReorder` and owns order, selection, panels, close policy, routing, and persistence. For a tab-local action followed by a separately pinned workspace action, set `trailingPlacement="adjacent"`, put the local action in `trailing`, and the workspace action in `end`; TabBar owns the shrinking/scrolling tabs and fixed action geometry, so do not add descendant flex overrides. Arrow/Home/End select on move by default and restore focus by bar/tab identity when controlled activation replaces the strip; pass `activation: 'manual'` (or `TabBar activation="manual"` per strip) so arrow keys move focus only and the user selects with Enter/Space/click — use it when selecting a tab is a heavy action. Use runtime-filtered `rootAttributes` for domain metadata and keep an optional `closeIcon` decorative and noninteractive.
|
|
142
144
|
11. Use token-controlled `TokenSearchField` when free text and removable structured filters share one editor. Editable text stays DOM-owned between token changes. The leading icon, first text line, clear action, and trailing slot share one fixed row when content wraps. The app owns parsing and suggestions; call `readTokenSearchField()` on input, empty `textContent` on clear, use `placeTokenSearchCaret()` after explicit controlled focus changes, and call `wireTokenSearchFields()` once so Enter submits without inserting a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. Enable `collapsible` for an animated iconic closed state, standalone or inside `ToolbarControlGroup`; the field keeps text or tokens expanded, and `wireTokenSearchFields()` 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 returned handle's `expanded(id)` signal or adopt your own via `collapsible.signals`; opt a behavior out only when the app must own it. Do not hand-roll the open handler, the focusout collapse, a clear-button mousedown guard, or reopening after controlled clear or Select All deletion — the helper keeps the replacement editor open and focused. Clear and deletion restoration finish at the replacement mutation checkpoint before the next input task, so shortcut-letter typing cannot escape to the page and delayed animation frames cannot overwrite a later selection. Persist both query and tokens returned by the DOM read. For real-app adoption it also provides a focusout keep-open exception (`data-token-search-keep-open` regions or `collapsible.keepOpenOn(target)`, so a sibling suggestions/date/help surface does not collapse an empty field), opt-in atomic-chip keyboard (`keyboard`: Backspace/Delete remove the adjacent chip via `onRemoveToken`, ArrowRight moves the caret past a trailing chip), and an `onEdit({id, editor, event})` input callback (the `InputEvent` lets you gate on `inputType`/`data`) — reach for these instead of re-adding hand-rolled keydown/input handlers around the field.
|
|
143
|
-
12. Demo work uses public production component subpaths and their browser-selected CSS. Use exactly three demo kinds: a `component` is one component with variants/states, a `composition` is a focused container of components commonly used together, and a `recipe` is the recommended production approach to a task. Organize catalog routes into product-important functional groups; within each group put all components before compositions, order each kind by importance with display-name alphabetical tie-breaking, and put recipes after every component/composition group. Show `Composition` as an entry tag instead of repeating the word in its name. Give every public visual component its own category-grouped catalog route; list themed third-party components under a clearly labeled collapsible ecosystem section, with a focused route for each. For the complete taxonomy, focused/composition decision, `CatalogExample` / `CatalogExampleStack` row/group nesting, exact specimen selection, geometry-overlay legend and exclusions, skip-marker behavior, and metadata ownership, follow the single [Catalog demo authoring contract](../docs/catalog.md#catalog-demo-authoring-contract), discovered machine-readably through [`catalog-authoring.json`](./catalog-authoring.json). Never infer structure from private `kui-catalog-*` classes. Project each component's deterministic repository-relative demo source and existing documentation path so the detail can expose `View demo source` and `Read guidance` links without a runtime export; also derive first-party component implementation paths from their canonical browser imports, and label Web Awesome documentation as Kerf integration guidance. Declare direct `uses` relationships so `Used by` stays derivable, and theme shell chrome through the same semantic tokens as the stage instead of drawing a substitute. Enable `wireCatalog({ revealSelection: true })` for long desktop sidebars so controlled selections remain visible without moving focus; keep its default compact media guard unless compact scrolling is an explicit product behavior, and use `revealCatalogEntry` for an initial deep link.
|
|
145
|
+
12. Demo work uses public production component subpaths and their browser-selected CSS. Focused demos and recipes have no local stylesheet, inline style, or styling-only class; use public components and `CatalogExample.viewport` for unavoidable specimen constraints (a recipe declares its frame through its exported `presentation`). When a recipe needs a capability no component offers, report the component gap instead of adding CSS. Use exactly three demo kinds: a `component` is one component with variants/states, a `composition` is a focused container of components commonly used together, and a `recipe` is the recommended production approach to a task. Organize catalog routes into product-important functional groups; within each group put all components before compositions, order each kind by importance with display-name alphabetical tie-breaking, and put recipes after every component/composition group. Show `Composition` as an entry tag instead of repeating the word in its name. Give every public visual component its own category-grouped catalog route; list themed third-party components under a clearly labeled collapsible ecosystem section, with a focused route for each. For the complete taxonomy, focused/composition decision, `CatalogExample` / `CatalogExampleStack` row/group nesting, exact specimen selection, geometry-overlay legend and exclusions, skip-marker behavior, and metadata ownership, follow the single [Catalog demo authoring contract](../docs/catalog.md#catalog-demo-authoring-contract), discovered machine-readably through [`catalog-authoring.json`](./catalog-authoring.json). Never infer structure from private `kui-catalog-*` classes. Project each component's deterministic repository-relative demo source and existing documentation path so the detail can expose `View demo source` and `Read guidance` links without a runtime export; also derive first-party component implementation paths from their canonical browser imports, and label Web Awesome documentation as Kerf integration guidance. Declare direct `uses` relationships so `Used by` stays derivable, and theme shell chrome through the same semantic tokens as the stage instead of drawing a substitute. Enable `wireCatalog({ revealSelection: true })` for long desktop sidebars so controlled selections remain visible without moving focus; keep its default compact media guard unless compact scrolling is an explicit product behavior, and use `revealCatalogEntry` for an initial deep link.
|
|
144
146
|
13. The Web Awesome theme makes Tooltip and Popover arrowless by default. Keep that default unless a pointer materially clarifies the anchor; opt back in with `--wa-tooltip-arrow-size`, `--kui-wa-popover-arrow-size`, or a popover's public `--arrow-size`, and use `without-arrow` when local no-arrow intent should survive theme changes. Its non-field chrome uses `--kui-wa-control-inset` (8px controls/items), `--kui-wa-surface-margin` / `--kui-wa-surface-inset` (8px around/inside Accordion, Card, Details, Callout, and Include), and `--kui-wa-container-inset` (16px Tab Panel content and framed disclosure regions). Plain Details use the 8px surface inset on the trigger and keep the body flush; plain Accordion items use it on both trigger and body. Outlined and sunken Details use the 16px container inset on the trigger and 8px on the body; framed Accordion items use 16px on both. Dialog maps its body to the 8px surface tier and its footer to the 16px container tier independently of `--spacing`. Override those tiers instead of restyling individual parts. Filled badges use each variant's quiet fill with normal text so their compact copy stays WCAG-AA readable; override the pair with `--kui-wa-badge-filled-background` / `--kui-wa-badge-filled-foreground`, not host selectors. When a dialog provides another dismissal affordance, put `hide-actions` on the `wa-dialog` host to hide its directly exported `header-actions` part; never chain `::part()` selectors through internal parts. Checkbox and Radio Group option regions, the Color Picker trigger, and Slider's complete interactive region receive the shared 8px logical inline outer inset because those controls have no bordered field shell. Known Date field captions and bordered text-like field hints align with values at the 9px border-plus-padding inset. OTP Input's label uses the same uppercase xs/650 treatment as other field labels, and both its label and hint use that 9px inset.
|
|
145
147
|
14. Treat the complete Web Awesome catalog as support coverage, not a recommendation list. Its UX sidebar marks superseded and exceptional entries `Discouraged`. Consider Popup when it replaces custom anchored positioning. Prefer Kerf `Select` over direct Dropdown/Dropdown Item/Select/Option composition, `SegmentedControl` over Button Group, `TabBar` or `SegmentedControl` over Web Awesome Tabs, `LucideIcon` over Web Awesome Icon, and `ResizableRegion` over Split Panel. Use Tree/Tree Item, Animated Image, and Comparison only for a specific required behavior; avoid Zoomable Frame.
|
|
146
|
-
15. Build sidebars, main areas, inspectors, and dialogs from `@kerfjs/ui/layout.css`: an unpadded `.kui-pane`, optional `.kui-pane__toolbar`, one scrolling `.kui-pane__content`, and optional `.kui-pane__footer`. Use `Row` instead of a hand-written flex-row wrapper, `List` instead of a hand-written flex-column wrapper, and `Grid` instead of repeated equal-track CSS when children need a fixed positive count of evenly distributed columns. `Row` defaults to left/full with an `xs` gap and no wrapping; Grid defaults to an `xs` gap and keeps each track at `minmax(0, 1fr)`; List retains full/top and zero-gap defaults. The app owns Grid's responsive column-count changes. Use `List` when rows or sections also need flex growth, one vertical scroll owner, or physical-edge dividers. `dividerSides`, `textInsets`, and `controlInsets` use canonical physical `t`/`r`/`b`/`l` order; text geometry wins where text and control selections overlap. `ListInsetText` and `ListInsetControl` inset all four sides by default and accept the same `sides` grammar for selective edges. A dialog body is generally a `List`: set `DialogSurface bodyInset="none"` when its list children own geometry, and wrap bare dialog prose in `ListInsetText` so it aligns with bordered siblings without a second inset. Add `.kui-content` for 24px major vertical separation and `.kui-content-item` for a child-owned 8px inline margin, 1px transparent-or-visible border, 8px padding, and 12px radius. Use the pill modifier for 22px. Do not pad pane shells, duplicate item geometry in wrappers, or create nested scroll owners.
|
|
147
|
-
15a. Use `Text` from `@kerfjs/ui/text` for ordinary headings and
|
|
148
|
+
15. Build sidebars, main areas, inspectors, and dialogs from `@kerfjs/ui/layout.css`: an unpadded `.kui-pane`, optional `.kui-pane__toolbar`, one scrolling `.kui-pane__content`, and optional `.kui-pane__footer`. Use `Row` instead of a hand-written flex-row wrapper, `List` instead of a hand-written flex-column wrapper, and `Grid` instead of repeated equal-track CSS when children need a fixed positive count of evenly distributed columns. `Row` defaults to left/full with an `xs` gap and no wrapping; Grid defaults to an `xs` gap and keeps each track at `minmax(0, 1fr)`; List retains full/top and zero-gap defaults. The app owns Grid's responsive column-count changes. When a Row, List, or Grid is the layout root of a definite-height parent (an app root or a fixed-height frame), pass `fill` so it takes that full height, and put route/test/recipe `data-*` markers in its `rootAttributes`; never wrap a layout in a frame `Pane` just for height or a root attribute, because its content slot becomes a scroll owner that never scrolls. Inside a flex layout use `flex`, not `fill`. Use `List` when rows or sections also need flex growth, one vertical scroll owner, or physical-edge dividers. `dividerSides`, `textInsets`, and `controlInsets` use canonical physical `t`/`r`/`b`/`l` order; text geometry wins where text and control selections overlap. `ListInsetText` and `ListInsetControl` inset all four sides by default and accept the same `sides` grammar for selective edges. A dialog body is generally a `List`: set `DialogSurface bodyInset="none"` when its list children own geometry, and wrap bare dialog prose in `ListInsetText` so it aligns with bordered siblings without a second inset. Add `.kui-content` for 24px major vertical separation and `.kui-content-item` for a child-owned 8px inline margin, 1px transparent-or-visible border, 8px padding, and 12px radius. Use the pill modifier for 22px. Do not pad pane shells, duplicate item geometry in wrappers, or create nested scroll owners.
|
|
149
|
+
15a. Use `Text` from `@kerfjs/ui/text` for ordinary headings, paragraphs, and inline secondary text outside toolbar title zones. Omit `variant` for the default `p`, pass `h1`–`h6` according to the document outline, or use `variant="span"` inside a row, label, or table cell; ordinary native attributes pass through. Express supporting, error, compact, or code text with the independent finite `tone="quiet|danger"`, `size="compact"`, and `font="monospace"` props instead of global utility classes or appearance-only heading choices. Block Text variants own a transparent 1px border and the standard item padding; `span` owns no box geometry. Keep toolbar identity/title content on `ToolbarText` and list-aligned bare prose on `ListInsetText`. `ToolbarText` below `xlarge` is intentionally quiet identity text with no tone option; a normal-color subsection title is `Text` with an `h2`–`h6` variant (or `ListHeader` for a list or menu section), never a recolored `ToolbarText`.
|
|
148
150
|
16. Keep a visible collapsible pane's collapse control in its own toolbar. When hidden, move its restore control into the adjacent main toolbar on the corresponding logical edge: leading for an inline-start sidebar and trailing for an inline-end inspector. Collapse the pane completely; do not preserve an empty icon-only rail.
|
|
149
|
-
17. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. Put identity/title text directly in the zone as `ToolbarText`; wrap interactive controls and icon tiles in `ToolbarControlGroup`. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a group. Configure zone behavior with `Toolbar.centerAlign` and its `responsive` / `responsiveAt` policy instead of selecting toolbar zone classes. For an ordinary icon/action control inside a group, use a plain `<button>` (the group styles `> button` fully) — that is the default; reach for `wa-button` only when you need a Web Awesome feature, chiefly a `wa-dropdown` popup trigger. A popup menu in a toolbar is a `single` group wrapping a `wa-dropdown` whose `slot="trigger"` `wa-button` is the toolbar button and whose `wa-dropdown-item`s are the menu, with the dropdown kept under `data-morph-skip-children`. For a compact mixed group, keep the standard item padding and raised selected chrome; the selected item paints over the outer border without an internal separator. For an avatar control, set `content="avatar"` and `avatarImage` on the group instead of inserting an `img`: a lone control paints the contained image on the outer group and a multi-button group paints it only on the pressed selection highlight. Compose panel/dialog/page headings with a direct extra-large `ToolbarText`, optional grouped icon, grouped trailing controls, and app-owned supporting copy below. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Split dormant and interactive regions: `ListHeader` fills the available inline width by default; set `width="content"` to shrink-wrap while retaining its normal geometry, or `inline` when it belongs in surrounding text without outer margin, border, or padding. It keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-list-header-action-icon-size
|
|
151
|
+
17. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. Put identity/title text directly in the zone as `ToolbarText`; wrap interactive controls and icon tiles in `ToolbarControlGroup`. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a group. Configure zone behavior with `Toolbar.centerAlign` and its `responsive` / `responsiveAt` policy instead of selecting toolbar zone classes. No policy clips an action: trailing (and stacked center) zones wrap whole groups onto another row, so never hide or shrink a toolbar control to make it fit; use `responsive="wrap"` for a heading whose title must stay whole, and move genuinely lower-priority commands into an app-owned "More" popup menu. For an ordinary icon/action control inside a group, use a plain `<button>` (the group styles `> button` fully) — that is the default; reach for `wa-button` only when you need a Web Awesome feature, chiefly a `wa-dropdown` popup trigger. A popup menu in a toolbar is a `single` group wrapping a `wa-dropdown` whose `slot="trigger"` `wa-button` is the toolbar button and whose `wa-dropdown-item`s are the menu, with the dropdown kept under `data-morph-skip-children`. Let the group size that trigger from its icon, localized text, and caret; do not force a width on the dropdown or button. For a compact mixed group, keep the standard item padding and raised selected chrome; the selected item paints over the outer border without an internal separator. For an avatar control, set `content="avatar"` and `avatarImage` on the group instead of inserting an `img`: a lone control paints the contained image on the outer group and a multi-button group paints it only on the pressed selection highlight. Compose panel/dialog/page headings with a direct extra-large `ToolbarText`, optional grouped icon, grouped trailing controls, and app-owned supporting copy below. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Split dormant and interactive regions: `ListHeader` fills the available inline width by default; set `width="content"` to shrink-wrap while retaining its normal geometry, or `inline` when it belongs in surrounding text without outer margin, border, or padding. It keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-list-header-action-icon-size` inside a fitted 36px hover square whose transparent hit layer keeps the pointer target at least 44px (44px wide by the 36px row in `density="compact"`); never shrink the target to match the visual, and never force the button to 44px. Pass every non-negative safe-integer section quantity through `count` with a localized full spoken `countLabel`; never concatenate it into `label` or put a number in `badge`. `ListItem.trailing` is dormant; use `ListActionRow` when primary and trailing actions need sibling native buttons. Its `label`, `icon`, and `trailingActionIcon` slots are also dormant and cannot contain controls. Let panes relocate at narrow widths instead of shrinking targets.
|
|
150
152
|
18. When a recurring concept has no matching export or production recipe, keep its semantics in a thin application adapter while reusing the public layout vocabulary. The composer recipe uses one visible form surface, toolbar title/supporting-copy ids, shared 8px field/action gutters, 24px major rhythm, and a conditional StateBanner as its only nested semantic surface; do not turn every section into a card or double-inset intrinsically bordered controls. The application-local `../docs/examples/command-palette-adapter.tsx` is reference source for one such missing concept, not an `@kerfjs/ui` runtime export or catalog recipe. The application owns its registration, ranking, history, permissions, availability, shortcut policy, focus policy, dispatch, and copy. If a missing concept recurs across products, open an upstream component or recipe request.
|
|
151
153
|
19. Compose `ValueTable` from typed `ValueTableRow` entries instead of handwritten `dt`/`dd` wrappers. Pass `icon` for the optional 24px leading visual; the row owns 8px of root-scaled top and bottom padding, the 8px iconless or 40px icon-bearing separator start, and the common 8px right inset.
|
|
152
154
|
20. Use the standard direct `Toolbar` composition for a panel, dialog, or page heading, not a private wrapper or custom heading row. Put an optional icon in a `ToolbarControlGroup`, the title directly in the leading zone as extra-large `ToolbarText`, and actions in a trailing group. Set `headingLevel` for page/section landmarks. Supporting copy stays below the toolbar as app-owned content. The app owns modal behavior, focus, dismissal, command policy, ids, and action handling.
|
|
153
|
-
21. Space with the official five-step scale, picked by how connected two elements are — not by eye. `0` `--kui-space-none` = no separation (one unit); `4px` `--kui-space-2xs` = very minor air on a connected cluster; `8px` `--kui-space-xs` = standard, between elements within a group; `16px` `--kui-space-m` = minor, between homogeneous groups; `24px` `--kui-space-l` = major, between heterogeneous groups (the `.kui-content` rhythm). The 8px-vs-24px distinction is inside-a-group vs between-major-differing-regions. `--kui-space-s` (12px) and `--kui-space-xl` (32px) are off-scale exceptions. For `Row.gap`, `Grid.gap`, or `List.gap`, pass a finite shorthand such as `gap="xs"` or `gap="m"`; use `Spacer` for one intentional typed empty width/height or a flexible main-axis gap. When one token is insufficient, import typed builders from the CSS-free `@kerfjs/ui/css-values` subpath. Preserve property grammar: lengths for gap/Spacer/Skeleton geometry, `flex()` for Row/Grid/List flex, and `uiColor()
|
|
154
|
-
22. Pick a whole-screen layout from the opt-in, tree-shakeable subpaths by data + interaction + device, and derive responsiveness from `@kerfjs/ui/device-class`'s `deviceClass()` (`compact` = handset or portrait tablet = one pane at a time). Simple/flat → `NavStack` with one entry (single pane), plus `TabScaffold` (`@kerfjs/ui/tab-scaffold`, iOS bottom tabs, each tab its own `NavStack`) for 2–5 co-equal sections on `compact`. Drill-down → `NavStack` (`@kerfjs/ui/nav-stack`), upgrading to `SplitView` (`@kerfjs/ui/split-view`, list-detail) once both panes fit (`atLeast('tablet')` landscape); `SplitView` collapses to a `NavStack` on `compact`. Keep each stack's ordered views controlled; put view-specific bottom chrome on `NavStackView.bottomToolbar` (or use the component-level persistent fallback), then call `wireNavStack` so content slides while the active view's top and bottom chrome cross-fade and focus moves into the new top view/restores on pop. Mark a preferred initial heading or control with `data-nav-focus` when DOM order is not sufficient. Complex tool with peripheral panels → `Workbench` (`@kerfjs/ui/workbench`, collapsible rails + drawer) `atLeast('desktop')`, degrading to `NavStack`/overlays below. These are declarative (the app owns the stack/selection/collapsed/active state as signals) with disposer-returning `wire…` helpers; each ships a companion CSS import and stays out of the barrel. For a standalone collapsible rail or bottom drawer outside a full shell, use `CollapsiblePanel` + `CollapsiblePanelToggle` (`@kerfjs/ui/collapsible-panel`) with `wireSidebar` (`@kerfjs/ui/wire-sidebar`): the standard collapse animation and per-side icon convention (`PanelLeft*`/`PanelRight*`/`PanelBottom*`) plus toggle, focus, compact overlay, and persistence semantics. Dialogs pick the same inner layout, then present per device class (full-screen modal on `compact`, inline on desktop). See `docs/app-layouts.md` and `docs/collapsible-panel.md`.
|
|
155
|
+
21. Space with the official five-step scale, picked by how connected two elements are — not by eye. `0` `--kui-space-none` = no separation (one unit); `4px` `--kui-space-2xs` = very minor air on a connected cluster; `8px` `--kui-space-xs` = standard, between elements within a group; `16px` `--kui-space-m` = minor, between homogeneous groups; `24px` `--kui-space-l` = major, between heterogeneous groups (the `.kui-content` rhythm). The 8px-vs-24px distinction is inside-a-group vs between-major-differing-regions. `--kui-space-s` (12px) and `--kui-space-xl` (32px) are off-scale exceptions. For `Row.gap`, `Grid.gap`, or `List.gap`, pass a finite shorthand such as `gap="xs"` or `gap="m"`; use `Spacer` for one intentional typed empty width/height or a flexible main-axis gap. When one token is insufficient, import typed builders from the CSS-free `@kerfjs/ui/css-values` subpath. Preserve property grammar: lengths for gap/Spacer/Skeleton geometry, `flex()` for Row/Grid/List flex, and a foreground color for choice icons: `uiColor()` with a `*-on-*` or text token (`success-on-quiet`, `text-quiet`) or `foregroundColorVar()`. The bare `success`/`warning`/`danger`/`pop`/`accent` tokens are quiet fill aliases, not foregrounds, and `SelectChoice.color` rejects them. Never cast between brands or emit raw row `style` declarations. `plus` is not standalone and must be wrapped in `calc`; `remify()` remains source-CSS syntax and is never a runtime prop value. See `docs/layout.md` "Spacing scale".
|
|
156
|
+
22. Pick a whole-screen layout from the opt-in, tree-shakeable subpaths by data + interaction + device, and derive responsiveness from `@kerfjs/ui/device-class`'s `deviceClass()` (`compact` = handset or portrait tablet = one pane at a time). Simple/flat → `NavStack` with one entry (single pane), plus `TabScaffold` (`@kerfjs/ui/tab-scaffold`, iOS bottom tabs, each tab its own `NavStack`) for 2–5 co-equal sections on `compact`. Drill-down → `NavStack` (`@kerfjs/ui/nav-stack`), upgrading to `SplitView` (`@kerfjs/ui/split-view`, list-detail) once both panes fit (`atLeast('tablet')` landscape); `SplitView` collapses to a `NavStack` on `compact`. Keep each stack's ordered views controlled; put view-specific bottom chrome on `NavStackView.bottomToolbar` (or use the component-level persistent fallback), then call `wireNavStack` so content slides while the active view's top and bottom chrome cross-fade and focus moves into the new top view/restores on pop. Mark a preferred initial heading or control with `data-nav-focus` when DOM order is not sufficient. Complex tool with peripheral panels → `Workbench` (`@kerfjs/ui/workbench`, collapsible rails + drawer) `atLeast('desktop')`, degrading to `NavStack`/overlays below. These are declarative (the app owns the stack/selection/collapsed/active state as signals) with disposer-returning `wire…` helpers; each ships a companion CSS import and stays out of the barrel. For a standalone collapsible rail or bottom drawer outside a full shell, use `CollapsiblePanel` + `CollapsiblePanelToggle` (`@kerfjs/ui/collapsible-panel`) with `wireSidebar` (`@kerfjs/ui/wire-sidebar`): the standard collapse animation and per-side icon convention (`PanelLeft*`/`PanelRight*`/`PanelBottom*`) plus toggle, focus, compact overlay, and persistence semantics. A compact overlay opens only on a user action: wire-up on a compact device and a wide-to-compact crossing collapse every panel, and a wide crossing restores the inline state. Seed the signal with `signal(device.value.compact)` and pass the panel's `inlineCollapsed` default so the first compact render has no overlay. Dialogs pick the same inner layout, then present per device class (full-screen modal on `compact`, inline on desktop). A top-level percentage-height shell needs a definite containing height: opt into `@kerfjs/ui/document.css` and put `.kui-app-root` on the one direct mount container, or provide equivalent application-owned geometry. The global baseline is deliberately absent from `styles.css` and component imports. See `docs/app-layouts.md`, `docs/document-baseline.md`, and `docs/collapsible-panel.md`.
|
|
157
|
+
23. Layouts and `Pane` are safe-area aware without configuration: backgrounds, separators, and dividers paint through a device's unsafe areas (a collapsible rail or drawer grows by the inset it docks to), content is padded only on the sides a region actually reaches, and block insets are scroll padding inside the scroller so first/last content can always be scrolled clear. Interior edges get nothing, and a collapsed rail hands its edge back to the center. Never add your own `env(safe-area-inset-*)` padding around a kerf layout or pane — that double-insets. In an app-owned arrangement of panes, pass `Pane.safeAreaEdges` (the sides that pane reaches; `[]` opts out); give an app bar or bottom bar that sits at a screen edge outside a Pane header or footer `Toolbar.safeAreaEdges` for the sides it reaches (for example `['block-start', 'inline-start', 'inline-end']`); or set `--kui-edge-inset-*: 0px` on a region for each edge it does not reach. The device insets are `--kui-safe-area-*` (defaulting to `env()`), overridable on `:root`. See `docs/app-layouts.md` "Safe areas".
|
|
155
158
|
|
|
156
159
|
Common mistakes:
|
|
157
160
|
|