@kerfjs/ui 5.0.0-beta.81 → 5.0.0-beta.83
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 +1 -0
- package/ai/catalog-authoring.json +1 -3
- package/ai/catalog-authoring.schema.json +3 -7
- package/ai/component-catalog.json +73 -2
- package/ai/component-composition.json +64 -1
- package/ai/components/README.md +1 -0
- package/ai/components/anchored-surface.md +86 -0
- package/ai/components/tab-navigator.md +3 -2
- package/ai/public-api-signatures-v1.md +74 -30
- package/ai/skill.md +2 -2
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/anchored-surface.d.ts +30 -0
- package/dist/anchored-surface.js +3 -0
- package/dist/anchored-surface.js.map +1 -0
- package/dist/app-tab.js +1 -1
- package/dist/browser/anchored-surface.js +3 -0
- package/dist/browser/catalog.js +4 -2
- package/dist/catalog-resources.d.ts +1 -1
- package/dist/catalog.d.ts +3 -3
- package/dist/catalog.js +51 -42
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-I4SDIMFA.js → chunk-2YPPNFYT.js} +2 -2
- package/dist/{chunk-I4SDIMFA.js.map → chunk-2YPPNFYT.js.map} +1 -1
- package/dist/{chunk-CVKIADIS.js → chunk-3BXDWEGB.js} +5 -5
- package/dist/{chunk-CVKIADIS.js.map → chunk-3BXDWEGB.js.map} +1 -1
- package/dist/{chunk-VHXHLZ6R.js → chunk-3DXHNLME.js} +3 -3
- package/dist/{chunk-VHXHLZ6R.js.map → chunk-3DXHNLME.js.map} +1 -1
- package/dist/{chunk-HFQDSSED.js → chunk-3PO7JEK7.js} +4 -4
- package/dist/{chunk-HFQDSSED.js.map → chunk-3PO7JEK7.js.map} +1 -1
- package/dist/chunk-6IVNMC6R.js +19 -0
- package/dist/chunk-6IVNMC6R.js.map +1 -0
- package/dist/{chunk-VR7MWD2X.js → chunk-CD5TOO22.js} +4 -13
- package/dist/chunk-CD5TOO22.js.map +1 -0
- package/dist/{chunk-H7ZJEI3H.js → chunk-DL55BSQX.js} +3 -3
- package/dist/{chunk-H7ZJEI3H.js.map → chunk-DL55BSQX.js.map} +1 -1
- package/dist/{chunk-LQKY5S4F.js → chunk-EA5R75B5.js} +4 -4
- package/dist/{chunk-LQKY5S4F.js.map → chunk-EA5R75B5.js.map} +1 -1
- package/dist/{chunk-VIGVNDXZ.js → chunk-GPCAXYMT.js} +4 -4
- package/dist/{chunk-VIGVNDXZ.js.map → chunk-GPCAXYMT.js.map} +1 -1
- package/dist/chunk-IOS2NXW7.js +53 -0
- package/dist/chunk-IOS2NXW7.js.map +1 -0
- package/dist/{chunk-HFLGWMG3.js → chunk-KAJVVN7Q.js} +3 -3
- package/dist/{chunk-HFLGWMG3.js.map → chunk-KAJVVN7Q.js.map} +1 -1
- package/dist/{chunk-NVKRZ5DI.js → chunk-LFPW4YC6.js} +4 -4
- package/dist/{chunk-NVKRZ5DI.js.map → chunk-LFPW4YC6.js.map} +1 -1
- package/dist/{chunk-75I3SNUJ.js → chunk-MN3POJFN.js} +2 -2
- package/dist/{chunk-75I3SNUJ.js.map → chunk-MN3POJFN.js.map} +1 -1
- package/dist/{chunk-5D3WDMIK.js → chunk-MORVLMDG.js} +4 -16
- package/dist/chunk-MORVLMDG.js.map +1 -0
- package/dist/{chunk-RRCD3A6G.js → chunk-MTCTX25G.js} +2 -2
- package/dist/{chunk-RRCD3A6G.js.map → chunk-MTCTX25G.js.map} +1 -1
- package/dist/{chunk-PQQQXJGV.js → chunk-NP3FSLNX.js} +2 -2
- package/dist/{chunk-PQQQXJGV.js.map → chunk-NP3FSLNX.js.map} +1 -1
- package/dist/{chunk-ARZ4RSQO.js → chunk-PGL6TXFW.js} +3 -3
- package/dist/{chunk-ARZ4RSQO.js.map → chunk-PGL6TXFW.js.map} +1 -1
- package/dist/{chunk-NVRJ7C3W.js → chunk-QQICWWNX.js} +4 -16
- package/dist/chunk-QQICWWNX.js.map +1 -0
- package/dist/{chunk-JZM3G6RT.js → chunk-TTGIHMDR.js} +4 -4
- package/dist/{chunk-JZM3G6RT.js.map → chunk-TTGIHMDR.js.map} +1 -1
- package/dist/{chunk-LRAIS3YB.js → chunk-U657IHFB.js} +1 -1
- package/dist/chunk-U657IHFB.js.map +1 -0
- package/dist/{chunk-3SXWV2IR.js → chunk-VLGQIYZ5.js} +2 -2
- package/dist/{chunk-3SXWV2IR.js.map → chunk-VLGQIYZ5.js.map} +1 -1
- package/dist/{chunk-YLJ6X3AC.js → chunk-XK4E225T.js} +2 -2
- package/dist/{chunk-YLJ6X3AC.js.map → chunk-XK4E225T.js.map} +1 -1
- package/dist/{chunk-BJLXE56E.js → chunk-XKZPBRU7.js} +4 -4
- package/dist/{chunk-BJLXE56E.js.map → chunk-XKZPBRU7.js.map} +1 -1
- package/dist/{chunk-62VMNBV2.js → chunk-XNXKIPU5.js} +2 -2
- package/dist/{chunk-62VMNBV2.js.map → chunk-XNXKIPU5.js.map} +1 -1
- package/dist/{chunk-EFDCALR3.js → chunk-Y43B6DS3.js} +2 -2
- package/dist/{chunk-EFDCALR3.js.map → chunk-Y43B6DS3.js.map} +1 -1
- package/dist/{chunk-3RE2JMZU.js → chunk-YOKEL75M.js} +2 -2
- package/dist/{chunk-3RE2JMZU.js.map → chunk-YOKEL75M.js.map} +1 -1
- package/dist/collapsible-panel.js +4 -3
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/css-values.d.ts +8 -1
- package/dist/css-values.js +1 -1
- package/dist/disclosure-arrow.js +1 -1
- package/dist/empty-state.js +1 -1
- package/dist/grid.d.ts +5 -4
- package/dist/grid.js +2 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +27 -25
- 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 +5 -4
- package/dist/list.js +2 -1
- package/dist/loading-spinner.js +1 -1
- package/dist/lucide-icon.js +1 -1
- package/dist/nav-stack.js +2 -1
- package/dist/row.d.ts +5 -4
- package/dist/row.js +2 -1
- package/dist/select.js +1 -1
- package/dist/skeleton.js +1 -1
- package/dist/spacer.js +1 -1
- package/dist/split-view.js +2 -1
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.js +1 -1
- package/dist/styles/anchored-surface.css +15 -0
- package/dist/styles/catalog/components/catalog-example.css +1 -1
- package/dist/styles/catalog/components/catalog-stage.css +26 -42
- package/dist/styles/catalog/components/catalog.css +0 -24
- package/dist/styles/components/collections/list/list.css +4 -1
- package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +15 -0
- package/dist/styles/components/layout/grid/grid.css +4 -1
- package/dist/styles/components/layout/row/row.css +4 -1
- package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +25 -9
- package/dist/styles/grid.css +4 -1
- package/dist/styles/list.css +4 -1
- package/dist/styles/row.css +4 -1
- package/dist/styles/styles.css +1 -0
- package/dist/styles/tab-navigator.css +25 -9
- package/dist/styles/webawesome.css +4 -1
- package/dist/tab-navigator.d.ts +10 -3
- package/dist/tab-navigator.js +2 -0
- package/dist/tab-navigator.js.map +1 -1
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/{types-C9BubXfz.d.ts → types-CXrh9Wyx.d.ts} +6 -2
- package/dist/value-table.js +1 -1
- package/dist/wire-catalog.d.ts +1 -9
- package/dist/wire-catalog.js +37 -205
- package/dist/wire-catalog.js.map +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.js +2 -1
- package/docs/anchored-surface.md +51 -0
- package/docs/catalog.md +41 -85
- package/docs/component-contract.md +3 -1
- package/docs/component-selection.md +19 -1
- package/docs/layout.md +13 -2
- package/docs/tab-navigator.md +12 -4
- package/docs/type-contracts.md +2 -0
- package/docs/ux-demo.md +4 -4
- package/docs/webawesome-theme.md +4 -1
- package/llms.txt +8 -3
- package/package.json +7 -1
- package/dist/chunk-5D3WDMIK.js.map +0 -1
- package/dist/chunk-LRAIS3YB.js.map +0 -1
- package/dist/chunk-NVRJ7C3W.js.map +0 -1
- package/dist/chunk-VR7MWD2X.js.map +0 -1
package/docs/catalog.md
CHANGED
|
@@ -37,7 +37,16 @@ description?, tags?, resources?, related? }] }`. Each entry becomes a sidebar
|
|
|
37
37
|
the row's quiet status treatment for decision metadata such as `Discouraged`.
|
|
38
38
|
- **`content`** — the rendered preview for the active entry. Keep a map of `id →
|
|
39
39
|
() => SafeHtml` in your app and call `renderers[active]()` in your render.
|
|
40
|
-
- **`
|
|
40
|
+
- **`backgroundStyle`** — the preview stage backdrop: `checkerboard` (default,
|
|
41
|
+
8px squares),
|
|
42
|
+
`vertical-stripes` (alternating 8px bands), `surface`, or `sunken`. The two
|
|
43
|
+
patterned choices share the main Pane's sunken surface and reveal transparent
|
|
44
|
+
specimen edges; `surface` paints an
|
|
45
|
+
opaque backdrop. `sunken` leaves the stage
|
|
46
|
+
transparent so the main Pane's semitransparent sunken surface paints only once.
|
|
47
|
+
A controlled app may pass its current choice on each render.
|
|
48
|
+
- **`brand`** — `{ title, subtitle?, logoUrl? }` for the sidebar header. The
|
|
49
|
+
optional logo paints through `ToolbarControlGroup.avatarImage`.
|
|
41
50
|
- **`secondarySections`** — an optional secondary "ecosystem" group shown below the
|
|
42
51
|
primary sections with a quieter treatment: `{ label, sections, collapsible?,
|
|
43
52
|
expanded? }`. When `collapsible`, the label is a disclosure toggle controlling
|
|
@@ -45,13 +54,14 @@ expanded? }`. When `collapsible`, the label is a disclosure toggle controlling
|
|
|
45
54
|
- Optional slots: `headerActions` (extra header controls), `sidebarFooter` (extra
|
|
46
55
|
sidebar content), and `status` (a footer status line).
|
|
47
56
|
|
|
48
|
-
The sidebar includes a
|
|
49
|
-
|
|
57
|
+
The sidebar includes a `TokenSearchField` in its fixed header for every Catalog
|
|
58
|
+
consumer, so the filter remains visible while the entries scroll. `wireCatalog`
|
|
59
|
+
filters entry names and section headings as text is typed,
|
|
50
60
|
ignoring case. A heading match shows all its entries; a matching secondary
|
|
51
61
|
group heading searches its sections too, even while that group is collapsed.
|
|
52
62
|
Empty sections disappear and an empty-result message appears when nothing
|
|
53
63
|
matches. The query survives controlled Catalog rerenders and clears through the
|
|
54
|
-
|
|
64
|
+
field's clear action. Filtering does not change the selected
|
|
55
65
|
entry or its preview.
|
|
56
66
|
|
|
57
67
|
Per-entry `resources` render as "open in new tab" links in the footer, and
|
|
@@ -73,11 +83,11 @@ chosen component.
|
|
|
73
83
|
|
|
74
84
|
Classify every entry before rendering it:
|
|
75
85
|
|
|
76
|
-
| Entry kind | Preview purpose |
|
|
77
|
-
| --------------- | ------------------------------------------------------------------------------------------------------- |
|
|
78
|
-
| **Component** | Show one public component, its meaningful variants, and adverse states. |
|
|
79
|
-
| **Composition** | Show components commonly used together in one focused container or surface. |
|
|
80
|
-
| **Recipe** | Teach the recommended production approach to a specific task, including ownership and application glue. |
|
|
86
|
+
| Entry kind | Preview purpose |
|
|
87
|
+
| --------------- | ------------------------------------------------------------------------------------------------------- |
|
|
88
|
+
| **Component** | Show one public component, its meaningful variants, and adverse states. |
|
|
89
|
+
| **Composition** | Show components commonly used together in one focused container or surface. |
|
|
90
|
+
| **Recipe** | Teach the recommended production approach to a specific task, including ownership and application glue. |
|
|
81
91
|
|
|
82
92
|
These three kinds are exhaustive. Theme/foundation showcases use a functional
|
|
83
93
|
category, third-party specimens use source metadata, and state galleries retain
|
|
@@ -93,10 +103,6 @@ field exists. Show a `Composition` tag rather than repeating the word in a
|
|
|
93
103
|
composition's display name; the generic `Catalog` accepts that through the
|
|
94
104
|
entry's `tags` array.
|
|
95
105
|
|
|
96
|
-
Keep `geometryOverlay` present and compute it from the active entry. Do not make
|
|
97
|
-
it a permanent catalog-wide `true`, and do not give individual specimens their
|
|
98
|
-
own overlay implementation.
|
|
99
|
-
|
|
100
106
|
### Required structure
|
|
101
107
|
|
|
102
108
|
- `Catalog` is the one shell. The app owns active-entry state and passes one
|
|
@@ -148,29 +154,17 @@ const buttonPreview = (
|
|
|
148
154
|
<CatalogExample label="Control" align="inline-control">
|
|
149
155
|
<SegmentedControl id="view" label="View" value="list" choices={choices} />
|
|
150
156
|
</CatalogExample>
|
|
151
|
-
<CatalogExample
|
|
152
|
-
label="Authoring note"
|
|
153
|
-
note="Explanatory chrome is not a specimen."
|
|
154
|
-
rootAttributes={{ "data-catalog-geometry-overlay-skip": "" }}
|
|
155
|
-
>
|
|
157
|
+
<CatalogExample label="Authoring note" note="Explain the specimen below.">
|
|
156
158
|
<p>Use the public helper contract.</p>
|
|
157
159
|
</CatalogExample>
|
|
158
160
|
</CatalogExampleStack>
|
|
159
161
|
);
|
|
160
162
|
```
|
|
161
163
|
|
|
162
|
-
The overlay selects every immediate child of a `CatalogExample`; the helper
|
|
163
|
-
marks its generated label and note so they are excluded automatically. It does
|
|
164
|
-
not recursively promote a nested child to be the specimen. Outside an example
|
|
165
|
-
row, it selects only top-level
|
|
166
|
-
`[data-component]` roots in the canvas and ignores nested component descendants.
|
|
167
|
-
These rules keep a row's label/group scaffolding out of the measurement and make
|
|
168
|
-
the authored nesting determine exactly what is inspected.
|
|
169
|
-
|
|
170
164
|
### Metadata ownership
|
|
171
165
|
|
|
172
|
-
Use `rootAttributes` on either helper for authoring metadata such as `data-demo
|
|
173
|
-
|
|
166
|
+
Use `rootAttributes` on either helper for authoring metadata such as `data-demo`;
|
|
167
|
+
the metadata lands on that helper's
|
|
174
168
|
rendered root. The slot accepts only `data-*` strings. Structural
|
|
175
169
|
`data-catalog-example`, `data-catalog-example-stack`, and `data-align` semantics
|
|
176
170
|
remain helper-owned and are rejected case-insensitively at runtime, including
|
|
@@ -181,50 +175,21 @@ The app owns entry ids, `kind`, routing, sources, relationships, and test hooks.
|
|
|
181
175
|
`Catalog.stageRootAttributes` places app-owned `data-*` state on the
|
|
182
176
|
catalog-owned preview stage without an extra wrapper; `data-catalog-stage`
|
|
183
177
|
remains protected.
|
|
178
|
+
The stage is the main Pane's direct scrolling content, owns an 8px inset, and
|
|
179
|
+
grows with tall specimens. The main Pane keeps its sunken appearance without a
|
|
180
|
+
deep inset.
|
|
184
181
|
The helpers own their structural markers, label/note anatomy, alignment marker,
|
|
185
182
|
and private classes. Component metadata such as margin/border/padding ownership
|
|
186
|
-
lives in `component-catalog.json
|
|
187
|
-
pixels.
|
|
188
|
-
|
|
189
|
-
### Geometry overlay and legend
|
|
183
|
+
lives in `component-catalog.json`.
|
|
190
184
|
|
|
191
|
-
|
|
192
|
-
`wireCatalogGeometryOverlay(root)` once after the first render and retain its
|
|
193
|
-
disposer alongside `wireCatalog`'s.
|
|
185
|
+
### Component metadata
|
|
194
186
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
| Quiet dashed outline | The border-box bound of a transparent selected specimen with no visible border. | A real CSS border, padding edge, or proof that the specimen owns size. |
|
|
200
|
-
|
|
201
|
-
The overlay recomputes from live rendered styles when specimens resize, preview
|
|
202
|
-
markup changes, theme/root attributes change, or loaded/inline stylesheets
|
|
203
|
-
change. Component CSS is therefore the source of truth; do not duplicate margin
|
|
204
|
-
or border values in demo data. The overlay does not visualize padding, gaps,
|
|
205
|
-
negative/zero margins, scroll overflow, hit targets, or nested descendants.
|
|
206
|
-
Inspect computed styles and the machine-readable `geometry` metadata for those
|
|
207
|
-
facts. Opaque borderless specimens do not receive the dashed bound.
|
|
208
|
-
|
|
209
|
-
```tsx
|
|
210
|
-
<Catalog
|
|
211
|
-
{...props}
|
|
212
|
-
geometryOverlay={activeEntry.kind === "component"}
|
|
213
|
-
content={renderers[active.value]()}
|
|
214
|
-
/>
|
|
215
|
-
```
|
|
187
|
+
Machine-readable geometry ownership metadata identifies whether the component,
|
|
188
|
+
its parent, or its children are responsible for margin, border, and padding.
|
|
189
|
+
Keep that metadata in `component-catalog.json` and use the rendered specimen to
|
|
190
|
+
review the actual appearance.
|
|
216
191
|
|
|
217
|
-
|
|
218
|
-
`CatalogExample` or `CatalogExampleStack` only when that whole subtree is
|
|
219
|
-
explanatory chrome rather than a specimen. The marker excludes the marked root
|
|
220
|
-
and every descendant from selection; it does not merely hide one band. It is
|
|
221
|
-
normally unnecessary in a composition because the active entry already sets the
|
|
222
|
-
global overlay to `false`.
|
|
223
|
-
|
|
224
|
-
Use the overlay together with machine-readable geometry ownership metadata; the
|
|
225
|
-
overlay verifies what is rendered, while metadata tells people and AI tools
|
|
226
|
-
whether the component, its parent, or its children are responsible for margin,
|
|
227
|
-
border, and padding. Catalogs for downstream components should conform to the
|
|
192
|
+
Catalogs for downstream components should conform to the
|
|
228
193
|
[`component-catalog-extension.schema.json`](../ai/component-catalog-extension.schema.json)
|
|
229
194
|
contract and can start from the checked
|
|
230
195
|
[`component-catalog-extension.json`](./examples/component-catalog-extension.json)
|
|
@@ -274,8 +239,8 @@ facts that source can prove without guessing at rendered intent:
|
|
|
274
239
|
|
|
275
240
|
- focused component routes import and use `CatalogExampleStack` and
|
|
276
241
|
`CatalogExample` from the public package;
|
|
277
|
-
- focused route metadata uses the helpers' `rootAttributes` slot
|
|
278
|
-
are not empty
|
|
242
|
+
- focused route metadata uses the helpers' `rootAttributes` slot and example rows
|
|
243
|
+
are not empty;
|
|
279
244
|
- every `@kerfjs/ui` import is a published package export and relative imports
|
|
280
245
|
do not reach into `ui/src`;
|
|
281
246
|
- focused demos have no relative stylesheet imports, inline style attributes,
|
|
@@ -285,20 +250,19 @@ facts that source can prove without guessing at rendered intent:
|
|
|
285
250
|
analyzer) import no stylesheet other than public `@kerfjs/ui` CSS, set no
|
|
286
251
|
inline style, and name only published layout and Web Awesome classes; each
|
|
287
252
|
recipe declares its catalog frame through an exported `presentation`; and
|
|
288
|
-
- the shell derives
|
|
289
|
-
from the active entry's source and kind.
|
|
253
|
+
- the shell derives the documented demo mode from the active entry's kind.
|
|
290
254
|
|
|
291
255
|
The gate deliberately does not infer component ownership from arbitrary class
|
|
292
|
-
names, margins, borders, or nested descendants. Runtime
|
|
293
|
-
|
|
256
|
+
names, margins, borders, or nested descendants. Runtime appearance remains the
|
|
257
|
+
browser suite's job.
|
|
294
258
|
|
|
295
259
|
A focused route may bypass the two public layout helpers only when the route's
|
|
296
260
|
stage geometry is itself the reviewed specimen. Add the narrow waiver to
|
|
297
261
|
[`catalog-conformance-exceptions.json`](../ux-demo/catalog-conformance-exceptions.json)
|
|
298
262
|
with the exact route, source file, stable diagnostic ids, a substantive reason,
|
|
299
263
|
and the reviewing `KF-*` ticket. Only helper/metadata rules are waivable;
|
|
300
|
-
private imports, local styling, private markup, empty examples,
|
|
301
|
-
|
|
264
|
+
private imports, local styling, private markup, empty examples, and shell-mode
|
|
265
|
+
drift always fail. The gate rejects duplicate, malformed, unused,
|
|
302
266
|
and stale exceptions, so delete a waiver when its route adopts the standard
|
|
303
267
|
helpers.
|
|
304
268
|
|
|
@@ -329,7 +293,6 @@ import {
|
|
|
329
293
|
import {
|
|
330
294
|
revealCatalogEntry,
|
|
331
295
|
wireCatalog,
|
|
332
|
-
wireCatalogGeometryOverlay,
|
|
333
296
|
} from "@kerfjs/ui/wire-catalog";
|
|
334
297
|
// A CSS-aware (browser-condition) bundler loads the Catalog's CSS with its import.
|
|
335
298
|
|
|
@@ -374,11 +337,7 @@ const renderers: Record<string, () => SafeHtml> = {
|
|
|
374
337
|
<CatalogExample label="Default" align="inline-control">
|
|
375
338
|
<Button label="Save" />
|
|
376
339
|
</CatalogExample>
|
|
377
|
-
<CatalogExample
|
|
378
|
-
label="Authoring note"
|
|
379
|
-
note="This explanatory row is deliberately excluded from inspection."
|
|
380
|
-
rootAttributes={{ "data-catalog-geometry-overlay-skip": "" }}
|
|
381
|
-
>
|
|
340
|
+
<CatalogExample label="Authoring note" note="Explain the specimen below.">
|
|
382
341
|
<p>The application owns product copy and actions.</p>
|
|
383
342
|
</CatalogExample>
|
|
384
343
|
</CatalogExampleStack>
|
|
@@ -401,7 +360,6 @@ const initial =
|
|
|
401
360
|
const active = signal(initial);
|
|
402
361
|
const collapsed = signal(false);
|
|
403
362
|
const theme = signal<"light" | "dark">("light");
|
|
404
|
-
const activeEntry = () => entries.find(({ id }) => id === active.value) ?? entries[0];
|
|
405
363
|
|
|
406
364
|
const app = document.getElementById("app")!;
|
|
407
365
|
mount(app, () => (
|
|
@@ -412,7 +370,6 @@ mount(app, () => (
|
|
|
412
370
|
content={renderers[active.value]?.() ?? <></>}
|
|
413
371
|
collapsed={collapsed.value}
|
|
414
372
|
theme={theme.value}
|
|
415
|
-
geometryOverlay={activeEntry().kind === "component"}
|
|
416
373
|
/>
|
|
417
374
|
));
|
|
418
375
|
|
|
@@ -430,7 +387,6 @@ wireCatalog(app, {
|
|
|
430
387
|
urlParam: "c", // mirror the active id into ?c=<id>
|
|
431
388
|
revealSelection: true, // reveal long desktop sidebars without moving focus
|
|
432
389
|
});
|
|
433
|
-
wireCatalogGeometryOverlay(app);
|
|
434
390
|
|
|
435
391
|
// Optional for an initial deep link whose row may start outside the viewport.
|
|
436
392
|
revealCatalogEntry(app, initial, { block: "center" });
|
|
@@ -483,7 +439,7 @@ matching options on `wireCatalog`) if they collide with your own action table.
|
|
|
483
439
|
|
|
484
440
|
`Catalog` composes public components (`Workbench`, `Pane`, `Toolbar`, `List`,
|
|
485
441
|
`ListHeader`, `ListItem`, `ListInsetText`, `Text`, …) and owns only the preview stage
|
|
486
|
-
(the checkerboard and
|
|
442
|
+
(the checkerboard and left-aligned canvas), the example viewport
|
|
487
443
|
options, and the brand mark's size. A bundler that honors the `browser` export
|
|
488
444
|
condition loads the Catalog's stylesheets and those of every component it renders
|
|
489
445
|
internally with the `@kerfjs/ui/catalog` import alone — no dependence on which other
|
|
@@ -502,7 +502,9 @@ spacing shorthand such as `gap="2xs"`, or pass a `CssLength` created by the
|
|
|
502
502
|
CSS-free `@kerfjs/ui/css-values` helpers: `space('m')`, `rem(0.25)`,
|
|
503
503
|
`lengthVar('--app-gap')`, or `calc(plus(rem(0.25), pct(10)))`. `plus` returns a
|
|
504
504
|
non-standalone `CssLengthExpression`; only `calc` turns it into a complete
|
|
505
|
-
length. `
|
|
505
|
+
length. `gap={{ column: 'none', row: 'm' }}` sets the two CSS gap axes
|
|
506
|
+
independently; each axis keeps the same typed spacing contract. `remify(18)` is
|
|
507
|
+
the runtime twin of source CSS's `remify(18px)`: it
|
|
506
508
|
takes a pixel count and returns a `CssLength` in rem (`1.125rem`) against the
|
|
507
509
|
same fixed 16px baseline. Raw CSS strings, and the source-only `remify(4px)`
|
|
508
510
|
string form, are not valid runtime props. The `CssLength` name intentionally includes percentages for UI dimension
|
|
@@ -61,6 +61,8 @@ Use `Row`, `Grid`, or `List` spacing shorthands for repeated sibling spacing: `g
|
|
|
61
61
|
`gap="m"`, and the other `UiSpaceName` values resolve to their matching
|
|
62
62
|
`--kui-space-*` token. Use `Spacer` for one deliberate empty width or height, or
|
|
63
63
|
pass `flex` to consume the remaining space along a flex parent's main axis.
|
|
64
|
+
Pass `gap={{ column: 'none', row: 'm' }}` when the axes need different values;
|
|
65
|
+
each axis accepts the same token or complete typed length as the scalar form.
|
|
64
66
|
The canonical rhythm is `none`, `2xs`, `xs`, `m`, and `l`; use `s` and `xl`
|
|
65
67
|
only as deliberate exceptions.
|
|
66
68
|
|
|
@@ -78,6 +80,7 @@ import { Row } from "@kerfjs/ui/row";
|
|
|
78
80
|
|
|
79
81
|
<List gap="xs">...</List>;
|
|
80
82
|
<Grid columns={3} gap="m">...</Grid>;
|
|
83
|
+
<Grid columns={3} gap={{ column: 'none', row: 'm' }}>...</Grid>;
|
|
81
84
|
<Row hAlign="full" vAlign="middle" gap="m">...</Row>;
|
|
82
85
|
<List gap={space("m")}>...</List>;
|
|
83
86
|
<List gap={calc(plus(rem(0.25), pct(10)))}>...</List>;
|
|
@@ -130,11 +133,17 @@ copyable reference compositions, not new monolithic components.
|
|
|
130
133
|
|
|
131
134
|
When presenting a component or recipe in a Catalog, use the authoritative
|
|
132
135
|
[Catalog demo authoring contract](./catalog.md#catalog-demo-authoring-contract)
|
|
133
|
-
rather than deriving preview structure
|
|
136
|
+
rather than deriving preview structure from these
|
|
134
137
|
selection entries.
|
|
135
138
|
|
|
136
139
|
## Missing recurring concepts
|
|
137
140
|
|
|
141
|
+
For arbitrary interactive help or popup content beside a control, use
|
|
142
|
+
[`openAnchoredSurface`](anchored-surface.md) with an accessible label; use
|
|
143
|
+
`openAnchoredSurfaceAt` for pointer coordinates. It owns the Kerf UI surface,
|
|
144
|
+
placement, focus, dismissal, and modal-dialog hosting while the app owns its
|
|
145
|
+
content and action policy. Use `PopupMenu` for a typed command list.
|
|
146
|
+
|
|
138
147
|
Kerf UI does not export a command-palette component. Do not invent a package
|
|
139
148
|
command-palette import.
|
|
140
149
|
The typed [application adapter example](./examples/command-palette-adapter.tsx)
|
|
@@ -558,6 +567,15 @@ menu. Call `closePopupMenu` for programmatic dismissal. The pointer anchor
|
|
|
558
567
|
retains Web Awesome's collision placement, focus management, submenu keyboard
|
|
559
568
|
navigation, and outside/Escape dismissal.
|
|
560
569
|
|
|
570
|
+
The dropdown's popup flips and shifts at viewport edges, limits its height to
|
|
571
|
+
the available space, and uses the browser top layer where supported. The app
|
|
572
|
+
supplies pointer coordinates and command policy; it does not need to calculate
|
|
573
|
+
menu offsets. For other anchored content, use Web Awesome `wa-tooltip` or
|
|
574
|
+
`wa-popover` when its interaction fits, and `wa-popup` when only placement is
|
|
575
|
+
needed. `kerfjs/overlay` provides a structural popover or tooltip with its own
|
|
576
|
+
lifecycle for applications that do not use Web Awesome. These are distinct
|
|
577
|
+
interaction contracts: a command menu still uses `PopupMenu`.
|
|
578
|
+
|
|
561
579
|
## Configuring recurring list rows
|
|
562
580
|
|
|
563
581
|
Configure the List family instead of selecting its descendant classes. Use
|
package/docs/layout.md
CHANGED
|
@@ -376,8 +376,12 @@ The two relationships that get confused most are 8px vs 24px: 8px is _inside_ a
|
|
|
376
376
|
group, 24px is _between_ major, differing regions. 16px sits between them for
|
|
377
377
|
same-kind groups.
|
|
378
378
|
|
|
379
|
-
For `Row.gap`, `Grid.gap`, and `List.gap`, pass
|
|
380
|
-
`
|
|
379
|
+
For `Row.gap`, `Grid.gap`, and `List.gap`, pass one token name (`gap="xs"`,
|
|
380
|
+
`gap="m"`) to use it on both axes, or pass
|
|
381
|
+
`gap={{ column: 'none', row: 'm' }}` to set each axis independently. Each axis
|
|
382
|
+
also accepts a complete typed `CssLength`, including `space('xs')` from
|
|
383
|
+
`@kerfjs/ui/css-values`. A List is a single vertical column, so its row gap
|
|
384
|
+
separates items; its column gap is exposed for the same CSS axis contract.
|
|
381
385
|
Use `rem`, `em`, `px`, `pct`, `lengthVar`, and `calc(plus(...))` only when a
|
|
382
386
|
named spacing relationship does not express the requirement; do not pass raw
|
|
383
387
|
CSS strings.
|
|
@@ -523,6 +527,13 @@ import { px } from "@kerfjs/ui/css-values";
|
|
|
523
527
|
<label>Unit <input /></label>
|
|
524
528
|
</Grid>;
|
|
525
529
|
|
|
530
|
+
<Grid columns={2} gap={{ column: 'none', row: 'm' }}>
|
|
531
|
+
<DocumentTile />
|
|
532
|
+
<DocumentTile />
|
|
533
|
+
<DocumentTile />
|
|
534
|
+
<DocumentTile />
|
|
535
|
+
</Grid>;
|
|
536
|
+
|
|
526
537
|
<Grid minColumnWidth={px(376)} gap="m">
|
|
527
538
|
<label>Provider <input /></label>
|
|
528
539
|
<label>Endpoint <input /></label>
|
package/docs/tab-navigator.md
CHANGED
|
@@ -144,9 +144,16 @@ paint the lowered surface behind short or long content. For a scene whose
|
|
|
144
144
|
only child is a `Pane` or `NavStack`, set appearance on that child's scroll
|
|
145
145
|
owner. See [Lowered work surfaces](layout.md#lowered-work-surfaces).
|
|
146
146
|
|
|
147
|
-
##
|
|
147
|
+
## Bar separator and scroll divider
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
`separator="auto"` (the default) draws the bar's top edge when the active scene
|
|
150
|
+
has `appearance: "sunken"`. It also follows the sunken appearance of a sole
|
|
151
|
+
`Pane`, or the active `Pane` view of a sole `NavStack`, inside that scene. The
|
|
152
|
+
edge stays visible when the content fits and as the active tab or nested view
|
|
153
|
+
changes. `separator="hidden"` suppresses the top line, including any scroll
|
|
154
|
+
divider or `chromeDividers="always"` line.
|
|
155
|
+
|
|
156
|
+
For a flat active scene, the bar has no permanent top line. With `wireScrollDividers(appRoot)`
|
|
150
157
|
installed (see [Layout › Scroll dividers](layout.md#scroll-dividers)), it shows
|
|
151
158
|
a line along its top edge only while the active scene's content continues below
|
|
152
159
|
it — not at the scroll end and not when the content fits. The line keys on
|
|
@@ -156,10 +163,11 @@ own bottom toolbar sits between them, so that toolbar's line applies instead.
|
|
|
156
163
|
The line is the bar's always-present 1px top border, colored only while shown,
|
|
157
164
|
so it never moves the bar or a tab.
|
|
158
165
|
|
|
159
|
-
`chromeDividers` configures
|
|
166
|
+
`chromeDividers` configures the scroll divider, with the same values as a `Pane`'s: `scroll`
|
|
160
167
|
(the default) follows the scroll state and draws nothing until the wiring runs;
|
|
161
168
|
`always` keeps the line, with or without the wiring; `none` drops it even when
|
|
162
|
-
the wiring reports content below.
|
|
169
|
+
the wiring reports content below. It does not suppress the sunken-surface
|
|
170
|
+
separator; use `separator="hidden"` for that.
|
|
163
171
|
|
|
164
172
|
## Tab badges
|
|
165
173
|
|
package/docs/type-contracts.md
CHANGED
|
@@ -121,6 +121,8 @@ move errors into casts without making the integration safer.
|
|
|
121
121
|
- Replace raw `List.gap` strings with a direct spacing shorthand (`"xs"`,
|
|
122
122
|
`"m"`) or a complete value from `@kerfjs/ui/css-values`. Replace
|
|
123
123
|
`gap="0.25rem"` with `gap={rem(0.25)}` and wrap sums with `calc(plus(...))`.
|
|
124
|
+
`Row`, `Grid`, and `List` also accept `gap={{ column: 'none', row: 'm' }}`;
|
|
125
|
+
each axis uses the same typed value contract.
|
|
124
126
|
- Replace raw `List.flex` shorthands with `flex(grow, shrink, basis)` or a finite
|
|
125
127
|
keyword; replace raw Skeleton dimensions with `px`/`rem`/`em`/`pct` builders;
|
|
126
128
|
and replace raw choice colors with a foreground token such as
|
package/docs/ux-demo.md
CHANGED
|
@@ -22,20 +22,20 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
|
|
|
22
22
|
|
|
23
23
|
- It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
|
|
24
24
|
- 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.
|
|
25
|
-
- The `PopupMenu` route's context example opens one app-owned menu either at the row's right-click point or below its adjacent, keyboard-operable Demand actions button.
|
|
25
|
+
- The `PopupMenu` route's context example opens one app-owned menu either at the row's right-click point or below its adjacent, keyboard-operable Demand actions button. The Nested decisions example shows the automatic phone-width submenu placement above or below its parent list, with native ArrowLeft return, Escape dismissal, and child selection.
|
|
26
26
|
- The `AppTab` and `TabBar` routes are focused component specimens: AppTab shows its selected, compact, segmented, icon-only, truncation, placeholder, pending, 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 pinned leading-tab specimen uses native strip scrolling so momentum and the final tab's end position remain natural; `snapTabs` stays an opt-in for applications that want whole-tab settling. The separate `Application tabs` composition route demonstrates controlled selection, matching panel content, overflow, adding (a new tab opens `pending` with a spinner and stays selectable while its panel shows a `Skeleton` heading until a simulated load completes), 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.
|
|
27
27
|
- 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.
|
|
28
28
|
- `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`.
|
|
29
29
|
- 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.
|
|
30
30
|
- 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.
|
|
31
|
-
- 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. The shared Catalog sidebar includes a filter for entry and heading names that works across first-party and ecosystem sections, including collapsed sections. 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
|
|
31
|
+
- 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. The shared Catalog sidebar includes a filter for entry and heading names that works across first-party and ecosystem sections, including collapsed sections. 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 preview stage sits directly in the sunken main Pane with its own 8px inset. The checkerboard belongs to that stage, rather than a viewport-height wrapper 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.
|
|
32
32
|
- 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.
|
|
33
33
|
- The list-detail dialog recipe is laid out like a `Workbench` on roomy devices: a headerless `DialogSurface` modal whose `SplitView` holds a full-height list sidebar `Pane` with its own toolbar beside a full-height detail `Pane` that carries the primary xl title, the `data-dialog="close"` control, and the record actions in its footer. Compact devices get a full-screen sheet with its own labeled header, footer actions, and a list-to-detail drill-down. Browser geometry coverage protects those relationships at wide and narrow widths.
|
|
34
34
|
- 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.
|
|
35
35
|
- The menu composition route demonstrates toolbar/content/footer anatomy, 24px major gaps, 8/1/8 child geometry, the full-width `ListHeader` count-or-badge/action split, logical-end 44px action targets with 18px visuals, and 44px row/header/toolbar targets. Its one chevron is a production 18px `DisclosureArrow` inside a controlled Tools header that actually reveals and hides content; ordinary Projects navigation has no false disclosure affordance. Browser assertions cover stable naming, expanded/content transitions, visual rotation, node identity, reduced motion, RTL alignment and containment, and wide, narrow, dark, and 200% layouts. The focused ListHeader route separately shows the shared neutral count pill for Attachments, Notes (including zero), and Duplicates plus a non-count legacy badge, full-width logical-end actions, and no disclosure without content.
|
|
36
36
|
- The `DisclosureArrow` route renders the production 18px root-scaled default without a catalog-only size override. Its two native buttons independently control the default right-to-down chevron and a recognizable right-facing replacement arrow with left-to-up custom directions; both use shortest-path rotation and their names stay stable while `aria-expanded` communicates state. Browser coverage verifies the custom clockwise path, pointer and native keyboard activation, focus, narrow and 200%-zoom stacking with contained labels, consumer sizing, and the independent `.5` Web Awesome glyph scale retained by Kerf `Select`.
|
|
37
37
|
- A detail with relationships renders one content-fitted `Components` footer selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Its popup owns the standard surface inset so headings and even long component names stay clear of the menu border. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
|
|
38
|
-
- Focused demos ship no local stylesheets, inline styles, or styling-only class names. They compose Kerf components first, Web Awesome surfaces where Kerf has no equivalent, and ordinary semantic HTML for the remainder. The catalog-owned `CatalogExample.viewport` contract supplies the few specimen constraints needed to demonstrate layout behavior. Recipes follow the same rule: they ship no stylesheets, inline styles, or styling-only classes, and the catalog frames each one in the `CatalogExample` viewport its exported `presentation` declares (full-width application arrangements span the canvas measure; narrower recipes sit in the
|
|
38
|
+
- Focused demos ship no local stylesheets, inline styles, or styling-only class names. They compose Kerf components first, Web Awesome surfaces where Kerf has no equivalent, and ordinary semantic HTML for the remainder. The catalog-owned `CatalogExample.viewport` contract supplies the few specimen constraints needed to demonstrate layout behavior. Recipes follow the same rule: they ship no stylesheets, inline styles, or styling-only classes, and the catalog frames each one in the `CatalogExample` viewport its exported `presentation` declares (full-width application arrangements span the canvas measure; narrower recipes sit in the left-aligned example column). Demo-only CSS is limited to the document-shell `style.css`, which carries no recipe rules. Decorative chrome and Web Awesome controls share the same semantic system palette instead of carrying a separate demo identity.
|
|
39
39
|
- Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. Individual Web Awesome action, form, layout, navigation, feedback, media, and helper routes provide focused theme specimens without a redundant aggregate route; ListItem and ListHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; the Headers composition demonstrates direct toolbar headings with an extra-large title, optional supporting copy, and grouped icon/actions; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone with tone-tinted badges, a scoped override, and a placeholder with and without a badge, the badged placeholder in every tone so captures cover the solid badge skeleton; the Application tabs composition uses a Web Awesome card with semantic header/footer slots and Kerf `Text` while demonstrating overflow, edge autoscroll, and controlled pointer/keyboard reordering.
|
|
40
40
|
- The ToolbarControlGroup route also demonstrates the public compact/tight,
|
|
41
41
|
mixed-content, filled-selection, nested-dropdown, and avatar/scrim contracts
|
|
@@ -48,7 +48,7 @@ deterministic state, and semantic layout owners from the [recipe guide](./recipe
|
|
|
48
48
|
collapsible-search specimen follows the selected group shape with a
|
|
49
49
|
concentric inner radius in both collapsed and expanded states.
|
|
50
50
|
- Recipe ownership notes are hidden by default. A page-toolbar toggle reveals each recipe's note as its `CatalogExample` note above the framed specimen, so instructional copy never covers the composition.
|
|
51
|
-
- Settings exercise light/dark, increased contrast, and reduced motion without changing component code. The theme action starts from the operating system's effective color scheme, always names the appearance it will switch to, and installs an explicit light or dark override when activated; contrast and reduced motion expose their independent preferences through `aria-pressed`.
|
|
51
|
+
- Settings exercise light/dark, increased contrast, and reduced motion without changing component code. The theme action starts from the operating system's effective color scheme, always names the appearance it will switch to, and installs an explicit light or dark override when activated; contrast and reduced motion expose their independent preferences through `aria-pressed`. The display-control group wraps whole independent controls at narrow widths so all stay reachable. The adjacent Background menu chooses an 8px-square checkerboard (default), 8px vertical stripes, surface, or sunken for the preview stage; the current choice stays checked while moving between entries.
|
|
52
52
|
- The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
|
|
53
53
|
- The focused Toast route calls Web Awesome's documented programmatic stack API and notes that Hot Sheet 2 still uses its own app-level toast implementation; the catalog demonstrates ecosystem support without implying current product adoption.
|
|
54
54
|
- Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, validates offline source/guidance hrefs and accessible wide/narrow/200%-zoom wrapping, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup and responsive find behavior, single/multiline search alignment, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
|
package/docs/webawesome-theme.md
CHANGED
|
@@ -67,7 +67,10 @@ Form fields carry the same content-item inset as the Kerf primitives: a 1px
|
|
|
67
67
|
border with 8px inside it (`--wa-form-control-border-width`,
|
|
68
68
|
`--wa-form-control-padding-block`, and `--wa-form-control-padding-inline` are set
|
|
69
69
|
to the `--kui-layout-item-border-width` / `--kui-layout-item-padding` values), so
|
|
70
|
-
the value sits 9px in from the field edge.
|
|
70
|
+
the value sits 9px in from the field edge. Native buttons and fields, plus Web
|
|
71
|
+
Awesome buttons and fields, use the 12px `--kui-layout-rounded-radius` through
|
|
72
|
+
`--wa-form-control-border-radius`; explicit pill and circular shapes retain
|
|
73
|
+
their own radius. Default-size single-line controls
|
|
71
74
|
share Kerf's 44px control height — the height of a Kerf `Select`, a toolbar
|
|
72
75
|
group, and a list row — so a `wa-input` or `wa-button` sits flush beside a Kerf
|
|
73
76
|
`Select`. The theme sets `--wa-form-control-height` to `2.75em` (44px at the
|
package/llms.txt
CHANGED
|
@@ -12,6 +12,9 @@ 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 arbitrary interactive help or popup content, use `openAnchoredSurface` or
|
|
16
|
+
`openAnchoredSurfaceAt` from `@kerfjs/ui/anchored-surface`; see
|
|
17
|
+
[AnchoredSurface](./docs/anchored-surface.md). The app owns content and actions.
|
|
15
18
|
For a public subpath containing several visual components, use a folder with one
|
|
16
19
|
component per source file and one owned stylesheet per visual component. Keep its
|
|
17
20
|
compatibility entrypoints implementation-free: JavaScript/TypeScript only re-exports
|
|
@@ -395,6 +398,9 @@ headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` for a
|
|
|
395
398
|
or explicit `scroll`, `always`, `none`), and a `NavStack` or `TabNavigator`
|
|
396
399
|
with its own `chromeDividers` (same values; a `SplitView` forwards it through
|
|
397
400
|
`compactStack`); name app-owned chrome in `targets`.
|
|
401
|
+
A `TabNavigator` also takes `separator="auto" | "hidden"`: auto draws the
|
|
402
|
+
bar's top edge beside a sunken active scene or nested Pane even when content
|
|
403
|
+
fits; hidden suppresses that edge, including scroll dividers.
|
|
398
404
|
Never add a border, or a permanent `dividerSides`, to fake one.
|
|
399
405
|
While a side overlay is open (a Workbench rail overlay, a `wireSidebar` compact
|
|
400
406
|
overlay, an overlay `CollapsiblePanel` or horizontal `ResizableRegion`), the
|
|
@@ -524,9 +530,8 @@ shell, `@kerfjs/ui/collapsible-panel` (`CollapsiblePanel`, `CollapsiblePanelTogg
|
|
|
524
530
|
- `@kerfjs/ui/wire-sidebar` (`wireSidebar`: toggle, focus, compact overlay, persistence; a compact overlay starts closed and opens only on a user action, and `inlineCollapsed` declares a panel's inline default).
|
|
525
531
|
`@kerfjs/ui/catalog` is another opt-in shell — a
|
|
526
532
|
`Catalog` component + `wireCatalog` helper for building a component-gallery catalog
|
|
527
|
-
(collapsible category sidebar + preview stage + resources footer, with
|
|
528
|
-
|
|
529
|
-
reveal for long desktop sidebars) from your own
|
|
533
|
+
(collapsible category sidebar + preview stage + resources footer, with
|
|
534
|
+
focus-preserving active-row reveal for long desktop sidebars) from your own
|
|
530
535
|
entries; controlled/stateless like the layouts. For any Catalog demo, follow
|
|
531
536
|
the [authoritative authoring contract](./docs/catalog.md#catalog-demo-authoring-contract)
|
|
532
537
|
rather than inferring structure from the shell or private CSS.
|
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.83",
|
|
4
4
|
"description": "Accessible, composable UI primitives for kerf applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -317,6 +317,11 @@
|
|
|
317
317
|
"browser": "./dist/browser/popup-menu.js",
|
|
318
318
|
"import": "./dist/popup-menu.js"
|
|
319
319
|
},
|
|
320
|
+
"./anchored-surface": {
|
|
321
|
+
"types": "./dist/anchored-surface.d.ts",
|
|
322
|
+
"browser": "./dist/browser/anchored-surface.js",
|
|
323
|
+
"import": "./dist/anchored-surface.js"
|
|
324
|
+
},
|
|
320
325
|
"./popup-menu/register": {
|
|
321
326
|
"types": "./dist/popup-menu-register.d.ts",
|
|
322
327
|
"import": "./dist/popup-menu-register.js"
|
|
@@ -390,6 +395,7 @@
|
|
|
390
395
|
"./loading-spinner.css": "./dist/styles/loading-spinner.css",
|
|
391
396
|
"./select.css": "./dist/styles/select.css",
|
|
392
397
|
"./popup-menu.css": "./dist/styles/popup-menu.css",
|
|
398
|
+
"./anchored-surface.css": "./dist/styles/anchored-surface.css",
|
|
393
399
|
"./skeleton.css": "./dist/styles/skeleton.css",
|
|
394
400
|
"./sunken-panel.css": "./dist/styles/sunken-panel.css",
|
|
395
401
|
"./segmented-control.css": "./dist/styles/segmented-control.css",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/layout/row/row.tsx"],"names":[],"mappings":";;;;;AAiBA,IAAM,sBAAA,uBAA6B,GAAA,CAAI;AAAA,EACrC,gBAAA;AAAA,EACA,cAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,kBAAA;AAAA,EACA;AACF,CAAC,CAAA;AAeD,IAAM,UAAA,GAAqC;AAAA,EACzC,MAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA;AACF,CAAA;AAgCO,SAAS,GAAA,CAAI;AAAA,EAClB,QAAA;AAAA,EACA,MAAA,GAAS,MAAA;AAAA,EACT,MAAA,GAAS,MAAA;AAAA,EACT,GAAA,GAAM,IAAA;AAAA,EACN,IAAA,GAAO,KAAA;AAAA,EACP,IAAA,GAAO,KAAA;AAAA,EACP,IAAA,GAAO,KAAA;AAAA,EACP,UAAA,GAAa,EAAA;AAAA,EACb,aAAA,GAAgB,EAAA;AAAA,EAChB,SAAA,GAAY,EAAA;AAAA,EACZ,iBAAiB,EAAC;AAAA,EAClB;AACF,CAAA,EAAa;AACX,EAAA,MAAM,kBAAA,GAAqB,oBAAA;AAAA,IACzB,cAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,WAAW,UAAA,CAAW,QAAA,CAAS,GAAkB,CAAA,GACnD,KAAA,CAAM,GAAkB,CAAA,GACxB,GAAA;AACJ,EAAA,MAAM,SAAA,GAAY,IAAA,KAAS,IAAA,GAAO,UAAA,GAAa,IAAA,IAAQ,MAAA;AACvD,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,kBAAkB,QAAQ,CAAA,CAAA;AAAA,IAC1B,SAAA,GAAY,CAAA,gBAAA,EAAmB,SAAS,CAAA,CAAA,GAAK;AAAA,GAC/C,CACG,MAAA,CAAO,OAAO,CAAA,CACd,KAAK,GAAG,CAAA;AAEX,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACE,GAAG,kBAAA;AAAA,MACJ,KAAA,EAAO,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,CAAG,IAAA,EAAK;AAAA,MACnC,gBAAA,EAAe,KAAA;AAAA,MACf,cAAA,EAAc,oBAAoB,MAAM,CAAA;AAAA,MACxC,cAAA,EAAc,kBAAkB,MAAM,CAAA;AAAA,MACtC,WAAA,EAAW,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA;AAAA,MAC/B,WAAA,EAAW,OAAO,MAAA,GAAS,MAAA;AAAA,MAC3B,WAAA,EAAW,OAAO,IAAI,CAAA;AAAA,MACtB,oBAAkB,UAAA,IAAc,MAAA;AAAA,MAChC,uBAAqB,aAAA,IAAiB,MAAA;AAAA,MACtC,KAAA;AAAA,MACA,IAAA;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ","file":"chunk-5D3WDMIK.js","sourcesContent":["import type { KerfUiContent } from '../../../shared/content/semantic-content.js';\nimport { filterDataAttributes } from '../../../shared/dom/extension-attributes.js';\nimport {\n type HorizontalAlignment,\n horizontalAlignment,\n type VerticalAlignment,\n verticalAlignment,\n} from '../../../shared/layout/flex-alignment.js';\nimport type { Sides } from '../../../shared/layout/sides.js';\nimport {\n type CssFlex,\n type CssFlexKeyword,\n type CssLength,\n space,\n type UiSpaceName,\n} from '../../../shared/styles/css-values.js';\n\nconst rowProtectedAttributes = new Set([\n 'data-component',\n 'data-h-align',\n 'data-v-align',\n 'data-flex',\n 'data-fill',\n 'data-wrap',\n 'data-text-insets',\n 'data-control-insets',\n]);\n\ntype RowRootAttributes = Readonly<\n Record<`data-${string}`, string | undefined> & {\n 'data-component'?: never;\n 'data-h-align'?: never;\n 'data-v-align'?: never;\n 'data-flex'?: never;\n 'data-fill'?: never;\n 'data-wrap'?: never;\n 'data-text-insets'?: never;\n 'data-control-insets'?: never;\n }\n>;\n\nconst spaceNames: readonly UiSpaceName[] = [\n 'none',\n '2xs',\n 'xs',\n 's',\n 'm',\n 'l',\n 'xl',\n];\n\nexport interface RowProps {\n children?: KerfUiContent;\n /** Horizontal distribution. Defaults to left. */\n hAlign?: HorizontalAlignment;\n /** Vertical alignment and wrapped-line distribution. Defaults to full. */\n vAlign?: VerticalAlignment;\n /** A named UI spacing token or typed CSS length. Defaults to xs. */\n gap?: UiSpaceName | CssLength;\n /** Allow this row to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */\n flex?: boolean | CssFlexKeyword | CssFlex;\n /**\n * Fill the height of a parent with a definite height, such as an app root or\n * a fixed-height frame, when this row is that parent's layout root. Inside a\n * flex layout use `flex` instead. Defaults to false.\n */\n fill?: boolean;\n /** Allow children to wrap onto additional lines. */\n wrap?: boolean;\n /** Physical sides that receive the standard 17px text inset. */\n textInsets?: Sides;\n /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */\n controlInsets?: Sides;\n className?: string;\n /** Safe `data-*` metadata; Row-owned structural attributes remain protected. */\n rootAttributes?: RowRootAttributes;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/** A horizontal flex row with explicit physical-axis alignment and spacing. */\nexport function Row({\n children,\n hAlign = 'left',\n vAlign = 'full',\n gap = 'xs',\n flex = false,\n fill = false,\n wrap = false,\n textInsets = '',\n controlInsets = '',\n className = '',\n rootAttributes = {},\n slot,\n}: RowProps) {\n const safeRootAttributes = filterDataAttributes(\n rootAttributes,\n rowProtectedAttributes,\n );\n const gapValue = spaceNames.includes(gap as UiSpaceName)\n ? space(gap as UiSpaceName)\n : gap;\n const flexValue = flex === true ? '1 1 auto' : flex || undefined;\n const style = [\n `--_kui-row-gap:${gapValue}`,\n flexValue ? `--_kui-row-flex:${flexValue}` : '',\n ]\n .filter(Boolean)\n .join(';');\n\n return (\n <div\n {...safeRootAttributes}\n class={`kui-row ${className}`.trim()}\n data-component=\"row\"\n data-h-align={horizontalAlignment(hAlign)}\n data-v-align={verticalAlignment(vAlign)}\n data-flex={String(Boolean(flex))}\n data-fill={fill ? 'true' : undefined}\n data-wrap={String(wrap)}\n data-text-insets={textInsets || undefined}\n data-control-insets={controlInsets || undefined}\n style={style}\n slot={slot}\n >\n {children}\n </div>\n );\n}\n\nexport type {\n HorizontalAlignment,\n VerticalAlignment,\n} from '../../../shared/layout/flex-alignment.js';\nexport type { Sides } from '../../../shared/layout/sides.js';\nexport type {\n CssFlex,\n CssFlexKeyword,\n CssLength,\n UiSpaceName,\n} from '../../../shared/styles/css-values.js';\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/styles/css-values.ts"],"names":[],"mappings":"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","file":"chunk-LRAIS3YB.js","sourcesContent":["declare const cssValueBrand: unique symbol;\ndeclare const cssLengthBrand: unique symbol;\ndeclare const cssLengthExpressionBrand: unique symbol;\ndeclare const cssFlexBrand: unique symbol;\ndeclare const cssColorBrand: unique symbol;\ndeclare const cssForegroundColorBrand: unique symbol;\n\n/**\n * A complete typed CSS value minted by a property-specific Kerf UI builder.\n *\n * This brand is an authoring correctness aid, not a sanitizer or security\n * boundary. Prefer the narrower property grammar, such as {@link CssLength},\n * whenever one is available.\n */\nexport type CssValue = string & {\n readonly [cssValueBrand]: 'CssValue';\n};\n\n/**\n * A complete CSS length-percentage value suitable for dimension-valued UI\n * props. Despite the concise name, percentages are intentionally included.\n */\nexport type CssLength = CssValue & {\n readonly [cssLengthBrand]: 'CssLength';\n};\n\n/**\n * An incomplete arithmetic expression. Wrap it with {@link calc} before\n * passing it to a prop that accepts {@link CssLength}.\n */\nexport type CssLengthExpression = string & {\n readonly [cssLengthExpressionBrand]: 'CssLengthExpression';\n};\n\n/** A complete CSS `flex` shorthand. It is not interchangeable with a length. */\nexport type CssFlex = CssValue & {\n readonly [cssFlexBrand]: 'CssFlex';\n};\n\n/** A complete CSS color value. It is not interchangeable with a length. */\nexport type CssColor = CssValue & {\n readonly [cssColorBrand]: 'CssColor';\n};\n\n/**\n * A complete CSS color meant to paint a foreground: text or an icon drawn over\n * a surface. Mint it with {@link uiColor} and a foreground token name\n * ({@link UiForegroundColorName}) or with {@link foregroundColorVar}. It is a\n * {@link CssColor}, but a plain `CssColor` is not a foreground color: fill,\n * border, and surface tokens are pale backgrounds that leave a foreground\n * nearly invisible.\n */\nexport type CssForegroundColor = CssColor & {\n readonly [cssForegroundColorBrand]: 'CssForegroundColor';\n};\n\nexport type CssFlexKeyword = 'none' | 'auto' | 'initial';\nexport type CssFlexBasis =\n | CssLength\n | 'auto'\n | 'content'\n | 'min-content'\n | 'max-content'\n | 'fit-content';\nexport type CssSizeKeyword =\n 'auto' | 'min-content' | 'max-content' | 'fit-content';\n/** A complete width/height value accepted by dimension-valued UI props. */\nexport type CssSize = CssLength | CssSizeKeyword;\n\nconst uiColorNames = [\n 'brand-border-loud',\n 'brand-border-normal',\n 'brand-border-quiet',\n 'brand-fill-loud',\n 'brand-fill-normal',\n 'brand-fill-quiet',\n 'brand-on-fill',\n 'brand-on-loud',\n 'brand-on-normal',\n 'brand-on-quiet',\n 'danger-border-loud',\n 'danger-border-normal',\n 'danger-border-quiet',\n 'danger-fill-loud',\n 'danger-fill-normal',\n 'danger-fill-quiet',\n 'danger-on-fill',\n 'danger-on-loud',\n 'danger-on-normal',\n 'danger-on-quiet',\n 'neutral-border-loud',\n 'neutral-border-normal',\n 'neutral-border-quiet',\n 'neutral-fill-loud',\n 'neutral-fill-normal',\n 'neutral-fill-quiet',\n 'neutral-on-loud',\n 'neutral-on-normal',\n 'neutral-on-quiet',\n 'pop-border-loud',\n 'pop-border-normal',\n 'pop-border-quiet',\n 'pop-fill-loud',\n 'pop-fill-normal',\n 'pop-fill-quiet',\n 'pop-on-fill',\n 'pop-on-loud',\n 'pop-on-normal',\n 'pop-on-quiet',\n 'success-border-loud',\n 'success-border-normal',\n 'success-border-quiet',\n 'success-fill-loud',\n 'success-fill-normal',\n 'success-fill-quiet',\n 'success-on-fill',\n 'success-on-loud',\n 'success-on-normal',\n 'success-on-quiet',\n 'surface',\n 'surface-lowered',\n 'surface-raised',\n 'text',\n 'text-link',\n 'text-quiet',\n 'warning-border-loud',\n 'warning-border-normal',\n 'warning-border-quiet',\n 'warning-fill-loud',\n 'warning-fill-normal',\n 'warning-fill-quiet',\n 'warning-on-fill',\n 'warning-on-loud',\n 'warning-on-normal',\n 'warning-on-quiet',\n] as const;\n\n/** Names of the public `--kui-color-*` semantic tokens. */\nexport type UiColorName = (typeof uiColorNames)[number];\n\n/** The semantic tokens that paint a foreground: the `*-on-*` and text roles. */\nexport type UiForegroundColorName = Extract<\n UiColorName,\n `${string}-on-${string}` | 'text' | 'text-quiet' | 'text-link'\n>;\n\n/** Kerf UI's complete spacing-token vocabulary. `s` and `xl` are exceptions. */\nexport type UiSpaceName = 'none' | '2xs' | 'xs' | 's' | 'm' | 'l' | 'xl';\n\nconst spaceVariables = {\n none: '--kui-space-none',\n '2xs': '--kui-space-2xs',\n xs: '--kui-space-xs',\n s: '--kui-space-s',\n m: '--kui-space-m',\n l: '--kui-space-l',\n xl: '--kui-space-xl',\n} as const satisfies Record<UiSpaceName, string>;\n\nfunction serializeNumber(value: number, helper: string): string {\n if (!Number.isFinite(value))\n throw new RangeError(`${helper}() requires a finite number.`);\n return String(Object.is(value, -0) ? 0 : value);\n}\n\nfunction serializeNonnegativeNumber(value: number, helper: string): string {\n const serialized = serializeNumber(value, helper);\n if (value < 0)\n throw new RangeError(`${helper}() requires nonnegative flex factors.`);\n return serialized;\n}\n\nfunction validateCustomPropertyName(\n name: string,\n helper: string,\n example: string,\n): void {\n if (!/^--[A-Za-z_][A-Za-z0-9_-]*$/.test(name))\n throw new TypeError(\n `${helper}() requires an ASCII custom property name such as ${example}.`,\n );\n}\n\nfunction dimension(value: number, unit: string, helper: string): CssLength {\n return `${serializeNumber(value, helper)}${unit}` as CssLength;\n}\n\n/** Create a complete pixel length. */\nexport function px(value: number): CssLength {\n return dimension(value, 'px', 'px');\n}\n\n/** Create a complete root-font-relative length. */\nexport function rem(value: number): CssLength {\n return dimension(value, 'rem', 'rem');\n}\n\n/** Converts a pixel value into rem units by dividing by 16. */\nexport function remify(value: number): CssLength {\n return rem(value / 16);\n}\n\n/** Create a complete current-font-relative length. */\nexport function em(value: number): CssLength {\n return dimension(value, 'em', 'em');\n}\n\n/** Create a complete percentage length. */\nexport function pct(value: number): CssLength {\n return dimension(value, '%', 'pct');\n}\n\n/** Resolve a Kerf UI spacing step to its public custom property. */\nexport function space(name: UiSpaceName): CssLength {\n const variable = spaceVariables[name];\n if (variable === undefined)\n throw new RangeError(\n `space() received unknown spacing name ${String(name)}.`,\n );\n return `var(${variable})` as CssLength;\n}\n\n/**\n * Reference an application-owned custom property whose contract is a CSS\n * length-percentage. The deliberately narrow name grammar keeps this helper\n * from becoming an arbitrary CSS-string constructor.\n */\nexport function lengthVar(\n name: `--${string}`,\n fallback?: CssLength,\n): CssLength {\n validateCustomPropertyName(name, 'lengthVar', '--app-gap');\n return `var(${name}${fallback === undefined ? '' : `, ${fallback}`})` as CssLength;\n}\n\n/** Combine two or more complete lengths into a non-standalone sum. */\nexport function plus(\n first: CssLength,\n second: CssLength,\n ...rest: readonly CssLength[]\n): CssLengthExpression {\n return [first, second, ...rest].join(' + ') as CssLengthExpression;\n}\n\n/** Turn a typed length expression into a complete CSS `calc()` value. */\nexport function calc(expression: CssLengthExpression): CssLength {\n return `calc(${expression})` as CssLength;\n}\n\n/** Build a complete, structured CSS flex shorthand. */\nexport function flex(\n grow: number,\n shrink = 1,\n basis: CssFlexBasis = 'auto',\n): CssFlex {\n return `${serializeNonnegativeNumber(grow, 'flex')} ${serializeNonnegativeNumber(shrink, 'flex')} ${basis}` as CssFlex;\n}\n\n/**\n * The brand {@link uiColor} returns for a token name: a\n * {@link CssForegroundColor} for a foreground token, otherwise a plain\n * {@link CssColor}.\n */\nexport type UiColor<Name extends UiColorName> =\n Name extends UiForegroundColorName ? CssForegroundColor : CssColor;\n\n/**\n * Resolve a public Kerf UI semantic color token. A foreground token name\n * (`success-on-quiet`, `text-quiet`, …) returns a {@link CssForegroundColor};\n * any other token returns a plain {@link CssColor}.\n */\nexport function uiColor<Name extends UiColorName>(name: Name): UiColor<Name> {\n if (!(uiColorNames as readonly string[]).includes(name))\n throw new RangeError(\n `uiColor() received unknown color name ${String(name)}.`,\n );\n return `var(--kui-color-${name})` as UiColor<Name>;\n}\n\n/** Reference an application-owned custom property whose contract is a color. */\nexport function colorVar(name: `--${string}`, fallback?: CssColor): CssColor {\n validateCustomPropertyName(name, 'colorVar', '--app-color');\n return `var(${name}${fallback === undefined ? '' : `, ${fallback}`})` as CssColor;\n}\n\n/**\n * Reference an application-owned custom property whose contract is a\n * foreground color (text or an icon over a surface). Use it where a prop\n * accepts only {@link CssForegroundColor}, such as `SelectChoice.color`.\n */\nexport function foregroundColorVar(\n name: `--${string}`,\n fallback?: CssForegroundColor,\n): CssForegroundColor {\n validateCustomPropertyName(name, 'foregroundColorVar', '--app-icon-color');\n return `var(${name}${fallback === undefined ? '' : `, ${fallback}`})` as CssForegroundColor;\n}\n\n/**\n * Use an application-chosen CSS color for foreground content. The browser\n * resolves the color syntax; this checks only that it is one declaration value.\n */\nexport function foregroundColor(value: string): CssForegroundColor {\n const color = value.trim();\n if (!color || /[;{}<>]|\\p{Cc}/u.test(color))\n throw new TypeError('foregroundColor() requires one CSS color value.');\n return color as CssForegroundColor;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/collections/list/list.tsx"],"names":[],"mappings":"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","file":"chunk-NVRJ7C3W.js","sourcesContent":["import type { KerfUiContent } from '../../../shared/content/semantic-content.js';\nimport { filterDataAttributes } from '../../../shared/dom/extension-attributes.js';\nimport {\n type HorizontalAlignment,\n horizontalAlignment,\n type ListVerticalAlignment,\n verticalAlignment,\n} from '../../../shared/layout/flex-alignment.js';\nimport type { Sides } from '../../../shared/layout/sides.js';\nimport {\n type CssFlex,\n type CssFlexKeyword,\n type CssLength,\n space,\n type UiSpaceName,\n} from '../../../shared/styles/css-values.js';\n\nconst listProtectedAttributes = new Set([\n 'data-component',\n 'data-gap',\n 'data-flex',\n 'data-fill',\n 'data-h-align',\n 'data-v-align',\n 'data-scrollable',\n 'data-text-insets',\n 'data-control-insets',\n]);\n\ntype ListRootAttributes = Readonly<\n Record<`data-${string}`, string | undefined> & {\n 'data-component'?: never;\n 'data-gap'?: never;\n 'data-flex'?: never;\n 'data-fill'?: never;\n 'data-h-align'?: never;\n 'data-v-align'?: never;\n 'data-scrollable'?: never;\n 'data-text-insets'?: never;\n 'data-control-insets'?: never;\n }\n>;\n\nconst spaceNames: readonly UiSpaceName[] = [\n 'none',\n '2xs',\n 'xs',\n 's',\n 'm',\n 'l',\n 'xl',\n];\n\nexport interface ListProps {\n children?: KerfUiContent;\n /** Make this list a labeled multi-select grid for `ContentItem selectionMode=\"multiple\"` rows. */\n selectionMode?: 'multiple';\n /** Accessible name for the multi-select grid. */\n ariaLabel?: string;\n /** Use the standard item gap, a named UI spacing token, or a typed CSS length. Defaults to no gap. */\n gap?: boolean | UiSpaceName | CssLength;\n /** Allow this list to grow/shrink, use a keyword, or supply a typed CSS flex shorthand. */\n flex?: boolean | CssFlexKeyword | CssFlex;\n /**\n * Fill the height of a parent with a definite height, such as an app root or\n * a fixed-height frame, when this list is that parent's layout root. Inside a\n * flex layout use `flex` instead. Defaults to false.\n */\n fill?: boolean;\n /** Horizontal alignment. Defaults to full to preserve stretch-aligned list children. */\n hAlign?: HorizontalAlignment;\n /** Vertical distribution. Defaults to top. */\n vAlign?: ListVerticalAlignment;\n /** Own vertical scrolling and overscroll containment. */\n scrollable?: boolean;\n /** Physical divider edges in canonical top/right/bottom/left order. */\n dividerSides?: Sides;\n /** Physical sides that receive the standard 17px text inset. */\n textInsets?: Sides;\n /** Physical sides that receive the standard 8px control inset. Text insets win on overlap. */\n controlInsets?: Sides;\n className?: string;\n /** Safe `data-*` metadata; List-owned structural attributes remain protected. */\n rootAttributes?: ListRootAttributes;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/**\n * A list's configuration, apart from its content and styling hooks: the props a\n * composite that renders a `List` for the app (the Workbench's `mainHeader` /\n * `mainFooter` chrome) forwards, so the app configures that list instead of\n * styling it. An omitted or `undefined` field keeps the composite's default.\n */\nexport type ListConfig = Pick<\n ListProps,\n 'gap' | 'hAlign' | 'vAlign' | 'dividerSides' | 'textInsets' | 'controlInsets'\n>;\n\n/** A stretch-aligned vertical stack with optional gap, flex, fill, scroll, and dividers. */\nexport function List({\n children,\n selectionMode,\n ariaLabel,\n gap = false,\n flex = false,\n fill = false,\n hAlign = 'full',\n vAlign = 'top',\n scrollable = false,\n dividerSides = '',\n textInsets = '',\n controlInsets = '',\n className = '',\n rootAttributes = {},\n slot,\n}: ListProps) {\n if (selectionMode === 'multiple' && !ariaLabel) {\n throw new Error('Multi-select List requires an ariaLabel');\n }\n const safeRootAttributes = filterDataAttributes(\n rootAttributes,\n listProtectedAttributes,\n );\n const gapValue =\n gap === true\n ? 'var(--kui-list-gap)'\n : spaceNames.includes(gap as UiSpaceName)\n ? space(gap as UiSpaceName)\n : gap || undefined;\n const flexValue = flex === true ? '1 1 auto' : flex || undefined;\n const style = [\n gapValue ? `--_kui-list-gap:${gapValue}` : '',\n flexValue ? `--_kui-list-flex:${flexValue}` : '',\n ]\n .filter(Boolean)\n .join(';');\n\n return (\n <div\n {...safeRootAttributes}\n class={`kui-list ${className}`.trim()}\n data-component=\"list\"\n role={selectionMode === 'multiple' ? 'grid' : undefined}\n aria-label={selectionMode === 'multiple' ? ariaLabel : undefined}\n aria-multiselectable={selectionMode === 'multiple' ? 'true' : undefined}\n data-gap={String(Boolean(gap))}\n data-flex={String(Boolean(flex))}\n data-fill={fill ? 'true' : undefined}\n data-h-align={horizontalAlignment(hAlign)}\n data-v-align={verticalAlignment(vAlign)}\n data-scrollable={String(scrollable)}\n divider-sides={dividerSides || undefined}\n data-text-insets={textInsets || undefined}\n data-control-insets={controlInsets || undefined}\n style={style || undefined}\n slot={slot}\n >\n {children}\n </div>\n );\n}\n\nexport type {\n HorizontalAlignment,\n ListVerticalAlignment,\n VerticalAlignment,\n} from '../../../shared/layout/flex-alignment.js';\nexport type { Sides } from '../../../shared/layout/sides.js';\nexport type {\n CssFlex,\n CssFlexKeyword,\n CssLength,\n UiSpaceName,\n} from '../../../shared/styles/css-values.js';\n"]}
|