@kerfjs/ui 4.4.1 → 4.5.0-beta.1
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 +425 -42
- package/ai/application-ui-diagnostic-ids-v1.json +92 -0
- package/ai/application-ui-profile-sync.cjs +529 -0
- package/ai/application-ui-profile.d.mts +1 -0
- package/ai/application-ui-profile.d.ts +102 -0
- package/ai/application-ui-profile.defaults.json +41 -0
- package/ai/application-ui-profile.mjs +626 -0
- package/ai/application-ui-profile.schema.json +130 -0
- package/ai/catalog-authoring.json +25 -0
- package/ai/catalog-authoring.schema.json +61 -0
- package/ai/compile-time-contracts-v1.json +102 -0
- package/ai/compile-time-contracts-v1.schema.json +49 -0
- package/ai/component-catalog-extension-v2.d.ts +7 -0
- package/ai/component-catalog-extension-v2.schema.json +7 -0
- package/ai/component-catalog-extension.schema.json +79 -0
- package/ai/component-catalog-v2-overrides.json +319 -0
- package/ai/component-catalog-v2.d.ts +67 -0
- package/ai/component-catalog-v2.json +4675 -0
- package/ai/component-catalog-v2.schema.json +246 -0
- package/ai/component-catalog.json +5826 -0
- package/ai/component-catalog.schema.json +95 -0
- package/ai/public-api-signatures-v1.md +1719 -0
- package/ai/skill.md +129 -21
- package/ai/webawesome-jsx-signatures-v1.md +106 -0
- package/analyzer/cli.mjs +46 -0
- package/analyzer/index.d.mts +45 -0
- package/analyzer/index.mjs +737 -0
- package/analyzer/report.schema.json +66 -0
- package/dist/app-tab.d.ts +14 -2
- package/dist/app-tab.js +3 -1
- package/dist/browser/app-tab.js +3 -2
- package/dist/browser/disclosure-arrow.js +4 -0
- package/dist/browser/empty-state.js +3 -3
- package/dist/browser/floating-toolbar.js +3 -0
- package/dist/browser/list-action-row.js +4 -0
- package/dist/browser/list-header.js +6 -0
- package/dist/browser/list-inset-control.js +3 -0
- package/dist/browser/list-inset-text.js +3 -0
- package/dist/browser/list-item.js +4 -0
- package/dist/browser/list.js +3 -0
- package/dist/browser/loading-spinner.js +2 -2
- package/dist/browser/lucide-icon.js +2 -2
- package/dist/browser/pane.js +3 -0
- package/dist/browser/panel-header.js +7 -0
- package/dist/browser/resizable-region.js +2 -2
- package/dist/browser/segmented-control.js +3 -2
- package/dist/browser/select.js +4 -3
- package/dist/browser/skeleton.js +3 -0
- package/dist/browser/state-banner.js +3 -2
- package/dist/browser/sunken-panel.js +3 -0
- 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 +3 -2
- package/dist/browser/toolbar.js +2 -2
- package/dist/browser/value-table.js +3 -2
- package/dist/catalog-resources.d.ts +34 -0
- package/dist/catalog-resources.js +22 -0
- package/dist/catalog-resources.js.map +1 -0
- package/dist/catalog.d.ts +150 -0
- package/dist/catalog.js +377 -0
- package/dist/catalog.js.map +1 -0
- package/dist/chunk-5VTB2BKP.js +35 -0
- package/dist/chunk-5VTB2BKP.js.map +1 -0
- package/dist/chunk-6WTETUZ6.js +21 -0
- package/dist/chunk-6WTETUZ6.js.map +1 -0
- package/dist/chunk-7KEUJIIC.js +218 -0
- package/dist/chunk-7KEUJIIC.js.map +1 -0
- package/dist/chunk-7XRDUG4Y.js +58 -0
- package/dist/chunk-7XRDUG4Y.js.map +1 -0
- package/dist/chunk-B3A3IVO5.js +83 -0
- package/dist/chunk-B3A3IVO5.js.map +1 -0
- package/dist/chunk-DPDBVKNA.js +113 -0
- package/dist/chunk-DPDBVKNA.js.map +1 -0
- package/dist/chunk-FEAMR7S4.js +68 -0
- package/dist/chunk-FEAMR7S4.js.map +1 -0
- package/dist/chunk-FEY65TBV.js +15 -0
- package/dist/chunk-FEY65TBV.js.map +1 -0
- package/dist/{chunk-TRNBHPNW.js → chunk-IJBSZ4NX.js} +11 -8
- package/dist/chunk-IJBSZ4NX.js.map +1 -0
- package/dist/chunk-J5BFYY7Q.js +35 -0
- package/dist/chunk-J5BFYY7Q.js.map +1 -0
- package/dist/chunk-JRKSK2HX.js +79 -0
- package/dist/chunk-JRKSK2HX.js.map +1 -0
- package/dist/chunk-KWWR5VMS.js +45 -0
- package/dist/chunk-KWWR5VMS.js.map +1 -0
- package/dist/chunk-LPOMRC4C.js +25 -0
- package/dist/chunk-LPOMRC4C.js.map +1 -0
- package/dist/chunk-M7JBER5U.js +41 -0
- package/dist/chunk-M7JBER5U.js.map +1 -0
- package/dist/chunk-NENBUYX5.js +79 -0
- package/dist/chunk-NENBUYX5.js.map +1 -0
- package/dist/chunk-NWSEP5RT.js +117 -0
- package/dist/chunk-NWSEP5RT.js.map +1 -0
- package/dist/chunk-OJWWHFH3.js +426 -0
- package/dist/chunk-OJWWHFH3.js.map +1 -0
- package/dist/chunk-PSKN344N.js +114 -0
- package/dist/chunk-PSKN344N.js.map +1 -0
- package/dist/chunk-PX3YSS5C.js +32 -0
- package/dist/chunk-PX3YSS5C.js.map +1 -0
- package/dist/chunk-QO55FN2Y.js +20 -0
- package/dist/chunk-QO55FN2Y.js.map +1 -0
- package/dist/chunk-R7ZWUN64.js +38 -0
- package/dist/chunk-R7ZWUN64.js.map +1 -0
- package/dist/chunk-RKVQEH4J.js +61 -0
- package/dist/chunk-RKVQEH4J.js.map +1 -0
- package/dist/chunk-SRSJO5QE.js +40 -0
- package/dist/chunk-SRSJO5QE.js.map +1 -0
- package/dist/chunk-SYDMYBPG.js +31 -0
- package/dist/chunk-SYDMYBPG.js.map +1 -0
- package/dist/chunk-TB6DY7H5.js +25 -0
- package/dist/chunk-TB6DY7H5.js.map +1 -0
- package/dist/chunk-UFZKLBCF.js +108 -0
- package/dist/chunk-UFZKLBCF.js.map +1 -0
- package/dist/{chunk-MQIZAD4A.js → chunk-UKKSMZYT.js} +76 -22
- package/dist/chunk-UKKSMZYT.js.map +1 -0
- package/dist/chunk-W5L2JSBY.js +40 -0
- package/dist/chunk-W5L2JSBY.js.map +1 -0
- package/dist/chunk-Y45PPBDV.js +105 -0
- package/dist/chunk-Y45PPBDV.js.map +1 -0
- package/dist/chunk-Y6LYKFXY.js +43 -0
- package/dist/chunk-Y6LYKFXY.js.map +1 -0
- package/dist/collapsible-panel.d.ts +66 -0
- package/dist/collapsible-panel.js +63 -0
- package/dist/collapsible-panel.js.map +1 -0
- package/dist/device-class.d.ts +62 -0
- package/dist/device-class.js +103 -0
- package/dist/device-class.js.map +1 -0
- 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/divider-sides-267FA7sY.d.ts +4 -0
- package/dist/empty-state.d.ts +1 -1
- package/dist/empty-state.js +1 -1
- package/dist/floating-toolbar.d.ts +28 -0
- package/dist/floating-toolbar.js +3 -0
- package/dist/floating-toolbar.js.map +1 -0
- package/dist/index.d.ts +20 -9
- package/dist/index.js +29 -18
- package/dist/list-action-row.d.ts +52 -0
- package/dist/list-action-row.js +5 -0
- package/dist/list-action-row.js.map +1 -0
- package/dist/list-header.d.ts +64 -0
- package/dist/list-header.js +7 -0
- package/dist/list-header.js.map +1 -0
- package/dist/list-inset-control.d.ts +16 -0
- package/dist/list-inset-control.js +3 -0
- package/dist/list-inset-control.js.map +1 -0
- package/dist/list-inset-text.d.ts +25 -0
- package/dist/list-inset-text.js +3 -0
- package/dist/list-inset-text.js.map +1 -0
- package/dist/list-item.d.ts +33 -0
- package/dist/list-item.js +5 -0
- package/dist/list-item.js.map +1 -0
- package/dist/list.d.ts +19 -0
- package/dist/list.js +3 -0
- package/dist/list.js.map +1 -0
- package/dist/loading-spinner.js +1 -1
- package/dist/nav-stack.d.ts +38 -0
- package/dist/nav-stack.js +5 -0
- package/dist/nav-stack.js.map +1 -0
- package/dist/pane.d.ts +49 -0
- package/dist/pane.js +4 -0
- package/dist/pane.js.map +1 -0
- package/dist/panel-header.d.ts +42 -0
- package/dist/panel-header.js +7 -0
- package/dist/panel-header.js.map +1 -0
- package/dist/resizable-region.d.ts +3 -1
- package/dist/resizable-region.js +1 -1
- package/dist/segmented-control.d.ts +8 -6
- package/dist/segmented-control.js +2 -1
- package/dist/select-register.d.ts +5 -1
- package/dist/select-register.js +3 -0
- package/dist/select-register.js.map +1 -1
- package/dist/select.d.ts +15 -6
- package/dist/select.js +2 -1
- package/dist/skeleton.d.ts +24 -0
- package/dist/skeleton.js +3 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/split-view.d.ts +42 -0
- package/dist/split-view.js +81 -0
- package/dist/split-view.js.map +1 -0
- package/dist/state-banner.d.ts +8 -3
- package/dist/state-banner.js +2 -1
- package/dist/styles/app-tab.css +152 -0
- package/dist/styles/catalog.css +575 -0
- package/dist/styles/collapsible-panel.css +146 -0
- package/dist/styles/disclosure-arrow.css +23 -0
- package/dist/styles/empty-state.css +47 -0
- package/dist/styles/floating-toolbar.css +38 -0
- package/dist/styles/foundation.css +362 -0
- package/dist/styles/layout.css +55 -0
- package/dist/styles/list-action-row.css +160 -0
- package/dist/styles/list-header.css +160 -0
- package/dist/styles/list-inset-control.css +7 -0
- package/dist/styles/list-inset-text.css +14 -0
- package/dist/styles/list-item.css +123 -0
- package/dist/styles/list.css +43 -0
- package/dist/styles/loading-spinner.css +21 -0
- package/dist/styles/lucide-icon.css +6 -0
- package/dist/styles/nav-stack.css +111 -0
- package/dist/styles/pane.css +62 -0
- package/dist/styles/panel-header.css +64 -0
- package/dist/styles/resizable-region.css +111 -0
- package/{src → dist/styles}/segmented-control.css +58 -31
- package/dist/styles/select.css +140 -0
- package/dist/styles/skeleton.css +31 -0
- package/dist/styles/split-view.css +35 -0
- package/dist/styles/state-banner.css +198 -0
- package/dist/styles/styles.css +24 -0
- package/dist/styles/sunken-panel.css +22 -0
- package/dist/styles/tab-bar.css +45 -0
- package/dist/styles/tab-scaffold.css +84 -0
- package/dist/styles/token-search-field.css +289 -0
- package/dist/styles/toolbar-control-group.css +225 -0
- package/dist/styles/toolbar-text.css +92 -0
- package/dist/styles/toolbar.css +97 -0
- package/dist/styles/value-table.css +75 -0
- package/dist/styles/webawesome.css +399 -0
- package/dist/styles/workbench.css +94 -0
- package/dist/sunken-panel.d.ts +19 -0
- package/dist/sunken-panel.js +3 -0
- package/dist/sunken-panel.js.map +1 -0
- package/dist/tab-bar.d.ts +10 -2
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +30 -0
- package/dist/tab-scaffold.js +51 -0
- package/dist/tab-scaffold.js.map +1 -0
- package/dist/token-search-field.d.ts +59 -0
- package/dist/token-search-field.js +4 -0
- package/dist/token-search-field.js.map +1 -0
- package/dist/toolbar-control-group.d.ts +11 -5
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.d.ts +31 -4
- package/dist/toolbar-text.js +2 -1
- package/dist/toolbar.d.ts +5 -3
- package/dist/toolbar.js +1 -1
- package/dist/value-table.d.ts +13 -3
- package/dist/value-table.js +2 -1
- package/dist/webawesome.d.ts +100 -0
- package/dist/webawesome.js +3 -0
- package/dist/webawesome.js.map +1 -0
- package/dist/wire-catalog.d.ts +57 -0
- package/dist/wire-catalog.js +302 -0
- package/dist/wire-catalog.js.map +1 -0
- package/dist/wire-nav-stack.d.ts +15 -0
- package/dist/wire-nav-stack.js +103 -0
- package/dist/wire-nav-stack.js.map +1 -0
- package/dist/wire-resizable-regions.d.ts +1 -1
- package/dist/wire-resizable-regions.js +1 -1
- package/dist/wire-sidebar.d.ts +43 -0
- package/dist/wire-sidebar.js +128 -0
- package/dist/wire-sidebar.js.map +1 -0
- package/dist/wire-tab-bars.d.ts +18 -2
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-tab-scaffold.d.ts +11 -0
- package/dist/wire-tab-scaffold.js +21 -0
- package/dist/wire-tab-scaffold.js.map +1 -0
- package/dist/wire-token-search-fields.d.ts +111 -0
- package/dist/wire-token-search-fields.js +5 -0
- package/dist/wire-token-search-fields.js.map +1 -0
- package/dist/workbench.d.ts +33 -0
- package/dist/workbench.js +64 -0
- package/dist/workbench.js.map +1 -0
- package/docs/accessibility.md +124 -4
- package/docs/app-layouts.md +61 -0
- package/docs/catalog.md +364 -0
- package/docs/collapsible-panel.md +129 -0
- package/docs/component-contract.md +312 -5
- package/docs/component-selection.md +219 -0
- package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
- package/docs/design/templates/empty-state/filtered.svg +1 -0
- package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
- package/docs/design/templates/empty-state/with-action.svg +1 -0
- package/docs/design/templates/empty-state-dark.svg +1 -0
- package/docs/design/templates/empty-state.svg +1 -0
- package/docs/design/templates/list/compact-dark.svg +1 -0
- package/docs/design/templates/list/compact.svg +1 -0
- package/docs/design/templates/list/stack-dark.svg +1 -0
- package/docs/design/templates/list/stack.svg +1 -0
- package/docs/design/templates/list-action-row/default-dark.svg +1 -0
- package/docs/design/templates/list-action-row/default.svg +1 -0
- package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
- package/docs/design/templates/list-action-row/selected.svg +1 -0
- package/docs/design/templates/list-action-row-dark.svg +1 -0
- package/docs/design/templates/list-action-row.svg +1 -0
- package/docs/design/templates/list-dark.svg +1 -0
- package/docs/design/templates/list-header/action-dark.svg +1 -0
- package/docs/design/templates/list-header/action.svg +1 -0
- package/docs/design/templates/list-header/count-dark.svg +1 -0
- package/docs/design/templates/list-header/count.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
- package/docs/design/templates/list-header-dark.svg +1 -0
- package/docs/design/templates/list-header.svg +1 -0
- package/docs/design/templates/list-item/default-dark.svg +1 -0
- package/docs/design/templates/list-item/default.svg +1 -0
- package/docs/design/templates/list-item/multiline-dark.svg +1 -0
- package/docs/design/templates/list-item/multiline.svg +1 -0
- package/docs/design/templates/list-item/selected-dark.svg +1 -0
- package/docs/design/templates/list-item/selected.svg +1 -0
- package/docs/design/templates/list-item/trailing-dark.svg +1 -0
- package/docs/design/templates/list-item/trailing.svg +1 -0
- package/docs/design/templates/list-item-dark.svg +1 -0
- package/docs/design/templates/list-item.svg +1 -0
- package/docs/design/templates/list.svg +1 -0
- package/docs/design/templates/panel-header/icon-actions-dark.svg +1 -0
- package/docs/design/templates/panel-header/icon-actions.svg +1 -0
- package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +1 -0
- package/docs/design/templates/panel-header/icon-summary-actions.svg +1 -0
- package/docs/design/templates/panel-header/no-icon-dark.svg +1 -0
- package/docs/design/templates/panel-header/no-icon.svg +1 -0
- package/docs/design/templates/panel-header/page-heading-dark.svg +1 -0
- package/docs/design/templates/panel-header/page-heading.svg +1 -0
- package/docs/design/templates/panel-header/title-only-dark.svg +1 -0
- package/docs/design/templates/panel-header/title-only.svg +1 -0
- package/docs/design/templates/panel-header-dark.svg +1 -0
- package/docs/design/templates/panel-header.svg +1 -0
- package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
- package/docs/design/templates/segmented-control/equal.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small.svg +1 -0
- package/docs/design/templates/segmented-control-dark.svg +1 -0
- package/docs/design/templates/segmented-control.svg +1 -0
- package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
- package/docs/design/templates/skeleton/avatar.svg +1 -0
- package/docs/design/templates/skeleton/block-dark.svg +1 -0
- package/docs/design/templates/skeleton/block.svg +1 -0
- package/docs/design/templates/skeleton/lines-dark.svg +1 -0
- package/docs/design/templates/skeleton/lines.svg +1 -0
- package/docs/design/templates/skeleton-dark.svg +1 -0
- package/docs/design/templates/skeleton.svg +1 -0
- package/docs/design/templates/state-banner/danger-dark.svg +1 -0
- package/docs/design/templates/state-banner/danger.svg +1 -0
- package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
- package/docs/design/templates/state-banner/info-badge.svg +1 -0
- package/docs/design/templates/state-banner/info-dark.svg +1 -0
- package/docs/design/templates/state-banner/info.svg +1 -0
- package/docs/design/templates/state-banner/success-dark.svg +1 -0
- package/docs/design/templates/state-banner/success.svg +1 -0
- package/docs/design/templates/state-banner/warning-dark.svg +1 -0
- package/docs/design/templates/state-banner/warning.svg +1 -0
- package/docs/design/templates/state-banner-dark.svg +1 -0
- package/docs/design/templates/state-banner.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
- package/docs/design/templates/tab-bar-dark.svg +1 -0
- package/docs/design/templates/tab-bar.svg +1 -0
- package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
- package/docs/design/templates/token-search-field/empty.svg +1 -0
- package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
- package/docs/design/templates/token-search-field/tokens.svg +1 -0
- package/docs/design/templates/token-search-field-dark.svg +1 -0
- package/docs/design/templates/token-search-field.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
- package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group.svg +1 -0
- package/docs/design/templates/toolbar-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/default.svg +1 -0
- package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/large.svg +1 -0
- package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/small.svg +1 -0
- package/docs/design/templates/toolbar-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-text.svg +1 -0
- package/docs/design/templates/toolbar.svg +1 -0
- package/docs/design/templates/value-table/details-dark.svg +1 -0
- package/docs/design/templates/value-table/details.svg +1 -0
- package/docs/design/templates/value-table-dark.svg +1 -0
- package/docs/design/templates/value-table.svg +1 -0
- package/docs/design/templates.md +130 -0
- package/docs/design-philosophy.md +59 -1
- package/docs/device-class.md +54 -0
- package/docs/examples/application-ui-profile.json +60 -0
- package/docs/examples/command-palette-adapter.tsx +120 -0
- package/docs/examples/component-catalog-extension-v2.json +81 -0
- package/docs/examples/component-catalog-extension.json +45 -0
- package/docs/layout.md +150 -0
- package/docs/nav-stack.md +56 -0
- package/docs/recipes.md +167 -0
- package/docs/split-view.md +49 -0
- package/docs/sunken-panel.md +53 -0
- package/docs/tab-scaffold.md +50 -0
- package/docs/type-contracts.md +102 -0
- package/docs/ui-analyzer.md +86 -0
- package/docs/ui-doctor.md +84 -0
- package/docs/ui-evaluator.md +122 -0
- package/docs/ux-demo.md +23 -9
- package/docs/webawesome-theme.md +87 -7
- package/docs/workbench.md +73 -0
- package/doctor/cli.mjs +115 -0
- package/doctor/config.schema.json +68 -0
- package/doctor/eslint-diagnostics.d.mts +8 -0
- package/doctor/eslint-diagnostics.mjs +7 -0
- package/doctor/index.d.mts +116 -0
- package/doctor/index.mjs +1257 -0
- package/doctor/report.schema.json +118 -0
- package/evaluator/cli.mjs +89 -0
- package/evaluator/index.d.mts +90 -0
- package/evaluator/index.mjs +1138 -0
- package/evaluator/report.schema.json +152 -0
- package/llms.txt +197 -14
- package/package.json +228 -46
- package/traversal-exclusions.mjs +27 -0
- package/ux-demo/recipes/app-shell.tsx +228 -0
- package/ux-demo/recipes/collapsible-sidebar.tsx +223 -0
- package/ux-demo/recipes/compact-toolbar.tsx +145 -0
- package/ux-demo/recipes/composer-form.tsx +151 -0
- package/ux-demo/recipes/list-detail-dialog.tsx +181 -0
- package/ux-demo/recipes/list-workspace-states.tsx +183 -0
- package/ux-demo/recipes/loaders.ts +36 -0
- package/ux-demo/recipes/loading-inspector.tsx +164 -0
- package/ux-demo/recipes/mount-recipe.ts +64 -0
- package/ux-demo/recipes/navigation-sidebar.tsx +137 -0
- package/ux-demo/recipes/navigation-stack.tsx +118 -0
- package/ux-demo/recipes/recipes.css +448 -0
- package/ux-demo/recipes/types.ts +19 -0
- package/ux-demo/recipes/workspace-header.tsx +67 -0
- package/dist/browser/dialog-header.js +0 -3
- package/dist/browser/menu-header.js +0 -3
- package/dist/browser/menu-item.js +0 -3
- package/dist/browser/page-header.js +0 -3
- package/dist/chunk-2PES33HS.js +0 -13
- package/dist/chunk-2PES33HS.js.map +0 -1
- package/dist/chunk-3O7ZEUW2.js +0 -27
- package/dist/chunk-3O7ZEUW2.js.map +0 -1
- package/dist/chunk-4HHCYSGT.js +0 -79
- package/dist/chunk-4HHCYSGT.js.map +0 -1
- package/dist/chunk-55DATV3A.js +0 -10
- package/dist/chunk-55DATV3A.js.map +0 -1
- package/dist/chunk-723YNJBH.js +0 -17
- package/dist/chunk-723YNJBH.js.map +0 -1
- package/dist/chunk-DAFFDO33.js +0 -14
- package/dist/chunk-DAFFDO33.js.map +0 -1
- package/dist/chunk-GY5WH7TO.js +0 -10
- package/dist/chunk-GY5WH7TO.js.map +0 -1
- package/dist/chunk-MBR3TYNL.js +0 -14
- package/dist/chunk-MBR3TYNL.js.map +0 -1
- package/dist/chunk-MFKMHK5W.js +0 -10
- package/dist/chunk-MFKMHK5W.js.map +0 -1
- package/dist/chunk-MQIZAD4A.js.map +0 -1
- package/dist/chunk-NHXX4JI5.js +0 -28
- 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 +0 -24
- package/dist/chunk-T5XNMVOI.js.map +0 -1
- package/dist/chunk-TRNBHPNW.js.map +0 -1
- package/dist/chunk-VHW5YNIB.js +0 -17
- package/dist/chunk-VHW5YNIB.js.map +0 -1
- package/dist/chunk-VKH2JLDZ.js +0 -10
- package/dist/chunk-VKH2JLDZ.js.map +0 -1
- package/dist/chunk-WVPYVDXY.js +0 -15
- package/dist/chunk-WVPYVDXY.js.map +0 -1
- package/dist/chunk-WY4CAEPG.js +0 -14
- package/dist/chunk-WY4CAEPG.js.map +0 -1
- package/dist/dialog-header.d.ts +0 -14
- package/dist/dialog-header.js +0 -3
- package/dist/dialog-header.js.map +0 -1
- package/dist/menu-header.d.ts +0 -15
- package/dist/menu-header.js +0 -3
- package/dist/menu-header.js.map +0 -1
- package/dist/menu-item.d.ts +0 -22
- package/dist/menu-item.js +0 -3
- package/dist/menu-item.js.map +0 -1
- package/dist/page-header.d.ts +0 -9
- package/dist/page-header.js +0 -3
- package/dist/page-header.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/src/webawesome.css +0 -178
package/docs/layout.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Pane and content layout
|
|
2
|
+
|
|
3
|
+
Import `Pane` from `@kerfjs/ui/pane`. Browser-aware bundlers receive its CSS
|
|
4
|
+
automatically; other consumers can import `@kerfjs/ui/pane.css`. Import
|
|
5
|
+
`@kerfjs/ui/layout.css` as well when using the related content-item and control
|
|
6
|
+
layout classes. The vocabulary deliberately describes structure rather than
|
|
7
|
+
location: the same pane can be placed anywhere.
|
|
8
|
+
|
|
9
|
+
## Anatomy
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
<Pane
|
|
13
|
+
element="aside"
|
|
14
|
+
label="Workspace"
|
|
15
|
+
contentElement="nav"
|
|
16
|
+
contentLabel="Workspace pages"
|
|
17
|
+
separators={["inline-end"]}
|
|
18
|
+
header={<Toolbar label="Workspace" ... />}
|
|
19
|
+
footer={<Toolbar label="Actions" ... />}
|
|
20
|
+
>
|
|
21
|
+
<section>...</section>
|
|
22
|
+
<section>...</section>
|
|
23
|
+
</Pane>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`Pane` has no padding. It reserves rows for an optional vertical header, one
|
|
27
|
+
scrolling vertical content area, and an optional footer. A header may contain a
|
|
28
|
+
top toolbar followed by secondary toolbar or status rows. A main area or dialog
|
|
29
|
+
often omits the footer; a navigation pane commonly uses all three. Fixed chrome
|
|
30
|
+
stays outside `.kui-pane__content`, which is the pane's only scroll owner.
|
|
31
|
+
|
|
32
|
+
Pass any combination of logical sides to `separators`: `block-start`,
|
|
33
|
+
`block-end`, `inline-start`, and `inline-end`. Every line is off by default and
|
|
34
|
+
each enabled side uses `--kui-pane-separator-width` (1px) and
|
|
35
|
+
`--kui-pane-separator-color` (`--kui-color-border`). Logical sides keep pane
|
|
36
|
+
boundaries correct in both left-to-right and right-to-left layouts.
|
|
37
|
+
|
|
38
|
+
`.kui-content` is a vertical stack with a 24px gap between major children.
|
|
39
|
+
Sections may contain adjacent `ListItem` rows without adding another major gap.
|
|
40
|
+
Ordinary surface-like children use `.kui-content-item` and own their complete
|
|
41
|
+
geometry:
|
|
42
|
+
|
|
43
|
+
- 8px inline margin from the pane edge
|
|
44
|
+
- 1px border, transparent by default
|
|
45
|
+
- 8px internal padding and 8px internal gap
|
|
46
|
+
- `calc(1px + remify(11px))`, or 12px, rounded corners
|
|
47
|
+
|
|
48
|
+
Use `.kui-content-item--pill` for the 22px pill radius expressed as
|
|
49
|
+
`calc(1px + remify(21px))`. Consumers can make a content item visible without
|
|
50
|
+
changing its geometry by setting `--kui-content-item-border` and
|
|
51
|
+
`--kui-content-item-background`.
|
|
52
|
+
|
|
53
|
+
Nested rounded edges stay concentric by subtracting their full inset from the
|
|
54
|
+
outer radius. For example, `SegmentedControl` places each item behind a 1px
|
|
55
|
+
control border and 1px control padding, so its default rounded-rectangle radius
|
|
56
|
+
is 10px inside the control's 12px radius.
|
|
57
|
+
|
|
58
|
+
A visible parent surface does not make every child another visible card. The
|
|
59
|
+
composer recipe keeps its form as the single surface, uses `PanelHeader` for
|
|
60
|
+
its task hierarchy, and places field and action control edges on the shared 8px
|
|
61
|
+
inline gutter instead of nesting them inside another padded content item. A
|
|
62
|
+
conditional `StateBanner` remains visibly distinct because it communicates
|
|
63
|
+
semantic status.
|
|
64
|
+
|
|
65
|
+
## Public roles and tokens
|
|
66
|
+
|
|
67
|
+
| Need | Class | Token / default |
|
|
68
|
+
| ---------------------------------------- | ------------------------- | ------------------------------------- |
|
|
69
|
+
| Unpadded header/content/footer structure | `Pane`, `.kui-pane` | — |
|
|
70
|
+
| Scrolling pane content | `.kui-pane__content` | — |
|
|
71
|
+
| Optional logical-edge separators | `Pane.separators` | `--kui-pane-separator-width: 1px` |
|
|
72
|
+
| Major vertical rhythm | `.kui-content` | `--kui-layout-content-gap: 24px` |
|
|
73
|
+
| Self-contained child geometry | `.kui-content-item` | 8px margin + 1px border + 8px padding |
|
|
74
|
+
| Pill child | `.kui-content-item--pill` | `--kui-layout-pill-radius: 22px` |
|
|
75
|
+
| Related controls | `.kui-control-cluster` | `--kui-layout-control-gap: 8px` |
|
|
76
|
+
| Inline metadata | `.kui-inline-metadata` | `--kui-layout-metadata-gap: 4px` |
|
|
77
|
+
| Explicit scroll owner outside a pane | `.kui-scroll-owner` | `overflow: auto` |
|
|
78
|
+
|
|
79
|
+
The component layer applies the same contract to `Toolbar`, `ListHeader`,
|
|
80
|
+
`ListItem`, `PanelHeader`, `StateBanner`, `ValueTable`,
|
|
81
|
+
`ValueTableRow`, tabs, and form controls. A value-table row separator starts at
|
|
82
|
+
the row's 8px content inset, or at 40px when the row contains its 24px leading
|
|
83
|
+
icon and 8px gap, and always ends 8px from the right edge. Each value-table row
|
|
84
|
+
also keeps 8px of root-scaled block padding independently of its semantic inline
|
|
85
|
+
inset. Most interactive rows and toolbar groups are 44px tall.
|
|
86
|
+
Toolbar groups reserve a real 1px outer border around a 42px inner area, even
|
|
87
|
+
when their border and background are transparent.
|
|
88
|
+
`PanelHeader` is a plain top `Toolbar` used as a panel, dialog, or page heading
|
|
89
|
+
and overrides no Toolbar styles. The leading zone holds the optional icon (a
|
|
90
|
+
normal bordered `ToolbarControlGroup` given a brand fill with a matching border,
|
|
91
|
+
a 22px glyph) and the title as extra-large `ToolbarText`; the app's trailing
|
|
92
|
+
controls go straight into the trailing zone, and the icon group is omitted when
|
|
93
|
+
no icon is passed. The optional subtitle is a separate row below the toolbar, its
|
|
94
|
+
text left-aligned with the title text.
|
|
95
|
+
|
|
96
|
+
## Spacing scale
|
|
97
|
+
|
|
98
|
+
Spacing is not a free choice. The official scale is five canonical steps, each
|
|
99
|
+
expressing exactly one relationship — pick the step by **how connected two
|
|
100
|
+
elements are**, not by eye. Every scalable value is `remify`-authored against the
|
|
101
|
+
fixed 16px baseline, so it delivers as `rem`.
|
|
102
|
+
|
|
103
|
+
| Value | Token | Relationship — when to use |
|
|
104
|
+
| ----- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
105
|
+
| 0px | `--kui-space-none` | No separation. The elements read as a single unit (a control and its own affordance, adjacent `ListItem` rows). |
|
|
106
|
+
| 4px | `--kui-space-2xs` | Very minor. Still one connected cluster, but readability or aesthetics demand a hair of air (inline metadata, an icon beside its label). |
|
|
107
|
+
| 8px | `--kui-space-xs` | Standard. Between elements **within a group** — the content-item padding/gutter, gaps between toolbar controls in a group. |
|
|
108
|
+
| 16px | `--kui-space-m` | Minor. Between **homogeneous groups** — two lists, two sibling sections of the same kind. |
|
|
109
|
+
| 24px | `--kui-space-l` | Major. Between **heterogeneous groups** — the `.kui-content` rhythm between major, differing regions. |
|
|
110
|
+
|
|
111
|
+
The two relationships that get confused most are 8px vs 24px: 8px is _inside_ a
|
|
112
|
+
group, 24px is _between_ major, differing regions. 16px sits between them for
|
|
113
|
+
same-kind groups.
|
|
114
|
+
|
|
115
|
+
`--kui-space-s` (12px) and `--kui-space-xl` (32px) exist but are **off the
|
|
116
|
+
canonical rhythm** — reach for them only as a deliberate exception, never as a
|
|
117
|
+
default step. Prefer the five canonical tokens so spacing stays legible and
|
|
118
|
+
consistent across every surface.
|
|
119
|
+
|
|
120
|
+
## Ownership rules
|
|
121
|
+
|
|
122
|
+
1. Do not pad a sidebar, main area, dialog, or `.kui-pane` shell. Children own
|
|
123
|
+
their own margin, border, background, padding, and radius.
|
|
124
|
+
2. Use 24px gaps for major vertical separation and 8px gaps inside an item or
|
|
125
|
+
between toolbar groups. Do not confuse the two relationships.
|
|
126
|
+
3. Wrap toolbar content in `ToolbarControlGroup`, including dormant text. A
|
|
127
|
+
transparent group still reserves the same 44px geometry as a visible group.
|
|
128
|
+
4. Keep one scrolling content owner per pane. Toolbar and footer siblings stay
|
|
129
|
+
fixed while the content scrolls.
|
|
130
|
+
5. A split item keeps dormant and interactive regions separate. For example,
|
|
131
|
+
`ListHeader` renders its title/count-or-badge cluster separately from its optional
|
|
132
|
+
logical-end 44px action. The header fills the available inline width and its
|
|
133
|
+
action glyph defaults to 18px; disclosure mode makes the title cluster itself
|
|
134
|
+
the button and supplies the production `DisclosureArrow` unless `actionIcon`
|
|
135
|
+
replaces it.
|
|
136
|
+
`ListActionRow` uses a noninteractive row root around sibling 44px primary
|
|
137
|
+
and trailing buttons. `ListItem.trailing` remains dormant content.
|
|
138
|
+
In multiline `ListItem` and `ListActionRow` rows, the leading icon stays
|
|
139
|
+
centered on the label's first line rather than the full wrapped label.
|
|
140
|
+
6. Reading width, column placement, and responsive relocation remain application
|
|
141
|
+
decisions. The shared classes define local geometry, not the whole shell.
|
|
142
|
+
7. A visible collapsible pane owns its collapse control in that pane's toolbar.
|
|
143
|
+
When the pane is hidden, move the restore control into the adjacent main
|
|
144
|
+
toolbar on the same logical edge: an inline-start pane restores from the
|
|
145
|
+
main toolbar's leading group, and an inline-end pane restores from its
|
|
146
|
+
trailing group. Do not leave an otherwise empty icon-only rail behind.
|
|
147
|
+
|
|
148
|
+
At narrow widths or 200% zoom, relocate or stack panes before shrinking targets.
|
|
149
|
+
The 8/1/8 item contract and 44px controls remain stable, so screenshots and
|
|
150
|
+
focus-order tests exercise the same model at every viewport.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Navigation stack
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui/nav-stack` is an iOS-style push/pop navigation stack: views slide in
|
|
4
|
+
and out over one another while the top chrome settles. A **single-pane layout is
|
|
5
|
+
a `NavStack` with one entry**. It is one of the opt-in app layouts (see
|
|
6
|
+
[`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
|
|
7
|
+
|
|
8
|
+
Import the component and its companion stylesheet:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { NavStack, type NavStackView } from "@kerfjs/ui/nav-stack";
|
|
12
|
+
import { wireNavStack } from "@kerfjs/ui/wire-nav-stack";
|
|
13
|
+
import "@kerfjs/ui/nav-stack.css";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## State lives in the app
|
|
17
|
+
|
|
18
|
+
Like every `@kerfjs/ui` component, `NavStack` is declarative: the app owns the
|
|
19
|
+
stack as a `signal<NavStackView[]>`, `NavStack({ views })` renders it, and
|
|
20
|
+
`wireNavStack` animates the transitions.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
const views = signal<NavStackView[]>([
|
|
24
|
+
{ key: "inbox", title: "Inbox", content: <InboxView /> },
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
// render inside mount():
|
|
28
|
+
<NavStack id="mail" label="Mail" views={views.value} />;
|
|
29
|
+
|
|
30
|
+
// once, after first render:
|
|
31
|
+
const dispose = wireNavStack(root, {
|
|
32
|
+
onBack: () => {
|
|
33
|
+
views.value = views.value.slice(0, -1);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// push / pop by editing the signal:
|
|
38
|
+
views.value = [
|
|
39
|
+
...views.value,
|
|
40
|
+
{ key: id, title: "Message", content: <MessageView id={id} /> },
|
|
41
|
+
];
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`NavStack` renders every entry stacked, the last one active and the rest kept
|
|
45
|
+
mounted (so their DOM state and focus survive) but hidden. Each entry carries a
|
|
46
|
+
`key` (stable identity), `content`, an optional `title`, and optional per-view
|
|
47
|
+
`toolbar` actions. The back control appears automatically once the stack has more
|
|
48
|
+
than one entry; `wireNavStack`'s `onBack` is where the app pops its own signal.
|
|
49
|
+
|
|
50
|
+
## Transitions
|
|
51
|
+
|
|
52
|
+
`wireNavStack(root, { onBack, duration? })` observes the rendered stack and
|
|
53
|
+
animates each change: a pushed view slides in from the trailing edge; a popped
|
|
54
|
+
view slides back off it over the revealed view. It returns a disposer. The
|
|
55
|
+
animation honors `prefers-reduced-motion` (transitions collapse to instant) and
|
|
56
|
+
`duration: 0` disables it. Applicable at every device size and inside dialogs.
|
package/docs/recipes.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
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; `ListHeader`, `ListItem`, 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 `ListHeader` 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
|
+
`PanelHeader` 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
|
+
## List-detail dialog
|
|
64
|
+
|
|
65
|
+
[Open the recipe](../ux-demo/?component=recipe-list-detail-dialog) · [TSX source](../ux-demo/recipes/list-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
|
+
`PanelHeader`, `ListHeader`, `ListItem`, and `ValueTable` own their included
|
|
70
|
+
anatomy. The application owns open state, selection, dismissal policy, and
|
|
71
|
+
record actions. `PanelHeader` places the trailing controls the app passes
|
|
72
|
+
directly into its top toolbar's trailing zone.
|
|
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
|
+
`PanelHeader` 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
|
+
## Navigation stack
|
|
113
|
+
|
|
114
|
+
[Open the recipe](../ux-demo/?component=recipe-navigation-stack) · [TSX source](../ux-demo/recipes/navigation-stack.tsx)
|
|
115
|
+
|
|
116
|
+
Drill from a library list into a detail and back with `NavStack`
|
|
117
|
+
(`@kerfjs/ui/nav-stack`): the app owns the stack as a signal of views and
|
|
118
|
+
pushes/pops it, `NavStack` renders it, and `wireNavStack` slides the content and
|
|
119
|
+
settles the chrome (reduced motion collapses the slide to instant). A live
|
|
120
|
+
`deviceClass()` badge shows the current size/orientation. See the layout guide
|
|
121
|
+
[`app-layouts.md`](app-layouts.md) for choosing among `NavStack`, `SplitView`,
|
|
122
|
+
`Workbench`, and `TabScaffold`.
|
|
123
|
+
|
|
124
|
+
## Loading inspector
|
|
125
|
+
|
|
126
|
+
[Open the recipe](../ux-demo/?component=recipe-loading-inspector) · [TSX source](../ux-demo/recipes/loading-inspector.tsx)
|
|
127
|
+
|
|
128
|
+
A record inspector whose per-record values load asynchronously. Every
|
|
129
|
+
value-bearing component (`PanelHeader`, `ValueTable`/`ValueTableRow`, `Select`,
|
|
130
|
+
`SegmentedControl`, `ListItem`, `StateBanner`) takes its `placeholder` from one
|
|
131
|
+
loading flag, so the same real chrome renders a faithful loading state and then
|
|
132
|
+
the populated record — no separate skeleton markup. The composition is the point;
|
|
133
|
+
`Skeleton` is the primitive it builds on. The app owns the loading lifecycle and
|
|
134
|
+
which values are still unknown.
|
|
135
|
+
|
|
136
|
+
## Collapsible sidebar
|
|
137
|
+
|
|
138
|
+
[Open the recipe](../ux-demo/?component=recipe-collapsible-sidebar) · [TSX source](../ux-demo/recipes/collapsible-sidebar.tsx)
|
|
139
|
+
|
|
140
|
+
A mini app frame whose left navigation rail and bottom activity drawer are
|
|
141
|
+
standalone `CollapsiblePanel`s (`@kerfjs/ui/collapsible-panel`) driven by
|
|
142
|
+
`wireSidebar` (`@kerfjs/ui/wire-sidebar`). `CollapsiblePanelToggle` supplies the
|
|
143
|
+
standard per-side glyph: a collapse toggle lives inside each panel and an expand
|
|
144
|
+
toggle lives in the always-visible main header, so a collapsed panel is still
|
|
145
|
+
reachable. `wireSidebar` owns the toggle delegation, moves focus into a panel on
|
|
146
|
+
open and restores it to the trigger on close, and — when a `deviceClass()` reports
|
|
147
|
+
`compact` — switches the open panel to a dismissable **overlay** (backdrop, Escape
|
|
148
|
+
and backdrop-click collapse, and a trapped Tab ring, the ARIA dialog pattern). It
|
|
149
|
+
also persists each panel's collapsed state through a supplied storage hook. The app
|
|
150
|
+
owns each `collapsed` signal, the panel sizes, and the content; the wire owns the
|
|
151
|
+
ephemeral interaction. For a full three-pane shell use `Workbench` instead — see
|
|
152
|
+
[`app-layouts.md`](app-layouts.md). This recipe is covered end-to-end across
|
|
153
|
+
Chromium, Firefox, and WebKit by `tests/browser/collapsible-sidebar-recipe.spec.ts`.
|
|
154
|
+
|
|
155
|
+
## Rules shared by every recipe
|
|
156
|
+
|
|
157
|
+
- Import `@kerfjs/ui/layout.css`; keep every pane unpadded and use exactly one
|
|
158
|
+
`.kui-pane__content` scroll owner for each real boundary.
|
|
159
|
+
- For direct `wa-*` JSX, import types from `@kerfjs/ui/webawesome`. Import only
|
|
160
|
+
individual Web Awesome registration modules and theme them with
|
|
161
|
+
`@kerfjs/ui/webawesome.css`.
|
|
162
|
+
- Prefer public props and variables at the composition boundary. When a recipe
|
|
163
|
+
needs responsive topology, join only classes listed in the catalog's
|
|
164
|
+
`publicClasses`; do not copy component markup or select descendants by tag,
|
|
165
|
+
id, attribute alone, or an unlisted implementation class.
|
|
166
|
+
- Start from the copyable mount adapter, or reproduce its complete boundary:
|
|
167
|
+
wire stable `data-action` hooks once and retain every disposer.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Split view (list-detail)
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui/split-view` is a list-detail layout: a list and a
|
|
4
|
+
detail side by side on roomy screens, collapsing to a `NavStack` (list → detail)
|
|
5
|
+
on compact ones. One of the opt-in app layouts (see
|
|
6
|
+
[`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { SplitView } from "@kerfjs/ui/split-view";
|
|
10
|
+
import { deviceClass } from "@kerfjs/ui/device-class";
|
|
11
|
+
import "@kerfjs/ui/split-view.css";
|
|
12
|
+
// plus nav-stack.css when the compact path is reachable, and
|
|
13
|
+
// wireResizableRegions / wireNavStack for the interactive behavior.
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Responsive by device class
|
|
17
|
+
|
|
18
|
+
`SplitView` is declarative; the app derives `compact` from the device class and
|
|
19
|
+
tracks its own selection:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const device = deviceClass();
|
|
23
|
+
const selected = signal<string | null>(null);
|
|
24
|
+
|
|
25
|
+
<SplitView
|
|
26
|
+
id="mail"
|
|
27
|
+
label="Mail"
|
|
28
|
+
compact={device.value.compact}
|
|
29
|
+
detailActive={selected.value !== null}
|
|
30
|
+
list={<ThreadList />}
|
|
31
|
+
detail={<Message id={selected.value} />}
|
|
32
|
+
listTitle="Threads"
|
|
33
|
+
detailTitle="Message"
|
|
34
|
+
resizable={{ size: 320, min: 220, max: 480 }}
|
|
35
|
+
/>;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- **Roomy** (`compact: false`): both panes show. With `resizable`, the list sits
|
|
39
|
+
in a `ResizableRegion` (wire it with `wireResizableRegions`); without it the
|
|
40
|
+
list takes a fixed `--kui-split-view-list-width` (default 320px) and the detail
|
|
41
|
+
fills the rest.
|
|
42
|
+
- **Compact** (`compact: true`): the split collapses to a `NavStack`. The list is
|
|
43
|
+
the root; when `detailActive` is true the detail is pushed over it with an
|
|
44
|
+
automatic back control (wire it with `wireNavStack`, whose `onBack` clears the
|
|
45
|
+
app's selection). This is the portrait-tablet / handset presentation; as a
|
|
46
|
+
dialog the compact form is a full-screen or large partial-cover modal.
|
|
47
|
+
|
|
48
|
+
Compose the interactive wiring from the existing helpers — `SplitView` adds no
|
|
49
|
+
wire of its own.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# SunkenPanel
|
|
2
|
+
|
|
3
|
+
`SunkenPanel` is a visually lowered application surface with one compact inset
|
|
4
|
+
and a vertical content stack. Use it for a main work area or nested panel whose
|
|
5
|
+
background should sit behind ordinary content, such as the primary stage in an
|
|
6
|
+
issue tracker or component catalog.
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
import { SunkenPanel } from '@kerfjs/ui/sunken-panel';
|
|
10
|
+
|
|
11
|
+
<SunkenPanel ariaLabel="Release workspace">
|
|
12
|
+
<ReleaseSummary />
|
|
13
|
+
<ReleaseChecks />
|
|
14
|
+
</SunkenPanel>;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The default `shape="rounded"` uses the shared rounded-rectangle radius. Choose
|
|
18
|
+
`shape="square"` for a flush or edge-to-edge area that needs `border-radius: 0`:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<SunkenPanel shape="square">
|
|
22
|
+
<Workspace />
|
|
23
|
+
</SunkenPanel>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Ownership
|
|
27
|
+
|
|
28
|
+
The root owns its lowered background, 8px padding, and 8px vertical gap.
|
|
29
|
+
Children own their borders and internal geometry. The application owns child
|
|
30
|
+
order, responsive placement, and scrolling; `SunkenPanel` deliberately does not
|
|
31
|
+
create another scroll container.
|
|
32
|
+
|
|
33
|
+
Use `ariaLabel` only when the surface is a distinct region people need to find
|
|
34
|
+
by name. With a label, the root receives `role="region"`; without one it remains
|
|
35
|
+
a non-landmark grouping.
|
|
36
|
+
|
|
37
|
+
Do not use `SunkenPanel` merely to add padding, as a substitute for pane
|
|
38
|
+
header/content/footer anatomy, or around a child that already owns the same
|
|
39
|
+
outer surface.
|
|
40
|
+
|
|
41
|
+
## Public styling boundary
|
|
42
|
+
|
|
43
|
+
Override the public properties at the composition boundary:
|
|
44
|
+
|
|
45
|
+
- `--kui-sunken-panel-background`
|
|
46
|
+
- `--kui-sunken-panel-foreground`
|
|
47
|
+
- `--kui-sunken-panel-padding`
|
|
48
|
+
- `--kui-sunken-panel-gap`
|
|
49
|
+
- `--kui-sunken-panel-radius`
|
|
50
|
+
|
|
51
|
+
The public root class is `.kui-sunken-panel`. Prefer the properties above over
|
|
52
|
+
styling descendants. The `square` shape deliberately overrides the radius
|
|
53
|
+
property with zero; use `rounded` when customizing the radius token.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Tab scaffold
|
|
2
|
+
|
|
3
|
+
`@kerfjs/ui/tab-scaffold` is a mobile-first, iOS-like bottom tab bar that switches
|
|
4
|
+
between major app sections, where **each tab keeps its own content mounted** —
|
|
5
|
+
usually a `NavStack`, so each tab's stack and scroll survive a switch. One of the
|
|
6
|
+
opt-in app layouts (see [`../../docs/23-app-layouts.md`](../../docs/23-app-layouts.md)).
|
|
7
|
+
It is distinct from `TabBar` (document-oriented, reorderable strips).
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { TabScaffold } from "@kerfjs/ui/tab-scaffold";
|
|
11
|
+
import { wireTabScaffold } from "@kerfjs/ui/wire-tab-scaffold";
|
|
12
|
+
import "@kerfjs/ui/tab-scaffold.css";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Controlled selection
|
|
16
|
+
|
|
17
|
+
The app owns the active tab (a signal); `TabScaffold` renders every tab's scene
|
|
18
|
+
(only the active one visible) plus the bottom bar, and `wireTabScaffold` reports
|
|
19
|
+
clicks.
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
const active = signal("home");
|
|
23
|
+
|
|
24
|
+
<TabScaffold
|
|
25
|
+
id="app"
|
|
26
|
+
label="Sections"
|
|
27
|
+
active={active.value}
|
|
28
|
+
tabs={[
|
|
29
|
+
{ id: "home", label: "Home", icon: <HomeIcon />, content: <HomeStack /> },
|
|
30
|
+
{
|
|
31
|
+
id: "search",
|
|
32
|
+
label: "Search",
|
|
33
|
+
icon: <SearchIcon />,
|
|
34
|
+
content: <SearchStack />,
|
|
35
|
+
},
|
|
36
|
+
]}
|
|
37
|
+
/>;
|
|
38
|
+
|
|
39
|
+
// once, after first render:
|
|
40
|
+
const dispose = wireTabScaffold(root, {
|
|
41
|
+
onSelect: (id) => {
|
|
42
|
+
active.value = id;
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Each `TabScaffoldTab` has an `id`, `label`, optional `icon`, and `content`. The
|
|
48
|
+
bottom bar respects the home-indicator safe area (`env(safe-area-inset-bottom)`)
|
|
49
|
+
and keeps 44px targets. On larger device classes, promote the tab set to a
|
|
50
|
+
`Workbench` rail or a persistent sidebar instead of a bottom bar.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Compile-time contracts
|
|
2
|
+
|
|
3
|
+
Kerf UI rejects invalid integrations at compile time only when TypeScript can
|
|
4
|
+
prove the relationship from one call. The versioned
|
|
5
|
+
[`compile-time-contracts-v1.json`](../ai/compile-time-contracts-v1.json)
|
|
6
|
+
artifact names those guarantees with stable `KUI-T###` ids. Its source fixture
|
|
7
|
+
is compiled twice: against `src/` and against declarations extracted from the
|
|
8
|
+
actual `npm pack` tarball. `npm run check:catalog` also verifies that every
|
|
9
|
+
contract still points to a real catalog entry/import and a symbol in the emitted
|
|
10
|
+
public-signature artifact.
|
|
11
|
+
|
|
12
|
+
## Strengthened contracts
|
|
13
|
+
|
|
14
|
+
| ID | Compile-time guarantee |
|
|
15
|
+
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| `KUI-T001` | `ListHeader` is a passive heading, a fully named/icon-bearing trailing action, or a controlled disclosure with `action` + `expanded`. Props ignored by the selected mode are rejected. |
|
|
17
|
+
| `KUI-T002` | `Select` has a visible `label` or an `ariaLabel`. |
|
|
18
|
+
| `KUI-T003` | A literal `Select.value` belongs to its literal `choices`. |
|
|
19
|
+
| `KUI-T004` | A literal `SegmentedControl.value` belongs to its literal `choices`. |
|
|
20
|
+
| `KUI-T005` | `expanded`, `expandAction`, and `expandLabel` exist only on `TokenSearchField({ collapsible: true })`. |
|
|
21
|
+
| `KUI-T006` | `TokenSearchEditorAttributes` accepts application `data-*` metadata but rejects component-owned identity, morph, count, and placeholder attributes. |
|
|
22
|
+
| `KUI-T007` | `PanelHeader.summaryId` cannot exist without rendered `summary` content. |
|
|
23
|
+
| `KUI-T008` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code. |
|
|
24
|
+
| `KUI-T009` | A literal `TabScaffold.active` id belongs to its literal `tabs`. |
|
|
25
|
+
| `KUI-T010` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback. |
|
|
26
|
+
| `KUI-T011` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all ToolbarControlGroup presentation axes. |
|
|
27
|
+
|
|
28
|
+
Literal identity inference is intentionally additive for dynamic applications.
|
|
29
|
+
When choices or ids arrive at runtime and the state signal is correctly typed as
|
|
30
|
+
`string`, widen the component explicitly:
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
<Select<string> value={loadedValue.value} choices={loadedChoices.value} ... />
|
|
34
|
+
<SegmentedControl<string> value={mode.value} choices={loadedModes.value} ... />
|
|
35
|
+
<TabScaffold<string> active={active.value} tabs={loadedTabs.value} ... />
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The widening is an honest statement that TypeScript cannot know the runtime
|
|
39
|
+
dataset. Do not cast a known literal typo merely to bypass the relationship.
|
|
40
|
+
|
|
41
|
+
## Complete public-surface audit
|
|
42
|
+
|
|
43
|
+
The following existing contracts were already exact and remain covered by the
|
|
44
|
+
ordinary source/type gates:
|
|
45
|
+
|
|
46
|
+
- required identity or accessible-name inputs on `AppTab`, `FloatingToolbar`,
|
|
47
|
+
`NavStack`, `SplitView`, `Workbench`, `TabBar`, `ResizableRegion`,
|
|
48
|
+
`SegmentedControl`, `TokenSearchField`, `PanelHeader`, and `ValueTable`;
|
|
49
|
+
- finite variants exported or represented as literal props on `DisclosureArrow`,
|
|
50
|
+
`FloatingToolbar`, `Pane`, `ResizableRegion`, `SegmentedControl`, `StateBanner`,
|
|
51
|
+
`SunkenPanel`, `ToolbarText`, `CollapsiblePanel`, `Catalog`, and the wire helpers;
|
|
52
|
+
- required controlled-state callbacks on `wireResizableRegions`, `wireTabBars`,
|
|
53
|
+
and `wireTabScaffold`;
|
|
54
|
+
- mutually exclusive count/badge metadata on `ListHeader`; and
|
|
55
|
+
- protected application metadata boundaries on `AppTab`, `ListItem`,
|
|
56
|
+
`ListActionRow`, `ListHeader`, `Pane`, `CatalogExample`, and
|
|
57
|
+
`CatalogExampleStack`.
|
|
58
|
+
|
|
59
|
+
The remaining components (`LucideIcon`, `DisclosureArrow`, `Toolbar`,
|
|
60
|
+
`ToolbarControlGroup`, `ListInsetControl`, `ListInsetText`, `LoadingSpinner`,
|
|
61
|
+
`Skeleton`, `EmptyState`, `StateBanner`, `ValueTableRow`, and the
|
|
62
|
+
pure helpers) have independent props with no further conditional invalid state
|
|
63
|
+
that TypeScript can truthfully remove. Optional labels on decorative icons and
|
|
64
|
+
unnamed non-landmark surfaces are intentional variants, not missing contracts.
|
|
65
|
+
|
|
66
|
+
## Boundaries TypeScript cannot prove
|
|
67
|
+
|
|
68
|
+
Types do not claim to validate:
|
|
69
|
+
|
|
70
|
+
- the contents of `SafeHtml` slots (for example, “only AppTab children” or “no
|
|
71
|
+
interactive descendants”);
|
|
72
|
+
- nonempty arrays, unique ids, numeric min/max ordering, or a controlled value
|
|
73
|
+
loaded from dynamic data;
|
|
74
|
+
- equality between ids rendered in separate calls, elements discovered later in
|
|
75
|
+
the DOM, or an ARIA relationship's live target;
|
|
76
|
+
- that a caller retained and invoked a wiring disposer; or
|
|
77
|
+
- CSS class/token use inside arbitrary strings.
|
|
78
|
+
|
|
79
|
+
Those remain catalog `KUI-C###` diagnostics, runtime filtering/guards, lint or
|
|
80
|
+
browser assertions. Encoding them as permissive-looking TypeScript brands would
|
|
81
|
+
move errors into casts without making the integration safer.
|
|
82
|
+
|
|
83
|
+
## Migration from the earlier declarations
|
|
84
|
+
|
|
85
|
+
- Add `actionLabel` and `actionIcon` to a non-toggle `ListHeader` action. For a
|
|
86
|
+
disclosure, set literal `toggle: true`, provide `action` and controlled
|
|
87
|
+
`expanded`, and remove trailing-action-only props.
|
|
88
|
+
- Give every `Select` either `label` or `ariaLabel`. Use an explicit `<string>`
|
|
89
|
+
generic only for genuinely dynamic choice data.
|
|
90
|
+
- Put `expanded`/expand-action props behind `collapsible: true`.
|
|
91
|
+
- Replace `wireTokenSearchFields({ keyboard: true })` with either
|
|
92
|
+
`{ keyboard: { onRemoveToken } }` or
|
|
93
|
+
`{ keyboard: { removeAdjacentToken: false } }` when only caret navigation is
|
|
94
|
+
wanted.
|
|
95
|
+
- Remove orphaned `summaryId`, or render the matching `summary`; add
|
|
96
|
+
`wrap: true` anywhere `ToolbarText.maxLines` is used.
|
|
97
|
+
- Replace `Toolbar({ divider: false })` with `dividerSides: ''`; the default
|
|
98
|
+
remains a bottom divider, while canonical combinations such as `tr` and
|
|
99
|
+
`trbl` select more physical edges.
|
|
100
|
+
|
|
101
|
+
JavaScript runtime behavior remains defensive for previously emitted calls, but
|
|
102
|
+
new TypeScript builds report these invalid or ignored combinations.
|