@kerfjs/ui 4.4.1 → 5.0.0-beta.4
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 +234 -5
- package/ai/component-catalog.json +4473 -0
- package/ai/component-catalog.schema.json +79 -0
- package/ai/public-api-signatures-v1.md +619 -0
- package/ai/skill.md +65 -11
- package/ai/webawesome-jsx-signatures-v1.md +106 -0
- package/dist/app-tab.d.ts +12 -2
- package/dist/app-tab.js +2 -1
- package/dist/browser/app-tab.js +2 -2
- package/dist/browser/dialog-header.js +4 -2
- package/dist/browser/disclosure-arrow.js +4 -0
- package/dist/browser/empty-state.js +3 -3
- package/dist/browser/loading-spinner.js +2 -2
- package/dist/browser/lucide-icon.js +2 -2
- package/dist/browser/menu-action-row.js +3 -0
- package/dist/browser/menu-header.js +4 -2
- package/dist/browser/menu-item.js +2 -2
- package/dist/browser/page-header.js +2 -2
- package/dist/browser/resizable-region.js +2 -2
- package/dist/browser/segmented-control.js +2 -2
- package/dist/browser/select.js +3 -3
- package/dist/browser/state-banner.js +2 -2
- package/dist/browser/tab-bar.js +2 -2
- package/dist/browser/token-search-field.js +4 -0
- package/dist/browser/toolbar-control-group.js +2 -2
- package/dist/browser/toolbar-text.js +2 -2
- package/dist/browser/toolbar.js +2 -2
- package/dist/browser/value-table.js +2 -2
- package/dist/chunk-56RJXPHG.js +43 -0
- package/dist/chunk-56RJXPHG.js.map +1 -0
- package/dist/chunk-7BR3DO2A.js +31 -0
- package/dist/chunk-7BR3DO2A.js.map +1 -0
- package/dist/chunk-7LDCTU7D.js +134 -0
- package/dist/chunk-7LDCTU7D.js.map +1 -0
- package/dist/{chunk-4HHCYSGT.js → chunk-AQNKJRFH.js} +2 -2
- package/dist/{chunk-4HHCYSGT.js.map → chunk-AQNKJRFH.js.map} +1 -1
- package/dist/{chunk-NHXX4JI5.js → chunk-CTQKI2XL.js} +3 -3
- package/dist/chunk-CTQKI2XL.js.map +1 -0
- package/dist/chunk-EGHL5S4P.js +33 -0
- package/dist/chunk-EGHL5S4P.js.map +1 -0
- package/dist/chunk-H5AGGVU5.js +75 -0
- package/dist/chunk-H5AGGVU5.js.map +1 -0
- package/dist/chunk-K3G72I6D.js +24 -0
- package/dist/chunk-K3G72I6D.js.map +1 -0
- package/dist/{chunk-3O7ZEUW2.js → chunk-M5JRI6MN.js} +3 -3
- package/dist/chunk-M5JRI6MN.js.map +1 -0
- package/dist/chunk-NPPD2A6O.js +40 -0
- package/dist/chunk-NPPD2A6O.js.map +1 -0
- package/dist/chunk-R6JHHBKQ.js +19 -0
- package/dist/chunk-R6JHHBKQ.js.map +1 -0
- package/dist/chunk-S3TPM2LP.js +23 -0
- package/dist/chunk-S3TPM2LP.js.map +1 -0
- package/dist/{chunk-T5XNMVOI.js → chunk-XZXRLY5Q.js} +14 -5
- package/dist/chunk-XZXRLY5Q.js.map +1 -0
- package/dist/dialog-header.d.ts +3 -2
- package/dist/dialog-header.js +3 -1
- package/dist/disclosure-arrow.d.ts +14 -0
- package/dist/disclosure-arrow.js +4 -0
- package/dist/disclosure-arrow.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +16 -11
- package/dist/menu-action-row.d.ts +50 -0
- package/dist/menu-action-row.js +4 -0
- package/dist/menu-action-row.js.map +1 -0
- package/dist/menu-header.d.ts +28 -2
- package/dist/menu-header.js +4 -1
- package/dist/menu-item.d.ts +10 -1
- package/dist/menu-item.js +2 -1
- package/dist/resizable-region.d.ts +3 -1
- package/dist/resizable-region.js +1 -1
- package/dist/select.js +1 -1
- package/dist/styles/app-tab.css +152 -0
- package/dist/styles/dialog-header.css +87 -0
- package/dist/styles/disclosure-arrow.css +23 -0
- package/dist/styles/empty-state.css +47 -0
- package/dist/styles/foundation.css +326 -0
- package/dist/styles/layout.css +73 -0
- package/dist/styles/loading-spinner.css +21 -0
- package/dist/styles/lucide-icon.css +6 -0
- package/dist/styles/menu-action-row.css +160 -0
- package/dist/styles/menu-header.css +143 -0
- package/dist/styles/menu-item.css +123 -0
- package/dist/styles/page-header.css +33 -0
- package/dist/styles/resizable-region.css +111 -0
- package/{src → dist/styles}/segmented-control.css +62 -31
- package/dist/styles/select.css +96 -0
- package/dist/styles/state-banner.css +173 -0
- package/dist/styles/styles.css +22 -0
- package/dist/styles/tab-bar.css +45 -0
- package/dist/styles/token-search-field.css +281 -0
- package/dist/styles/toolbar-control-group.css +161 -0
- package/dist/styles/toolbar-text.css +29 -0
- package/dist/styles/toolbar.css +52 -0
- package/dist/styles/value-table.css +75 -0
- package/{src → dist/styles}/webawesome.css +119 -29
- package/dist/token-search-field.d.ts +43 -0
- package/dist/token-search-field.js +4 -0
- package/dist/token-search-field.js.map +1 -0
- package/dist/value-table.d.ts +10 -2
- package/dist/value-table.js +1 -1
- package/dist/webawesome.d.ts +100 -0
- package/dist/webawesome.js +3 -0
- package/dist/webawesome.js.map +1 -0
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-token-search-fields.d.ts +11 -0
- package/dist/wire-token-search-fields.js +5 -0
- package/dist/wire-token-search-fields.js.map +1 -0
- package/docs/accessibility.md +60 -3
- package/docs/component-contract.md +131 -3
- package/docs/component-selection.md +133 -0
- package/docs/design-philosophy.md +38 -0
- package/docs/examples/command-palette-adapter.tsx +70 -0
- package/docs/layout.md +106 -0
- package/docs/recipes.md +124 -0
- package/docs/ux-demo.md +23 -9
- package/docs/webawesome-theme.md +15 -0
- package/llms.txt +25 -2
- package/package.json +70 -27
- package/ux-demo/recipes/app-shell.tsx +32 -0
- package/ux-demo/recipes/compact-toolbar.tsx +43 -0
- package/ux-demo/recipes/composer-form.tsx +63 -0
- package/ux-demo/recipes/list-workspace-states.tsx +26 -0
- package/ux-demo/recipes/loaders.ts +27 -0
- package/ux-demo/recipes/master-detail-dialog.tsx +45 -0
- package/ux-demo/recipes/mount-recipe.ts +38 -0
- package/ux-demo/recipes/navigation-sidebar.tsx +26 -0
- package/ux-demo/recipes/recipes.css +344 -0
- package/ux-demo/recipes/types.ts +11 -0
- package/ux-demo/recipes/workspace-header.tsx +12 -0
- package/dist/chunk-3O7ZEUW2.js.map +0 -1
- package/dist/chunk-723YNJBH.js +0 -17
- package/dist/chunk-723YNJBH.js.map +0 -1
- package/dist/chunk-NHXX4JI5.js.map +0 -1
- package/dist/chunk-OPCJRCYO.js +0 -19
- package/dist/chunk-OPCJRCYO.js.map +0 -1
- package/dist/chunk-T5XNMVOI.js.map +0 -1
- package/dist/chunk-VKH2JLDZ.js +0 -10
- package/dist/chunk-VKH2JLDZ.js.map +0 -1
- package/dist/chunk-WY4CAEPG.js +0 -14
- package/dist/chunk-WY4CAEPG.js.map +0 -1
- package/src/app-tab.css +0 -20
- package/src/dialog-header.css +0 -9
- package/src/empty-state.css +0 -6
- package/src/foundation.css +0 -106
- package/src/loading-spinner.css +0 -4
- package/src/lucide-icon.css +0 -1
- package/src/menu-header.css +0 -13
- package/src/menu-item.css +0 -12
- package/src/page-header.css +0 -3
- package/src/resizable-region.css +0 -19
- package/src/select.css +0 -18
- package/src/state-banner.css +0 -31
- package/src/styles.css +0 -18
- package/src/tab-bar.css +0 -4
- package/src/toolbar-control-group.css +0 -25
- package/src/toolbar-text.css +0 -4
- package/src/toolbar.css +0 -9
- package/src/value-table.css +0 -5
package/docs/recipes.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Production composition recipes
|
|
2
|
+
|
|
3
|
+
These seven reference compositions sit between individual primitives and product
|
|
4
|
+
code. Open each stable UX-catalog route to run it, then copy the linked TSX and
|
|
5
|
+
CSS. The examples import public package subpaths, use semantic layout owners,
|
|
6
|
+
and keep state in a per-instance application adapter. They are not new
|
|
7
|
+
monolithic components.
|
|
8
|
+
|
|
9
|
+
Copy the recipe source together with the catalog-independent
|
|
10
|
+
[`mount-recipe.ts`](../ux-demo/recipes/mount-recipe.ts) adapter. It mounts the
|
|
11
|
+
controller at one stable application root, uses `delegateActions()` for recipe
|
|
12
|
+
commands, forwards form and dialog lifecycle events, wires resize commits with
|
|
13
|
+
the public `onCommit` callback, retains every disposer, and returns one
|
|
14
|
+
idempotent disposer:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { createRecipe } from './navigation-sidebar.js';
|
|
18
|
+
import { mountRecipe } from './mount-recipe.js';
|
|
19
|
+
|
|
20
|
+
const root = document.querySelector<HTMLElement>('#navigation')!;
|
|
21
|
+
const stopRecipe = mountRecipe(root, createRecipe(announce));
|
|
22
|
+
window.addEventListener('pagehide', stopRecipe, { once: true });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The adapter is delivered as reference source, not a new package runtime export.
|
|
26
|
+
`delegate()` remains a valid alternative when an application needs selector-
|
|
27
|
+
specific dispatch; either way, wire once at a stable root and retain disposal.
|
|
28
|
+
|
|
29
|
+
## Desktop application shell
|
|
30
|
+
|
|
31
|
+
[Open the recipe](../ux-demo/?component=recipe-app-shell) · [TSX source](../ux-demo/recipes/app-shell.tsx) · [shared CSS](../ux-demo/recipes/recipes.css)
|
|
32
|
+
|
|
33
|
+
Use `Toolbar`, controlled `ResizableRegion` panes, and one
|
|
34
|
+
`.kui-pane__content` scroll owner per pane. The recipe owns the shell topology;
|
|
35
|
+
the app owns routing, responsive pane visibility, sizes, persistence, and data.
|
|
36
|
+
Keep each visible pane's collapse action in its own toolbar. Once hidden, put
|
|
37
|
+
its restore action in the adjacent main toolbar on the same logical edge:
|
|
38
|
+
leading for an inline-start sidebar and trailing for an inline-end inspector.
|
|
39
|
+
Collapse the pane completely rather than preserving an empty icon rail.
|
|
40
|
+
Adapt only public `--kui-layout-*` and component variables.
|
|
41
|
+
|
|
42
|
+
## Navigation sidebar
|
|
43
|
+
|
|
44
|
+
[Open the recipe](../ux-demo/?component=recipe-navigation-sidebar) · [TSX source](../ux-demo/recipes/navigation-sidebar.tsx)
|
|
45
|
+
|
|
46
|
+
One unpadded `.kui-pane` owns toolbar/content/footer structure. Its
|
|
47
|
+
`.kui-content` uses 24px major gaps; `MenuHeader`, `MenuItem`, and other
|
|
48
|
+
`.kui-content-item` children own their 8px margin, 1px border, and 8px padding.
|
|
49
|
+
Rows and footer toolbar groups remain 44px tall. The app owns routes,
|
|
50
|
+
permissions, labels, selection, valid section counts and their localized
|
|
51
|
+
`countLabel` phrases, non-count badge content, disclosure state, and revealed
|
|
52
|
+
content. A toggled `MenuHeader` supplies the production `DisclosureArrow` when
|
|
53
|
+
no custom `actionIcon` is needed; ordinary navigation rows stay chevron-free.
|
|
54
|
+
|
|
55
|
+
## Workspace header
|
|
56
|
+
|
|
57
|
+
[Open the recipe](../ux-demo/?component=recipe-workspace-header) · [TSX source](../ux-demo/recipes/workspace-header.tsx)
|
|
58
|
+
|
|
59
|
+
`PageHeader` owns the page heading while one control cluster holds secondary,
|
|
60
|
+
overflow, and primary actions. The app owns authorization and command policy;
|
|
61
|
+
controls relocate without changing focus order.
|
|
62
|
+
|
|
63
|
+
## Master-detail dialog
|
|
64
|
+
|
|
65
|
+
[Open the recipe](../ux-demo/?component=recipe-master-detail-dialog) · [TSX source](../ux-demo/recipes/master-detail-dialog.tsx)
|
|
66
|
+
|
|
67
|
+
The production Web Awesome dialog owns modal focus and Escape; the thin recipe
|
|
68
|
+
adapter restores the invoking control consistently after the hide event.
|
|
69
|
+
`DialogHeader`, `MenuHeader`, `MenuItem`, and `ValueTable` own their included
|
|
70
|
+
anatomy. The application owns open state, selection, dismissal policy, and
|
|
71
|
+
record actions. Pass the dialog action children directly; `DialogHeader` places
|
|
72
|
+
them in its top toolbar's action group and uses the localized `actionsLabel`.
|
|
73
|
+
The header sits on the dialog edge while retaining its internal control inset;
|
|
74
|
+
the selected title receives the full content gutter, the metadata table fills
|
|
75
|
+
the available detail width between the usual outer margins, and the action
|
|
76
|
+
cluster uses one outer gutter without a second content-item inset.
|
|
77
|
+
Do not rebuild the dialog or reach into private shadow parts.
|
|
78
|
+
|
|
79
|
+
## Composer form
|
|
80
|
+
|
|
81
|
+
[Open the recipe](../ux-demo/?component=recipe-composer-form) · [TSX source](../ux-demo/recipes/composer-form.tsx)
|
|
82
|
+
|
|
83
|
+
`DialogHeader` supplies the task title and summary, with their ids referenced
|
|
84
|
+
by the form. Production fields own labels, help, and native focus. The field
|
|
85
|
+
and footer control edges sit directly on the shared 8px inline gutter rather
|
|
86
|
+
than acquiring a second content-item padding inset; major children remain 24px
|
|
87
|
+
apart and related controls use 8px gaps. The app owns
|
|
88
|
+
validation, drafts, permissions, and transport. Persistent error or success
|
|
89
|
+
feedback is the only nested semantic surface and uses `StateBanner`, not a toast.
|
|
90
|
+
Because upgraded Web Awesome fields retain live value properties, controlled
|
|
91
|
+
resets synchronize both those properties and the rendered value attributes;
|
|
92
|
+
the Reset action also announces `Draft reset` through the catalog live region.
|
|
93
|
+
|
|
94
|
+
## List workspace states
|
|
95
|
+
|
|
96
|
+
[Open the recipe](../ux-demo/?component=recipe-list-workspace-states) · [TSX source](../ux-demo/recipes/list-workspace-states.tsx)
|
|
97
|
+
|
|
98
|
+
The same content region moves deterministically through loading, empty,
|
|
99
|
+
populated, stale/background refresh, and error/retry states. The recipe owns
|
|
100
|
+
feedback placement; the app owns fetching, cache age, retry policy, and domain
|
|
101
|
+
rows.
|
|
102
|
+
|
|
103
|
+
## Compact toolbar choices and actions
|
|
104
|
+
|
|
105
|
+
[Open the recipe](../ux-demo/?component=recipe-compact-toolbar) · [TSX source](../ux-demo/recipes/compact-toolbar.tsx)
|
|
106
|
+
|
|
107
|
+
Use `ToolbarControlGroup` for related commands, `SegmentedControl` for a few
|
|
108
|
+
visible exclusive choices, `Select` for a longer value list, and an ordinary
|
|
109
|
+
button for an independent command. The app owns values, actions, persistence,
|
|
110
|
+
and responsive priority.
|
|
111
|
+
|
|
112
|
+
## Rules shared by every recipe
|
|
113
|
+
|
|
114
|
+
- Import `@kerfjs/ui/layout.css`; keep every pane unpadded and use exactly one
|
|
115
|
+
`.kui-pane__content` scroll owner for each real boundary.
|
|
116
|
+
- For direct `wa-*` JSX, import types from `@kerfjs/ui/webawesome`. Import only
|
|
117
|
+
individual Web Awesome registration modules and theme them with
|
|
118
|
+
`@kerfjs/ui/webawesome.css`.
|
|
119
|
+
- Prefer public props and variables at the composition boundary. When a recipe
|
|
120
|
+
needs responsive topology, join only classes listed in the catalog's
|
|
121
|
+
`publicClasses`; do not copy component markup or select descendants by tag,
|
|
122
|
+
id, attribute alone, or an unlisted implementation class.
|
|
123
|
+
- Start from the copyable mount adapter, or reproduce its complete boundary:
|
|
124
|
+
wire stable `data-action` hooks once and retain every disposer.
|
package/docs/ux-demo.md
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
# UX catalog contract
|
|
2
2
|
|
|
3
|
-
Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation.
|
|
3
|
+
Run `npm run dev` from `ui/`. The catalog is a development and review surface, not a second implementation. The development server allows repo-owned assets used by the catalog, and its production bundle uses relative asset URLs so the complete catalog remains intact below preview and proxy paths.
|
|
4
|
+
|
|
5
|
+
Seven lazy production-composition routes live under the `Recipes` category.
|
|
6
|
+
Each `?component=recipe-*` route uses public package primitives, real wiring,
|
|
7
|
+
deterministic state, and semantic layout owners from the [recipe guide](./recipes.md).
|
|
4
8
|
|
|
5
9
|
- It imports public component subpaths, exercising their browser-selected reachable CSS rather than a catalog-only style path.
|
|
10
|
+
- The `MenuActionRow` route demonstrates sibling primary/trailing native controls, independent disabled states, controlled selection, first-line leading-icon alignment for wrapped labels, and an application-owned native popover relationship without nesting controls. The `MenuItem` route demonstrates the same multiline leading-icon anchor alongside its single-control navigation contract.
|
|
11
|
+
- The `AppTab` route demonstrates a runtime-filtered domain metadata slot and a decorative replacement close icon without changing TabBar selection, close, reorder, or focus ownership. The `ResizableRegion` route likewise replaces only the decorative handle glyph while retaining separator semantics and shared wiring.
|
|
6
12
|
- Every first-party visual component and every free Web Awesome 3.12 component has its own stable `?component=<id>` URL and focused demo. Composite header, menu, feedback, and theme scenarios remain addressable alongside their subcomponents; the root opens the first Kerf component.
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
13
|
+
- `ai/component-catalog.json` is the canonical catalog metadata. A deterministic checked-in projection supplies the demo's unique id, category, kind (`component`, `composition`, or `recipe`), source (`kerf` or `webawesome`), name, description, direct `uses` dependencies, existing documentation path, and demo-source path; first-party, recipe, and Web Awesome renderer maps are exhaustively typed against its generated id unions. Demo paths resolve to `ui/ux-demo/main.tsx`, the matching `ui/ux-demo/recipes/*.tsx` file, or `ui/ux-demo/webawesome-demos.tsx` according to the canonical entry. First-party component entries additionally derive `ui/src/<browser-subpath>.tsx` from their canonical `delivery.browserImport`.
|
|
14
|
+
- Every detail footer groups compact `Demo source` and `Guidance` actions; first-party component details also expose `Component source`. Web Awesome details say `Integration guidance` to distinguish the local integration contract from the upstream component implementation. Each button retains its repository-relative path in the DOM for verification and uses a deploy-safe absolute GitHub blob URL with a contextual new-tab name; the demo does not depend on a local source server or add a package runtime export. At phone widths, the resource group and related-component selector stack as full-width rows, with any resource overflow contained locally so labels cannot overlap the selector or widen the document.
|
|
15
|
+
- The master/detail shell groups first-party routes by category with production `MenuHeader` and `MenuItem` components. The 70 ecosystem components live in a separate `Web Awesome` disclosure with seven subgroups, so they remain discoverable without being presented as first-class `@kerfjs/ui` exports. Direct Web Awesome routes open the disclosure and reveal the selected row at wide sizes; narrow layouts keep the expanded list in document flow rather than introducing a competing nested scroller.
|
|
16
|
+
- The shell itself uses `@kerfjs/ui/layout.css`: sidebars, details, previews, dialogs, and resize specimens share unpadded panes plus self-contained content items. Its sidebar identity uses the Kerf logo beside a vertically centered title, with the subtitle in its own aligned row like `DialogHeader`; the current-view heading is also a toolbar. Collapsing the catalog removes the pane completely and moves its restore action to the main toolbar's leading edge instead of leaving an icon-only rail. The selected view description remains inset below the page toolbar. The checkerboard belongs to the complete scrollable preview region rather than a nested card. A lightly separated footer keeps live status above compact source, guidance, and relationship groups. The focused `ResizableRegion` specimen stretches its surface through the available stage height and reports committed width in that shared status footer; narrow layouts retain local horizontal access when the pane is resized wider than the stage, and the split remains readable at 200% root scaling. Geometry coverage checks wide, intermediate, narrow, and 200% zoom layouts.
|
|
17
|
+
- The composer-form recipe demonstrates one coherent visible form surface with `DialogHeader` title/summary hierarchy, fields and actions on the shared 8px control gutter, and 24px major rhythm. Its conditional error or success `StateBanner` is the only nested semantic surface. Reset synchronizes the upgraded Web Awesome fields' live value properties with their empty controlled attributes and announces `Draft reset`; browser coverage preserves behavior and verifies wide, narrow, dark, validation, success, reset, 200% zoom, and forced-colors states.
|
|
18
|
+
- The master-detail dialog recipe places its header directly on the dialog edge while preserving the header's internal inset, gives the selected project title the complete outer-plus-inner content gutter, lets the value table fill the available detail column between the shared outer margins, and keeps record actions on one outer gutter without an extra content-item inset. Browser geometry coverage protects those relationships at wide and narrow widths.
|
|
19
|
+
- Catalog-only brand marks, preview dimensions, checkerboard tiles, and the compound display-settings control retain local geometry because they are specimen chrome or intrinsic control anatomy rather than reusable application spacing roles.
|
|
20
|
+
- The menu composition route demonstrates toolbar/content/footer anatomy, 24px major gaps, 8/1/8 child geometry, the full-width `MenuHeader` count-or-badge/action split, logical-end 44px action targets with 18px visuals, and 44px row/header/toolbar targets. Its one chevron is a production 18px `DisclosureArrow` inside a controlled Tools header that actually reveals and hides content; ordinary Projects navigation has no false disclosure affordance. Browser assertions cover stable naming, expanded/content transitions, visual rotation, node identity, reduced motion, RTL alignment and containment, and wide, narrow, dark, and 200% layouts. The focused MenuHeader route separately shows the shared neutral count pill for Attachments, Notes (including zero), and Duplicates plus a non-count legacy badge, full-width logical-end actions, and no disclosure without content.
|
|
21
|
+
- The `DisclosureArrow` route renders the production 18px root-scaled default without a catalog-only size override. Its two native buttons independently control the default right-to-down chevron and a recognizable right-facing replacement arrow with left-to-up custom directions; both use shortest-path rotation and their names stay stable while `aria-expanded` communicates state. Browser coverage verifies the custom clockwise path, pointer and native keyboard activation, focus, narrow and 200%-zoom stacking with contained labels, consumer sizing, and the independent `.5` Web Awesome glyph scale retained by Kerf `Select`.
|
|
22
|
+
- A detail with relationships renders one grouped footer selector: `Uses` links to direct dependencies and `Used by` links to reverse dependents. Choosing either navigates to that component's demo. The relationship group is absent when both sets are empty; no duplicate summary is rendered.
|
|
10
23
|
- Demo-only CSS supplies only the catalog shell and stages; component appearance stays in package CSS. Decorative chrome and Web Awesome controls share the same semantic system palette instead of carrying a separate demo identity.
|
|
11
|
-
- Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; StateBanner exposes every built-in tone plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
|
|
12
|
-
-
|
|
13
|
-
- The action
|
|
24
|
+
- Scenarios use deterministic data and cover real variants, long content, disabled state, selection, progress, feedback, overlays, media, helpers, and stateful interactions. The Web Awesome theme route remains the broad system view, while individual action, form, layout, navigation, feedback, media, and helper routes provide focused specimens; MenuItem and MenuHeader demonstrate typed domain metadata, protected native button/disclosure semantics, and a native popover trigger relationship without product props; DialogHeader demonstrates its top-toolbar identity/action groups, optional subtitle row, and 24px icon inside a 34px circle; the Select route verifies that option icons survive unrelated Kerf rerenders and selected slot content follows value changes; the Animation route exposes preset, easing, duration, playback-rate, and transport controls with live lifecycle status; observer routes provide user-driven intersection, mutation, and resize events with live results; ToolbarControlGroup exposes all eight Hot Sheet 2 variants, loads the Web Awesome registrations needed by its popup specimen, and preserves the upgraded dropdown's managed light-DOM children across Kerf rerenders; the Toolbar route composes a collapsible TokenSearchField inside a group, animates between the empty iconic and complete field states, stays open while populated, and focuses the revealed editor; SegmentedControl covers toolbar, rounded-rectangle, pill, equal-width, and disabled presentations; TokenSearchField covers editable ordered chips, pointer and keyboard removal, caret-preserving controlled deletion, editing, clearing, free text, disabled state, and fixed first-line alignment while content wraps; StateBanner exposes every built-in tone plus a scoped override; TabBar demonstrates overflow, edge autoscroll, and controlled pointer/keyboard reordering.
|
|
25
|
+
- Recipe ownership notes are hidden by default. A page-toolbar toggle reveals them as floating warning-toned notes with a folded corner, keeping instructional copy visually distinct and removable from the specimen flow.
|
|
26
|
+
- Settings exercise light/dark, increased contrast, and reduced motion without changing component code. The theme action starts from the operating system's effective color scheme, always names the appearance it will switch to, and installs an explicit light or dark override when activated; contrast and reduced motion expose their independent preferences through `aria-pressed`.
|
|
27
|
+
- The footer action log is an `aria-live` region, making interactions visible to both people and browser tests.
|
|
14
28
|
- The focused Toast route calls Web Awesome's documented programmatic stack API and notes that Hot Sheet 2 still uses its own app-level toast implementation; the catalog demonstrates ecosystem support without implying current product adoption.
|
|
15
|
-
- Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup behavior, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
|
|
16
|
-
- The production build keeps the catalog shell and Kerf routes in the entry chunk, then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route
|
|
29
|
+
- Browser coverage walks every Kerf, composition, and Web Awesome route, checks every listed Web Awesome custom element is registered in its focused specimen, exercises the ecosystem disclosure and both relationship directions, validates offline source/guidance hrefs and accessible wide/narrow/200%-zoom wrapping, and covers tab selection/reordering/overflow, semantic palettes and scoped overrides, light/dark rendering, toolbar popup and responsive find behavior, single/multiline search alignment, menu alignment, banner urgency, keyboard resizing, live custom-element values, and representative wide/narrow captures.
|
|
30
|
+
- The production build keeps the catalog shell and Kerf routes in the entry chunk, then dynamically imports the complete Web Awesome specimen and registration surface when an ecosystem route, the broad Web Awesome theme route, or the ToolbarControlGroup popup specimen is selected. Directly addressed routes pass through the same loader.
|
|
17
31
|
|
|
18
32
|
Before handoff, inspect the actual captures for readability, context, alignment, clipping, spacing, responsiveness, focus, and obvious defects. Fix and recapture rather than treating the screenshot as proof by itself.
|
package/docs/webawesome-theme.md
CHANGED
|
@@ -4,11 +4,18 @@
|
|
|
4
4
|
free components and the Kerf/Hot Sheet 2 design system.
|
|
5
5
|
|
|
6
6
|
```ts
|
|
7
|
+
import type {} from '@kerfjs/ui/webawesome';
|
|
7
8
|
import '@kerfjs/ui/webawesome.css';
|
|
8
9
|
import '@awesome.me/webawesome/dist/components/button/button.js';
|
|
9
10
|
import '@awesome.me/webawesome/dist/components/input/input.js';
|
|
10
11
|
```
|
|
11
12
|
|
|
13
|
+
The type-only package import augments `kerfjs/jsx-runtime` with the 70
|
|
14
|
+
catalog-supported `wa-*` elements. It has no runtime side effects. The catalog
|
|
15
|
+
gate keeps the declaration tags aligned with Web Awesome's installed custom
|
|
16
|
+
elements manifest, while individual component imports remain the only
|
|
17
|
+
registration boundary.
|
|
18
|
+
|
|
12
19
|
The CSS entry includes Web Awesome's native, utility, palette, and default-theme
|
|
13
20
|
styles, then overrides the public semantic contract in
|
|
14
21
|
`@layer wa-theme-overrides`. It does not import or register component
|
|
@@ -53,6 +60,11 @@ chevrons keep the same visual weight. The Accordion and Details components
|
|
|
53
60
|
retain their own rotation behavior because scaling uses `transform` while
|
|
54
61
|
their open state uses the independent `rotate` property.
|
|
55
62
|
|
|
63
|
+
That scale applies to each Web Awesome component's intrinsic glyph and is
|
|
64
|
+
independent of Kerf `DisclosureArrow`, whose default box is 18px relative to
|
|
65
|
+
the root font size and whose consumer override is
|
|
66
|
+
`--kui-disclosure-arrow-size`.
|
|
67
|
+
|
|
56
68
|
Tooltip and Popover use arrowless floating surfaces by default, matching Hot
|
|
57
69
|
Sheet 2. The theme sets Web Awesome's public `--wa-tooltip-arrow-size` token to
|
|
58
70
|
`0px` and maps each popover's public `--arrow-size` property from
|
|
@@ -129,6 +141,9 @@ static content and labels that constraint next to the rendered output.
|
|
|
129
141
|
|
|
130
142
|
The catalog lists every free Web Awesome component so support can be inspected;
|
|
131
143
|
listing does not make each component the preferred Kerf application pattern.
|
|
144
|
+
The [need-first decision matrix](./component-selection.md#web-awesome-overlap-policy)
|
|
145
|
+
is the exhaustive Kerf/Web Awesome overlap index; the rules below explain the
|
|
146
|
+
highest-risk choices.
|
|
132
147
|
|
|
133
148
|
- Consider `wa-popup` for low-level anchored positioning when its flip, shift,
|
|
134
149
|
or placement behavior avoids custom positioning code. Prefer the higher-level
|
package/llms.txt
CHANGED
|
@@ -4,6 +4,11 @@ Accessible, production-backed UI primitives for kerf applications.
|
|
|
4
4
|
|
|
5
5
|
- [Package guide](./README.md)
|
|
6
6
|
- [AI skill](./ai/skill.md)
|
|
7
|
+
- [Machine-readable component catalog](./ai/component-catalog.json)
|
|
8
|
+
- [Public API signature snapshot](./ai/public-api-signatures-v1.md)
|
|
9
|
+
- [Web Awesome JSX signature snapshot](./ai/webawesome-jsx-signatures-v1.md)
|
|
10
|
+
- [Need-first component selection matrix](./docs/component-selection.md)
|
|
11
|
+
- [Production composition recipes](./docs/recipes.md)
|
|
7
12
|
- [Component contract](./docs/component-contract.md)
|
|
8
13
|
- [Web Awesome theme contract](./docs/webawesome-theme.md)
|
|
9
14
|
- [Accessibility and keyboard contracts](./docs/accessibility.md)
|
|
@@ -13,9 +18,27 @@ Accessible, production-backed UI primitives for kerf applications.
|
|
|
13
18
|
|
|
14
19
|
The UX catalog is a category-grouped master/detail review surface with a focused route for every public visual component plus composition demos. Its collapsible ecosystem section also lists all 70 free Web Awesome 3.12 components, each with a focused themed route. Related entries use one grouped `Uses` / `Used by` selector across both sets; the shell, display preferences, and selected component share one semantic theme.
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
Choose from the interface need: search the catalog, reuse a semantic match, compose existing primitives, add a thin application adapter for product concerns, and use custom markup only for a genuinely different contract. The selection matrix covers every public visual/helper export and the supported-but-not-preferred Web Awesome overlaps, including required wiring, application ownership, import subpaths, alternatives, and recipe links.
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
There is no `@kerfjs/ui` command-palette runtime export or production recipe. The typed application-local example at `docs/examples/command-palette-adapter.tsx` demonstrates how a missing concept can reuse public layout vocabulary without inventing a package API. Ranking, history, permissions, availability, focus policy, actions, and copy stay in the app; propose recurring behavior upstream.
|
|
24
|
+
|
|
25
|
+
AI tools that need exhaustive retrieval should load `@kerfjs/ui/ai/component-catalog.json`. It is the canonical checked-in source for stable ids, exports, purpose, selection guidance, relationships, delivery and side effects, wiring, application ownership, variants, accessibility, public CSS hooks, catalog routes, and current documentation/recipe links. Its adjacent JSON Schema describes the format.
|
|
26
|
+
|
|
27
|
+
Prefer an equivalent component prop or `--kui-*` token for customization. When composition-specific layout needs CSS, the catalog's `publicClasses` arrays are the exact supported anatomy boundary: selectors may join documented public classes, but must not reach into a component by tag, id, attribute alone, or an unlisted class.
|
|
28
|
+
|
|
29
|
+
Before emitting code, load `@kerfjs/ui/ai/public-api-signatures-v1.md` for the exact declaration signatures used by the authoring corpus. It records accepted import paths, prop names, callback shapes, and disposer return types from emitted declarations rather than prose examples.
|
|
30
|
+
|
|
31
|
+
Public components: `LucideIcon`, `DisclosureArrow`, `Toolbar`, `ToolbarControlGroup`, `ToolbarText`, `SegmentedControl`, `TokenSearchField`, `readTokenSearchField`, `placeTokenSearchCaret`, `wireTokenSearchFields`, `MenuItem`, `MenuActionRow`, `MenuHeader`, `ResizableRegion`, `wireResizableRegions`, `AppTab`, `TabBar`, `wireTabBars`, `reorderTabs`, `PageHeader`, `LoadingSpinner`, `Select`, `StateBanner`, `EmptyState`, `DialogHeader`, `ValueTable`, and `ValueTableRow`. `MenuItem`, `MenuActionRow`, `MenuHeader`, and `AppTab` accept typed, runtime-filtered application `data-*` metadata through `rootAttributes`; MenuActionRow `trailingActionAttributes` and MenuHeader `triggerAttributes` additionally support native popover target/action and `aria-controls`/`aria-haspopup`, while component-owned action, identity, selection, disclosure, naming, disabled, icon, and role semantics stay protected. `MenuHeader` uses the mutually exclusive `count` plus required localized `countLabel` pair for non-negative safe-integer section quantities, including zero; it renders the shared neutral pill and joins the spoken phrase to the owning heading or disclosure button. Never concatenate a count into `label` or put numeric content in the legacy non-count `badge` SafeHtml slot. In real toggle mode, omitting `actionIcon` supplies one production 18px `DisclosureArrow` driven by `expanded`; a custom icon replaces it, and the app must reveal matching content. Ordinary navigation must not borrow a disclosure chevron. `AppTab.closeIcon` and `ResizableRegion.handleIcon` replace decorative dormant glyphs without taking control ownership. `MenuItem.trailing` is dormant; use `MenuActionRow` for sibling primary/trailing native buttons, keeping its `label`, `icon`, and `trailingActionIcon` SafeHtml dormant too. `ValueTableRow` owns semantic `dt`/`dd` markup, 8px of root-scaled top and bottom padding, and an optional 24px leading icon; its separator begins 8px from the left without an icon or 40px with one and ends 8px from the right. `DisclosureArrow` is a decorative, controlled open/closed indicator with configurable directions, shortest-path rotation (counterclockwise for a 180-degree closed-to-open tie), replaceable right-oriented icon content, an 18px root-scaled default, and a scoped `--kui-disclosure-arrow-size` override; the owning control retains interaction and expanded-state semantics. `SegmentedControl` is controlled and supports toolbar, rounded-rectangle, and pill presentations with native pressed buttons in sequential Tab order. `Select` preserves icon-bearing option slots across Kerf rerenders, keys custom selected content by the controlled value, and separately keeps its intrinsic Web Awesome expand glyph at `--kui-disclosure-icon-scale: .5`. `TokenSearchField` controls tokens while keeping editable text DOM-owned between token changes, combining free text with ordered atomic filter chips while leaving query parsing and results to the application; its leading icon, first text line, clear action, and trailing slot stay aligned to one fixed row when text wraps. Its optional `collapsible` mode animates between an empty iconic action and the complete field, works standalone or inside `ToolbarControlGroup`, and keeps populated searches expanded while the app controls transient focus/open state. Call `wireTokenSearchFields` once to make Enter submit without inserting a contenteditable line break and to preserve focus plus the text-relative caret across controlled keyboard chip deletion; visual wrapping remains available. Semantic state palettes are opinionated, Web Awesome-compatible, and overridable globally, by tone, or per instance.
|
|
32
|
+
|
|
33
|
+
`DialogHeader` composes a real top `Toolbar`: its borderless identity group keeps the optional 24px icon in a 34px circular background and the first title line centered against an automatic action `ToolbarControlGroup`. Pass action children directly and add a localized `actionsLabel` when that group needs a name. The optional `summary`/`summaryId` row remains below the title; the application owns modal focus, dismissal, commands, and action handling.
|
|
34
|
+
|
|
35
|
+
Pane and navigation composition: import `@kerfjs/ui/layout.css` and use an unpadded `.kui-pane` with optional `.kui-pane__toolbar`, one scrolling `.kui-pane__content`, and optional `.kui-pane__footer`. Add `.kui-content` for 24px major vertical gaps. Use `.kui-content-item` for children that own 8px inline margin, 1px transparent-or-visible border, 8px padding, and 12px rounded corners; the pill modifier uses 22px. This vocabulary applies equally to sidebars, main areas, inspectors, and dialogs. A visible collapsible pane owns collapse in its own toolbar. When hidden, move restore into the adjacent main toolbar on the same logical edge—leading for an inline-start pane, trailing for inline-end—and remove the pane instead of leaving an icon-only rail.
|
|
36
|
+
|
|
37
|
+
Composer form recipe: keep the form as one visible surface, use `DialogHeader` title/summary ids to label and describe it, and align field and action control edges on the shared 8px inline gutter without a second content-item padding inset. Retain 24px major rhythm. A conditional `StateBanner` is the only nested semantic surface. Do not make every form section another card. When resetting controlled upgraded Web Awesome fields, synchronize their live `value` properties as well as the rendered value attributes.
|
|
38
|
+
|
|
39
|
+
Toolbar and split-item geometry: wrap every toolbar item, including dormant text, in `ToolbarControlGroup`. Groups stay 44px outside (`calc(2px + remify(42px))`) even when their border and background are transparent, with 8px between groups and inside items. `MenuHeader` fills the available inline width and separates its dormant title/count-or-badge cluster from its optional logical-end 44px action; the visible action glyph defaults to 18px through `--kui-menu-header-action-icon-size`, while disclosure mode makes the title cluster the action. `MenuActionRow` keeps primary and trailing 44px actions as sibling native buttons inside a noninteractive row. Multiline `MenuItem` and `MenuActionRow` leading icons stay centered on the first text line while later lines extend below. Do not pad pane shells or add competing wrapper insets. See `docs/layout.md`.
|
|
40
|
+
|
|
41
|
+
Import visual components from explicit JavaScript subpaths. A CSS-aware browser bundler selects generated wrappers that bring in the foundation, the component CSS, and styles for reachable UI subcomponents; unrelated CSS stays out. The root barrel and `@kerfjs/ui/unstyled` are CSS-free; use `styles.css` with the root barrel only when the complete layer is intentional. Manual CSS exports remain available for custom pipelines, and application overrides follow package styles in the cascade. Direct `wa-*` JSX uses `import type {} from '@kerfjs/ui/webawesome'`, the side-effect-free declaration boundary synchronized with all 70 catalog-supported elements. `@kerfjs/ui/webawesome.css` is the optional CSS-only theme; it includes Web Awesome's base stylesheet, registers no elements, and remains overridable through public `--wa-*` tokens. CSS, its generated browser wrappers, and `@kerfjs/ui/select/register` are the declared side effects. `kerfjs` is an external peer. The Web Awesome peer is optional and needed for its components. `wireTabBars` includes proximity-based horizontal edge autoscroll during same-bar drag reordering.
|
|
19
42
|
|
|
20
43
|
The Web Awesome theme renders Tooltip and Popover without pointer arrows by default. Restore them only when the anchor relationship needs the pointer, using `--wa-tooltip-arrow-size`, `--kui-wa-popover-arrow-size`, or one popover's public `--arrow-size`; `without-arrow` remains available for explicit local intent.
|
|
21
44
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kerfjs/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-beta.4",
|
|
4
4
|
"description": "Accessible, composable UI primitives for kerf applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Brian Westphal <brian.westphal@bleugris.com>",
|
|
8
|
-
"homepage": "https://brianwestphal.github.io/kerf/docs/
|
|
8
|
+
"homepage": "https://brianwestphal.github.io/kerf/docs/component-packages/",
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
11
|
"url": "https://github.com/brianwestphal/kerf",
|
|
@@ -44,6 +44,11 @@
|
|
|
44
44
|
"browser": "./dist/browser/lucide-icon.js",
|
|
45
45
|
"import": "./dist/lucide-icon.js"
|
|
46
46
|
},
|
|
47
|
+
"./disclosure-arrow": {
|
|
48
|
+
"types": "./dist/disclosure-arrow.d.ts",
|
|
49
|
+
"browser": "./dist/browser/disclosure-arrow.js",
|
|
50
|
+
"import": "./dist/disclosure-arrow.js"
|
|
51
|
+
},
|
|
47
52
|
"./toolbar": {
|
|
48
53
|
"types": "./dist/toolbar.d.ts",
|
|
49
54
|
"browser": "./dist/browser/toolbar.js",
|
|
@@ -59,6 +64,11 @@
|
|
|
59
64
|
"browser": "./dist/browser/toolbar-text.js",
|
|
60
65
|
"import": "./dist/toolbar-text.js"
|
|
61
66
|
},
|
|
67
|
+
"./menu-action-row": {
|
|
68
|
+
"types": "./dist/menu-action-row.d.ts",
|
|
69
|
+
"browser": "./dist/browser/menu-action-row.js",
|
|
70
|
+
"import": "./dist/menu-action-row.js"
|
|
71
|
+
},
|
|
62
72
|
"./menu-item": {
|
|
63
73
|
"types": "./dist/menu-item.d.ts",
|
|
64
74
|
"browser": "./dist/browser/menu-item.js",
|
|
@@ -112,10 +122,23 @@
|
|
|
112
122
|
"browser": "./dist/browser/segmented-control.js",
|
|
113
123
|
"import": "./dist/segmented-control.js"
|
|
114
124
|
},
|
|
125
|
+
"./token-search-field": {
|
|
126
|
+
"types": "./dist/token-search-field.d.ts",
|
|
127
|
+
"browser": "./dist/browser/token-search-field.js",
|
|
128
|
+
"import": "./dist/token-search-field.js"
|
|
129
|
+
},
|
|
130
|
+
"./wire-token-search-fields": {
|
|
131
|
+
"types": "./dist/wire-token-search-fields.d.ts",
|
|
132
|
+
"import": "./dist/wire-token-search-fields.js"
|
|
133
|
+
},
|
|
115
134
|
"./select/register": {
|
|
116
135
|
"types": "./dist/select-register.d.ts",
|
|
117
136
|
"import": "./dist/select-register.js"
|
|
118
137
|
},
|
|
138
|
+
"./webawesome": {
|
|
139
|
+
"types": "./dist/webawesome.d.ts",
|
|
140
|
+
"import": "./dist/webawesome.js"
|
|
141
|
+
},
|
|
119
142
|
"./state-banner": {
|
|
120
143
|
"types": "./dist/state-banner.d.ts",
|
|
121
144
|
"browser": "./dist/browser/state-banner.js",
|
|
@@ -140,55 +163,73 @@
|
|
|
140
163
|
"types": "./dist/index.d.ts",
|
|
141
164
|
"import": "./dist/index.js"
|
|
142
165
|
},
|
|
143
|
-
"./foundation.css": "./
|
|
144
|
-
"./
|
|
145
|
-
"./
|
|
146
|
-
"./
|
|
147
|
-
"./
|
|
148
|
-
"./
|
|
149
|
-
"./toolbar
|
|
150
|
-
"./
|
|
151
|
-
"./
|
|
152
|
-
"./
|
|
153
|
-
"./
|
|
154
|
-
"./
|
|
155
|
-
"./
|
|
156
|
-
"./
|
|
157
|
-
"./
|
|
158
|
-
"./
|
|
159
|
-
"./
|
|
160
|
-
"./
|
|
161
|
-
"./
|
|
162
|
-
"./
|
|
166
|
+
"./foundation.css": "./dist/styles/foundation.css",
|
|
167
|
+
"./layout.css": "./dist/styles/layout.css",
|
|
168
|
+
"./webawesome.css": "./dist/styles/webawesome.css",
|
|
169
|
+
"./styles.css": "./dist/styles/styles.css",
|
|
170
|
+
"./lucide-icon.css": "./dist/styles/lucide-icon.css",
|
|
171
|
+
"./disclosure-arrow.css": "./dist/styles/disclosure-arrow.css",
|
|
172
|
+
"./toolbar.css": "./dist/styles/toolbar.css",
|
|
173
|
+
"./toolbar-control-group.css": "./dist/styles/toolbar-control-group.css",
|
|
174
|
+
"./toolbar-text.css": "./dist/styles/toolbar-text.css",
|
|
175
|
+
"./menu-action-row.css": "./dist/styles/menu-action-row.css",
|
|
176
|
+
"./menu-item.css": "./dist/styles/menu-item.css",
|
|
177
|
+
"./menu-header.css": "./dist/styles/menu-header.css",
|
|
178
|
+
"./resizable-region.css": "./dist/styles/resizable-region.css",
|
|
179
|
+
"./app-tab.css": "./dist/styles/app-tab.css",
|
|
180
|
+
"./tab-bar.css": "./dist/styles/tab-bar.css",
|
|
181
|
+
"./page-header.css": "./dist/styles/page-header.css",
|
|
182
|
+
"./loading-spinner.css": "./dist/styles/loading-spinner.css",
|
|
183
|
+
"./select.css": "./dist/styles/select.css",
|
|
184
|
+
"./segmented-control.css": "./dist/styles/segmented-control.css",
|
|
185
|
+
"./token-search-field.css": "./dist/styles/token-search-field.css",
|
|
186
|
+
"./state-banner.css": "./dist/styles/state-banner.css",
|
|
187
|
+
"./empty-state.css": "./dist/styles/empty-state.css",
|
|
188
|
+
"./dialog-header.css": "./dist/styles/dialog-header.css",
|
|
189
|
+
"./value-table.css": "./dist/styles/value-table.css",
|
|
163
190
|
"./ai/*": "./ai/*",
|
|
164
191
|
"./docs/*": "./docs/*"
|
|
165
192
|
},
|
|
166
193
|
"files": [
|
|
167
194
|
"dist",
|
|
168
|
-
"src/*.css",
|
|
169
195
|
"ai",
|
|
170
196
|
"docs",
|
|
197
|
+
"ux-demo/recipes",
|
|
171
198
|
"llms.txt",
|
|
172
199
|
"README.md",
|
|
173
200
|
"LICENSE"
|
|
174
201
|
],
|
|
175
202
|
"scripts": {
|
|
176
|
-
"build": "tsup && node ../scripts/clean-dist-output.mjs dist && node scripts/build-browser-entries.mjs",
|
|
203
|
+
"build": "tsup && node ../scripts/clean-dist-output.mjs dist && node scripts/build-css.mjs && node scripts/build-browser-entries.mjs",
|
|
177
204
|
"dev": "npm run build && vite --config ux-demo/vite.config.ts",
|
|
178
205
|
"demo:build": "npm run build && vite build --config ux-demo/vite.config.ts && node scripts/check-demo-bundle.mjs",
|
|
179
206
|
"demo:serve": "vite preview --config ux-demo/vite.config.ts",
|
|
180
|
-
"
|
|
207
|
+
"format:css": "prettier --write \"src/**/*.css\" \"ux-demo/**/*.css\"",
|
|
208
|
+
"check:format": "prettier --check \"src/**/*.css\" \"ux-demo/**/*.css\"",
|
|
209
|
+
"catalog:sync": "node scripts/sync-component-catalog.mjs",
|
|
210
|
+
"check:catalog": "node scripts/sync-component-catalog.mjs --check && node scripts/check-component-catalog.mjs",
|
|
211
|
+
"check:recipes": "node scripts/check-recipes.mjs",
|
|
212
|
+
"check:ai-regressions": "node scripts/check-ai-regressions.mjs && node scripts/audit-ai-regression-results.mjs",
|
|
213
|
+
"check:ai-signatures": "node scripts/sync-ai-public-signatures.mjs --check",
|
|
214
|
+
"ai:signatures:sync": "npm run build && node scripts/sync-ai-public-signatures.mjs",
|
|
215
|
+
"ai:regressions:compile": "node scripts/compile-ai-regression-response.mjs",
|
|
216
|
+
"ai:regressions:prepare": "node scripts/prepare-ai-regression.mjs",
|
|
217
|
+
"ai:regressions:record": "node scripts/record-ai-regression-run.mjs",
|
|
218
|
+
"ai:regressions:score": "node scripts/score-ai-regression.mjs",
|
|
219
|
+
"check:guidance": "node scripts/check-decision-guidance.mjs",
|
|
220
|
+
"lint": "eslint src tests ux-demo docs/examples --max-warnings 0",
|
|
181
221
|
"typecheck": "node node_modules/typescript7/bin/tsc --noEmit",
|
|
182
222
|
"test": "npm run test:unit && npm run test:bundle",
|
|
183
223
|
"test:unit": "vitest run tests/unit --coverage",
|
|
184
224
|
"test:bundle": "npm run build && vitest run tests/bundle",
|
|
225
|
+
"test:consumer-types": "npm run build && node node_modules/typescript7/bin/tsc -p tests/consumer-types/webawesome/tsconfig.json",
|
|
185
226
|
"test:e2e": "npm run demo:build && playwright test",
|
|
186
|
-
"check": "npm run lint && npm run typecheck && npm test && npm run demo:build && npm pack --dry-run",
|
|
227
|
+
"check": "npm run check:format && npm run check:catalog && npm run check:recipes && npm run check:guidance && npm run check:ai-regressions && npm run lint && npm run typecheck && npm test && npm run test:consumer-types && npm run check:ai-signatures && npm run demo:build && npm pack --dry-run",
|
|
187
228
|
"prepublishOnly": "npm run check"
|
|
188
229
|
},
|
|
189
230
|
"peerDependencies": {
|
|
190
231
|
"@awesome.me/webawesome": "^3.12.0",
|
|
191
|
-
"kerfjs": "^
|
|
232
|
+
"kerfjs": "^5.0.0-0",
|
|
192
233
|
"lucide": "^1.43.0"
|
|
193
234
|
},
|
|
194
235
|
"peerDependenciesMeta": {
|
|
@@ -209,6 +250,8 @@
|
|
|
209
250
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
210
251
|
"happy-dom": "^20.9.0",
|
|
211
252
|
"lucide": "1.43.0",
|
|
253
|
+
"postcss": "^8.5.28",
|
|
254
|
+
"prettier": "^3.9.6",
|
|
212
255
|
"tsup": "^8.3.0",
|
|
213
256
|
"typescript": "^6.0.3",
|
|
214
257
|
"typescript7": "npm:typescript@^7.0.2",
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import '@kerfjs/ui/layout.css';
|
|
2
|
+
import './recipes.css';
|
|
3
|
+
|
|
4
|
+
import { LucideIcon } from '@kerfjs/ui/lucide-icon';
|
|
5
|
+
import { MenuHeader } from '@kerfjs/ui/menu-header';
|
|
6
|
+
import { MenuItem } from '@kerfjs/ui/menu-item';
|
|
7
|
+
import { PageHeader } from '@kerfjs/ui/page-header';
|
|
8
|
+
import { ResizableRegion } from '@kerfjs/ui/resizable-region';
|
|
9
|
+
import { Toolbar } from '@kerfjs/ui/toolbar';
|
|
10
|
+
import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
|
|
11
|
+
import { ToolbarText } from '@kerfjs/ui/toolbar-text';
|
|
12
|
+
import { ValueTable, ValueTableRow } from '@kerfjs/ui/value-table';
|
|
13
|
+
import { signal } from 'kerfjs';
|
|
14
|
+
import { Bell, Folder, Inbox, Settings } from 'lucide';
|
|
15
|
+
|
|
16
|
+
import type { RecipeFactory } from './types.js';
|
|
17
|
+
|
|
18
|
+
export const createRecipe: RecipeFactory = (announce) => {
|
|
19
|
+
const selected = signal('inbox');
|
|
20
|
+
const navigationSize = signal(224);
|
|
21
|
+
const inspectorSize = signal(240);
|
|
22
|
+
const responsivePane = signal<'content' | 'navigation' | 'inspector'>('content');
|
|
23
|
+
const render = () => <section class="kui-recipe recipe-shell kui-recipe__surface kui-layout" data-recipe="recipe-app-shell" data-responsive-pane={responsivePane.value}>
|
|
24
|
+
<Toolbar label="Atlas workspace" leading={<ToolbarControlGroup appearance="borderless" single><ToolbarText text="Atlas" /></ToolbarControlGroup>} trailing={<div class="recipe-shell__toolbar-actions"><ToolbarControlGroup className="recipe-shell__responsive-controls" appearance="borderless" label="Workspace panes"><button type="button" aria-label="Show navigation" aria-controls="recipe-shell-navigation" aria-pressed={String(responsivePane.value === 'navigation')} data-action="recipe-action" data-recipe-command="show-navigation"><LucideIcon icon={Folder} name="folder" /></button><button type="button" aria-label="Show content" aria-controls="recipe-shell-content" aria-pressed={String(responsivePane.value === 'content')} data-action="recipe-action" data-recipe-command="show-content"><LucideIcon icon={Inbox} name="inbox" /></button><button type="button" aria-label="Show inspector" aria-controls="recipe-shell-inspector" aria-pressed={String(responsivePane.value === 'inspector')} data-action="recipe-action" data-recipe-command="show-inspector"><LucideIcon icon={Settings} name="settings" /></button></ToolbarControlGroup><ToolbarControlGroup appearance="borderless" label="Workspace controls"><button type="button" aria-label="Notifications" data-action="recipe-action" data-recipe-command="notify"><LucideIcon icon={Bell} name="bell" /></button><button type="button" aria-label="Settings" data-action="recipe-action" data-recipe-command="settings"><LucideIcon icon={Settings} name="settings" /></button></ToolbarControlGroup></div>} />
|
|
25
|
+
<div class="recipe-shell__body">
|
|
26
|
+
<div id="recipe-shell-navigation" class="recipe-shell__nav-region"><ResizableRegion id="recipe-navigation" label="Navigation" size={navigationSize.value} min={180} max={320}><aside class="recipe-shell__nav kui-pane"><MenuHeader label="Workspace" /><nav class="recipe-shell__nav-list kui-pane__content kui-content" aria-label="Workspace"><section><MenuItem action="recipe-action" itemId="inbox" label="Inbox" icon={<LucideIcon icon={Inbox} name="inbox" />} selected={selected.value === 'inbox'} /><MenuItem action="recipe-action" itemId="projects" label="Projects with a deliberately wrapping title" icon={<LucideIcon icon={Folder} name="folder" />} selected={selected.value === 'projects'} multiline /></section></nav></aside></ResizableRegion></div>
|
|
27
|
+
<main id="recipe-shell-content" class="recipe-shell__main kui-pane"><PageHeader title={selected.value === 'inbox' ? 'Inbox triage' : 'Active projects'} action={<button class="kui-recipe__button" data-primary="true" type="button" data-action="recipe-action" data-recipe-command="new">New task</button>} /><div class="recipe-shell__main-body kui-pane__content kui-content"><p class="kui-recipe__ownership kui-content-item">Recipe owns pane geometry and one scroll owner per pane. The app owns routing, data, pane visibility, sizes, and persistence.</p><div class="recipe-shell__cards">{['Release accessibility audit', 'Prepare tablet navigation', 'Review stale-data states', 'Confirm package boundaries'].map((title) => <article class="recipe-shell__card kui-content-item"><strong>{title}</strong><p class="kui-recipe__muted">Assigned to the interface systems team · due this week</p></article>)}</div></div></main>
|
|
28
|
+
<div id="recipe-shell-inspector" class="recipe-shell__inspector-region"><ResizableRegion id="recipe-inspector" label="Inspector" size={inspectorSize.value} min={200} max={360} edge="start"><aside class="recipe-shell__inspector kui-pane"><PageHeader title="Inspector" /><div class="recipe-shell__inspector-body kui-pane__content kui-content"><ValueTable label="Selected task"><ValueTableRow label="Status" value="In review" /><ValueTableRow label="Owner" value="Mara Chen" /><ValueTableRow label="Priority" value="High" /></ValueTable></div></aside></ResizableRegion></div>
|
|
29
|
+
</div>
|
|
30
|
+
</section>;
|
|
31
|
+
return { render, action(command, element) { const pane = command.match(/^show-(navigation|content|inspector)$/)?.[1] as typeof responsivePane.value | undefined; if (pane) responsivePane.value = pane; const id = element.dataset.itemId; if (id) selected.value = id; announce(id ? `Opened ${id}` : pane ? `Showing ${pane}` : command === 'new' ? 'New task requested' : `${command} requested`); }, resize(id, size) { if (id === 'recipe-navigation') navigationSize.value = size; if (id === 'recipe-inspector') inspectorSize.value = size; announce(`${id} resized to ${size}px`); } };
|
|
32
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import '@kerfjs/ui/layout.css';
|
|
2
|
+
import '@kerfjs/ui/select/register';
|
|
3
|
+
import './recipes.css';
|
|
4
|
+
|
|
5
|
+
import { LucideIcon } from '@kerfjs/ui/lucide-icon';
|
|
6
|
+
import { SegmentedControl } from '@kerfjs/ui/segmented-control';
|
|
7
|
+
import { Select } from '@kerfjs/ui/select';
|
|
8
|
+
import { Toolbar } from '@kerfjs/ui/toolbar';
|
|
9
|
+
import { ToolbarControlGroup } from '@kerfjs/ui/toolbar-control-group';
|
|
10
|
+
import { ToolbarText } from '@kerfjs/ui/toolbar-text';
|
|
11
|
+
import { signal } from 'kerfjs';
|
|
12
|
+
import { Filter, MoreHorizontal, RefreshCw } from 'lucide';
|
|
13
|
+
|
|
14
|
+
import type { RecipeFactory } from './types.js';
|
|
15
|
+
|
|
16
|
+
export const createRecipe: RecipeFactory = (announce) => {
|
|
17
|
+
const view = signal('list');
|
|
18
|
+
const sort = signal('updated');
|
|
19
|
+
const filtered = signal(false);
|
|
20
|
+
const render = () => <section class="kui-recipe recipe-compact-toolbar kui-recipe__surface kui-layout" data-recipe="recipe-compact-toolbar">
|
|
21
|
+
<Toolbar
|
|
22
|
+
label="Task workspace controls"
|
|
23
|
+
leading={<ToolbarControlGroup appearance="borderless" single><ToolbarText text="Tasks" size="small" /></ToolbarControlGroup>}
|
|
24
|
+
center={<ToolbarControlGroup><SegmentedControl id="recipe-view" label="View" value={view.value} action="recipe-action" appearance="toolbar" shape="pill" size="small" choices={[{ value: 'list', label: 'List' }, { value: 'board', label: 'Board' }, { value: 'timeline', label: 'Timeline' }]} /></ToolbarControlGroup>}
|
|
25
|
+
trailing={<div class="kui-control-cluster"><Select name="recipe-sort" value={sort.value} ariaLabel="Sort tasks" fitMenu choices={[{ value: 'updated', label: 'Recently updated' }, { value: 'priority', label: 'Priority' }, { value: 'owner', label: 'Owner' }, { value: 'created', label: 'Created date' }]} /><ToolbarControlGroup label="Task actions" buttonAppearance="push"><button type="button" aria-label="Toggle filters" aria-pressed={String(filtered.value)} data-action="recipe-action" data-recipe-command="filter"><LucideIcon icon={Filter} name="filter" /></button><button type="button" aria-label="Refresh tasks" data-action="recipe-action" data-recipe-command="refresh"><LucideIcon icon={RefreshCw} name="refresh-cw" /></button></ToolbarControlGroup><button class="kui-recipe__button" type="button" aria-label="More task actions" data-action="recipe-action" data-recipe-command="more"><LucideIcon icon={MoreHorizontal} name="more-horizontal" /></button></div>}
|
|
26
|
+
/>
|
|
27
|
+
<div class="recipe-compact-toolbar__guide kui-content-item"><div><strong>ToolbarControlGroup</strong><span class="kui-recipe__muted">Related commands or pressed tools.</span></div><div><strong>SegmentedControl</strong><span class="kui-recipe__muted">A few visible exclusive choices.</span></div><div><strong>Select</strong><span class="kui-recipe__muted">A longer controlled value list.</span></div><div><strong>Ordinary button</strong><span class="kui-recipe__muted">One independent command.</span></div></div>
|
|
28
|
+
<p class="kui-recipe__ownership kui-content-item">The recipe owns control semantics and wrapping. The app owns values, actions, persistence, and responsive priority.</p>
|
|
29
|
+
</section>;
|
|
30
|
+
return {
|
|
31
|
+
render,
|
|
32
|
+
action(command, element) {
|
|
33
|
+
const value = element.dataset.segmentValue;
|
|
34
|
+
if (value) view.value = value;
|
|
35
|
+
if (command === 'filter') filtered.value = !filtered.value;
|
|
36
|
+
announce(value ? `View: ${value}` : command === 'filter' ? `Filters ${filtered.value ? 'on' : 'off'}` : `${command} requested`);
|
|
37
|
+
},
|
|
38
|
+
change(element) {
|
|
39
|
+
const value = (element as HTMLElement & { value?: string }).value;
|
|
40
|
+
if (element.getAttribute('name') === 'recipe-sort' && value) { sort.value = value; announce(`Sort: ${value}`); }
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
};
|