kerfjs 5.0.0-beta.55 → 5.0.0-beta.57
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/CHANGELOG.md +659 -0
- package/README.md +3 -3
- package/ai/cursorrules +19 -10
- package/ai/manifest.json +9 -7
- package/ai/skill.md +16 -6
- package/dist/actions.d.ts +1 -1
- package/dist/actions.js +8 -4
- package/dist/actions.js.map +1 -1
- package/dist/chunk-JN7EJM3B.js +86 -0
- package/dist/chunk-JN7EJM3B.js.map +1 -0
- package/dist/{chunk-WMRFXSLG.js → chunk-TLL3KXT6.js} +5 -2
- package/dist/chunk-TLL3KXT6.js.map +1 -0
- package/dist/{delegate-CL9VTZFb.d.ts → delegate--CrfgYtC.d.ts} +0 -34
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/jsx-runtime.d.ts +1 -0
- package/dist/list.js +1 -1
- package/dist/overlay.js +2 -2
- package/dist/remount.js +1 -1
- package/dist/router.js +1 -1
- package/dist/scope.d.ts +1 -1
- package/dist/scope.js +2 -2
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/setup/index.mjs +4 -4
- package/dist/chunk-WMRFXSLG.js.map +0 -1
- package/dist/chunk-X6OQ2HTR.js +0 -64
- package/dist/chunk-X6OQ2HTR.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,665 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
- **`ui-public-boundaries` inspects `kerfjs/html` tagged templates.** The
|
|
10
|
+
no-build `html` template writes the same markup as JSX, so its static
|
|
11
|
+
`class` attributes now carry `KUI-L101` (private `kui-*` anatomy) and
|
|
12
|
+
`KUI-L103` (a component's anatomy on your own element):
|
|
13
|
+
``html`<aside class="kui-pane">` `` is reported with `Pane` to render, and a
|
|
14
|
+
plain ``html`<div class="kui-content-item">` `` with `ContentItem`, while a
|
|
15
|
+
`<ul>` carrier keeps the placeable class. The template's static parts are
|
|
16
|
+
read by the runtime's own rules; a class a hole supplies (`class=${cls}`)
|
|
17
|
+
is unknown and skipped.
|
|
18
|
+
|
|
19
|
+
- **`KUI-L103`'s "render X instead" answer has one implementation.**
|
|
20
|
+
`@kerfjs/ui` ships `ai/component-class-owners.cjs`, the rule that decides
|
|
21
|
+
which cataloged classes are a component's anatomy and which export renders
|
|
22
|
+
each. `eslint-plugin-kerfjs`'s `ui-public-boundaries` loads it beside
|
|
23
|
+
`application-ui-profile-sync.cjs`, and the generated `ai/components/*.md`
|
|
24
|
+
pages use it too, so the lint message and the component reference can no
|
|
25
|
+
longer name different components (a test pins their agreement over the whole
|
|
26
|
+
shipped catalog). A vendored `profileContractPath` directory must now also
|
|
27
|
+
hold `component-class-owners.cjs`; an `@kerfjs/ui` without it reports
|
|
28
|
+
`KUI-L090`.
|
|
29
|
+
|
|
30
|
+
- **A delegated click no longer re-fires on a target the re-render recycled.**
|
|
31
|
+
A delegated handler that writes a signal makes `mount()` re-render
|
|
32
|
+
synchronously, inside the same event dispatch; when the morph recycled the
|
|
33
|
+
clicked element in place into a control with a different `data-action`,
|
|
34
|
+
delegates registered later for the same event matched the recycled element
|
|
35
|
+
and fired too (a `request` click immediately ran `cancel` and undid itself).
|
|
36
|
+
`delegate()`, `delegateCapture()`, and `kerfjs/actions`' `delegateActions()`
|
|
37
|
+
now resolve every delegate's match against the DOM as it stood when the
|
|
38
|
+
event reached the first kerf delegate listener, like per-element listeners:
|
|
39
|
+
a delegate that matched at dispatch start still fires (unless an earlier
|
|
40
|
+
handler removed its element), one that matches only after a re-render does
|
|
41
|
+
not, and `delegateActions()` dispatches by the action value the element
|
|
42
|
+
carried at dispatch start. `mount()` stays synchronous. The
|
|
43
|
+
wrap-it-in-a-distinct-element workaround is no longer needed. Cost: about
|
|
44
|
+
0.11 KB min+gzip, which moves the advertised core size from ~12 KB to
|
|
45
|
+
~13 KB.
|
|
46
|
+
|
|
47
|
+
- **Component packages declare `placeableClasses`, and `KUI-L103` covers
|
|
48
|
+
their anatomy classes.** `create-kerf-component`'s `kerf.components.json`
|
|
49
|
+
accepts optional `boundaries.placeableClasses` (a subset of
|
|
50
|
+
`publicClasses`) and `boundaries.rootElement`, validates them, and copies
|
|
51
|
+
them into the generated `component-composition.json`.
|
|
52
|
+
`eslint-plugin-kerfjs`'s `ui-public-boundaries` now checks every class the
|
|
53
|
+
application or third-party catalogs in `.kerf-ui-profile.json` declare, not
|
|
54
|
+
only `kui-*` names: `<div class="acme-meter">` is reported with the
|
|
55
|
+
package's component to render (`Meter`), while its placeable classes stay
|
|
56
|
+
allowed. The scaffold template's README explains the field, and
|
|
57
|
+
`@kerfjs/ui/ai/skill.md` (1.50.0) applies the rule to package entries.
|
|
58
|
+
|
|
59
|
+
- **`eslint-plugin-kerfjs` `ui-public-boundaries` reports a plain
|
|
60
|
+
`<div class="kui-content-item">` as `ContentItem` recreated (`KUI-L103`).**
|
|
61
|
+
The `kui-content-item` classes stay placeable on another carrier (a `<ul>`, a
|
|
62
|
+
`Text`, a control-cluster `<footer>`), but a plain `<div>` carrying them is
|
|
63
|
+
exactly what `ContentItem` renders; the diagnostic now says to render
|
|
64
|
+
`ContentItem`. The `@kerfjs/ui` catalogs gain an additive `rootElement`
|
|
65
|
+
field (selection entry and composition `boundaries.rootElement`) naming the
|
|
66
|
+
element a component renders around its own placeable classes, and
|
|
67
|
+
`check:guidance` derives its repository check from the same fields, so the
|
|
68
|
+
package check and the downstream diagnostic cannot drift. The rule also
|
|
69
|
+
inspects the static class names of a template-literal `class` /
|
|
70
|
+
`className` value.
|
|
71
|
+
|
|
72
|
+
- **`eslint-plugin-kerfjs` `ui-public-boundaries` reports a component's
|
|
73
|
+
anatomy class written onto an application-owned element (`KUI-L103`).**
|
|
74
|
+
`<div class="kui-toolbar">`, `<aside class="kui-pane">`, or
|
|
75
|
+
`<wa-dropdown class="kui-popup-menu">` recreates a component by class and
|
|
76
|
+
inherits (and invites restyling of) its CSS; the diagnostic names the
|
|
77
|
+
component to render instead (`Toolbar`, `Pane`, `PopupMenu`). The
|
|
78
|
+
`@kerfjs/ui` catalogs gain `placeableClasses` (selection entry field and
|
|
79
|
+
composition `boundaries.placeableClasses`) naming the public classes an
|
|
80
|
+
application may place itself — the layout utilities, `kui-app-root`, the
|
|
81
|
+
`kui-content-item` geometry on a non-div carrier, and Web Awesome
|
|
82
|
+
`hide-actions` — and `check:catalog` requires every class-only entry to list
|
|
83
|
+
its classes there. A component's own element (`<Toolbar className="kui-toolbar">`)
|
|
84
|
+
is not reported. The README pane example and `@kerfjs/ui/ai/skill.md`
|
|
85
|
+
(1.47.0) now render `Pane` instead of writing its classes, and each generated
|
|
86
|
+
`ai/components/*.md` page states which classes are placeable and which
|
|
87
|
+
component renders the rest.
|
|
88
|
+
|
|
89
|
+
- **`kerf-ui-analyze` retires `KUI-L005` ("forced component dimension").**
|
|
90
|
+
`KUI-L019` already reports any application rule whose subject is a cataloged
|
|
91
|
+
component, dimensions included, so `.kui-pane { width: 300px }` was reported
|
|
92
|
+
twice (an error and a review finding). L005's only other shape, a public
|
|
93
|
+
class as an ancestor (`.kui-pane .app-sidebar { width: 240px }`), sizes the
|
|
94
|
+
application's own element and is allowed. The analyzer no longer emits it;
|
|
95
|
+
the id stays in the diagnostic registry (now listing it under `retired`) so
|
|
96
|
+
existing profile exceptions that name it still load.
|
|
97
|
+
|
|
98
|
+
- **`KUI-L301` now reports every `Discouraged` Web Awesome element rendered
|
|
99
|
+
directly.** The `@kerfjs/ui` package-default application profile avoids all
|
|
100
|
+
fifteen Discouraged entries (Dropdown/Dropdown Item → `PopupMenu`,
|
|
101
|
+
Select/Option → `Select`, Tab Group/Tab/Tab Panel → `TabBar`, Icon →
|
|
102
|
+
`LucideIcon`, Split Panel → `ResizableRegion`, Button Group →
|
|
103
|
+
`SegmentedControl`, Tree/Tree Item → `List`, and Animated Image, Comparison,
|
|
104
|
+
and Zoomable Frame → `ContentItem`), each with a rationale, and
|
|
105
|
+
`eslint-plugin-kerfjs` `ui-preferences` (and `kerf-ui-doctor`, which runs it)
|
|
106
|
+
now resolves a custom-element JSX tag such as `<wa-dropdown>` to its catalog
|
|
107
|
+
entry, not only an imported component. A declared component's own source
|
|
108
|
+
stays exempt because it is the wrapper that owns the element. A test keeps
|
|
109
|
+
the profile's avoid set equal to the catalog's Discouraged set in both
|
|
110
|
+
directions and lints the UX demo, recipes, and catalog shell clean;
|
|
111
|
+
`@kerfjs/ui/ai/skill.md` (1.46.0) names the diagnostic.
|
|
112
|
+
|
|
113
|
+
- **New (`@kerfjs/ui`): `NavStack` and `TabScaffold` take `chromeDividers`
|
|
114
|
+
(`'scroll' | 'always' | 'none'`), like `Pane`.** `scroll` (the default) keeps
|
|
115
|
+
today's behavior: the stack's top-chrome and bottom-toolbar lines and the
|
|
116
|
+
scaffold's bar line follow the scroll once `wireScrollDividers` is installed.
|
|
117
|
+
`always` draws them permanently without the wiring; `none` never draws them,
|
|
118
|
+
even when wired. The top `Toolbar`'s own `toolbarConfig.dividerSides` is
|
|
119
|
+
unaffected. `SplitView` forwards it to its compact stack through
|
|
120
|
+
`compactStack.chromeDividers`.
|
|
121
|
+
|
|
122
|
+
- **Fix (`@kerfjs/ui`): a `Pane` that is the only content of a `NavStack` view
|
|
123
|
+
or a `TabScaffold` scene fills it.** It used to size to its content, so the
|
|
124
|
+
view scrolled the whole Pane and its header scrolled away. Now the Pane's own
|
|
125
|
+
content scrolls under its pinned header and footer, the Pane's slots take the
|
|
126
|
+
safe-area edges the view or scene still reaches, and scroll dividers key on
|
|
127
|
+
the Pane's content. The rule lives in `pane.css`, as the Workbench-region fill
|
|
128
|
+
does.
|
|
129
|
+
|
|
130
|
+
- **The `@kerfjs/ui` suite-v3 AI-regression protocol test runs in every gate
|
|
131
|
+
again.** `ui/tests/integration/ai-regressions-v3.test.ts` was selected by no
|
|
132
|
+
package script, so neither `npm --prefix ui run check` nor CI executed it. It
|
|
133
|
+
now runs as `test:ai-regressions` inside the package's `npm test`, and a new
|
|
134
|
+
unit guard fails when any file under `ui/tests` is selected by no script
|
|
135
|
+
reachable from `check` or `test:e2e`.
|
|
136
|
+
|
|
137
|
+
- **Importing `@kerfjs/ui/catalog` or `@kerfjs/ui/workbench` now ships every
|
|
138
|
+
stylesheet those composites render.** `catalog`, `workbench`, `nav-stack`,
|
|
139
|
+
`split-view`, `tab-scaffold`, and `collapsible-panel` gain a CSS-aware
|
|
140
|
+
`browser` export condition whose generated wrapper imports the foundation, the
|
|
141
|
+
CSS of each internally rendered component (Pane, Toolbar, ToolbarText, List,
|
|
142
|
+
ListItem, ListInsetText, Row, PopupMenu, …) in dependency order, and the
|
|
143
|
+
composite's own CSS. Apps no longer hand-import `pane.css`, and a Catalog
|
|
144
|
+
sidebar's styling no longer depends on whether the app happens to import
|
|
145
|
+
`ListInsetText` elsewhere. The list is derived from the source import graph
|
|
146
|
+
(now following parent-relative imports), the `import` condition stays
|
|
147
|
+
CSS-free, and the companion `.css` subpaths remain for pipelines without the
|
|
148
|
+
`browser` condition. `@kerfjs/ui/ai/skill.md` is 1.46.0.
|
|
149
|
+
|
|
150
|
+
- **The `@kerfjs/ui` AI guidance now states every maintainer design rule
|
|
151
|
+
explicitly.** An audit of the package's tickets folded the missing rules into
|
|
152
|
+
`@kerfjs/ui/ai/skill.md` (1.45.0), `llms.txt`, and the design docs: prefer a
|
|
153
|
+
Kerf UI component over a Web Awesome component over a raw tag and never render
|
|
154
|
+
a `Discouraged` Web Awesome element directly (only through the Kerf component
|
|
155
|
+
that wraps it); nothing wraps inside a `ToolbarControlGroup` and a
|
|
156
|
+
`ToolbarText` is one ellipsized line unless `wrap`/`maxLines` is set; a
|
|
157
|
+
`Select` or `PopupMenu` trigger drops its focus ring while its popup is open
|
|
158
|
+
and the ring follows the control's shape (never add ring CSS); dividers earn
|
|
159
|
+
their place (`dividerSides=""` over self-spacing content or between a pane's
|
|
160
|
+
header and its list, never padded around); a control sits near where its
|
|
161
|
+
effect appears; and text aligns to text and icons to icons — a border is
|
|
162
|
+
never the alignment anchor. The ToolbarControlGroup catalog entry no longer
|
|
163
|
+
points at a hand-written `wa-dropdown` trigger, and the bundled kerf app
|
|
164
|
+
configs (`ai/skill.md` 1.25.0, `ai/cursorrules` 1.23.0) name the List family
|
|
165
|
+
and carry the "components own their styles; configure, never override" rule.
|
|
166
|
+
|
|
167
|
+
- **Applications and component packages are held to the component-ownership
|
|
168
|
+
rules `@kerfjs/ui` enforces internally: components own their styles and are
|
|
169
|
+
configured, never overridden.** `kerf-ui-analyze` and `kerf-ui-doctor` add
|
|
170
|
+
four error diagnostics (review findings under `--adoption`):
|
|
171
|
+
- `KUI-L019` — a CSS rule whose subject is another package's cataloged
|
|
172
|
+
component: a public class (`.kui-toolbar`, `.acme-meter`), its
|
|
173
|
+
`[data-component]` root, or a Web Awesome tag the package themes
|
|
174
|
+
(`wa-button`). Your own element in a component's context
|
|
175
|
+
(`.kui-toolbar > .my-widget`), keying on its state inside `:has()`, and
|
|
176
|
+
setting its public tokens stay allowed.
|
|
177
|
+
- `KUI-L020` — reading or writing another component's private `--_*`
|
|
178
|
+
variable (`--_kui-list-gap`).
|
|
179
|
+
- `KUI-L021` — overriding a token a typed prop sets (`--kui-list-gap` is
|
|
180
|
+
`<List gap>`); the message quotes the prop's example.
|
|
181
|
+
- `KUI-L022` — a class placed on a component's root (`<Toolbar
|
|
182
|
+
className="header" />`) that the application then styles.
|
|
183
|
+
|
|
184
|
+
Each message names the component's configuration (typed props, public
|
|
185
|
+
tokens, variants) and ends by routing a need with no prop to a component-gap
|
|
186
|
+
report instead of an override. Ownership is per package, from the nearest
|
|
187
|
+
`package.json`, so a component package styling its own components — including
|
|
188
|
+
in a Kerf UI parent's context — passes, and third-party catalogs declared in
|
|
189
|
+
`.kerf-ui-profile.json` get the same protection. `eslint-plugin-kerfjs` adds
|
|
190
|
+
`ui-component-ownership` (in `recommended-ui` and `strict-ui`, `error`),
|
|
191
|
+
reporting `KUI-L020` / `KUI-L021` in style strings, style objects, and
|
|
192
|
+
`style.setProperty()`. The `create-kerf-component` scaffold now ships a
|
|
193
|
+
`counter.css` that styles only Counter (configured by a typed `size` prop and
|
|
194
|
+
a `--kerf-counter-gap` token, with context named after Counter and a
|
|
195
|
+
Toolbar-context rule in its own stylesheet), plus `npm run check:styles`
|
|
196
|
+
(`kerf-ui-analyze`, from a new `@kerfjs/ui` dev dependency) in
|
|
197
|
+
`prepublishOnly`.
|
|
198
|
+
|
|
199
|
+
- **Behavior change (`@kerfjs/ui`): `NavStack` and `TabScaffold` chrome
|
|
200
|
+
dividers follow the scroll.** The `NavStack` bottom toolbar's and the
|
|
201
|
+
`TabScaffold` bar's top borders are no longer permanent. With `wireScrollDividers`
|
|
202
|
+
installed, a `NavStack`'s top chrome draws a line along its bottom edge only
|
|
203
|
+
while the active view's content is scrolled beneath it, its bottom toolbar a
|
|
204
|
+
line along its top edge only while more content lies below, and a
|
|
205
|
+
`TabScaffold` bar a line along its top edge only while the active scene's
|
|
206
|
+
content continues below it — none when the content fits. The line follows
|
|
207
|
+
the active view across a push or pop and the active scene across a tab
|
|
208
|
+
switch, and keys on whichever element actually scrolls: the view or scene
|
|
209
|
+
itself, or, through a sole child with no chrome of its own on that edge, a
|
|
210
|
+
`Pane`'s content or a nested `NavStack`'s active view (a sole `Pane`'s own
|
|
211
|
+
header or footer draws that boundary instead). Each layout draws the line
|
|
212
|
+
from its own stylesheet (an overlaid pseudo-element on the stack's chrome; the
|
|
213
|
+
bottom toolbar and bar keep their 1px top border, transparent until shown),
|
|
214
|
+
so nothing moves; the wiring
|
|
215
|
+
writes only its `data-scroll-overflow` / `data-scroll-divider` state. Without
|
|
216
|
+
the wiring those edges draw no line.
|
|
217
|
+
|
|
218
|
+
- **Behavior change (`@kerfjs/ui`): toolbars no longer draw a divider by
|
|
219
|
+
default; dividers between pinned chrome and scrolling content follow the
|
|
220
|
+
scroll.** `Toolbar`'s `dividerSides` now defaults to `''` (was `'b'`), and
|
|
221
|
+
the `Workbench` (`mainToolbar`, `mainBottomToolbar`, panel toolbars, and the
|
|
222
|
+
`mainHeader` / `mainFooter` lists), `CollapsiblePanel`, and `Catalog`
|
|
223
|
+
toolbars no longer place dividers of their own. Instead a `Pane` draws one
|
|
224
|
+
line under its header and one over its footer, wherever that chrome ends:
|
|
225
|
+
the header's only while content is scrolled beneath it, the footer's only
|
|
226
|
+
while more content lies below, and neither when the content fits. The new
|
|
227
|
+
`@kerfjs/ui/wire-scroll-dividers` subpath's `wireScrollDividers(root, {
|
|
228
|
+
targets? })` reports that state — call it once at the app root and keep its
|
|
229
|
+
disposer (`wireCatalog` installs it for its own root). It also marks each
|
|
230
|
+
side of a `TabBar` strip with tabs scrolled out of view, and lets a
|
|
231
|
+
`Toolbar` or `List` named as chrome in `targets` draw the divider facing an
|
|
232
|
+
app-owned scroller. The wiring only writes the `data-scroll-overflow` /
|
|
233
|
+
`data-scroll-divider` state attributes; each component draws its own line,
|
|
234
|
+
and no state moves the chrome, the content, or a tab. `Pane` gains
|
|
235
|
+
`chromeDividers` (`scroll` default, `always`, `none`; also in `PaneConfig`,
|
|
236
|
+
so `mainPane` and a panel's `pane` forward it) and TabBar the
|
|
237
|
+
`--kui-tab-bar-divider-color` token. To keep a permanent line, set
|
|
238
|
+
`chromeDividers: "always"` on the pane or an explicit `dividerSides`.
|
|
239
|
+
|
|
240
|
+
- **`@kerfjs/ui`: `Pane`, `NavStack`, `SplitView`, and `TabScaffold` fill a
|
|
241
|
+
Workbench region from their own stylesheets.** As a Workbench main area's
|
|
242
|
+
or panel's only child, each sets its own `height: 100%` in that context,
|
|
243
|
+
which the Workbench marks on its regions (`data-workbench-main`, and the new
|
|
244
|
+
`data-workbench-panel-content` on each panel's content); `workbench.css` no
|
|
245
|
+
longer sizes another component's root (it still fills a nested Workbench).
|
|
246
|
+
Rendering is unchanged.
|
|
247
|
+
|
|
248
|
+
- **`@kerfjs/ui`: layouts name the covered-floating-controls context after
|
|
249
|
+
themselves, and no longer write `FloatingToolbar`'s inset token.** Workbench,
|
|
250
|
+
CollapsiblePanel, and ResizableRegion each provide their own private
|
|
251
|
+
`--_kui-<layout>-covered` context in place of the shared
|
|
252
|
+
`--_kui-floating-covered` flag; `FloatingToolbar` and every restore corner
|
|
253
|
+
read all three. A restore corner no longer sets
|
|
254
|
+
`--kui-floating-toolbar-inset: 0px`; `FloatingToolbar` zeroes its own inset
|
|
255
|
+
inside a restore corner (a toolbar's own `inset` still wins). Rendering is
|
|
256
|
+
unchanged. One edge case changes: a floating control inside an open overlay
|
|
257
|
+
of one layout type that is itself covered by another layout type's open
|
|
258
|
+
overlay (for example, a ResizableRegion overlay in a Workbench work area
|
|
259
|
+
under an open rail overlay) now stays hidden.
|
|
260
|
+
|
|
261
|
+
- **`@kerfjs/ui`: `ListHeader`, `NavStack`, `Select`, and `StateBanner` no
|
|
262
|
+
longer restyle a composed child through a class on its root.** Each child
|
|
263
|
+
now styles itself: `Text` sets a `ListHeader` label from `text.css`, `Badge`
|
|
264
|
+
spaces a `Select` count and a `StateBanner` badge from `badge.css`, and a
|
|
265
|
+
`NavStack` title uses `ToolbarText`'s new `fill` option. Rendering is
|
|
266
|
+
unchanged. `ToolbarText` gains `fill` (grow into the free space of its
|
|
267
|
+
toolbar zone and truncate there). The `kui-list-header__label`,
|
|
268
|
+
`kui-nav-stack__title`, `kui-select__count`, and `kui-state-banner__badge`
|
|
269
|
+
classes are removed; select a ListHeader label with
|
|
270
|
+
`.kui-list-header__title > .kui-text` and a NavStack title with
|
|
271
|
+
`.kui-toolbar-text[data-fill="true"]` if a test needs to find them.
|
|
272
|
+
|
|
273
|
+
- **`@kerfjs/ui` ships a markdown component reference for AI assistants.**
|
|
274
|
+
`@kerfjs/ui/ai/components/README.md` indexes every catalog entry in one line,
|
|
275
|
+
and `@kerfjs/ui/ai/components/<id>.md` gives each entry a short page: when to
|
|
276
|
+
use and avoid it, imports and CSS/registration side effects, a link to its
|
|
277
|
+
exact props, legal parents and zones (JSX prop and child count), app- and
|
|
278
|
+
wiring-owned state, geometry, accessibility obligations, its styling boundary
|
|
279
|
+
(configure through props; do not override internals), and its diagnostics.
|
|
280
|
+
`llms.txt` and the shipped skill now send assistants there instead of to the
|
|
281
|
+
263 KB `component-catalog.json` and 221 KB `component-composition.json`,
|
|
282
|
+
which remain the canonical sources for tools. The pages are generated from
|
|
283
|
+
those two files and a package check fails when they drift.
|
|
284
|
+
|
|
285
|
+
- **Breaking (`@kerfjs/ui` and `create-kerf-component`): the composition
|
|
286
|
+
catalog files are renamed so they no longer read as a "v2" of the component
|
|
287
|
+
catalog.** The composition catalog is a generated composition layer beside
|
|
288
|
+
`component-catalog.json`, not a newer version of it. Update any import,
|
|
289
|
+
`$schema`, `$ref`, or `.kerf-ui-profile.json` catalog location that names an
|
|
290
|
+
old path:
|
|
291
|
+
|
|
292
|
+
| Old path | New path |
|
|
293
|
+
| -------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
294
|
+
| `@kerfjs/ui/ai/component-catalog-v2.json` | `@kerfjs/ui/ai/component-composition.json` |
|
|
295
|
+
| `@kerfjs/ui/ai/component-catalog-v2.schema.json` | `@kerfjs/ui/ai/component-composition.schema.json` |
|
|
296
|
+
| `@kerfjs/ui/ai/component-catalog-v2.d.ts` | `@kerfjs/ui/ai/component-composition.d.ts` |
|
|
297
|
+
| `@kerfjs/ui/ai/component-catalog-v2-overrides.json` | `@kerfjs/ui/ai/component-composition-rules.json` |
|
|
298
|
+
| `@kerfjs/ui/ai/component-catalog-extension-v2.schema.json` | `@kerfjs/ui/ai/component-composition-extension.schema.json` |
|
|
299
|
+
| `@kerfjs/ui/ai/component-catalog-extension-v2.d.ts` | `@kerfjs/ui/ai/component-composition-extension.d.ts` |
|
|
300
|
+
| `@kerfjs/ui/docs/examples/component-catalog-extension-v2.json` | `@kerfjs/ui/docs/examples/component-composition-extension.json` |
|
|
301
|
+
| scaffolded package `component-catalog-v2.json` | scaffolded package `component-composition.json` |
|
|
302
|
+
|
|
303
|
+
The exported types follow: `ComponentCatalogV2` → `ComponentComposition`,
|
|
304
|
+
`CatalogCompositionEntryV2` → `CatalogCompositionEntry`,
|
|
305
|
+
`ConsumerComponentCatalogV2` → `ConsumerComponentComposition`, and
|
|
306
|
+
`ConsumerCatalogEntryV2` → `ConsumerCompositionEntry`. In the repository,
|
|
307
|
+
`npm run catalog:v2:sync` is now `npm run composition:sync`
|
|
308
|
+
(`catalog:sync` still runs both generators). The file contents and their
|
|
309
|
+
`schemaVersion` are unchanged.
|
|
310
|
+
|
|
311
|
+
- **Breaking (`@kerfjs/ui`, `create-kerf-component`, and `kerfjs setup`): the
|
|
312
|
+
composition catalog's own fields no longer read as a "v2" of the component
|
|
313
|
+
catalog.** In `component-composition.json` (and any consumer composition
|
|
314
|
+
catalog), `"schemaVersion": 2` is now `"schemaVersion": 1` — the composition
|
|
315
|
+
format's own first version, independent of `component-catalog.json`'s — and
|
|
316
|
+
`compatibility.v1Catalog` is now `compatibility.componentCatalog`. A
|
|
317
|
+
scaffolded package's `kerf.components.json` renames its `v1Catalog` field to
|
|
318
|
+
`componentCatalog` the same way. Regenerate scaffolded catalogs
|
|
319
|
+
(`npm run catalog:generate`), and change any `.kerf-ui-profile.json` `composition`
|
|
320
|
+
location from `"schemaVersion": 2` to `"schemaVersion": 1`; a catalog still
|
|
321
|
+
carrying the old fields fails validation with a message naming the rename.
|
|
322
|
+
The `ComponentComposition` type follows (`schemaVersion: 1`,
|
|
323
|
+
`compatibility.componentCatalog`).
|
|
324
|
+
|
|
325
|
+
- **`@kerfjs/ui` `NavStack` bars use the standard `Toolbar` padding.** The
|
|
326
|
+
navigation bar dropped its toolbar's block padding to stay 44px tall, so its
|
|
327
|
+
title and controls touched the bar's edges. It now keeps the 8px above and
|
|
328
|
+
below that every other toolbar has (60px plus any top safe-area inset).
|
|
329
|
+
|
|
330
|
+
- **`@kerfjs/ui` multiline list rows break long camelCase names at word
|
|
331
|
+
boundaries.** A string label in a `multiline` `ListItem` or `ListActionRow`
|
|
332
|
+
(such as a Catalog sidebar entry) gets `<wbr>` break opportunities at its
|
|
333
|
+
camelCase and acronym boundaries, so `QuickTicketComposer` wraps as
|
|
334
|
+
`QuickTicket` / `Composer` instead of `QuickTicketComp` / `oser`. Breaking
|
|
335
|
+
mid-word remains the fallback for a single word that is still too wide.
|
|
336
|
+
- **`wbr` is a typed JSX intrinsic element.** The runtime already rendered it
|
|
337
|
+
as a void element; `JSX.IntrinsicElements` now declares it.
|
|
338
|
+
|
|
339
|
+
- **`@kerfjs/ui` `Badge` has a text-free dot, and `TabScaffold` tabs can show
|
|
340
|
+
it.** `Badge` takes `size="dot"`: an 8px solid circle in the badge's tone,
|
|
341
|
+
with no children, appearance, or shape. Because a dot has no text, its props
|
|
342
|
+
require either a `label` (announced as an image, for example
|
|
343
|
+
`label="New activity"`) or `ariaHidden` when the owning component already
|
|
344
|
+
announces it. `TabScaffoldTab` accepts `badge: true` for the iOS tab-bar dot
|
|
345
|
+
(new content without a count), centered on the icon's top-trailing corner;
|
|
346
|
+
its `badgeLabel` is required and joins the tab's accessible name
|
|
347
|
+
("Settings, Update available"). `BadgeTextProps` and `BadgeDotProps` are
|
|
348
|
+
exported from the root barrel.
|
|
349
|
+
|
|
350
|
+
- **`@kerfjs/ui` `Toolbar` truncates the leading title instead of letting the
|
|
351
|
+
center zone cover it.** When a leading identity, a `center` group, and
|
|
352
|
+
trailing actions no longer fit one row, the center zone keeps its whole
|
|
353
|
+
content width and the leading title ends in an ellipsis. Previously the
|
|
354
|
+
center track could shrink below its group, which then painted over the
|
|
355
|
+
title — a pushed `NavStack` view with a center group did this at 390px. The
|
|
356
|
+
fix holds under every `responsive` policy and both `centerAlign` values, and
|
|
357
|
+
a toolbar that fits lays out exactly as before.
|
|
358
|
+
|
|
359
|
+
- **`@kerfjs/ui` `ValueTableRow` keeps its value readable beside a long
|
|
360
|
+
label.** A long single-word label (such as `ConnectionStateBanner` in a
|
|
361
|
+
390px-wide table) no longer squeezes the value to 0px wide, where it wrapped
|
|
362
|
+
one character per line while the label ran underneath it. The label and value
|
|
363
|
+
now share the row as two content-sized columns: a short value stays whole on
|
|
364
|
+
one line, the label wraps at its spaces and ends an over-long word in an
|
|
365
|
+
ellipsis before the value, and when both are long they split the row.
|
|
366
|
+
|
|
367
|
+
- **`@kerfjs/ui` pinned chrome can give way in a short pane.** `Pane` takes
|
|
368
|
+
`chromePlacement="auto"`: its header and footer stay pinned while the pane is
|
|
369
|
+
at least 480px (30rem) tall, and below that the whole pane scrolls as one
|
|
370
|
+
column so tall chrome never squeezes the content to nothing.
|
|
371
|
+
`WorkbenchChromePlacement` gains `"auto"` for `mainHeaderPlacement` /
|
|
372
|
+
`mainFooterPlacement`, and `Catalog` now defaults `headerPlacement` /
|
|
373
|
+
`footerPlacement` to `"auto"`. Its entry description is pinned under the
|
|
374
|
+
toolbar, which at 200% text size on a short window had left the preview 0px
|
|
375
|
+
tall.
|
|
376
|
+
|
|
377
|
+
- **`@kerfjs/ui` `TabScaffold` tabs can show a badge.** Give a
|
|
378
|
+
`TabScaffoldTab` a `badge` (a count or short string) to show the iOS tab-bar
|
|
379
|
+
badge — a compact, solid danger `Badge` at the top-trailing corner of the
|
|
380
|
+
tab's icon. The badge is hidden from assistive technology; the tab's
|
|
381
|
+
accessible name becomes `"<label>, <badgeLabel>"` (for example
|
|
382
|
+
"Inbox, 3 unread"), with `badgeLabel` defaulting to the badge text. Tabs
|
|
383
|
+
without a badge are unchanged.
|
|
384
|
+
|
|
385
|
+
- **`@kerfjs/ui` `ListItem` and `ListActionRow` labels stay out of the
|
|
386
|
+
trailing slot.** A long single-word primary label (such as a Catalog sidebar
|
|
387
|
+
entry named `ConnectionStateBanner` in a narrow rail) no longer runs
|
|
388
|
+
underneath trailing status text. Single-line rows now end the primary label
|
|
389
|
+
and description with a real ellipsis (previously the text was cut mid-glyph
|
|
390
|
+
with no "…"), and `multiline` rows break a word wider than the label column
|
|
391
|
+
onto the next line.
|
|
392
|
+
|
|
393
|
+
- **Custom elements keep the `role` and `aria-*` they set on themselves across
|
|
394
|
+
re-renders.** Web components conventionally own their host's ARIA (Web
|
|
395
|
+
Awesome's `wa-option` sets `role="option"` / `aria-selected`, `wa-divider`
|
|
396
|
+
sets `role="separator"`; Shoelace and FAST do the same), but a template never
|
|
397
|
+
mentions them, so the morph and the keyed-list attribute fast path stripped
|
|
398
|
+
them on the next re-render. On any custom element (hyphenated tag), `role`
|
|
399
|
+
and `aria-*` are now element-owned like `open`: never removed just because
|
|
400
|
+
the template omits them. The template can still set or change them, a signal
|
|
401
|
+
binding set to `null` / `false` still removes them, and plain elements are
|
|
402
|
+
unchanged. Trade-off: omitting one from a static template no longer removes
|
|
403
|
+
it from a custom element — render an explicit value (`aria-pressed="false"`)
|
|
404
|
+
instead.
|
|
405
|
+
|
|
406
|
+
- **`@kerfjs/ui` layout regions no longer slow edits to long lists inside
|
|
407
|
+
them.** The rule that clears the safe-area context for the children of a
|
|
408
|
+
Workbench main area or panel, SplitView list or detail, NavStack view,
|
|
409
|
+
TabScaffold scene, or CollapsiblePanel content made each child re-check the
|
|
410
|
+
region in Chromium on every DOM change. A 1000-row list placed directly in
|
|
411
|
+
one of these regions took about 46ms per style recalculation (2ms with the
|
|
412
|
+
list one level down), and now takes about 0.2ms. Layout and insets are
|
|
413
|
+
unchanged.
|
|
414
|
+
|
|
415
|
+
- **`@kerfjs/ui` `CollapsiblePanel` no longer slows insertions into long
|
|
416
|
+
lists.** An expanded inline panel clears its screen edge for its siblings;
|
|
417
|
+
the rules that did so selected the siblings themselves
|
|
418
|
+
(`:has(~ panel)` and `panel ~ *`), which in Chromium made each row inserted
|
|
419
|
+
into or removed from a long list restyle the list's other rows — about
|
|
420
|
+
14.6ms instead of 0.2ms per insertion beside a 1000-row list. The panel's
|
|
421
|
+
container now carries a flag that a CSS style query reads, so a list pays
|
|
422
|
+
nothing. The edge still clears for the panel's siblings and returns when it
|
|
423
|
+
collapses; the one difference is that a sibling placed before a left panel
|
|
424
|
+
or after a right or bottom panel now loses the edge too.
|
|
425
|
+
|
|
426
|
+
- **`@kerfjs/ui` restore corners and covered floating controls no longer slow
|
|
427
|
+
every style recalculation.** The `CollapsiblePanel` / `ResizableRegion`
|
|
428
|
+
restore-corner anchor scope (`:has(> restore) > * > * > *`) and the static
|
|
429
|
+
side-overlay covered flag (`:has(> open overlay) > :not(overlay)`) put a
|
|
430
|
+
universal selector after a `:has()` container, which in Chromium restyled
|
|
431
|
+
the children of every ancestor of each DOM change — about 85ms instead of
|
|
432
|
+
1ms per edit beside a 1000-row list. The drawer now publishes its anchor
|
|
433
|
+
only beside a restore control's container (or its work-area column), and
|
|
434
|
+
the covered flag is set on the overlay's container and cleared on its
|
|
435
|
+
overlay children. Restore-corner placement and covered-control hiding are
|
|
436
|
+
unchanged; a new CSS unit test rejects the costly `:has()` shapes.
|
|
437
|
+
|
|
438
|
+
- **`@kerfjs/ui` adds `ContentItem`.** Import it from
|
|
439
|
+
`@kerfjs/ui/content-item` to render one self-contained pane child with the
|
|
440
|
+
shared 8px margin, 1px border, and 8px padding. Framing and shape are typed
|
|
441
|
+
props instead of hand-written modifier classes:
|
|
442
|
+
- `frame="framed"` paints the neutral border in the 1px the item always
|
|
443
|
+
reserves, so its geometry does not change (replacing
|
|
444
|
+
`class="kui-content-item kui-content-item--framed"`);
|
|
445
|
+
- `shape="pill"` selects the 22px radius.
|
|
446
|
+
- `ariaLabel` names a distinct region, and `focusTarget` makes the item a
|
|
447
|
+
programmatic focus target (`tabindex="-1"`), such as a NavStack view's
|
|
448
|
+
`data-nav-focus` target.
|
|
449
|
+
|
|
450
|
+
The `.kui-content-item` classes stay public for elements that must carry the
|
|
451
|
+
geometry themselves; their CSS now lives in `content-item.css`, which
|
|
452
|
+
`layout.css` still imports. There is no filled variant — use `SunkenPanel`
|
|
453
|
+
for a lowered surface. The catalog, demos, and recipes use the component.
|
|
454
|
+
|
|
455
|
+
- **`@kerfjs/ui` icon-only toolbar buttons show the same help tag.** An
|
|
456
|
+
icon-only `<button>` or link inside a `ToolbarControlGroup`, named by
|
|
457
|
+
`aria-label` with no visible text, now shows the help tag icon-only Selects
|
|
458
|
+
and PopupMenus show, with the same 500ms hover delay, keyboard-focus-only
|
|
459
|
+
display, press/Escape dismissal, and quick scanning along a toolbar. The tag
|
|
460
|
+
is `aria-hidden` and never renames the button. A button with a native
|
|
461
|
+
`title`, or with its own Web Awesome tooltip, keeps that and gets no second
|
|
462
|
+
tag. The `select/register` and `popup-menu/register` imports install it; a
|
|
463
|
+
toolbar with icon-only buttons but neither control imports the new
|
|
464
|
+
`@kerfjs/ui/help-tags/register` subpath, which registers only the tooltip.
|
|
465
|
+
|
|
466
|
+
- **`@kerfjs/ui` icon-only Select and PopupMenu triggers show a help tag.**
|
|
467
|
+
An icon-only `Select` (single or `multiple` with `triggerIcon`) and an
|
|
468
|
+
icon-only `PopupMenu` now show a Web Awesome tooltip naming the control on
|
|
469
|
+
hover (after 500ms) and on keyboard focus: a filter reads "Filter by label:
|
|
470
|
+
Bug, Docs", a single Select its name and current choice, a menu its `label`.
|
|
471
|
+
It hides while the popup is open, on a press, and on Escape. The tag repeats
|
|
472
|
+
the accessible name, so it is `aria-hidden` and never joins
|
|
473
|
+
`aria-labelledby`; screen readers still hear the name once. It is installed
|
|
474
|
+
by the existing `select/register` and `popup-menu/register` imports, needs no
|
|
475
|
+
props, and never appears for labeled Selects or text menu triggers.
|
|
476
|
+
|
|
477
|
+
- **`@kerfjs/ui` toolbar groups drop their focus ring while a popup is open.**
|
|
478
|
+
A `ToolbarControlGroup` that owns the ring (`focusRing="outline"` or
|
|
479
|
+
`"halo"`) no longer paints it while a Select listbox or `PopupMenu` inside it
|
|
480
|
+
is open; the open popup shows focus, and the ring returns when the popup
|
|
481
|
+
closes with focus still in the group. This matches the per-control Select
|
|
482
|
+
and PopupMenu triggers.
|
|
483
|
+
|
|
484
|
+
- **`@kerfjs/ui` multiple `Select` can be an icon-only toolbar filter menu.**
|
|
485
|
+
Set `selectedPresentation: 'icon-only'` on a `multiple` Select together with
|
|
486
|
+
a fixed `triggerIcon` (for example a funnel `LucideIcon`); the types require
|
|
487
|
+
`triggerIcon` there and reject it everywhere else. The trigger keeps the
|
|
488
|
+
icon-only caret pill used in a `ToolbarControlGroup`, and:
|
|
489
|
+
- shows the fixed icon whatever is chosen;
|
|
490
|
+
- adds a compact count `Badge` while any choice is chosen;
|
|
491
|
+
- ends the combobox's accessible name with the chosen labels in choice order
|
|
492
|
+
("Filter by label: Bug, Docs"), leaving the hidden value text empty so the
|
|
493
|
+
list is not announced twice.
|
|
494
|
+
|
|
495
|
+
The popup still stays open while choices toggle.
|
|
496
|
+
|
|
497
|
+
- **`@kerfjs/ui` `Select` options keep their accessibility semantics across
|
|
498
|
+
re-renders.** Re-rendering a Select (for example after its `change` handler
|
|
499
|
+
updates the controlling signal) removed the `role="option"` and
|
|
500
|
+
`aria-selected` Web Awesome sets on each option, so screen readers lost every
|
|
501
|
+
option's role and selected state. They now survive in single and `multiple`
|
|
502
|
+
Selects through the core rule below (custom elements own their `role` /
|
|
503
|
+
`aria-*`), with no Select-specific restore.
|
|
504
|
+
|
|
505
|
+
- **`@kerfjs/ui` `Select` dividers keep their separator role across
|
|
506
|
+
re-renders.** The `wa-divider` a `separatorBefore` choice adds lost the
|
|
507
|
+
`role="separator"` and `aria-orientation` Web Awesome sets on it the first
|
|
508
|
+
time the enclosing mount re-rendered, and never got them back, so assistive
|
|
509
|
+
technology stopped announcing the break between option runs. The Select now
|
|
510
|
+
renders both attributes itself.
|
|
511
|
+
|
|
512
|
+
- **`@kerfjs/ui` `Select` and `PopupMenu` re-renders no longer force Web
|
|
513
|
+
Awesome updates.** Web Awesome reflects its default `size`, `appearance`,
|
|
514
|
+
`placement`, and `orientation` onto `wa-select`, `wa-dropdown`, and
|
|
515
|
+
`wa-divider`; the templates omitted them, so every re-render of the enclosing
|
|
516
|
+
mount stripped them and each host ran a full Lit update to put them back. The
|
|
517
|
+
templates now render those defaults (`wa-select size="m"
|
|
518
|
+
appearance="outlined" placement="bottom"`, `wa-dropdown size="m"`, divider
|
|
519
|
+
`orientation="horizontal"`), so an unchanged re-render leaves the hosts
|
|
520
|
+
alone. No visual change. The `@kerfjs/ui/webawesome` JSX declarations type
|
|
521
|
+
these attributes.
|
|
522
|
+
|
|
523
|
+
- **`@kerfjs/ui` Workbench and CollapsiblePanel panes and chrome lists are
|
|
524
|
+
configurable.** The `Pane`s these layouts compose around app content now
|
|
525
|
+
take the new `PaneConfig` type (`contentElement`, `contentLabel`,
|
|
526
|
+
`separators`, `safeAreaEdges`): a Workbench panel's or `CollapsiblePanel`'s
|
|
527
|
+
`pane`, and the Workbench's `mainPane`. The `List`s holding `mainHeader` /
|
|
528
|
+
`mainFooter` take the new `ListConfig` type (`gap`, `hAlign`, `vAlign`,
|
|
529
|
+
`dividerSides`, `textInsets`, `controlInsets`) through `mainHeaderList` /
|
|
530
|
+
`mainFooterList`. Previously this configuration was hard-coded, so, for
|
|
531
|
+
example, a navigator rail could not render its content as a `nav` landmark
|
|
532
|
+
without restyling. Omitted or `undefined` fields keep today's defaults, and
|
|
533
|
+
the default markup is unchanged.
|
|
534
|
+
|
|
535
|
+
- **`@kerfjs/ui` `SplitView` forwards its `ResizableRegion` and compact
|
|
536
|
+
`NavStack` configuration.** `resizable` now also takes the list region's
|
|
537
|
+
`separator`, `handleIcon`, `contentOverflow`, and collapse options
|
|
538
|
+
(`collapsed`, `transitioning`, `collapseMotion`, `restoreControl`,
|
|
539
|
+
`restorePosition`), so an app can hide the list and float a restore control
|
|
540
|
+
without leaving `SplitView`. The new `compactStack` prop forwards the compact
|
|
541
|
+
stack's `toolbarConfig`, `backIcon`, `backText`, `hideToolbar`, and
|
|
542
|
+
persistent `bottomToolbar`, and gives the list and detail views their own
|
|
543
|
+
toolbar groups (`list` / `detail`: `leading`, `center`, `toolbar`,
|
|
544
|
+
`bottomToolbar`). Omitted options keep today's defaults.
|
|
545
|
+
|
|
546
|
+
- **`@kerfjs/ui` `NavStack` renders its top chrome as a real `Toolbar`.** The
|
|
547
|
+
back control is a borderless `ToolbarControlGroup` and the title a
|
|
548
|
+
`ToolbarText` in the leading zone, and the stack now forwards:
|
|
549
|
+
- `toolbarConfig` — the `Toolbar` configuration (`dividerSides`,
|
|
550
|
+
`centerAlign`, `responsive`, `responsiveAt`, `safeAreaEdges`) plus `label`,
|
|
551
|
+
`titleSize` (default `large`), and `headingLevel`;
|
|
552
|
+
- per-view `leading` and `center` groups beside the existing trailing
|
|
553
|
+
`toolbar`;
|
|
554
|
+
- `backIcon` and visible `backText` for the back control.
|
|
555
|
+
|
|
556
|
+
The defaults keep today's bar: no divider, one 44px band, and the top and
|
|
557
|
+
side safe-area edges claimed. The hand-rolled `kui-nav-stack__lead` and
|
|
558
|
+
`kui-nav-stack__actions` wrappers are gone (their content now sits in the
|
|
559
|
+
toolbar's zones), and `kui-nav-stack__back` / `kui-nav-stack__back-icon`
|
|
560
|
+
are no longer styled by the stack.
|
|
561
|
+
|
|
562
|
+
- **`@kerfjs/ui` configures with props instead of token overrides.** Five
|
|
563
|
+
settings apps previously reached through raw `--kui-*` overrides now have a
|
|
564
|
+
typed prop or modifier; each defaults to today's rendering, and the
|
|
565
|
+
underlying tokens keep working:
|
|
566
|
+
- `FloatingToolbar` `inset` (a `CssLength`, default `--kui-space-m`) moves
|
|
567
|
+
it from the container edges. Omit it inside a restore corner, which owns
|
|
568
|
+
the inset.
|
|
569
|
+
- `DisclosureArrow` `size` (a `CssLength`, default 18px root-scaled).
|
|
570
|
+
- `ListHeader` `headingLevel` (1–6, default 2) sets the label's native
|
|
571
|
+
heading element for passive and trailing-action headers; a toggle header
|
|
572
|
+
has no heading and rejects it at compile time.
|
|
573
|
+
- `TokenSearchField` `clearIcon` replaces the clear action's glyph.
|
|
574
|
+
- `.kui-content-item--framed` paints the standard neutral border on a
|
|
575
|
+
content item that marks a real distinction, with no geometry change.
|
|
576
|
+
|
|
577
|
+
The docs and AI guidance also stop recommending `--kui-edge-inset-*`
|
|
578
|
+
overrides: an app-owned arrangement passes `safeAreaEdges` to its panes and
|
|
579
|
+
edge-claiming toolbars (including the layout toolbar configurations, which
|
|
580
|
+
forward it).
|
|
581
|
+
|
|
582
|
+
- **`@kerfjs/ui` hides a work area's floating controls under an open side
|
|
583
|
+
overlay.** While a side overlay is open — a Workbench rail (static
|
|
584
|
+
`presentation: "overlay"`, or a responsive overlay whose breakpoint applies),
|
|
585
|
+
a `wireSidebar` compact overlay or `presentation="overlay"`
|
|
586
|
+
`CollapsiblePanel`, or a horizontal `presentation="overlay"`
|
|
587
|
+
`ResizableRegion` — the covered work area's `FloatingToolbar`s and the
|
|
588
|
+
layouts' restore corners are now hidden (`visibility: hidden`, so they also
|
|
589
|
+
leave the tab order and the accessibility tree) instead of relying on
|
|
590
|
+
stacking order alone. A phone-width app could otherwise paint the main
|
|
591
|
+
column's floating drawer toggle over its open inspector. They come back as
|
|
592
|
+
the overlay closes; the overlay's own floating controls and a bottom
|
|
593
|
+
drawer's overlay are unaffected.
|
|
594
|
+
|
|
595
|
+
- **`@kerfjs/ui` `Select` supports multiple selection.** Set `multiple: true`
|
|
596
|
+
and pass the chosen values as an array `value`; the props are typed so an
|
|
597
|
+
array value requires `multiple`. Behavior:
|
|
598
|
+
- The popup stays open while the person toggles choices with the pointer,
|
|
599
|
+
Enter, or Space, and closes only on an outside press, Escape, or focus
|
|
600
|
+
leaving.
|
|
601
|
+
- Each chosen option shows a check.
|
|
602
|
+
- The closed control summarizes the chosen labels in choice order as a short
|
|
603
|
+
localized list, or shows `placeholderText` when none are chosen.
|
|
604
|
+
- `change` / `input` report the value array, and a form submits every chosen
|
|
605
|
+
value under `name`.
|
|
606
|
+
- Re-rendering with a different `value` updates the selection even after the
|
|
607
|
+
person has interacted.
|
|
608
|
+
|
|
609
|
+
`renderSelected` and `selectedPresentation="icon-only"` stay single-value
|
|
610
|
+
only.
|
|
611
|
+
|
|
612
|
+
- **`@kerfjs/ui` `PopupMenu` now looks exactly like `Select`.** Both read one
|
|
613
|
+
shared popup contract, so a command menu beside a value list in a toolbar
|
|
614
|
+
draws the same popup. `PopupMenu` changes:
|
|
615
|
+
- its caret turns to face the open menu;
|
|
616
|
+
- its menu fades in without scaling;
|
|
617
|
+
- it takes the Select listbox's 4px inset (also under the default
|
|
618
|
+
`menuInset="standard"`);
|
|
619
|
+
- its rows use the Select option's height, padding, and reserved check
|
|
620
|
+
column, and its icons use the same 16px size and 8px gap;
|
|
621
|
+
- a keyboard-focused item takes the Select's current-option fill instead of
|
|
622
|
+
a focus ring;
|
|
623
|
+
- headings render as the Select's uppercase group titles rather than slotted
|
|
624
|
+
`h3` elements.
|
|
625
|
+
|
|
626
|
+
An open `Select` also no longer paints its trigger focus ring. The open
|
|
627
|
+
listbox shows focus, and the ring returns when it closes.
|
|
628
|
+
|
|
629
|
+
- **`@kerfjs/ui` `Catalog` exposes its sidebar and toolbar configuration.** New
|
|
630
|
+
`sidebar` prop (size, `resizable`, `separator`, `collapseMotion`,
|
|
631
|
+
`presentation`, `responsiveOverlayAt`, `compactOverlay`, and the header
|
|
632
|
+
`toolbar`'s `ToolbarConfig`) forwarded to its Workbench rail, plus
|
|
633
|
+
`mainToolbar` / `footerToolbar` `ToolbarConfig` props. `wireCatalog` takes
|
|
634
|
+
`sidebarSize` / `sidebarStorageKey` to wire a resizable sidebar. Today's
|
|
635
|
+
288px fixed sidebar and toolbar policies remain the defaults.
|
|
636
|
+
|
|
637
|
+
- **`@kerfjs/ui` composed toolbars are configurable.** `WorkbenchMainToolbar`,
|
|
638
|
+
`WorkbenchMainBottomToolbar`, and the panel `toolbar` of a Workbench panel or
|
|
639
|
+
`CollapsiblePanel` now take the `Toolbar`'s configuration: the new
|
|
640
|
+
`ToolbarConfig` type, covering `dividerSides`, `centerAlign`, `responsive`,
|
|
641
|
+
`responsiveAt`, and `safeAreaEdges`. Previously their dividers were
|
|
642
|
+
hard-coded, so for example dropping the line between a toolbar and its
|
|
643
|
+
content took CSS. Today's dividers remain the defaults. Also:
|
|
644
|
+
- `mainBottomToolbar` and panel toolbars take a `center`;
|
|
645
|
+
- panel toggles take localized `showLabel` / `hideLabel`;
|
|
646
|
+
- a Workbench panel's floating restore controls follow its
|
|
647
|
+
`restorePosition`.
|
|
648
|
+
|
|
649
|
+
- **Breaking (`@kerfjs/ui`): removed the pre-release compatibility surface.**
|
|
650
|
+
- The `DividerSides` type is gone; use `Sides`, from the renamed `sides`
|
|
651
|
+
module.
|
|
652
|
+
- `ListInsetText.horizontalOnly` and its `kui-list-inset-text--horizontal`
|
|
653
|
+
class are gone; pass `sides="rl"` instead.
|
|
654
|
+
- The legacy color tokens are gone, both as `--kui-color-*` properties and
|
|
655
|
+
as `uiColor()` names. Replace them with their canonical tokens:
|
|
656
|
+
|
|
657
|
+
| Removed | Use instead |
|
|
658
|
+
| --------------------------------------------- | ----------------------- |
|
|
659
|
+
| `border` | `neutral-border-normal` |
|
|
660
|
+
| `border-quiet` | `neutral-border-quiet` |
|
|
661
|
+
| `accent` | `brand-fill-normal` |
|
|
662
|
+
| `accent-text` | `brand-on-quiet` |
|
|
663
|
+
| `pop` | `pop-fill-quiet` |
|
|
664
|
+
| `pop-text` | `pop-on-fill` |
|
|
665
|
+
| `success`, `warning`, `danger` | `*-fill-quiet` |
|
|
666
|
+
| `success-text`, `warning-text`, `danger-text` | `*-on-quiet` |
|
|
667
|
+
|
|
9
668
|
- **New `@kerfjs/ui` `PopupMenu` (`@kerfjs/ui/popup-menu`).** It renders a
|
|
10
669
|
trigger and typed commands, headings, and dividers over the Web Awesome
|
|
11
670
|
dropdown. Put it in a `single` `ToolbarControlGroup` with `nestedDropdown`, or
|