@kerfjs/ui 5.0.0-beta.55 → 5.0.0-beta.56
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 +12 -10
- package/ai/compile-time-contracts-v1.json +2 -2
- package/ai/component-catalog-v2.json +149 -39
- package/ai/component-catalog.json +202 -51
- package/ai/public-api-signatures-v1.md +439 -97
- package/ai/skill.md +43 -40
- package/ai/webawesome-jsx-signatures-v1.md +8 -2
- package/dist/app-tab.js +1 -1
- package/dist/badge.d.ts +35 -7
- package/dist/badge.js +1 -1
- package/dist/browser/content-item.js +3 -0
- package/dist/browser/select.js +1 -0
- package/dist/catalog-resources.d.ts +12 -1
- package/dist/catalog.d.ts +14 -3
- package/dist/catalog.js +52 -43
- package/dist/catalog.js.map +1 -1
- package/dist/{chunk-WZ2GLEY4.js → chunk-2G2ZJQYK.js} +2 -14
- package/dist/chunk-2G2ZJQYK.js.map +1 -0
- package/dist/{chunk-D7GWXIAS.js → chunk-5I7QGQVT.js} +1 -1
- package/dist/chunk-5I7QGQVT.js.map +1 -0
- package/dist/{chunk-RNRUVPJ3.js → chunk-63ZWBGE3.js} +3 -2
- package/dist/chunk-63ZWBGE3.js.map +1 -0
- package/dist/{chunk-44IG7PFV.js → chunk-6CINWX2E.js} +3 -3
- package/dist/{chunk-44IG7PFV.js.map → chunk-6CINWX2E.js.map} +1 -1
- package/dist/{chunk-C2FAYFDE.js → chunk-AGXQQFKZ.js} +2 -2
- package/dist/{chunk-C2FAYFDE.js.map → chunk-AGXQQFKZ.js.map} +1 -1
- package/dist/{chunk-ATHXW6RP.js → chunk-AHEG27M7.js} +2 -2
- package/dist/{chunk-ATHXW6RP.js.map → chunk-AHEG27M7.js.map} +1 -1
- package/dist/{chunk-7GUQYHKA.js → chunk-AK67F6QN.js} +4 -1
- package/dist/chunk-AK67F6QN.js.map +1 -0
- package/dist/chunk-BFPBS5Y4.js +250 -0
- package/dist/chunk-BFPBS5Y4.js.map +1 -0
- package/dist/{chunk-5OLVUEND.js → chunk-DG6G5EPF.js} +4 -6
- package/dist/chunk-DG6G5EPF.js.map +1 -0
- package/dist/{chunk-MTM43SJO.js → chunk-G33XC2NB.js} +1 -1
- package/dist/chunk-G33XC2NB.js.map +1 -0
- package/dist/{chunk-TZ5IHWB2.js → chunk-K6UNVKDK.js} +3 -3
- package/dist/{chunk-TZ5IHWB2.js.map → chunk-K6UNVKDK.js.map} +1 -1
- package/dist/{chunk-HMQ2ZDGG.js → chunk-KROD7KZK.js} +3 -2
- package/dist/chunk-KROD7KZK.js.map +1 -0
- package/dist/{chunk-5NDQABWK.js → chunk-M7QV3IHH.js} +3 -1
- package/dist/chunk-M7QV3IHH.js.map +1 -0
- package/dist/{chunk-RNZAKSME.js → chunk-MA6Z6EIM.js} +3 -3
- package/dist/{chunk-RNZAKSME.js.map → chunk-MA6Z6EIM.js.map} +1 -1
- package/dist/chunk-MA73TA6D.js +42 -0
- package/dist/chunk-MA73TA6D.js.map +1 -0
- package/dist/{chunk-ZMBU522F.js → chunk-MEY33VWB.js} +2 -2
- package/dist/{chunk-ZMBU522F.js.map → chunk-MEY33VWB.js.map} +1 -1
- package/dist/{chunk-T22EM4ET.js → chunk-MOBRFZH3.js} +81 -31
- package/dist/chunk-MOBRFZH3.js.map +1 -0
- package/dist/chunk-MP2GDBQZ.js +50 -0
- package/dist/chunk-MP2GDBQZ.js.map +1 -0
- package/dist/{chunk-3NBL3OUQ.js → chunk-NAPE7WUS.js} +2 -2
- package/dist/chunk-NAPE7WUS.js.map +1 -0
- package/dist/{chunk-BSKEH3UC.js → chunk-NBYTIVHS.js} +2 -2
- package/dist/{chunk-BSKEH3UC.js.map → chunk-NBYTIVHS.js.map} +1 -1
- package/dist/chunk-O3EXIBEZ.js +114 -0
- package/dist/chunk-O3EXIBEZ.js.map +1 -0
- package/dist/{chunk-R754MHXX.js → chunk-PK4N5QTO.js} +7 -6
- package/dist/chunk-PK4N5QTO.js.map +1 -0
- package/dist/{chunk-EAX2GSS4.js → chunk-QQOTQTDD.js} +51 -20
- package/dist/chunk-QQOTQTDD.js.map +1 -0
- package/dist/{chunk-LZXQVUGN.js → chunk-T4UZTNZ4.js} +4 -4
- package/dist/{chunk-LZXQVUGN.js.map → chunk-T4UZTNZ4.js.map} +1 -1
- package/dist/{chunk-N7FHFNT6.js → chunk-V65EOWNF.js} +33 -5
- package/dist/chunk-V65EOWNF.js.map +1 -0
- package/dist/{chunk-GUIHMIT2.js → chunk-VNPQDCQZ.js} +3 -3
- package/dist/{chunk-GUIHMIT2.js.map → chunk-VNPQDCQZ.js.map} +1 -1
- package/dist/{chunk-5I22LMCV.js → chunk-VZOFZPSO.js} +2 -2
- package/dist/chunk-VZOFZPSO.js.map +1 -0
- package/dist/{chunk-4BIMLITI.js → chunk-WLG6SSNT.js} +5 -3
- package/dist/chunk-WLG6SSNT.js.map +1 -0
- package/dist/{chunk-NE5JAOQS.js → chunk-WOUF7YXM.js} +3 -3
- package/dist/{chunk-NE5JAOQS.js.map → chunk-WOUF7YXM.js.map} +1 -1
- package/dist/collapsible-panel.d.ts +14 -3
- package/dist/collapsible-panel.js +13 -16
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/content-item.d.ts +46 -0
- package/dist/content-item.js +4 -0
- package/dist/content-item.js.map +1 -0
- package/dist/css-values.d.ts +5 -11
- package/dist/css-values.js +1 -1
- package/dist/disclosure-arrow.d.ts +4 -1
- package/dist/disclosure-arrow.js +1 -1
- package/dist/floating-toolbar.d.ts +17 -6
- package/dist/floating-toolbar.js +1 -1
- package/dist/grid.js +1 -1
- package/dist/help-tags-register.d.ts +13 -0
- package/dist/help-tags-register.js +9 -0
- package/dist/help-tags-register.js.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +27 -26
- package/dist/list-action-row.js +1 -1
- package/dist/list-header.d.ts +14 -1
- package/dist/list-header.js +1 -1
- package/dist/list-inset-control.d.ts +1 -1
- package/dist/list-inset-control.js +1 -1
- package/dist/list-inset-text.d.ts +3 -10
- package/dist/list-inset-text.js +1 -1
- package/dist/list-item.js +1 -1
- package/dist/list.d.ts +10 -3
- package/dist/list.js +1 -1
- package/dist/nav-stack.d.ts +38 -5
- package/dist/nav-stack.js +4 -1
- package/dist/pane.d.ts +20 -2
- package/dist/pane.js +1 -1
- package/dist/{panel-toolbar-BFQxFITu.d.ts → panel-toolbar-C8sF-YZC.d.ts} +12 -1
- package/dist/popup-menu-register.js +3 -2
- package/dist/popup-menu-register.js.map +1 -1
- package/dist/popup-menu.js +1 -1
- package/dist/row.d.ts +1 -1
- package/dist/row.js +1 -1
- package/dist/select-register.js +70 -3
- package/dist/select-register.js.map +1 -1
- package/dist/select.d.ts +46 -8
- package/dist/select.js +2 -1
- package/dist/{divider-sides-BzB6rphT.d.ts → sides-BPSWde0A.d.ts} +1 -3
- package/dist/skeleton.js +1 -1
- package/dist/spacer.js +1 -1
- package/dist/split-view.d.ts +31 -3
- package/dist/split-view.js +33 -5
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.js +1 -1
- package/dist/styles/badge.css +13 -0
- package/dist/styles/catalog/components/catalog-example.css +1 -1
- package/dist/styles/catalog/components/catalog-stage.css +1 -1
- package/dist/styles/collapsible-panel.css +192 -39
- package/dist/styles/content-item.css +30 -0
- package/dist/styles/floating-toolbar.css +5 -0
- package/dist/styles/foundation.css +28 -19
- package/dist/styles/layout.css +1 -21
- package/dist/styles/list-action-row.css +13 -1
- package/dist/styles/list-inset-text.css +1 -2
- package/dist/styles/list-item.css +14 -1
- package/dist/styles/list.css +1 -1
- package/dist/styles/nav-stack.css +29 -64
- package/dist/styles/pane.css +33 -1
- package/dist/styles/popup-menu.css +78 -0
- package/dist/styles/resizable-region.css +53 -10
- package/dist/styles/select.css +39 -12
- package/dist/styles/split-view.css +21 -25
- package/dist/styles/tab-scaffold.css +36 -14
- package/dist/styles/text.css +1 -1
- package/dist/styles/token-search-field.css +4 -4
- package/dist/styles/toolbar-control-group.css +31 -12
- package/dist/styles/toolbar.css +14 -5
- package/dist/styles/value-table.css +19 -2
- package/dist/styles/webawesome.css +3 -1
- package/dist/styles/workbench.css +134 -31
- package/dist/tab-scaffold.d.ts +35 -1
- package/dist/tab-scaffold.js +50 -16
- package/dist/tab-scaffold.js.map +1 -1
- package/dist/token-search-field.d.ts +4 -2
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/toolbar.d.ts +12 -4
- package/dist/toolbar.js +1 -1
- package/dist/{types-D6vzj-VT.d.ts → types-DXAGogOS.d.ts} +39 -6
- package/dist/value-table.js +1 -1
- package/dist/webawesome.d.ts +7 -1
- package/dist/wire-catalog.d.ts +12 -1
- package/dist/wire-catalog.js +10 -2
- package/dist/wire-catalog.js.map +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +59 -22
- package/dist/workbench.js +1 -1
- package/docs/accessibility.md +116 -7
- package/docs/app-layouts.md +9 -7
- package/docs/catalog.md +17 -3
- package/docs/collapsible-panel.md +26 -7
- package/docs/component-contract.md +36 -10
- package/docs/component-selection.md +41 -40
- package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
- package/docs/design/templates/empty-state/with-action.svg +1 -1
- package/docs/design/templates/empty-state-dark.svg +1 -1
- package/docs/design/templates/empty-state.svg +1 -1
- package/docs/design/templates/list/compact-dark.svg +1 -1
- package/docs/design/templates/list/compact.svg +1 -1
- package/docs/design/templates/list/stack-dark.svg +1 -1
- package/docs/design/templates/list/stack.svg +1 -1
- package/docs/design/templates/list-action-row/default-dark.svg +1 -1
- package/docs/design/templates/list-action-row/default.svg +1 -1
- package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
- package/docs/design/templates/list-action-row/selected.svg +1 -1
- package/docs/design/templates/list-action-row-dark.svg +1 -1
- package/docs/design/templates/list-action-row.svg +1 -1
- package/docs/design/templates/list-dark.svg +1 -1
- package/docs/design/templates/list-item/default-dark.svg +1 -1
- package/docs/design/templates/list-item/default.svg +1 -1
- package/docs/design/templates/list-item/selected-dark.svg +1 -1
- package/docs/design/templates/list-item/selected.svg +1 -1
- package/docs/design/templates/list-item/trailing-dark.svg +1 -1
- package/docs/design/templates/list-item/trailing.svg +1 -1
- package/docs/design/templates/list-item-dark.svg +1 -1
- package/docs/design/templates/list-item.svg +1 -1
- package/docs/design/templates/list.svg +1 -1
- package/docs/design/templates/value-table/details-dark.svg +1 -1
- package/docs/design/templates/value-table/details.svg +1 -1
- package/docs/design/templates/value-table-dark.svg +1 -1
- package/docs/design/templates/value-table.svg +1 -1
- package/docs/design-philosophy.md +1 -1
- package/docs/examples/command-palette-adapter.tsx +3 -2
- package/docs/layout.md +69 -29
- package/docs/nav-stack.md +45 -1
- package/docs/recipes.md +4 -3
- package/docs/split-view.md +46 -2
- package/docs/tab-scaffold.md +50 -1
- package/docs/type-contracts.md +17 -17
- package/docs/ux-demo.md +2 -2
- package/docs/webawesome-theme.md +1 -2
- package/docs/workbench.md +45 -9
- package/llms.txt +32 -4
- package/package.json +13 -2
- package/ux-demo/recipes/collapsible-sidebar.tsx +3 -2
- package/ux-demo/recipes/navigation-sidebar.tsx +3 -2
- package/ux-demo/recipes/navigation-stack.tsx +3 -2
- package/dist/chunk-3NBL3OUQ.js.map +0 -1
- package/dist/chunk-4BIMLITI.js.map +0 -1
- package/dist/chunk-5I22LMCV.js.map +0 -1
- package/dist/chunk-5NDQABWK.js.map +0 -1
- package/dist/chunk-5OLVUEND.js.map +0 -1
- package/dist/chunk-7GUQYHKA.js.map +0 -1
- package/dist/chunk-D7GWXIAS.js.map +0 -1
- package/dist/chunk-DQA5FMOX.js +0 -85
- package/dist/chunk-DQA5FMOX.js.map +0 -1
- package/dist/chunk-EAX2GSS4.js.map +0 -1
- package/dist/chunk-HMQ2ZDGG.js.map +0 -1
- package/dist/chunk-MTM43SJO.js.map +0 -1
- package/dist/chunk-N7FHFNT6.js.map +0 -1
- package/dist/chunk-R754MHXX.js.map +0 -1
- package/dist/chunk-RNRUVPJ3.js.map +0 -1
- package/dist/chunk-T22EM4ET.js.map +0 -1
- package/dist/chunk-WZ2GLEY4.js.map +0 -1
- package/dist/chunk-XD7DWYXG.js +0 -34
- package/dist/chunk-XD7DWYXG.js.map +0 -1
package/ai/skill.md
CHANGED
|
@@ -97,11 +97,11 @@ Quick routing:
|
|
|
97
97
|
| Page, panel, or dialog heading | `Toolbar` + `ToolbarText` | Use a direct extra-large `ToolbarText`, optional grouped icon, grouped trailing controls, and app-owned supporting copy below. |
|
|
98
98
|
| Exclusive choice | `TabBar`, `SegmentedControl`, or `Select` | Tabs switch tabpanels; segments expose a few choices; Select handles a longer value list. |
|
|
99
99
|
| Status or absent content | `StateBanner` or `EmptyState` | `StateBanner` accepts an optional terse, tone-tinted `badge` beside its title for a count or compact status; do not rely on color alone. Web Awesome callouts suit ecosystem content; toasts are transient and never the only record of important state. |
|
|
100
|
-
| Compact metadata | `Badge` | Configure status, count, category, or metadata presentation with tone, appearance, shape, and size props. Do not build badges with application CSS or use them as interactive controls.
|
|
100
|
+
| Compact metadata | `Badge` | Configure status, count, category, or metadata presentation with tone, appearance, shape, and size props. For new content without a count use `size="dot"` (no children) with a `label` or `ariaHidden`. Do not build badges with application CSS or use them as interactive controls. |
|
|
101
101
|
| Loading placeholder | a component's `placeholder` prop, or `Skeleton` | Set `placeholder` on a value-bearing component (`Select`, `ListHeader`, `ListItem`, `ValueTableRow`, `SegmentedControl`, `StateBanner`, `AppTab`, `ToolbarText`, `ListActionRow`) to render its real chrome with value slots as subtle **unanimated** skeletons and interactivity disabled; only unknown values change, so a placeholder is never dimmed or styled as disabled (a control whose own `disabled` / `trailingActionDisabled` / choice `disabled` / `actionDisabled` prop is set keeps its live disabled tone via the reserved `data-kui-disabled` marker) — do not hand-rebuild an empty state. Component-drawn chrome stays live too: a `ListItem` or `ListActionRow` placeholder keeps its `busy` spinner while its `status` text (and a `ListItem`'s `trailing` content, always a value) becomes a skeleton. `Skeleton` is the standalone block for a custom slot. A known `AppTab` that is still opening takes `pending` instead: it keeps its name (quiet, and as its accessible name) and the same geometry, shows your `trailing` spinner, and stays selectable (close and drag disabled) so your panel can show placeholders until it loads. Use `LoadingSpinner` for known busy activity, not a skeleton; never animate a skeleton; prefer it over `wa-skeleton`. |
|
|
102
102
|
| Adjustable or asymmetric columns | `ResizableRegion` or CSS grid | Use `ResizableRegion` only for a user-operable controlled separator, CSS grid for asymmetric/intrinsic tracks, and `Grid` for static equal-width tracks. |
|
|
103
103
|
| Structured or ordinary search | `TokenSearchField` or an input | Use token search only when text and ordered filter chips form one editor. |
|
|
104
|
-
| Command menu | `PopupMenu` | Put it in a `single` `ToolbarControlGroup` with `nestedDropdown`; register `@kerfjs/ui/popup-menu/register` once. Use `Select` when the user chooses a persistent value.
|
|
104
|
+
| Command menu | `PopupMenu` | Put it in a `single` `ToolbarControlGroup` with `nestedDropdown`; register `@kerfjs/ui/popup-menu/register` once. It draws the same popup as `Select` (caret, rows, check column, group titles); do not restyle either to match the other. Use `Select` when the user chooses a persistent value. |
|
|
105
105
|
| Key/value facts | `ValueTable` with `ValueTableRow` | Use rows with `icon` only when the optional 24px leading visual adds context. |
|
|
106
106
|
| Command search | Application-local semantics using shared layout vocabulary | No runtime component or production recipe is exported; the app owns behavior, policy, and copy. |
|
|
107
107
|
|
|
@@ -142,58 +142,61 @@ Hard rules:
|
|
|
142
142
|
3. Keep state, product copy, persistence, and domain mappings in the application. Do not add product-specific actions or fields to a generic component. Put ListItem/ListActionRow/ListHeader/AppTab domain `data-*` metadata in `rootAttributes`; use ListActionRow `trailingActionAttributes` and ListHeader `triggerAttributes` only for domain `data-*` or native popover target/action and `aria-controls`/`aria-haspopup`. These slots do not replace component-owned action, item/tab identity, selection, disclosure, naming, disabled, icon, or role semantics.
|
|
143
143
|
4. Wire `data-action` hooks from one stable root with `delegate()` or `delegateActions()` and retain disposers.
|
|
144
144
|
5. Use the opinionated `--kui-color-*` semantic ramps and component-level override properties. Use `pop` only for attractive non-status emphasis such as featured, novel, or celebratory content; never substitute it for info, success, warning, or danger. Override tokens at the narrowest useful scope and prefer equivalent props/tokens. `publicClasses` exist for diagnostics, tooling, tests, and package-owned composition, not application styling. Never target a Kerf or Web Awesome component from application CSS; add missing configuration to the owner.
|
|
145
|
-
6. `Select` is pure until the app explicitly imports `@kerfjs/ui/select/register`;
|
|
146
|
-
7. Decorative icons are hidden; controls are named; focus is visible; state never relies on color alone; reduced motion and increased contrast remain usable. `DisclosureArrow` defaults to an 18px root-scaled visual
|
|
145
|
+
6. `Select` is pure until the app explicitly imports `@kerfjs/ui/select/register`; once registered, an icon-only `Select` (and, via `@kerfjs/ui/popup-menu/register`, an icon-only `PopupMenu`) shows an `aria-hidden` help tag repeating its accessible name (and a Select's current choice) on hover and keyboard focus, hidden while its popup is open — never add a `title` or your own tooltip to these triggers. The same installer tags an icon-only `<button>` or link in a `ToolbarControlGroup` named by `aria-label` (no visible text, no `title`); a toolbar with such buttons but no registered Select or PopupMenu imports `@kerfjs/ui/help-tags/register` once. Name those buttons with `aria-label`, not `title`: a `title` keeps the browser tooltip and skips the tag. Do not import Web Awesome's full registration bundle. When an app writes direct `wa-*` JSX, add `import type {} from '@kerfjs/ui/webawesome'` for the catalog-supported intrinsic-element declarations, import the CSS-only `@kerfjs/ui/webawesome.css` theme once, and keep importing individual Web Awesome component modules so their JavaScript remains tree-shakeable. The type boundary emits no code and registers nothing. Pass icon-bearing `choices` and `renderSelected` content normally: `Select` preserves its slotted option icons across Kerf rerenders and keys selected content by the controlled value, so app wrappers must not add competing morph-control attributes. Give it a visible `label` or an `ariaLabel` for a visually hidden name; the nonempty visible label takes precedence and the package names the actual shadow combobox, including `renderSelected`, without adding label geometry. Use `hint` for persistent supporting text below the control and `placeholderText` only for the empty value inside the closed control; loading placeholders retain the hint. For any number of choices set `multiple` with an array `value`: the popup stays open while choices toggle and closes on an outside press or Escape, the closed control summarizes the chosen labels, and `change` reports the value array; do not add app code to keep it open or to render tags. For a toolbar filter menu, add `selectedPresentation="icon-only"` plus a fixed `triggerIcon` (such as a funnel `LucideIcon`): the trigger shows that icon, a count badge while any choice is chosen, and ends its accessible name with the chosen labels ("Filter by label: Bug, Docs"); do not render your own count badge or rewrite the name. When testing name or description, target the element with the `combobox` role rather than the `wa-select` wrapper. Web Awesome's hint attribute and explicit hint slot both describe that shadow combobox across supported engines; do not add an `aria-description` mirror or application shadow-DOM patch.
|
|
146
|
+
7. Decorative icons are hidden; controls are named; focus is visible; state never relies on color alone; reduced motion and increased contrast remain usable. `DisclosureArrow` defaults to an 18px root-scaled visual; size it with its typed `size` prop (`size={remify(24)}`); it never becomes the interaction or accessible-name owner. Put it in an owning native control with a stable accessible name and controlled `aria-expanded`. `ListHeader` toggle mode supplies it when `actionIcon` is omitted, but the app must reveal matching content; a custom icon replaces it. Author replacement icon content facing right before its configured direction transform is applied. Direction changes take the shortest rotation path, with counterclockwise chosen for a 180-degree closed-to-open tie. Kerf `Select` separately keeps its intrinsic Web Awesome expand glyph at `--kui-disclosure-icon-scale: .5`.
|
|
147
147
|
8. `ResizableRegion` uses `wireResizableRegions()` for Arrow, Shift+Arrow, Home/End, and pointer behavior. The app owns size persistence. `handleIcon` replaces decorative dormant glyph content only. Set `responsiveFillAt="narrow"` or `"compact"` when a responsive composition exposes the pane as its only inline track; the component fills that track and hides its dormant separator. For compact overlays, set `presentation="overlay"` and the overlay maximum variables. A lone child (normally a `Pane`) fills the region and owns scrolling; several children stack at natural height. Do not add descendant width/height overrides.
|
|
148
148
|
9. Use controlled `SegmentedControl` for a small exclusive choice set. Select `appearance="toolbar"` when nesting it inside `ToolbarControlGroup`; use rounded or pill shapes for standalone contexts. The package derives inset hover/selection radii from the owning outer shape through `--kui-layout-highlight-inset`; do not restyle an inner highlight to an independent radius. Handle its action, update `value`, keep meaningful choice labels, and preserve every enabled native button in sequential Tab order.
|
|
149
149
|
10. Compose `AppTab` inside controlled `TabBar`; call `wireTabBars()` once and retain its disposer. It owns same-bar drag mechanics, including proximity-based horizontal edge autoscroll, while the app applies `onReorder` and owns order, selection, panels, close policy, routing, and persistence. For a tab-local action followed by a separately pinned workspace action, set `trailingPlacement="adjacent"`, put the local action in `trailing`, and the workspace action in `end`; TabBar owns the shrinking/scrolling tabs and fixed action geometry, so do not add descendant flex overrides. Arrow/Home/End select on move by default and restore focus by bar/tab identity when controlled activation replaces the strip; pass `activation: 'manual'` (or `TabBar activation="manual"` per strip) so arrow keys move focus only and the user selects with Enter/Space/click — use it when selecting a tab is a heavy action. Use runtime-filtered `rootAttributes` for domain metadata and keep an optional `closeIcon` decorative and noninteractive.
|
|
150
|
-
11. Use token-controlled `TokenSearchField` when free text and removable structured filters share one editor. Editable text stays DOM-owned between token changes; to replace the text programmatically (reseeding a persistent dialog's field on reopen, applying a saved search) change the field's `revision` along with `query`, never re-key a wrapper. The leading icon, first text line, clear action, and trailing slot share one fixed row when content wraps. The app owns parsing and suggestions; call `readTokenSearchField()` on input, empty `textContent` on clear, use `placeTokenSearchCaret()` after explicit controlled focus changes, and call `wireTokenSearchFields()` once so Enter submits without inserting a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. Enable `collapsible` for an animated iconic closed state, standalone or inside `ToolbarControlGroup`; the field keeps text or tokens expanded, and `wireTokenSearchFields()` manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse). Bind the field's `expanded` to the returned handle's `expanded(id)` signal or adopt your own via `collapsible.signals`; opt a behavior out only when the app must own it. Do not hand-roll the open handler, the focusout collapse, a clear-button mousedown guard, or reopening after controlled clear or Select All deletion — the helper keeps the replacement editor open and focused. Clear and deletion restoration finish at the replacement mutation checkpoint before the next input task, so shortcut-letter typing cannot escape to the page and delayed animation frames cannot overwrite a later selection. Persist both query and tokens returned by the DOM read. For real-app adoption it also provides a focusout keep-open exception (`data-token-search-keep-open` regions or `collapsible.keepOpenOn(target)`, so a sibling suggestions/date/help surface does not collapse an empty field), opt-in atomic-chip keyboard (`keyboard`: Backspace/Delete remove the adjacent chip via `onRemoveToken`, ArrowRight moves the caret past a trailing chip), and an `onEdit({id, editor, event})` input callback (the `InputEvent` lets you gate on `inputType`/`data`) — reach for these instead of re-adding hand-rolled keydown/input handlers around the field.
|
|
150
|
+
11. Use token-controlled `TokenSearchField` when free text and removable structured filters share one editor. Editable text stays DOM-owned between token changes; to replace the text programmatically (reseeding a persistent dialog's field on reopen, applying a saved search) change the field's `revision` along with `query`, never re-key a wrapper. The leading icon, first text line, clear action, and trailing slot share one fixed row when content wraps. The app owns parsing and suggestions; call `readTokenSearchField()` on input, empty `textContent` on clear, use `placeTokenSearchCaret()` after explicit controlled focus changes, and call `wireTokenSearchFields()` once so Enter submits without inserting a line break and keyboard chip deletion restores focus plus the text-relative caret after controlled replacement. `clearIcon` replaces the clear action's glyph (its `clearLabel` still names it). Enable `collapsible` for an animated iconic closed state, standalone or inside `ToolbarControlGroup`; the field keeps text or tokens expanded, and `wireTokenSearchFields()` manages the transient expand/collapse/focus by default (activate to reveal + focus, Escape or empty blur to collapse). Bind the field's `expanded` to the returned handle's `expanded(id)` signal or adopt your own via `collapsible.signals`; opt a behavior out only when the app must own it. Do not hand-roll the open handler, the focusout collapse, a clear-button mousedown guard, or reopening after controlled clear or Select All deletion — the helper keeps the replacement editor open and focused. Clear and deletion restoration finish at the replacement mutation checkpoint before the next input task, so shortcut-letter typing cannot escape to the page and delayed animation frames cannot overwrite a later selection. Persist both query and tokens returned by the DOM read. For real-app adoption it also provides a focusout keep-open exception (`data-token-search-keep-open` regions or `collapsible.keepOpenOn(target)`, so a sibling suggestions/date/help surface does not collapse an empty field), opt-in atomic-chip keyboard (`keyboard`: Backspace/Delete remove the adjacent chip via `onRemoveToken`, ArrowRight moves the caret past a trailing chip), and an `onEdit({id, editor, event})` input callback (the `InputEvent` lets you gate on `inputType`/`data`) — reach for these instead of re-adding hand-rolled keydown/input handlers around the field.
|
|
151
151
|
12. Demo work uses public production component subpaths and their browser-selected CSS. Focused demos and recipes have no local stylesheet, inline style, or styling-only class; use public components and `CatalogExample.viewport` for unavoidable specimen constraints (a recipe declares its frame through its exported `presentation`). When a recipe needs a capability no component offers, report the component gap instead of adding CSS. Use exactly three demo kinds: a `component` is one component with variants/states, a `composition` is a focused container of components commonly used together, and a `recipe` is the recommended production approach to a task. Organize catalog routes into product-important functional groups; within each group put all components before compositions, order each kind by importance with display-name alphabetical tie-breaking, and put recipes after every component/composition group. Show `Composition` as an entry tag instead of repeating the word in its name. Give every public visual component its own category-grouped catalog route; list themed third-party components under a clearly labeled collapsible ecosystem section, with a focused route for each. For the complete taxonomy, focused/composition decision, `CatalogExample` / `CatalogExampleStack` row/group nesting, exact specimen selection, geometry-overlay legend and exclusions, skip-marker behavior, and metadata ownership, follow the single [Catalog demo authoring contract](../docs/catalog.md#catalog-demo-authoring-contract), discovered machine-readably through [`catalog-authoring.json`](./catalog-authoring.json). Never infer structure from private `kui-catalog-*` classes. Project each component's deterministic repository-relative demo source and existing documentation path so the detail can expose `View demo source` and `Read guidance` links without a runtime export; also derive first-party component implementation paths from their canonical browser imports, and label Web Awesome documentation as Kerf integration guidance. Declare direct `uses` relationships so `Used by` stays derivable, and theme shell chrome through the same semantic tokens as the stage instead of drawing a substitute. Enable `wireCatalog({ revealSelection: true })` for long desktop sidebars so controlled selections remain visible without moving focus; keep its default compact media guard unless compact scrolling is an explicit product behavior, and use `revealCatalogEntry` for an initial deep link.
|
|
152
152
|
13. The Web Awesome theme makes Tooltip and Popover arrowless by default. Keep that default unless a pointer materially clarifies the anchor; opt back in with `--wa-tooltip-arrow-size`, `--kui-wa-popover-arrow-size`, or a popover's public `--arrow-size`, and use `without-arrow` when local no-arrow intent should survive theme changes. Its non-field chrome uses `--kui-wa-control-inset` (8px controls/items), `--kui-wa-surface-margin` / `--kui-wa-surface-inset` (8px around/inside Accordion, Card, Details, Callout, and Include), and `--kui-wa-container-inset` (16px Tab Panel content and framed disclosure regions). Plain Details use the 8px surface inset on the trigger and keep the body flush; plain Accordion items use it on both trigger and body. Outlined and sunken Details use the 16px container inset on the trigger and 8px on the body; framed Accordion items use 16px on both. Dialog maps its body to the 8px surface tier and its footer to the 16px container tier independently of `--spacing`. Override those tiers instead of restyling individual parts. Filled badges use each variant's quiet fill with normal text so their compact copy stays WCAG-AA readable; override the pair with `--kui-wa-badge-filled-background` / `--kui-wa-badge-filled-foreground`, not host selectors. When a dialog provides another dismissal affordance, put `hide-actions` on the `wa-dialog` host to hide its directly exported `header-actions` part; never chain `::part()` selectors through internal parts. Checkbox and Radio Group option regions, the Color Picker trigger, and Slider's complete interactive region receive the shared 8px logical inline outer inset because those controls have no bordered field shell. Known Date field captions and bordered text-like field hints align with values at the 9px border-plus-padding inset. OTP Input's label uses the same uppercase xs/650 treatment as other field labels, and both its label and hint use that 9px inset.
|
|
153
153
|
14. Treat the complete Web Awesome catalog as support coverage, not a recommendation list. Its UX sidebar marks superseded and exceptional entries `Discouraged`. Consider Popup when it replaces custom anchored positioning. Prefer Kerf `Select` over direct Dropdown/Dropdown Item/Select/Option composition, `SegmentedControl` over Button Group, `TabBar` or `SegmentedControl` over Web Awesome Tabs, `LucideIcon` over Web Awesome Icon, and `ResizableRegion` over Split Panel. Use Tree/Tree Item, Animated Image, and Comparison only for a specific required behavior; avoid Zoomable Frame.
|
|
154
|
-
15. Build sidebars, main areas, inspectors, and dialogs from `@kerfjs/ui/layout.css`: an unpadded `.kui-pane`, optional `.kui-pane__toolbar`, one scrolling `.kui-pane__content`, and optional `.kui-pane__footer`. Use `Row` instead of a hand-written flex-row wrapper, `List` instead of a hand-written flex-column wrapper, and `Grid` instead of repeated equal-track CSS when children need a fixed positive count of evenly distributed columns. `Row` defaults to left/full with an `xs` gap and no wrapping; Grid defaults to an `xs` gap and keeps each track at `minmax(0, 1fr)`; List retains full/top and zero-gap defaults. The app owns Grid's responsive column-count changes. When a Row, List, or Grid is the layout root of a definite-height parent (an app root or a fixed-height frame), pass `fill` so it takes that full height, and put route/test/recipe `data-*` markers in its `rootAttributes`; never wrap a layout in a frame `Pane` just for height or a root attribute, because its content slot becomes a scroll owner that never scrolls. Inside a flex layout use `flex`, not `fill`. Use `List` when rows or sections also need flex growth, one vertical scroll owner, or physical-edge dividers. `dividerSides`, `textInsets`, and `controlInsets` use canonical physical `t`/`r`/`b`/`l` order; text geometry wins where text and control selections overlap. `ListInsetText` and `ListInsetControl` inset all four sides by default and accept the same `sides` grammar for selective edges. A dialog body is generally a `List`: set `DialogSurface bodyInset="none"` when its list children own geometry, and wrap bare dialog prose in `ListInsetText` so it aligns with bordered siblings without a second inset. Add `.kui-content` for 24px major vertical separation and
|
|
154
|
+
15. Build sidebars, main areas, inspectors, and dialogs from `@kerfjs/ui/layout.css`: an unpadded `.kui-pane`, optional `.kui-pane__toolbar`, one scrolling `.kui-pane__content`, and optional `.kui-pane__footer`. Use `Row` instead of a hand-written flex-row wrapper, `List` instead of a hand-written flex-column wrapper, and `Grid` instead of repeated equal-track CSS when children need a fixed positive count of evenly distributed columns. `Row` defaults to left/full with an `xs` gap and no wrapping; Grid defaults to an `xs` gap and keeps each track at `minmax(0, 1fr)`; List retains full/top and zero-gap defaults. The app owns Grid's responsive column-count changes. When a Row, List, or Grid is the layout root of a definite-height parent (an app root or a fixed-height frame), pass `fill` so it takes that full height, and put route/test/recipe `data-*` markers in its `rootAttributes`; never wrap a layout in a frame `Pane` just for height or a root attribute, because its content slot becomes a scroll owner that never scrolls. Inside a flex layout use `flex`, not `fill`. Use `List` when rows or sections also need flex growth, one vertical scroll owner, or physical-edge dividers. `dividerSides`, `textInsets`, and `controlInsets` use canonical physical `t`/`r`/`b`/`l` order; text geometry wins where text and control selections overlap. `ListInsetText` and `ListInsetControl` inset all four sides by default and accept the same `sides` grammar for selective edges. A dialog body is generally a `List`: set `DialogSurface bodyInset="none"` when its list children own geometry, and wrap bare dialog prose in `ListInsetText` so it aligns with bordered siblings without a second inset. Add `.kui-content` for 24px major vertical separation and render each ordinary surface-like child as `ContentItem` (`@kerfjs/ui/content-item`) for a child-owned 8px inline margin, 1px border, 8px padding, and 12px radius. Choose `frame="framed"` (neutral border in the always-reserved 1px, same geometry) only for a real distinction and `shape="pill"` for 22px; do not hand-write `<div class="kui-content-item">` or its modifier classes. There is no filled variant: a lowered filled surface is `SunkenPanel`. Do not pad pane shells, duplicate item geometry in wrappers, or create nested scroll owners.
|
|
155
155
|
15a. Use `Text` from `@kerfjs/ui/text` for ordinary headings, paragraphs, and inline secondary text outside toolbar title zones. Omit `variant` for the default `p`, pass `h1`–`h6` according to the document outline, or use `variant="span"` inside a row, label, or table cell; ordinary native attributes pass through. Express supporting, error, compact, or code text with the independent finite `tone="quiet|danger"`, `size="compact"`, and `font="monospace"` props instead of global utility classes or appearance-only heading choices. Block Text variants own a transparent 1px border and the standard item padding; `span` owns no box geometry. Keep toolbar identity/title content on `ToolbarText` and list-aligned bare prose on `ListInsetText`. `ToolbarText` below `xlarge` is intentionally quiet identity text with no tone option; a normal-color subsection title is `Text` with an `h2`–`h6` variant (or `ListHeader` for a list or menu section), never a recolored `ToolbarText`.
|
|
156
156
|
16. Keep a visible collapsible pane's collapse control in its own toolbar. When hidden, move its restore control into the adjacent main toolbar on the corresponding logical edge: leading for an inline-start sidebar and trailing for an inline-end inspector. Collapse the pane completely; do not preserve an empty icon-only rail.
|
|
157
|
-
17. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. Put identity/title text directly in the zone as `ToolbarText`; wrap interactive controls and icon tiles in `ToolbarControlGroup`. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a group. Configure zone behavior with `Toolbar.centerAlign` and its `responsive` / `responsiveAt` policy instead of selecting toolbar zone classes. No policy clips an action: trailing (and stacked center) zones wrap whole groups onto another row, so never hide or shrink a toolbar control to make it fit; use `responsive="wrap"` for a heading whose title must stay whole, and move genuinely lower-priority commands into an app-owned "More" popup menu. For an ordinary icon/action control inside a group, use a plain `<button>` (the group styles `> button` fully) — that is the default; reach for `wa-button` only when you need a Web Awesome feature. A popup menu in a toolbar is a `PopupMenu` (`@kerfjs/ui/popup-menu`, registered once via `@kerfjs/ui/popup-menu/register`) in a `single` group with `nestedDropdown` and a `menuInset`: give it typed `items` (commands with `action`s, `heading`s, `divider`s) and a trigger `icon` plus visible `text` or an accessible `label`; never hand-write `wa-dropdown` / `wa-dropdown-item` markup for a menu. Let the group size that trigger from its icon, localized text, and caret; do not force a width on the menu or button. For a compact mixed group, keep the standard item padding and raised selected chrome; the selected item paints over the outer border without an internal separator. For an avatar control, set `content="avatar"` and `avatarImage` on the group instead of inserting an `img`: a lone control paints the contained image on the outer group and a multi-button group paints it only on the pressed selection highlight. Compose panel/dialog/page headings with a direct extra-large `ToolbarText`, optional grouped icon, grouped trailing controls, and app-owned supporting copy below. A non-interactive heading icon is an icon tile: put the `LucideIcon` directly in its `ToolbarControlGroup` (it takes an icon button's 40px slot and centers), never inside a `<button>` that does nothing. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Each zone item sits in the toolbar's top 44px control band, centered by its own size; a taller item (a wrapped title, a second trailing row) starts at the band's top and grows down without moving the others, so a wrapped `ToolbarText` keeps trailing controls beside its first line — do not add alignment CSS for this. Split dormant and interactive regions: `ListHeader` fills the available inline width by default; set `width="content"` to shrink-wrap while retaining its normal geometry, or `inline` when it belongs in surrounding text without outer margin, border, or padding. It keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-list-header-action-icon-size` inside a fitted 36px hover square whose transparent hit layer keeps the pointer target at least 44px (44px wide by the 36px row in `density="compact"`); never shrink the target to match the visual, and never force the button to 44px. Pass every non-negative safe-integer section quantity through `count` with a localized full spoken `countLabel`; never concatenate it into `label` or put a number in `badge`. `ListItem.trailing` is dormant; use `ListActionRow` when primary and trailing actions need sibling native buttons. Its `label`, `icon`, and `trailingActionIcon` slots are also dormant and cannot contain controls. Let panes relocate at narrow widths instead of shrinking targets.
|
|
157
|
+
17. The only direct children of a `Toolbar` zone (`leading`/`center`/`trailing`) are `ToolbarText` (identity/title text) and `ToolbarControlGroup`; never drop bare buttons, inputs, links, or arbitrary markup straight into a zone. Put identity/title text directly in the zone as `ToolbarText`; wrap interactive controls and icon tiles in `ToolbarControlGroup`. `SegmentedControl`, `Select`, a collapsible `TokenSearchField`, and Web Awesome controls all live inside a group. Configure zone behavior with `Toolbar.centerAlign` and its `responsive` / `responsiveAt` policy instead of selecting toolbar zone classes. No policy clips an action: trailing (and stacked center) zones wrap whole groups onto another row, and the center zone never compresses below its content (a leading title too long for the row truncates with an ellipsis instead), so never hide or shrink a toolbar control to make it fit; use `responsive="wrap"` for a heading whose title must stay whole, and move genuinely lower-priority commands into an app-owned "More" popup menu. For an ordinary icon/action control inside a group, use a plain `<button>` (the group styles `> button` fully) — that is the default; reach for `wa-button` only when you need a Web Awesome feature. A popup menu in a toolbar is a `PopupMenu` (`@kerfjs/ui/popup-menu`, registered once via `@kerfjs/ui/popup-menu/register`) in a `single` group with `nestedDropdown` and a `menuInset`: give it typed `items` (commands with `action`s, `heading`s, `divider`s) and a trigger `icon` plus visible `text` or an accessible `label`; never hand-write `wa-dropdown` / `wa-dropdown-item` markup for a menu. Let the group size that trigger from its icon, localized text, and caret; do not force a width on the menu or button. For a compact mixed group, keep the standard item padding and raised selected chrome; the selected item paints over the outer border without an internal separator. For an avatar control, set `content="avatar"` and `avatarImage` on the group instead of inserting an `img`: a lone control paints the contained image on the outer group and a multi-button group paints it only on the pressed selection highlight. Compose panel/dialog/page headings with a direct extra-large `ToolbarText`, optional grouped icon, grouped trailing controls, and app-owned supporting copy below. A non-interactive heading icon is an icon tile: put the `LucideIcon` directly in its `ToolbarControlGroup` (it takes an icon button's 40px slot and centers), never inside a `<button>` that does nothing. A group remains 44px outside (`calc(2px + remify(42px))`) when its border/background are transparent; use 8px between groups and inside items. Each zone item sits in the toolbar's top 44px control band, centered by its own size; a taller item (a wrapped title, a second trailing row) starts at the band's top and grows down without moving the others, so a wrapped `ToolbarText` keeps trailing controls beside its first line — do not add alignment CSS for this. Split dormant and interactive regions: `ListHeader` fills the available inline width by default; set `width="content"` to shrink-wrap while retaining its normal geometry, or `inline` when it belongs in surrounding text without outer margin, border, or padding. It keeps its label and mutually exclusive semantic count or non-count `badge` together, with an independent logical-end action unless disclosure mode makes the title cluster the button. Its action visual defaults to 18px through `--kui-list-header-action-icon-size` inside a fitted 36px hover square whose transparent hit layer keeps the pointer target at least 44px (44px wide by the 36px row in `density="compact"`); never shrink the target to match the visual, and never force the button to 44px. Pass every non-negative safe-integer section quantity through `count` with a localized full spoken `countLabel`; never concatenate it into `label` or put a number in `badge`. `ListItem.trailing` is dormant; use `ListActionRow` when primary and trailing actions need sibling native buttons. Its `label`, `icon`, and `trailingActionIcon` slots are also dormant and cannot contain controls. Let panes relocate at narrow widths instead of shrinking targets.
|
|
158
158
|
18. When a recurring concept has no matching export or production recipe, keep its semantics in a thin application adapter while reusing the public layout vocabulary. The composer recipe uses one visible form surface, toolbar title/supporting-copy ids, shared 8px field/action gutters, 24px major rhythm, and a conditional StateBanner as its only nested semantic surface; do not turn every section into a card or double-inset intrinsically bordered controls. The application-local `../docs/examples/command-palette-adapter.tsx` is reference source for one such missing concept, not an `@kerfjs/ui` runtime export or catalog recipe. The application owns its registration, ranking, history, permissions, availability, shortcut policy, focus policy, dispatch, and copy. If a missing concept recurs across products, open an upstream component or recipe request.
|
|
159
159
|
19. Compose `ValueTable` from typed `ValueTableRow` entries instead of handwritten `dt`/`dd` wrappers. Pass `icon` for the optional 24px leading visual; the row owns 8px of root-scaled top and bottom padding, the 8px iconless or 40px icon-bearing separator start, and the common 8px right inset.
|
|
160
160
|
20. Use the standard direct `Toolbar` composition for a panel, dialog, or page heading, not a private wrapper or custom heading row. Put an optional icon in a `ToolbarControlGroup`, the title directly in the leading zone as extra-large `ToolbarText`, and actions in a trailing group. Set `headingLevel` for page/section landmarks. Supporting copy stays below the toolbar as app-owned content. The app owns modal behavior, focus, dismissal, command policy, ids, and action handling.
|
|
161
|
-
21. Space with the official five-step scale, picked by how connected two elements are — not by eye. `0` `--kui-space-none` = no separation (one unit); `4px` `--kui-space-2xs` = very minor air on a connected cluster; `8px` `--kui-space-xs` = standard, between elements within a group; `16px` `--kui-space-m` = minor, between homogeneous groups; `24px` `--kui-space-l` = major, between heterogeneous groups (the `.kui-content` rhythm). The 8px-vs-24px distinction is inside-a-group vs between-major-differing-regions. `--kui-space-s` (12px) and `--kui-space-xl` (32px) are off-scale exceptions. For `Row.gap`, `Grid.gap`, or `List.gap`, pass a finite shorthand such as `gap="xs"` or `gap="m"`; use `Spacer` for one intentional typed empty width/height or a flexible main-axis gap. When one token is insufficient, import typed builders from the CSS-free `@kerfjs/ui/css-values` subpath. Preserve property grammar: lengths for gap/Spacer/Skeleton geometry, `flex()` for Row/Grid/List flex, and a foreground color for choice icons: `uiColor()` with a `*-on-*` or text token (`success-on-quiet`, `text-quiet`) or `foregroundColorVar()`.
|
|
162
|
-
22. Pick a whole-screen layout from the opt-in, tree-shakeable subpaths by data + interaction + device, and derive responsiveness from `@kerfjs/ui/device-class`'s `deviceClass()` (`compact` = handset or portrait tablet = one pane at a time). Simple/flat → `NavStack` with one entry (single pane), plus `TabScaffold` (`@kerfjs/ui/tab-scaffold`, iOS bottom tabs, each tab its own `NavStack`) for 2–5 co-equal sections on `compact
|
|
163
|
-
23. Layouts and `Pane` are safe-area aware without configuration: backgrounds, separators, and dividers paint through a device's unsafe areas (a collapsible rail or drawer grows by the inset it docks to), content is padded only on the sides a region actually reaches, and block insets are scroll padding inside the scroller so first/last content can always be scrolled clear. Interior edges get nothing, and a collapsed rail hands its edge back to the center. Never add your own `env(safe-area-inset-*)` padding around a kerf layout or pane — that double-insets. In an app-owned arrangement of panes, pass `Pane.safeAreaEdges` (the sides that pane reaches; `[]` opts out); give an app bar or bottom bar that sits at a screen edge outside a Pane header or footer `Toolbar.safeAreaEdges` for the sides it reaches (for example `['block-start', 'inline-start', 'inline-end']`);
|
|
161
|
+
21. Space with the official five-step scale, picked by how connected two elements are — not by eye. `0` `--kui-space-none` = no separation (one unit); `4px` `--kui-space-2xs` = very minor air on a connected cluster; `8px` `--kui-space-xs` = standard, between elements within a group; `16px` `--kui-space-m` = minor, between homogeneous groups; `24px` `--kui-space-l` = major, between heterogeneous groups (the `.kui-content` rhythm). The 8px-vs-24px distinction is inside-a-group vs between-major-differing-regions. `--kui-space-s` (12px) and `--kui-space-xl` (32px) are off-scale exceptions. For `Row.gap`, `Grid.gap`, or `List.gap`, pass a finite shorthand such as `gap="xs"` or `gap="m"`; use `Spacer` for one intentional typed empty width/height or a flexible main-axis gap. When one token is insufficient, import typed builders from the CSS-free `@kerfjs/ui/css-values` subpath. Preserve property grammar: lengths for gap/Spacer/Skeleton geometry, `flex()` for Row/Grid/List flex, and a foreground color for choice icons: `uiColor()` with a `*-on-*` or text token (`success-on-quiet`, `text-quiet`) or `foregroundColorVar()`. Fill tokens (`*-fill-*`) are backgrounds, not foregrounds, and `SelectChoice.color` rejects them. Never cast between brands or emit raw row `style` declarations. `plus` is not standalone and must be wrapped in `calc`; `remify()` remains source-CSS syntax and is never a runtime prop value. See `docs/layout.md` "Spacing scale".
|
|
162
|
+
22. Pick a whole-screen layout from the opt-in, tree-shakeable subpaths by data + interaction + device, and derive responsiveness from `@kerfjs/ui/device-class`'s `deviceClass()` (`compact` = handset or portrait tablet = one pane at a time). Simple/flat → `NavStack` with one entry (single pane), plus `TabScaffold` (`@kerfjs/ui/tab-scaffold`, iOS bottom tabs, each tab its own `NavStack`) for 2–5 co-equal sections on `compact`; show an unread count on a tab with its `badge` (a number or short string, rendered as the top-trailing danger `Badge` on the icon) plus a localized `badgeLabel` ("3 unread", folded into the tab's name as "Inbox, 3 unread"), never by concatenating the count into `label` or restyling the badge; for new content without a count pass `badge: true` (the text-free dot) with its required `badgeLabel` ("New activity"). Drill-down → `NavStack` (`@kerfjs/ui/nav-stack`), upgrading to `SplitView` (`@kerfjs/ui/split-view`, list-detail) once both panes fit (`atLeast('tablet')` landscape); `SplitView` collapses to a `NavStack` on `compact`. Keep each stack's ordered views controlled; put view-specific bottom chrome on `NavStackView.bottomToolbar` (or use the component-level persistent fallback), then call `wireNavStack` so content slides while the active view's top and bottom chrome cross-fade and focus moves into the new top view/restores on pop. Mark a preferred initial heading or control with `data-nav-focus` when DOM order is not sufficient. The stack's top chrome is a real `Toolbar`: put a view's groups on `NavStackView.leading` / `center` / `toolbar` (trailing), and configure the bar through `toolbarConfig` (`ToolbarConfig` plus `label`, `titleSize`, `headingLevel`) and `backIcon` / `backText`, never with CSS. `SplitView` forwards both of its inner components: `resizable` takes the list `ResizableRegion`'s `separator`, `handleIcon`, `contentOverflow`, and collapse props (`collapsed`, `transitioning`, `collapseMotion`, `restoreControl`, `restorePosition`), and `compactStack` takes the compact stack's `toolbarConfig`, `backIcon`, `backText`, `hideToolbar`, `bottomToolbar`, plus `list` / `detail` view toolbars (`leading`, `center`, `toolbar`, `bottomToolbar`). Complex tool with peripheral panels → `Workbench` (`@kerfjs/ui/workbench`, collapsible rails + drawer) `atLeast('desktop')`, degrading to `NavStack`/overlays below. Workbench panels are fixed-size unless a panel opts in with `resizable` (`true` or `{ min, max }`); then render `size={sizeSignal.value}` and call `wireWorkbench(root, { id, panels: { leftRail: { size: sizeSignal, storageKey? } }, deviceClass })` from `@kerfjs/ui/wire-workbench` for pointer drags, arrow/Shift+arrow/Home/End on the separator, persistence, and no resizing on `compact` classes. Inline rails, fixed or resizable, leave the work area `mainMinSize` px of width (a `Workbench` prop, default 320, `0` off), and an inline bottom drawer leaves it `mainMinHeight` px of height (default 120, `0` off): resizable panels stop growing there, rails shrink in proportion when the Workbench narrows and the drawer when it gets shorter, so do not add your own main-area `min-width` / `min-height`. Rails present as overlays below the `narrow` Workbench container breakpoint (704px) by default — `responsiveOverlayAt: "compact"` moves that to 448px and `"never"` keeps a rail inline — and a bottom drawer opts in the same way; do not derive `presentation` from `deviceClass`, and overlays never resize. At compact widths an overlay rail fills the Workbench less a 44px dismiss strip (`--kui-workbench-overlay-dismiss-margin`) on the side away from its edge; `compactOverlay: "full"` makes one rail fill it. Pass each such panel's `collapsed` signal to `wireWorkbench` too (`panels: { leftRail: { collapsed: navCollapsed } }`, `size` optional) so the overlay is transient like `wireSidebar`'s compact overlay: it collapses when the breakpoint begins to apply (the inline state returns when it ends), takes focus on its first control and keeps Tab inside it while open, and closes on Escape or an outside press with focus returned to its toggle (give each toggle `aria-controls` naming its panel's derived id, `<workbench id>-left-rail` / `-right-rail` / `-bottom-drawer`, so a panel open at wire-up has a focus target too); opening one overlay closes the other open overlays, so they never cover each other's controls; do not hand-roll those listeners (`dismissOverlays: false` opts out of all of it, `exclusiveOverlays: false` of exclusivity only). Collapse stays the app's `collapsed` flag and keeps the size, so an expanded panel returns at its last size. Let the Workbench compose panel toolbars: give each panel `toolbar: { label, title?, panelOnly?, center?, constant?, toggle: { action, name, showLabel?, hideLabel? } }` and the work area `mainToolbar` (and `mainBottomToolbar` for a drawer). Each of these toolbars takes the `Toolbar`'s configuration (`ToolbarConfig`: `dividerSides`, `centerAlign`, `responsive`, `responsiveAt`, `safeAreaEdges`); configure a composed toolbar through those props (for example `dividerSides: ""` to drop its divider), never with CSS. Configure the composed panes and chrome lists the same way: a panel's `pane` (and `CollapsiblePanel`'s `pane`) and the Workbench's `mainPane` take `PaneConfig` (`contentElement`, `contentLabel`, `separators`, `safeAreaEdges` — for example `{ contentElement: "nav", contentLabel: "Files" }` for a navigator rail), and `mainHeaderList` / `mainFooterList` take `ListConfig` (`gap`, `hAlign`, `vAlign`, `dividerSides`, `textInsets`, `controlInsets`). Open, a panel's toolbar holds its title and panel-only groups, then its constant groups and its toggle last; closed, its constant groups and toggle move to the work area — a left rail's lead `mainToolbar`, a right rail's end it, a drawer's trail `mainBottomToolbar` or float in a corner `FloatingToolbar` — and panel-only groups stay behind. Do not hand-place panel toggles in the work-area toolbar or render a second copy, and handle only the toggle's `data-action`. A collapsed panel renders `inert` and `aria-hidden` by itself, on the rail/drawer and its content (its `restoreControl` stays outside and reachable), so do not add your own `tabindex`, `aria-hidden`, or `inert` to hide it. These are declarative (the app owns the stack/selection/collapsed/active state as signals) with disposer-returning `wire…` helpers; each ships a companion CSS import and stays out of the barrel. For a standalone collapsible rail or bottom drawer outside a full shell, use `CollapsiblePanel` + `CollapsiblePanelToggle` (`@kerfjs/ui/collapsible-panel`) with `wireSidebar` (`@kerfjs/ui/wire-sidebar`): the standard collapse animation and per-side icon convention (`PanelLeft*`/`PanelRight*`/`PanelBottom*`) plus toggle, focus, compact overlay, and persistence semantics. Give a standalone panel the same `toolbar` roles and render `CollapsiblePanelRelocated` in your work-area toolbar (first in leading for a left rail, last in trailing for a right rail, a bottom toolbar or a `FloatingToolbar` restoreControl for a drawer) instead of hand-placing a second toggle. A collapsed `CollapsiblePanel`, `ResizableRegion`, or Workbench panel renders `inert` and `aria-hidden` by itself, so do not hide collapsed content yourself either. Likewise an open side overlay (a Workbench rail overlay, a `wireSidebar` compact overlay, an overlay `CollapsiblePanel` or horizontal `ResizableRegion`) hides the covered work area's `FloatingToolbar`s and restore corners by itself until it closes, so do not hide them from overlay state. A compact overlay opens only on a user action: wire-up on a compact device and a wide-to-compact crossing collapse every panel, and a wide crossing restores the inline state. Seed the signal with `signal(device.value.compact)` and pass the panel's `inlineCollapsed` default so the first compact render has no overlay. Dialogs pick the same inner layout, then present per device class (full-screen modal on `compact`, inline on desktop). A list-detail dialog is a Workbench-shaped layout on roomy devices: a full-height list sidebar and a full-height main column, each a `Pane` with its own top `Toolbar`; the main column holds the primary xl title, the close control (`data-dialog="close"` in a `without-header` Web Awesome dialog), and the record actions, never a header spanning both columns. A top-level percentage-height shell needs a definite containing height: opt into `@kerfjs/ui/document.css` and put `.kui-app-root` on the one direct mount container, or provide equivalent application-owned geometry. The global baseline is deliberately absent from `styles.css` and component imports. See `docs/app-layouts.md`, `docs/document-baseline.md`, and `docs/collapsible-panel.md`.
|
|
163
|
+
23. Layouts and `Pane` are safe-area aware without configuration: backgrounds, separators, and dividers paint through a device's unsafe areas (a collapsible rail or drawer grows by the inset it docks to), content is padded only on the sides a region actually reaches, and block insets are scroll padding inside the scroller so first/last content can always be scrolled clear. Interior edges get nothing, and a collapsed rail hands its edge back to the center. Never add your own `env(safe-area-inset-*)` padding around a kerf layout or pane — that double-insets. In an app-owned arrangement of panes, pass `Pane.safeAreaEdges` (the sides that pane reaches; `[]` opts out); give an app bar or bottom bar that sits at a screen edge outside a Pane header or footer `Toolbar.safeAreaEdges` for the sides it reaches (for example `['block-start', 'inline-start', 'inline-end']`); the Workbench `mainToolbar` / `mainBottomToolbar` and panel `toolbar` configurations forward the same `safeAreaEdges` to their `Toolbar`. Do not set the internal `--kui-edge-inset-*` context yourself. The device insets are `--kui-safe-area-*` (defaulting to `env()`), overridable on `:root`. See `docs/app-layouts.md` "Safe areas".
|
|
164
164
|
|
|
165
165
|
Common mistakes:
|
|
166
166
|
|
|
167
|
-
| Mistake | Fix
|
|
168
|
-
| -------------------------------------------------------------------------------------------- |
|
|
169
|
-
| Hard-coded project/transport action in a component | Pass a semantic `data-action` string from an application adapter
|
|
170
|
-
| Per-instance signal at module scope | Create state in the application or a factory and pass it in
|
|
171
|
-
| `role="menuitem"` on one button | Use the native button, or implement the complete ARIA menu widget
|
|
172
|
-
| Put a button/link/interactive role in `ListItem.trailing` or a `ListActionRow` SafeHtml slot | Use `ListActionRow` for the sibling controls and keep its label/icon slots dormant
|
|
173
|
-
| Copy ListItem/ListHeader markup to add product data or a popover target | Use the typed `rootAttributes`/`triggerAttributes` slots and retain the component's protected semantics
|
|
174
|
-
| Concatenate a section count into a ListHeader label or numeric badge | Pass `count={value}` and a localized full phrase such as `countLabel="3 notes"`; reserve `badge` for non-count SafeHtml
|
|
175
|
-
| Put a chevron on navigation that reveals nothing | Remove it, or model a real controlled `ListHeader` disclosure; omit `actionIcon` to use the production `DisclosureArrow`
|
|
176
|
-
| Test only a custom-element attribute | Assert live property, emitted event, focus, and rendered output
|
|
177
|
-
| Import all Web Awesome components | Import only `@kerfjs/ui/select/register` for `Select`, or individual Web Awesome modules for other controls; `webawesome.css` registers no JavaScript
|
|
178
|
-
| Write `wa-*` JSX without activating its types | Add `import type {} from '@kerfjs/ui/webawesome'`; registration and theme imports remain separate
|
|
179
|
-
| Maintain a root list of component styles | Import each visual component from its JS subpath; its reachable CSS follows automatically
|
|
180
|
-
| Add demo-only markup for a production state | Add the state to the production component, then render that export in the catalog
|
|
181
|
-
| Choose a listed ecosystem component by default | Apply the component-selection guidance above; catalog coverage means supported and themed, not preferred
|
|
182
|
-
| Add sidebar-specific wrapper padding | Use the unpadded `.kui-pane`; let `ListHeader`, `ListItem`, and
|
|
183
|
-
| Put bare text or controls directly in a toolbar slot | Wrap every item in `ToolbarControlGroup`, using `appearance="borderless"` for transparent chrome
|
|
184
|
-
| Reach for `wa-button` for a plain control inside a `ToolbarControlGroup` | Use a plain `<button>` (the group styles it fully); for a popup menu use `PopupMenu`, which renders its own trigger
|
|
185
|
-
|
|
|
186
|
-
|
|
|
187
|
-
|
|
|
188
|
-
|
|
|
189
|
-
|
|
|
190
|
-
|
|
|
191
|
-
|
|
|
192
|
-
|
|
|
193
|
-
|
|
|
194
|
-
|
|
|
195
|
-
|
|
|
196
|
-
|
|
|
167
|
+
| Mistake | Fix |
|
|
168
|
+
| -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
169
|
+
| Hard-coded project/transport action in a component | Pass a semantic `data-action` string from an application adapter |
|
|
170
|
+
| Per-instance signal at module scope | Create state in the application or a factory and pass it in |
|
|
171
|
+
| `role="menuitem"` on one button | Use the native button, or implement the complete ARIA menu widget |
|
|
172
|
+
| Put a button/link/interactive role in `ListItem.trailing` or a `ListActionRow` SafeHtml slot | Use `ListActionRow` for the sibling controls and keep its label/icon slots dormant |
|
|
173
|
+
| Copy ListItem/ListHeader markup to add product data or a popover target | Use the typed `rootAttributes`/`triggerAttributes` slots and retain the component's protected semantics |
|
|
174
|
+
| Concatenate a section count into a ListHeader label or numeric badge | Pass `count={value}` and a localized full phrase such as `countLabel="3 notes"`; reserve `badge` for non-count SafeHtml |
|
|
175
|
+
| Put a chevron on navigation that reveals nothing | Remove it, or model a real controlled `ListHeader` disclosure; omit `actionIcon` to use the production `DisclosureArrow` |
|
|
176
|
+
| Test only a custom-element attribute | Assert live property, emitted event, focus, and rendered output |
|
|
177
|
+
| Import all Web Awesome components | Import only `@kerfjs/ui/select/register` for `Select`, or individual Web Awesome modules for other controls; `webawesome.css` registers no JavaScript |
|
|
178
|
+
| Write `wa-*` JSX without activating its types | Add `import type {} from '@kerfjs/ui/webawesome'`; registration and theme imports remain separate |
|
|
179
|
+
| Maintain a root list of component styles | Import each visual component from its JS subpath; its reachable CSS follows automatically |
|
|
180
|
+
| Add demo-only markup for a production state | Add the state to the production component, then render that export in the catalog |
|
|
181
|
+
| Choose a listed ecosystem component by default | Apply the component-selection guidance above; catalog coverage means supported and themed, not preferred |
|
|
182
|
+
| Add sidebar-specific wrapper padding | Use the unpadded `.kui-pane`; let `ListHeader`, `ListItem`, and `ContentItem` own their 8/1/8 geometry |
|
|
183
|
+
| Put bare text or controls directly in a toolbar slot | Wrap every item in `ToolbarControlGroup`, using `appearance="borderless"` for transparent chrome |
|
|
184
|
+
| Reach for `wa-button` for a plain control inside a `ToolbarControlGroup` | Use a plain `<button>` (the group styles it fully); for a popup menu use `PopupMenu`, which renders its own trigger |
|
|
185
|
+
| Add a `title` or `wa-tooltip` to an icon-only `Select` or `PopupMenu` trigger | Nothing: the register import already shows an `aria-hidden` help tag with the accessible name on hover and keyboard focus |
|
|
186
|
+
| Add a `title` or `wa-tooltip` to an icon-only toolbar `<button>` | Name it with `aria-label` only; a registered Select/PopupMenu or `@kerfjs/ui/help-tags/register` shows the same help tag for it |
|
|
187
|
+
| Hand-write `wa-dropdown` / `wa-dropdown-item` markup for an action menu | Use `PopupMenu` with typed `items` in a `single` `ToolbarControlGroup` with `nestedDropdown` |
|
|
188
|
+
| Let the document, pane, and list all scroll | Keep fixed chrome outside one `.kui-pane__content` scroll owner per pane |
|
|
189
|
+
| Invent an `@kerfjs/ui` command-palette export | Keep the semantics application-local, reuse public layout vocabulary, and propose recurring behavior upstream |
|
|
190
|
+
| Handwrite `ValueTable` row wrappers or compensate their padding or separators | Compose `ValueTableRow`; its root-scaled block padding, optional icon hook, and separator geometry are public contract |
|
|
191
|
+
| Rebuild a panel/dialog heading with bespoke CSS or a private wrapper | Compose a plain `Toolbar` with a direct xl `ToolbarText`, optional grouped icon, grouped trailing controls, and app-owned supporting copy below |
|
|
192
|
+
| Put an xl `ToolbarText` in a narrow rail or drawer header | Use the default size there as pane identity; xl truncates at the minimum size ("Ins…" in a 160px rail) |
|
|
193
|
+
| Force a `width`/`height`/`padding` on a component to size or space it | Let it size to its content and tokens; a forced box leaves a halo or a stretched oval — adjust an icon-size or spacing token, not the box |
|
|
194
|
+
| Wrap a component or region in a card, border, backdrop, or outline to "contain" it | Let it sit on the surface; add a `ContentItem frame="framed"` only for a real distinction — hierarchy comes from alignment, spacing, and type first |
|
|
195
|
+
| Override a component's `--kui-*` token to inset, size, or frame it | Use the typed prop: `FloatingToolbar inset`, `DisclosureArrow size`, `ListHeader headingLevel`, `TokenSearchField clearIcon`, `ContentItem frame="framed"` |
|
|
196
|
+
| Add another container's padding on top of a content-item's own margin | Pick one owner of the inset; a pane has no padding and its `.kui-content` children own the 8/1/8 geometry — stacking them double-insets |
|
|
197
|
+
| Put raw prose directly in a padded dialog body | Compose the body as `List`, set `bodyInset="none"` for list-owned geometry, and wrap bare prose in `ListInsetText` |
|
|
198
|
+
| Keep chrome, a label, or a readout that aids no decision | Delete it; every element must help a person decide or act |
|
|
199
|
+
| Override a component's default size or color because it "looks off" | Trust the default (a LucideIcon is 24px) and fix the surrounding layout instead |
|
|
197
200
|
|
|
198
201
|
## Select lifecycle ownership
|
|
199
202
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Web Awesome JSX signatures for the UI authoring corpus
|
|
2
2
|
|
|
3
|
-
Generated from the emitted `@kerfjs/ui@5.0.0-beta.
|
|
3
|
+
Generated from the emitted `@kerfjs/ui@5.0.0-beta.56` declaration boundary. Import `@kerfjs/ui/webawesome` for type effects when authoring direct `wa-*` JSX. The module emits no runtime behavior and does not register custom elements.
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
6
|
import { KerfCustomElement } from 'kerfjs/jsx-runtime';
|
|
@@ -30,6 +30,9 @@ declare module 'kerfjs/jsx-runtime' {
|
|
|
30
30
|
placeholder?: string;
|
|
31
31
|
hint?: string;
|
|
32
32
|
disabled?: boolean;
|
|
33
|
+
size?: 'xs' | 's' | 'm' | 'l' | 'xl';
|
|
34
|
+
appearance?: 'filled' | 'outlined' | 'filled-outlined';
|
|
35
|
+
placement?: 'top' | 'bottom';
|
|
33
36
|
};
|
|
34
37
|
'wa-option': KerfCustomElement & {
|
|
35
38
|
value?: string;
|
|
@@ -45,7 +48,9 @@ declare module 'kerfjs/jsx-runtime' {
|
|
|
45
48
|
'wa-comparison': KerfCustomElement;
|
|
46
49
|
'wa-copy-button': KerfCustomElement;
|
|
47
50
|
'wa-details': KerfCustomElement;
|
|
48
|
-
'wa-divider': KerfCustomElement
|
|
51
|
+
'wa-divider': KerfCustomElement & {
|
|
52
|
+
orientation?: 'horizontal' | 'vertical';
|
|
53
|
+
};
|
|
49
54
|
'wa-dialog': KerfCustomElement;
|
|
50
55
|
'wa-drawer': KerfCustomElement;
|
|
51
56
|
'wa-button': KerfCustomElement & {
|
|
@@ -56,6 +61,7 @@ declare module 'kerfjs/jsx-runtime' {
|
|
|
56
61
|
};
|
|
57
62
|
'wa-dropdown': KerfCustomElement & {
|
|
58
63
|
placement?: string;
|
|
64
|
+
size?: 'xs' | 's' | 'm' | 'l' | 'xl';
|
|
59
65
|
};
|
|
60
66
|
'wa-dropdown-item': KerfCustomElement;
|
|
61
67
|
'wa-format-bytes': KerfCustomElement;
|
package/dist/app-tab.js
CHANGED
package/dist/badge.d.ts
CHANGED
|
@@ -3,10 +3,17 @@ import { SafeHtml } from 'kerfjs';
|
|
|
3
3
|
type BadgeTone = 'neutral' | 'brand' | 'pop' | 'success' | 'warning' | 'danger';
|
|
4
4
|
type BadgeAppearance = 'quiet' | 'solid' | 'outline';
|
|
5
5
|
type BadgeShape = 'pill' | 'rounded';
|
|
6
|
+
/** Size of a text badge. A text-free dot is the separate `size: 'dot'` form. */
|
|
6
7
|
type BadgeSize = 'compact' | 'default';
|
|
7
|
-
interface
|
|
8
|
-
children: SafeHtml | string | number;
|
|
8
|
+
interface BadgeCommonProps {
|
|
9
9
|
tone?: BadgeTone;
|
|
10
|
+
className?: string;
|
|
11
|
+
/** Native named-slot assignment when composed inside a web component. */
|
|
12
|
+
slot?: string;
|
|
13
|
+
}
|
|
14
|
+
/** A badge that shows a short status, count, or category as visible text. */
|
|
15
|
+
interface BadgeTextProps extends BadgeCommonProps {
|
|
16
|
+
children: SafeHtml | string | number;
|
|
10
17
|
appearance?: BadgeAppearance;
|
|
11
18
|
shape?: BadgeShape;
|
|
12
19
|
size?: BadgeSize;
|
|
@@ -14,11 +21,32 @@ interface BadgeProps {
|
|
|
14
21
|
label?: string;
|
|
15
22
|
/** Hide a repeated visual badge from assistive technology. */
|
|
16
23
|
ariaHidden?: boolean;
|
|
17
|
-
className?: string;
|
|
18
|
-
/** Native named-slot assignment when composed inside a web component. */
|
|
19
|
-
slot?: string;
|
|
20
24
|
}
|
|
25
|
+
/** A labeled dot, announced as an image with its own accessible name. */
|
|
26
|
+
interface BadgeDotLabeled {
|
|
27
|
+
/** Accessible name for the dot, for example `"New activity"`. */
|
|
28
|
+
label: string;
|
|
29
|
+
ariaHidden?: false;
|
|
30
|
+
}
|
|
31
|
+
/** A decorative dot whose meaning the owning component already announces. */
|
|
32
|
+
interface BadgeDotDecorative {
|
|
33
|
+
label?: never;
|
|
34
|
+
ariaHidden: true;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A text-free status dot (the iOS "new content" dot): a small solid circle in
|
|
38
|
+
* the badge's tone. It has no visible text, so it must either carry its own
|
|
39
|
+
* accessible `label` (exposed as an image) or be `ariaHidden` because the
|
|
40
|
+
* surrounding component already folds its meaning into an accessible name.
|
|
41
|
+
*/
|
|
42
|
+
type BadgeDotProps = BadgeCommonProps & {
|
|
43
|
+
size: 'dot';
|
|
44
|
+
children?: never;
|
|
45
|
+
appearance?: never;
|
|
46
|
+
shape?: never;
|
|
47
|
+
} & (BadgeDotLabeled | BadgeDotDecorative);
|
|
48
|
+
type BadgeProps = BadgeTextProps | BadgeDotProps;
|
|
21
49
|
/** Compact, non-interactive metadata whose tone, emphasis, and shape are configured by props. */
|
|
22
|
-
declare function Badge(
|
|
50
|
+
declare function Badge(props: BadgeProps): SafeHtml;
|
|
23
51
|
|
|
24
|
-
export { Badge, type BadgeAppearance, type BadgeProps, type BadgeShape, type BadgeSize, type BadgeTone };
|
|
52
|
+
export { Badge, type BadgeAppearance, type BadgeDotProps, type BadgeProps, type BadgeShape, type BadgeSize, type BadgeTextProps, type BadgeTone };
|
package/dist/badge.js
CHANGED
package/dist/browser/select.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import { C as CatalogResource } from './types-
|
|
1
|
+
import { C as CatalogResource } from './types-DXAGogOS.js';
|
|
2
2
|
import './semantic-content-BbzjvSu9.js';
|
|
3
3
|
import 'kerfjs';
|
|
4
|
+
import './toolbar.js';
|
|
5
|
+
import './pane.js';
|
|
6
|
+
import './sides-BPSWde0A.js';
|
|
7
|
+
import './workbench.js';
|
|
8
|
+
import './list.js';
|
|
9
|
+
import './css-values.js';
|
|
10
|
+
import './flex-alignment-4ms8ZbV8.js';
|
|
11
|
+
import './resizable-region.js';
|
|
12
|
+
import './panel-toolbar-C8sF-YZC.js';
|
|
13
|
+
import './lucide-icon.js';
|
|
14
|
+
import 'lucide';
|
|
4
15
|
|
|
5
16
|
/**
|
|
6
17
|
* Standard resource labels for a Kerf catalog detail footer. Keep these labels
|
package/dist/catalog.d.ts
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import * as kerfjs from 'kerfjs';
|
|
2
2
|
import { SafeHtml } from 'kerfjs';
|
|
3
|
-
import { a as CatalogProps } from './types-
|
|
4
|
-
export { b as CatalogBrand, c as CatalogEntry, d as CatalogRelated, C as CatalogResource, e as CatalogSecondaryGroup, f as CatalogSection, g as CatalogStageRootAttributes } from './types-
|
|
3
|
+
import { a as CatalogProps } from './types-DXAGogOS.js';
|
|
4
|
+
export { b as CatalogBrand, c as CatalogEntry, d as CatalogRelated, C as CatalogResource, e as CatalogSecondaryGroup, f as CatalogSection, g as CatalogStageRootAttributes } from './types-DXAGogOS.js';
|
|
5
5
|
import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
6
|
+
import './toolbar.js';
|
|
7
|
+
import './pane.js';
|
|
8
|
+
import './sides-BPSWde0A.js';
|
|
9
|
+
import './workbench.js';
|
|
10
|
+
import './list.js';
|
|
11
|
+
import './css-values.js';
|
|
12
|
+
import './flex-alignment-4ms8ZbV8.js';
|
|
13
|
+
import './resizable-region.js';
|
|
14
|
+
import './panel-toolbar-C8sF-YZC.js';
|
|
15
|
+
import './lucide-icon.js';
|
|
16
|
+
import 'lucide';
|
|
6
17
|
|
|
7
18
|
/**
|
|
8
19
|
* Controlled, stateless component-catalog shell: a `Workbench` whose left rail
|
|
@@ -11,7 +22,7 @@ import { K as KerfUiContent } from './semantic-content-BbzjvSu9.js';
|
|
|
11
22
|
* standard toggle moves into the entry toolbar while it is collapsed, and on
|
|
12
23
|
* a small screen the sidebar overlays the stage like any Workbench rail.
|
|
13
24
|
*/
|
|
14
|
-
declare function Catalog({ id, brand, sections, active, content, collapsed, theme, headerActions, secondarySections, sidebarFooter, status, geometryOverlay, stageRootAttributes, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, headerPlacement, footerPlacement, className, slot, }: CatalogProps): kerfjs.SafeHtml;
|
|
25
|
+
declare function Catalog({ id, brand, sections, active, content, collapsed, theme, headerActions, secondarySections, sidebarFooter, status, geometryOverlay, stageRootAttributes, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, headerPlacement, footerPlacement, sidebar, mainToolbar, footerToolbar, className, slot, }: CatalogProps): kerfjs.SafeHtml;
|
|
15
26
|
|
|
16
27
|
/** How a specimen aligns its visible edge with its `ListHeader` label. */
|
|
17
28
|
type CatalogExampleAlign = 'glyph' | 'inline-control' | 'none';
|