@kerfjs/ui 5.0.0-beta.4 → 5.0.0-beta.41
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 +342 -108
- package/ai/application-ui-diagnostic-ids-v1.json +100 -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 +132 -0
- package/ai/compile-time-contracts-v1.schema.json +51 -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 +496 -0
- package/ai/component-catalog-v2.d.ts +90 -0
- package/ai/component-catalog-v2.json +5678 -0
- package/ai/component-catalog-v2.schema.json +298 -0
- package/ai/component-catalog.json +2694 -417
- package/ai/component-catalog.schema.json +39 -0
- package/ai/public-api-signatures-v1.md +1591 -126
- package/ai/skill.md +140 -54
- package/ai/webawesome-jsx-signatures-v1.md +2 -1
- package/analyzer/cli.mjs +49 -0
- package/analyzer/index.d.mts +46 -0
- package/analyzer/index.mjs +1086 -0
- package/analyzer/report.schema.json +66 -0
- package/dist/app-tab.d.ts +15 -4
- package/dist/app-tab.js +3 -1
- package/dist/browser/app-tab.js +1 -0
- package/dist/browser/floating-toolbar.js +3 -0
- package/dist/browser/list-action-row.js +5 -0
- package/dist/browser/list-header.js +7 -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 +5 -0
- package/dist/browser/list.js +3 -0
- package/dist/browser/pane.js +3 -0
- package/dist/browser/row.js +3 -0
- package/dist/browser/segmented-control.js +1 -0
- package/dist/browser/select.js +1 -0
- package/dist/browser/skeleton.js +3 -0
- package/dist/browser/spacer.js +3 -0
- package/dist/browser/state-banner.js +1 -0
- package/dist/browser/sunken-panel.js +3 -0
- package/dist/browser/surface-scaffold.js +3 -0
- package/dist/browser/text.js +3 -0
- package/dist/browser/toolbar-text.js +1 -0
- package/dist/browser/value-table.js +1 -0
- package/dist/catalog-resources.d.ts +35 -0
- package/dist/catalog-resources.js +22 -0
- package/dist/catalog-resources.js.map +1 -0
- package/dist/catalog.d.ts +151 -0
- package/dist/catalog.js +396 -0
- package/dist/catalog.js.map +1 -0
- package/dist/chunk-2TYUFZUC.js +22 -0
- package/dist/chunk-2TYUFZUC.js.map +1 -0
- package/dist/chunk-6NDBE2OK.js +32 -0
- package/dist/chunk-6NDBE2OK.js.map +1 -0
- package/dist/chunk-6WTETUZ6.js +21 -0
- package/dist/chunk-6WTETUZ6.js.map +1 -0
- package/dist/chunk-AQFWBG23.js +83 -0
- package/dist/chunk-AQFWBG23.js.map +1 -0
- package/dist/chunk-B6JYRX3P.js +51 -0
- package/dist/chunk-B6JYRX3P.js.map +1 -0
- package/dist/chunk-C34NZOLJ.js +53 -0
- package/dist/chunk-C34NZOLJ.js.map +1 -0
- package/dist/chunk-CAB6NQ2T.js +102 -0
- package/dist/chunk-CAB6NQ2T.js.map +1 -0
- package/dist/chunk-CK6P5AUK.js +136 -0
- package/dist/chunk-CK6P5AUK.js.map +1 -0
- package/dist/chunk-DAO2VFTT.js +129 -0
- package/dist/chunk-DAO2VFTT.js.map +1 -0
- package/dist/chunk-DEYOGDE7.js +141 -0
- package/dist/chunk-DEYOGDE7.js.map +1 -0
- package/dist/chunk-DYBPI5O2.js +25 -0
- package/dist/chunk-DYBPI5O2.js.map +1 -0
- package/dist/{chunk-TRNBHPNW.js → chunk-E34TE2ZI.js} +12 -8
- package/dist/chunk-E34TE2ZI.js.map +1 -0
- package/dist/chunk-EYALJQQD.js +25 -0
- package/dist/chunk-EYALJQQD.js.map +1 -0
- package/dist/chunk-I45QO2IZ.js +88 -0
- package/dist/chunk-I45QO2IZ.js.map +1 -0
- package/dist/chunk-I4V3UDSA.js +79 -0
- package/dist/chunk-I4V3UDSA.js.map +1 -0
- package/dist/chunk-OGTJNHRG.js +42 -0
- package/dist/chunk-OGTJNHRG.js.map +1 -0
- package/dist/chunk-OL4TC3U2.js +156 -0
- package/dist/chunk-OL4TC3U2.js.map +1 -0
- package/dist/chunk-OMSRSL3B.js +120 -0
- package/dist/chunk-OMSRSL3B.js.map +1 -0
- package/dist/chunk-R3EBGEAW.js +46 -0
- package/dist/chunk-R3EBGEAW.js.map +1 -0
- package/dist/chunk-SOK5EUF7.js +25 -0
- package/dist/chunk-SOK5EUF7.js.map +1 -0
- package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
- package/dist/chunk-SRSJO5QE.js.map +1 -0
- package/dist/chunk-T7DOWS6T.js +61 -0
- package/dist/chunk-T7DOWS6T.js.map +1 -0
- package/dist/chunk-TIGSG6BX.js +218 -0
- package/dist/chunk-TIGSG6BX.js.map +1 -0
- package/dist/chunk-UDHHKZWO.js +41 -0
- package/dist/chunk-UDHHKZWO.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-V6GQKOX2.js +39 -0
- package/dist/chunk-V6GQKOX2.js.map +1 -0
- package/dist/chunk-V6HCTCVD.js +46 -0
- package/dist/chunk-V6HCTCVD.js.map +1 -0
- package/dist/chunk-VDUJZF5G.js +515 -0
- package/dist/chunk-VDUJZF5G.js.map +1 -0
- package/dist/chunk-VXFGMDA7.js +31 -0
- package/dist/chunk-VXFGMDA7.js.map +1 -0
- package/dist/chunk-WN52USHV.js +115 -0
- package/dist/chunk-WN52USHV.js.map +1 -0
- package/dist/chunk-WSXUPSID.js +43 -0
- package/dist/chunk-WSXUPSID.js.map +1 -0
- package/dist/chunk-X5GRDQ7V.js +50 -0
- package/dist/chunk-X5GRDQ7V.js.map +1 -0
- package/dist/{chunk-56RJXPHG.js → chunk-Y6LYKFXY.js} +1 -1
- package/dist/{chunk-56RJXPHG.js.map → chunk-Y6LYKFXY.js.map} +1 -1
- package/dist/chunk-ZASJJMGV.js +17 -0
- package/dist/chunk-ZASJJMGV.js.map +1 -0
- package/dist/chunk-ZPWZIVI6.js +47 -0
- package/dist/chunk-ZPWZIVI6.js.map +1 -0
- package/dist/collapsible-panel.d.ts +77 -0
- package/dist/collapsible-panel.js +84 -0
- package/dist/collapsible-panel.js.map +1 -0
- package/dist/css-values.d.ts +75 -0
- package/dist/css-values.js +3 -0
- package/dist/css-values.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.js +1 -1
- package/dist/divider-sides-BzB6rphT.d.ts +6 -0
- package/dist/empty-state.d.ts +3 -2
- package/dist/empty-state.js +1 -1
- package/dist/flex-alignment-CF8NLj7i.d.ts +4 -0
- package/dist/floating-toolbar.d.ts +29 -0
- package/dist/floating-toolbar.js +3 -0
- package/dist/floating-toolbar.js.map +1 -0
- package/dist/index.d.ts +28 -14
- package/dist/index.js +34 -23
- package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +19 -8
- package/dist/list-action-row.js +7 -0
- package/dist/list-action-row.js.map +1 -0
- package/dist/list-header.d.ts +80 -0
- package/dist/list-header.js +9 -0
- package/dist/list-header.js.map +1 -0
- package/dist/list-inset-control.d.ts +20 -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 +29 -0
- package/dist/list-inset-text.js +3 -0
- package/dist/list-inset-text.js.map +1 -0
- package/dist/list-item.d.ts +45 -0
- package/dist/list-item.js +7 -0
- package/dist/list-item.js.map +1 -0
- package/dist/list.d.ts +30 -0
- package/dist/list.js +5 -0
- package/dist/list.js.map +1 -0
- package/dist/loading-spinner.js +1 -1
- package/dist/nav-stack.d.ts +41 -0
- package/dist/nav-stack.js +6 -0
- package/dist/nav-stack.js.map +1 -0
- package/dist/pane.d.ts +50 -0
- package/dist/pane.js +4 -0
- package/dist/pane.js.map +1 -0
- package/dist/resizable-region.d.ts +21 -3
- package/dist/resizable-region.js +1 -1
- package/dist/row.d.ts +28 -0
- package/dist/row.js +5 -0
- package/dist/row.js.map +1 -0
- package/dist/segmented-control.d.ts +11 -8
- package/dist/segmented-control.js +3 -1
- package/dist/select-register.d.ts +5 -1
- package/dist/select-register.js +129 -1
- package/dist/select-register.js.map +1 -1
- package/dist/select.d.ts +34 -8
- package/dist/select.js +3 -1
- package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
- package/dist/skeleton.d.ts +25 -0
- package/dist/skeleton.js +4 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/spacer.d.ts +16 -0
- package/dist/spacer.js +4 -0
- package/dist/spacer.js.map +1 -0
- package/dist/split-view.d.ts +43 -0
- package/dist/split-view.js +82 -0
- package/dist/split-view.js.map +1 -0
- package/dist/state-banner.d.ts +11 -5
- package/dist/state-banner.js +3 -1
- package/dist/styles/app-tab.css +26 -0
- package/dist/styles/catalog.css +582 -0
- package/dist/styles/collapsible-panel.css +212 -0
- package/dist/styles/floating-toolbar.css +38 -0
- package/dist/styles/foundation.css +80 -0
- package/dist/styles/layout.css +2 -20
- package/dist/styles/list-action-row.css +224 -0
- package/dist/styles/list-header.css +224 -0
- package/dist/styles/list-inset-control.css +23 -0
- package/dist/styles/list-inset-text.css +30 -0
- package/dist/styles/list-item.css +171 -0
- package/dist/styles/list.css +118 -0
- package/dist/styles/nav-stack.css +141 -0
- package/dist/styles/pane.css +62 -0
- package/dist/styles/resizable-region.css +158 -0
- package/dist/styles/row.css +92 -0
- package/dist/styles/segmented-control.css +10 -7
- package/dist/styles/select.css +115 -2
- package/dist/styles/skeleton.css +31 -0
- package/dist/styles/spacer.css +11 -0
- package/dist/styles/split-view.css +35 -0
- package/dist/styles/state-banner.css +49 -10
- package/dist/styles/styles.css +10 -5
- package/dist/styles/sunken-panel.css +22 -0
- package/dist/styles/surface-scaffold.css +51 -0
- package/dist/styles/tab-bar.css +24 -0
- package/dist/styles/tab-scaffold.css +85 -0
- package/dist/styles/text.css +6 -0
- package/dist/styles/token-search-field.css +14 -2
- package/dist/styles/toolbar-control-group.css +235 -8
- package/dist/styles/toolbar-text.css +65 -2
- package/dist/styles/toolbar.css +47 -2
- package/dist/styles/webawesome.css +171 -0
- package/dist/styles/workbench.css +187 -0
- package/dist/sunken-panel.d.ts +20 -0
- package/dist/sunken-panel.js +3 -0
- package/dist/sunken-panel.js.map +1 -0
- package/dist/surface-scaffold.d.ts +26 -0
- package/dist/surface-scaffold.js +3 -0
- package/dist/surface-scaffold.js.map +1 -0
- package/dist/tab-bar.d.ts +24 -6
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +31 -0
- package/dist/tab-scaffold.js +51 -0
- package/dist/tab-scaffold.js.map +1 -0
- package/dist/text.d.ts +20 -0
- package/dist/text.js +3 -0
- package/dist/text.js.map +1 -0
- package/dist/token-search-field.d.ts +30 -13
- package/dist/token-search-field.js +1 -1
- package/dist/toolbar-control-group.d.ts +33 -7
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.d.ts +31 -4
- package/dist/toolbar-text.js +3 -1
- package/dist/toolbar.d.ts +10 -7
- package/dist/toolbar.js +1 -1
- package/dist/value-table.d.ts +7 -3
- package/dist/value-table.js +3 -1
- package/dist/webawesome.d.ts +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 +18 -0
- package/dist/wire-nav-stack.js +228 -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 +48 -0
- package/dist/wire-sidebar.js +147 -0
- package/dist/wire-sidebar.js.map +1 -0
- package/dist/wire-tab-bars.d.ts +19 -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 +104 -4
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +44 -0
- package/dist/workbench.js +106 -0
- package/dist/workbench.js.map +1 -0
- package/docs/accessibility.md +155 -20
- package/docs/app-layouts.md +61 -0
- package/docs/catalog.md +364 -0
- package/docs/collapsible-panel.md +142 -0
- package/docs/component-contract.md +258 -28
- package/docs/component-integration.md +63 -0
- package/docs/component-selection.md +219 -57
- 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/inline-action-dark.svg +1 -0
- package/docs/design/templates/list-header/inline-action.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/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 +128 -0
- package/docs/design-philosophy.md +33 -11
- package/docs/device-class.md +54 -0
- package/docs/examples/application-ui-profile.json +60 -0
- package/docs/examples/command-palette-adapter.tsx +68 -17
- package/docs/examples/component-catalog-extension-v2.json +80 -0
- package/docs/examples/component-catalog-extension.json +45 -0
- package/docs/layout.md +169 -38
- package/docs/nav-stack.md +77 -0
- package/docs/recipes.md +64 -16
- package/docs/split-view.md +55 -0
- package/docs/sunken-panel.md +53 -0
- package/docs/surface-scaffold.md +25 -0
- package/docs/tab-scaffold.md +52 -0
- package/docs/type-contracts.md +127 -0
- package/docs/ui-analyzer.md +118 -0
- package/docs/ui-doctor.md +91 -0
- package/docs/ui-evaluator.md +122 -0
- package/docs/ux-demo.md +29 -10
- package/docs/webawesome-theme.md +82 -8
- package/docs/workbench.md +123 -0
- package/doctor/cli.mjs +115 -0
- package/doctor/config.schema.json +68 -0
- package/doctor/eslint-diagnostics.d.mts +24 -0
- package/doctor/eslint-diagnostics.mjs +24 -0
- package/doctor/index.d.mts +116 -0
- package/doctor/index.mjs +1272 -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 +260 -32
- package/package.json +205 -38
- package/traversal-exclusions.mjs +27 -0
- package/ux-demo/recipes/app-shell.tsx +223 -13
- package/ux-demo/recipes/collapsible-sidebar.tsx +230 -0
- package/ux-demo/recipes/compact-toolbar.tsx +116 -12
- package/ux-demo/recipes/composer-form.tsx +120 -19
- package/ux-demo/recipes/list-detail-dialog.tsx +205 -0
- package/ux-demo/recipes/list-workspace-states.tsx +176 -9
- package/ux-demo/recipes/loaders.ts +13 -4
- package/ux-demo/recipes/loading-inspector.tsx +191 -0
- package/ux-demo/recipes/mount-recipe.ts +31 -5
- package/ux-demo/recipes/navigation-sidebar.tsx +123 -11
- package/ux-demo/recipes/navigation-stack.tsx +121 -0
- package/ux-demo/recipes/recipes.css +125 -16
- package/ux-demo/recipes/types.ts +9 -1
- package/ux-demo/recipes/workspace-header.tsx +72 -3
- package/dist/browser/dialog-header.js +0 -5
- package/dist/browser/menu-action-row.js +0 -3
- package/dist/browser/menu-header.js +0 -5
- 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-55DATV3A.js +0 -10
- package/dist/chunk-55DATV3A.js.map +0 -1
- package/dist/chunk-7BR3DO2A.js.map +0 -1
- package/dist/chunk-7LDCTU7D.js +0 -134
- package/dist/chunk-7LDCTU7D.js.map +0 -1
- package/dist/chunk-AQNKJRFH.js +0 -79
- package/dist/chunk-AQNKJRFH.js.map +0 -1
- package/dist/chunk-CTQKI2XL.js +0 -28
- package/dist/chunk-CTQKI2XL.js.map +0 -1
- package/dist/chunk-DAFFDO33.js +0 -14
- package/dist/chunk-DAFFDO33.js.map +0 -1
- package/dist/chunk-EGHL5S4P.js +0 -33
- package/dist/chunk-EGHL5S4P.js.map +0 -1
- package/dist/chunk-GY5WH7TO.js +0 -10
- package/dist/chunk-GY5WH7TO.js.map +0 -1
- package/dist/chunk-H5AGGVU5.js +0 -75
- package/dist/chunk-H5AGGVU5.js.map +0 -1
- package/dist/chunk-K3G72I6D.js +0 -24
- package/dist/chunk-K3G72I6D.js.map +0 -1
- package/dist/chunk-M5JRI6MN.js +0 -27
- package/dist/chunk-M5JRI6MN.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-NPPD2A6O.js +0 -40
- package/dist/chunk-NPPD2A6O.js.map +0 -1
- package/dist/chunk-R6JHHBKQ.js +0 -19
- package/dist/chunk-R6JHHBKQ.js.map +0 -1
- package/dist/chunk-S3TPM2LP.js +0 -23
- package/dist/chunk-S3TPM2LP.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-WVPYVDXY.js +0 -15
- package/dist/chunk-WVPYVDXY.js.map +0 -1
- package/dist/chunk-XZXRLY5Q.js +0 -33
- package/dist/chunk-XZXRLY5Q.js.map +0 -1
- package/dist/dialog-header.d.ts +0 -15
- package/dist/dialog-header.js +0 -5
- package/dist/dialog-header.js.map +0 -1
- package/dist/menu-action-row.js +0 -4
- package/dist/menu-action-row.js.map +0 -1
- package/dist/menu-header.d.ts +0 -41
- package/dist/menu-header.js +0 -6
- package/dist/menu-header.js.map +0 -1
- package/dist/menu-item.d.ts +0 -31
- package/dist/menu-item.js +0 -4
- 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/dist/styles/dialog-header.css +0 -87
- package/dist/styles/menu-action-row.css +0 -160
- package/dist/styles/menu-header.css +0 -143
- package/dist/styles/menu-item.css +0 -123
- package/dist/styles/page-header.css +0 -33
- package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
package/docs/layout.md
CHANGED
|
@@ -1,29 +1,42 @@
|
|
|
1
1
|
# Pane and content layout
|
|
2
2
|
|
|
3
|
-
Import `@kerfjs/ui/
|
|
4
|
-
|
|
5
|
-
|
|
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.
|
|
6
8
|
|
|
7
9
|
## Anatomy
|
|
8
10
|
|
|
9
11
|
```tsx
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
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
|
+
>
|
|
13
21
|
<section>...</section>
|
|
14
22
|
<section>...</section>
|
|
15
|
-
|
|
16
|
-
<footer class="kui-pane__footer"><Toolbar label="Actions" ... /></footer>
|
|
17
|
-
</aside>
|
|
23
|
+
</Pane>
|
|
18
24
|
```
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
scrolling content area, and an optional footer. A
|
|
22
|
-
|
|
23
|
-
|
|
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.
|
|
24
37
|
|
|
25
38
|
`.kui-content` is a vertical stack with a 24px gap between major children.
|
|
26
|
-
Sections may contain adjacent `
|
|
39
|
+
Sections may contain adjacent `ListItem` rows without adding another major gap.
|
|
27
40
|
Ordinary surface-like children use `.kui-content-item` and own their complete
|
|
28
41
|
geometry:
|
|
29
42
|
|
|
@@ -37,8 +50,15 @@ Use `.kui-content-item--pill` for the 22px pill radius expressed as
|
|
|
37
50
|
changing its geometry by setting `--kui-content-item-border` and
|
|
38
51
|
`--kui-content-item-background`.
|
|
39
52
|
|
|
53
|
+
Nested selection and hover highlights stay concentric and match their owning
|
|
54
|
+
control's shape. Their inner radius is the outer radius minus the full inset,
|
|
55
|
+
expressed by the shared `--kui-layout-highlight-inset` token (2px by default).
|
|
56
|
+
For example, `SegmentedControl` and `ToolbarControlGroup` place each highlight
|
|
57
|
+
behind a 1px control border and 1px control padding, so a rounded control's 12px
|
|
58
|
+
outer radius produces a 10px highlight radius; a 22px pill produces 20px.
|
|
59
|
+
|
|
40
60
|
A visible parent surface does not make every child another visible card. The
|
|
41
|
-
composer recipe keeps its form as the single surface, uses `
|
|
61
|
+
composer recipe keeps its form as the single surface, uses a heading `Toolbar` for
|
|
42
62
|
its task hierarchy, and places field and action control edges on the shared 8px
|
|
43
63
|
inline gutter instead of nesting them inside another padded content item. A
|
|
44
64
|
conditional `StateBanner` remains visibly distinct because it communicates
|
|
@@ -46,19 +66,20 @@ semantic status.
|
|
|
46
66
|
|
|
47
67
|
## Public roles and tokens
|
|
48
68
|
|
|
49
|
-
| Need
|
|
50
|
-
|
|
|
51
|
-
| Unpadded
|
|
52
|
-
| Scrolling pane content
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
| Need | Class | Token / default |
|
|
70
|
+
| ---------------------------------------- | ------------------------- | ------------------------------------- |
|
|
71
|
+
| Unpadded header/content/footer structure | `Pane`, `.kui-pane` | — |
|
|
72
|
+
| Scrolling pane content | `.kui-pane__content` | — |
|
|
73
|
+
| Optional logical-edge separators | `Pane.separators` | `--kui-pane-separator-width: 1px` |
|
|
74
|
+
| Major vertical rhythm | `.kui-content` | `--kui-layout-content-gap: 24px` |
|
|
75
|
+
| Self-contained child geometry | `.kui-content-item` | 8px margin + 1px border + 8px padding |
|
|
76
|
+
| Pill child | `.kui-content-item--pill` | `--kui-layout-pill-radius: 22px` |
|
|
77
|
+
| Related controls | `.kui-control-cluster` | `--kui-layout-control-gap: 8px` |
|
|
78
|
+
| Inline metadata | `.kui-inline-metadata` | `--kui-layout-metadata-gap: 4px` |
|
|
79
|
+
| Explicit scroll owner outside a pane | `.kui-scroll-owner` | `overflow: auto` |
|
|
80
|
+
|
|
81
|
+
The component layer applies the same contract to `Toolbar`, `ListHeader`,
|
|
82
|
+
`ListItem`, `Toolbar`, `StateBanner`, `ValueTable`,
|
|
62
83
|
`ValueTableRow`, tabs, and form controls. A value-table row separator starts at
|
|
63
84
|
the row's 8px content inset, or at 40px when the row contains its 24px leading
|
|
64
85
|
icon and 8px gap, and always ends 8px from the right edge. Each value-table row
|
|
@@ -66,12 +87,122 @@ also keeps 8px of root-scaled block padding independently of its semantic inline
|
|
|
66
87
|
inset. Most interactive rows and toolbar groups are 44px tall.
|
|
67
88
|
Toolbar groups reserve a real 1px outer border around a 42px inner area, even
|
|
68
89
|
when their border and background are transparent.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
Panel, dialog, and page headings are plain `Toolbar` compositions. The leading
|
|
91
|
+
zone holds an optional icon `ToolbarControlGroup` and a direct extra-large
|
|
92
|
+
`ToolbarText`; actions belong in a trailing group. Omit empty groups. Supporting
|
|
93
|
+
copy is app-owned content below the toolbar and aligns with the intended content
|
|
94
|
+
edge.
|
|
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
|
+
For `Row.gap` and `List.gap`, pass these token names directly (`gap="xs"`, `gap="m"`) or use
|
|
116
|
+
`space('xs')` from `@kerfjs/ui/css-values` when composing a value in JavaScript.
|
|
117
|
+
Use `rem`, `em`, `px`, `pct`, `lengthVar`, and `calc(plus(...))` only when a
|
|
118
|
+
named spacing relationship does not express the requirement; do not pass raw
|
|
119
|
+
CSS strings.
|
|
120
|
+
|
|
121
|
+
## Text
|
|
122
|
+
|
|
123
|
+
Use `Text` from `@kerfjs/ui/text` for ordinary semantic headings and
|
|
124
|
+
paragraphs. Its required `variant` renders the corresponding native `h1`–`h6`
|
|
125
|
+
or `p` element, and ordinary global, `data-*`, and `aria-*` attributes pass
|
|
126
|
+
through. Every variant resets its native margin, owns a 1px transparent border,
|
|
127
|
+
and supplies the standard 8px item padding; native typography remains available
|
|
128
|
+
to the surrounding composition. Choose heading levels from the document
|
|
129
|
+
outline, not for visual size. Toolbar identity and page-heading compositions
|
|
130
|
+
continue to use `ToolbarText`.
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import { Text } from "@kerfjs/ui/text";
|
|
134
|
+
|
|
135
|
+
<Text variant="h2" id="details-title">Details</Text>;
|
|
136
|
+
<Text variant="p" aria-describedby="details-title">Supporting copy</Text>;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Spacer
|
|
140
|
+
|
|
141
|
+
Use `Spacer` from `@kerfjs/ui/spacer` for one intentional empty dimension that
|
|
142
|
+
is not the repeated relationship owned by a parent's `gap`. Its `width` and
|
|
143
|
+
`height` accept the same finite `UiSpaceName` vocabulary or a complete typed
|
|
144
|
+
`CssLength`. Fixed spacers use `flex: 0 0 auto`, so flex layouts do not compress
|
|
145
|
+
the requested dimension. Pass `flex` to use `1 1 auto` and consume the remaining
|
|
146
|
+
space along a `Row`, `List`, or other flex parent's main axis.
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import { Row } from "@kerfjs/ui/row";
|
|
150
|
+
import { Spacer } from "@kerfjs/ui/spacer";
|
|
151
|
+
|
|
152
|
+
<Row gap="none">
|
|
153
|
+
<button>Back</button>
|
|
154
|
+
<Spacer flex />
|
|
155
|
+
<button>Save</button>
|
|
156
|
+
</Row>;
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Spacer is always decorative (`aria-hidden="true"`), accepts no children, and
|
|
160
|
+
uses physical width and height. Prefer `Row.gap` or `List.gap` for uniform
|
|
161
|
+
sibling rhythm, and never use Spacer to cancel or duplicate component-owned
|
|
162
|
+
insets.
|
|
163
|
+
|
|
164
|
+
## Row and List alignment
|
|
165
|
+
|
|
166
|
+
Use `Row` for a horizontal flex layout and `List` for a vertical one. `Row`
|
|
167
|
+
defaults to `hAlign="left"`, `vAlign="full"`, `gap="xs"`, and no wrapping;
|
|
168
|
+
`List` retains its existing full-width, top-aligned defaults and zero gap.
|
|
169
|
+
Both components accept the same physical-axis vocabulary:
|
|
170
|
+
|
|
171
|
+
| Axis | Values and aliases | Flex behavior |
|
|
172
|
+
| ---------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
|
|
173
|
+
| Horizontal | `left` / `l` / `flex-start`; `center` / `c` / `space-around`; `right` / `r` / `flex-end`; `full` / `f` / `space-between` | Distributes a Row's children; aligns a List's children across its width. |
|
|
174
|
+
| Vertical | `top` / `t` / `flex-start`; `middle` / `m` / `c` / `space-around`; `bottom` / `b` / `flex-end`; `full` / `f` / `space-between` | Aligns a Row's children across its height; distributes a List's children. |
|
|
175
|
+
|
|
176
|
+
Cross-axis `space-around` and `space-between` are not valid `align-items`
|
|
177
|
+
values. Kerf therefore maps `middle` to centered items and `full` to stretched
|
|
178
|
+
items, while applying the requested distribution to `align-content` when a
|
|
179
|
+
Row wraps. Prefer the descriptive values in application code; the short and
|
|
180
|
+
CSS-shaped aliases are provided for compact or migrated call sites.
|
|
181
|
+
|
|
182
|
+
`Row.flex` and `List.flex` share one typed participation contract. Omit the prop
|
|
183
|
+
for the CSS initial value, pass `true` for `1 1 auto`, select the finite
|
|
184
|
+
`"none"`, `"auto"`, or `"initial"` keyword, or use
|
|
185
|
+
`flex(grow, shrink, basis)` from `@kerfjs/ui/css-values` for a complete branded
|
|
186
|
+
shorthand. A `CssLength` is not a flex shorthand, and arbitrary strings are
|
|
187
|
+
rejected by the type contract.
|
|
188
|
+
|
|
189
|
+
Both components also accept `textInsets` and `controlInsets` using the shared
|
|
190
|
+
physical `Sides` union. Values follow canonical top/right/bottom/left order
|
|
191
|
+
(`t`, `rb`, `tbl`, `trbl`, and so on). A selected control side adds 8px of
|
|
192
|
+
padding. A selected text side adds the complete content-item alignment inset —
|
|
193
|
+
8px outer margin + 1px border + 8px inner padding, represented as 17px of
|
|
194
|
+
container padding. When both props select the same side, the text inset wins.
|
|
195
|
+
|
|
196
|
+
`ListInsetText` and `ListInsetControl` use the same `sides` vocabulary and
|
|
197
|
+
default to `trbl`. The text wrapper applies its complete 8/1/8 geometry only on
|
|
198
|
+
selected sides; the control wrapper applies its 8px outer margin only on
|
|
199
|
+
selected sides. `ListInsetText.horizontalOnly` remains a deprecated alias for
|
|
200
|
+
`sides="rl"`; an explicit `sides` value takes precedence.
|
|
201
|
+
|
|
202
|
+
`--kui-space-s` (12px) and `--kui-space-xl` (32px) exist but are **off the
|
|
203
|
+
canonical rhythm** — reach for them only as a deliberate exception, never as a
|
|
204
|
+
default step. Prefer the five canonical tokens so spacing stays legible and
|
|
205
|
+
consistent across every surface.
|
|
75
206
|
|
|
76
207
|
## Ownership rules
|
|
77
208
|
|
|
@@ -84,14 +215,14 @@ title. The icon visual is 24px inside a 34px circular background.
|
|
|
84
215
|
4. Keep one scrolling content owner per pane. Toolbar and footer siblings stay
|
|
85
216
|
fixed while the content scrolls.
|
|
86
217
|
5. A split item keeps dormant and interactive regions separate. For example,
|
|
87
|
-
`
|
|
218
|
+
`ListHeader` renders its title/count-or-badge cluster separately from its optional
|
|
88
219
|
logical-end 44px action. The header fills the available inline width and its
|
|
89
220
|
action glyph defaults to 18px; disclosure mode makes the title cluster itself
|
|
90
221
|
the button and supplies the production `DisclosureArrow` unless `actionIcon`
|
|
91
222
|
replaces it.
|
|
92
|
-
`
|
|
93
|
-
and trailing buttons. `
|
|
94
|
-
In multiline `
|
|
223
|
+
`ListActionRow` uses a noninteractive row root around sibling 44px primary
|
|
224
|
+
and trailing buttons. `ListItem.trailing` remains dormant content.
|
|
225
|
+
In multiline `ListItem` and `ListActionRow` rows, the leading icon stays
|
|
95
226
|
centered on the label's first line rather than the full wrapped label.
|
|
96
227
|
6. Reading width, column placement, and responsive relocation remain application
|
|
97
228
|
decisions. The shared classes define local geometry, not the whole shell.
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
{
|
|
25
|
+
key: "inbox",
|
|
26
|
+
title: "Inbox",
|
|
27
|
+
content: <InboxView />,
|
|
28
|
+
bottomToolbar: <InboxStatus />,
|
|
29
|
+
},
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
// render inside mount():
|
|
33
|
+
<NavStack id="mail" label="Mail" views={views.value} />;
|
|
34
|
+
|
|
35
|
+
// once, after first render:
|
|
36
|
+
const dispose = wireNavStack(root, {
|
|
37
|
+
onBack: () => {
|
|
38
|
+
views.value = views.value.slice(0, -1);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// push / pop by editing the signal:
|
|
43
|
+
views.value = [
|
|
44
|
+
...views.value,
|
|
45
|
+
{ key: id, title: "Message", content: <MessageView id={id} /> },
|
|
46
|
+
];
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`NavStack` renders every entry stacked, the last one active and the rest kept
|
|
50
|
+
mounted (so their DOM state survives) but hidden. Each entry carries a
|
|
51
|
+
`key` (stable identity), `content`, an optional `title`, optional per-view
|
|
52
|
+
`toolbar` actions, and an optional per-view `bottomToolbar`. The component-level
|
|
53
|
+
`bottomToolbar` remains a persistent fallback for views that do not provide one.
|
|
54
|
+
The back control appears automatically once the stack has more than one entry;
|
|
55
|
+
`wireNavStack`'s `onBack` is where the app pops its own signal.
|
|
56
|
+
|
|
57
|
+
Focus follows the controlled stack automatically. On push, `wireNavStack`
|
|
58
|
+
remembers the focused descendant of the departing view and moves focus into the
|
|
59
|
+
new top view. On pop it restores that exact descendant when it still exists. A
|
|
60
|
+
new view prefers a descendant marked `data-nav-focus`, then its first enabled
|
|
61
|
+
focusable descendant, and finally the view container itself. Use
|
|
62
|
+
`data-nav-focus` for a heading or primary control when DOM order is not the best
|
|
63
|
+
initial reading position; it may use `tabindex="-1"` for programmatic focus.
|
|
64
|
+
When a remembered control was removed during the controlled rerender, the same
|
|
65
|
+
fallback order applies. Disposing the helper stops future transitions and
|
|
66
|
+
cleans up any temporary fallback `tabindex` it added.
|
|
67
|
+
|
|
68
|
+
## Transitions
|
|
69
|
+
|
|
70
|
+
`wireNavStack(root, { onBack, duration? })` observes the rendered stack and
|
|
71
|
+
animates each change: a pushed view slides in from the trailing edge; a popped
|
|
72
|
+
view slides back off it over the revealed view; and snapshots of the previous
|
|
73
|
+
top and bottom chrome cross-fade into the active view's chrome. Focus moves to
|
|
74
|
+
the new top view independently of animation duration. It returns a disposer.
|
|
75
|
+
The animation honors `prefers-reduced-motion` (transitions collapse to instant)
|
|
76
|
+
and `duration: 0` disables it. Applicable at every device size and inside
|
|
77
|
+
dialogs.
|
package/docs/recipes.md
CHANGED
|
@@ -14,12 +14,12 @@ the public `onCommit` callback, retains every disposer, and returns one
|
|
|
14
14
|
idempotent disposer:
|
|
15
15
|
|
|
16
16
|
```ts
|
|
17
|
-
import { createRecipe } from
|
|
18
|
-
import { mountRecipe } from
|
|
17
|
+
import { createRecipe } from "./navigation-sidebar.js";
|
|
18
|
+
import { mountRecipe } from "./mount-recipe.js";
|
|
19
19
|
|
|
20
|
-
const root = document.querySelector<HTMLElement>(
|
|
20
|
+
const root = document.querySelector<HTMLElement>("#navigation")!;
|
|
21
21
|
const stopRecipe = mountRecipe(root, createRecipe(announce));
|
|
22
|
-
window.addEventListener(
|
|
22
|
+
window.addEventListener("pagehide", stopRecipe, { once: true });
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
The adapter is delivered as reference source, not a new package runtime export.
|
|
@@ -44,32 +44,32 @@ Adapt only public `--kui-layout-*` and component variables.
|
|
|
44
44
|
[Open the recipe](../ux-demo/?component=recipe-navigation-sidebar) · [TSX source](../ux-demo/recipes/navigation-sidebar.tsx)
|
|
45
45
|
|
|
46
46
|
One unpadded `.kui-pane` owns toolbar/content/footer structure. Its
|
|
47
|
-
`.kui-content` uses 24px major gaps; `
|
|
47
|
+
`.kui-content` uses 24px major gaps; `ListHeader`, `ListItem`, and other
|
|
48
48
|
`.kui-content-item` children own their 8px margin, 1px border, and 8px padding.
|
|
49
49
|
Rows and footer toolbar groups remain 44px tall. The app owns routes,
|
|
50
50
|
permissions, labels, selection, valid section counts and their localized
|
|
51
51
|
`countLabel` phrases, non-count badge content, disclosure state, and revealed
|
|
52
|
-
content. A toggled `
|
|
52
|
+
content. A toggled `ListHeader` supplies the production `DisclosureArrow` when
|
|
53
53
|
no custom `actionIcon` is needed; ordinary navigation rows stay chevron-free.
|
|
54
54
|
|
|
55
55
|
## Workspace header
|
|
56
56
|
|
|
57
57
|
[Open the recipe](../ux-demo/?component=recipe-workspace-header) · [TSX source](../ux-demo/recipes/workspace-header.tsx)
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
The page heading is a direct `Toolbar`/`ToolbarText` composition while one control cluster holds secondary,
|
|
60
60
|
overflow, and primary actions. The app owns authorization and command policy;
|
|
61
61
|
controls relocate without changing focus order.
|
|
62
62
|
|
|
63
|
-
##
|
|
63
|
+
## List-detail dialog
|
|
64
64
|
|
|
65
|
-
[Open the recipe](../ux-demo/?component=recipe-
|
|
65
|
+
[Open the recipe](../ux-demo/?component=recipe-list-detail-dialog) · [TSX source](../ux-demo/recipes/list-detail-dialog.tsx)
|
|
66
66
|
|
|
67
67
|
The production Web Awesome dialog owns modal focus and Escape; the thin recipe
|
|
68
68
|
adapter restores the invoking control consistently after the hide event.
|
|
69
|
-
`
|
|
69
|
+
`Toolbar`, `ListHeader`, `ListItem`, and `ValueTable` own their included
|
|
70
70
|
anatomy. The application owns open state, selection, dismissal policy, and
|
|
71
|
-
record actions.
|
|
72
|
-
|
|
71
|
+
record actions. A `ToolbarControlGroup` places the app's trailing controls in
|
|
72
|
+
the top toolbar's trailing zone.
|
|
73
73
|
The header sits on the dialog edge while retaining its internal control inset;
|
|
74
74
|
the selected title receives the full content gutter, the metadata table fills
|
|
75
75
|
the available detail width between the usual outer margins, and the action
|
|
@@ -80,8 +80,11 @@ Do not rebuild the dialog or reach into private shadow parts.
|
|
|
80
80
|
|
|
81
81
|
[Open the recipe](../ux-demo/?component=recipe-composer-form) · [TSX source](../ux-demo/recipes/composer-form.tsx)
|
|
82
82
|
|
|
83
|
-
`
|
|
84
|
-
|
|
83
|
+
A direct `ToolbarText` supplies the task title, with app-owned supporting copy
|
|
84
|
+
below; their ids are referenced by the form. `ListInsetText` gives that bare
|
|
85
|
+
supporting copy the same content-item text inset as the fields instead of
|
|
86
|
+
duplicating its geometry in recipe CSS. Production fields own labels, help, and
|
|
87
|
+
native focus. The field
|
|
85
88
|
and footer control edges sit directly on the shared 8px inline gutter rather
|
|
86
89
|
than acquiring a second content-item padding inset; major children remain 24px
|
|
87
90
|
apart and related controls use 8px gaps. The app owns
|
|
@@ -106,8 +109,53 @@ rows.
|
|
|
106
109
|
|
|
107
110
|
Use `ToolbarControlGroup` for related commands, `SegmentedControl` for a few
|
|
108
111
|
visible exclusive choices, `Select` for a longer value list, and an ordinary
|
|
109
|
-
button for an independent command.
|
|
110
|
-
|
|
112
|
+
button for an independent command. Even that independent button sits in a
|
|
113
|
+
single-control `ToolbarControlGroup`, which keeps its height, pill shape, focus,
|
|
114
|
+
and hover treatment aligned with the adjacent toolbar controls. The app owns
|
|
115
|
+
values, actions, persistence, and responsive priority.
|
|
116
|
+
|
|
117
|
+
## Navigation stack
|
|
118
|
+
|
|
119
|
+
[Open the recipe](../ux-demo/?component=recipe-navigation-stack) · [TSX source](../ux-demo/recipes/navigation-stack.tsx)
|
|
120
|
+
|
|
121
|
+
Drill from a library list into a detail and back with `NavStack`
|
|
122
|
+
(`@kerfjs/ui/nav-stack`): the app owns the stack as a signal of views and
|
|
123
|
+
pushes/pops it, `NavStack` renders it, and `wireNavStack` slides the content and
|
|
124
|
+
settles the chrome (reduced motion collapses the slide to instant). A live
|
|
125
|
+
`deviceClass()` badge shows the current size/orientation. See the layout guide
|
|
126
|
+
[`app-layouts.md`](app-layouts.md) for choosing among `NavStack`, `SplitView`,
|
|
127
|
+
`Workbench`, and `TabScaffold`.
|
|
128
|
+
|
|
129
|
+
## Loading inspector
|
|
130
|
+
|
|
131
|
+
[Open the recipe](../ux-demo/?component=recipe-loading-inspector) · [TSX source](../ux-demo/recipes/loading-inspector.tsx)
|
|
132
|
+
|
|
133
|
+
A record inspector whose per-record values load asynchronously. Every
|
|
134
|
+
value-bearing component (`ToolbarText`, `ValueTable`/`ValueTableRow`, `Select`,
|
|
135
|
+
`SegmentedControl`, `ListItem`, `StateBanner`) takes its `placeholder` from one
|
|
136
|
+
loading flag, so the same real chrome renders a faithful loading state and then
|
|
137
|
+
the populated record — no separate skeleton markup. The composition is the point;
|
|
138
|
+
`Skeleton` is the primitive it builds on. The app owns the loading lifecycle and
|
|
139
|
+
which values are still unknown.
|
|
140
|
+
|
|
141
|
+
## Collapsible sidebar
|
|
142
|
+
|
|
143
|
+
[Open the recipe](../ux-demo/?component=recipe-collapsible-sidebar) · [TSX source](../ux-demo/recipes/collapsible-sidebar.tsx)
|
|
144
|
+
|
|
145
|
+
A mini app frame whose left navigation rail and bottom activity drawer are
|
|
146
|
+
standalone `CollapsiblePanel`s (`@kerfjs/ui/collapsible-panel`) driven by
|
|
147
|
+
`wireSidebar` (`@kerfjs/ui/wire-sidebar`). `CollapsiblePanelToggle` supplies the
|
|
148
|
+
standard per-side glyph: a collapse toggle lives inside each panel and an expand
|
|
149
|
+
toggle lives in the always-visible main header, so a collapsed panel is still
|
|
150
|
+
reachable. `wireSidebar` owns the toggle delegation, moves focus into a panel on
|
|
151
|
+
open and restores it to the trigger on close, and — when a `deviceClass()` reports
|
|
152
|
+
`compact` — switches the open panel to a dismissable **overlay** (backdrop, Escape
|
|
153
|
+
and backdrop-click collapse, and a trapped Tab ring, the ARIA dialog pattern). It
|
|
154
|
+
also persists each panel's collapsed state through a supplied storage hook. The app
|
|
155
|
+
owns each `collapsed` signal, the panel sizes, and the content; the wire owns the
|
|
156
|
+
ephemeral interaction. For a full three-pane shell use `Workbench` instead — see
|
|
157
|
+
[`app-layouts.md`](app-layouts.md). This recipe is covered end-to-end across
|
|
158
|
+
Chromium, Firefox, and WebKit by `tests/browser/collapsible-sidebar-recipe.spec.ts`.
|
|
111
159
|
|
|
112
160
|
## Rules shared by every recipe
|
|
113
161
|
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
The catalog's **Interactive compact drill-down** example exercises this exact
|
|
49
|
+
controlled flow: a list action sets the selected message, `detailActive` pushes
|
|
50
|
+
its detail, and the wired Back action clears the selection to reveal the
|
|
51
|
+
preserved list. Use the same structure in application demos so compact examples
|
|
52
|
+
show the interaction rather than rendering a disconnected detail state.
|
|
53
|
+
|
|
54
|
+
Compose the interactive wiring from the existing helpers — `SplitView` adds no
|
|
55
|
+
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,25 @@
|
|
|
1
|
+
# Dialog and popup surface scaffolds
|
|
2
|
+
|
|
3
|
+
`DialogSurface` and `PopupSurface` configure recurring Web Awesome surface
|
|
4
|
+
geometry without application `::part()` overrides. They do not replace Web
|
|
5
|
+
Awesome behavior: the application still owns open state, focus lifecycle,
|
|
6
|
+
dismissal policy, ids, labels, and content.
|
|
7
|
+
|
|
8
|
+
Wrap one `wa-dialog` with `DialogSurface`. Choose `size` (`small`, `medium`, or
|
|
9
|
+
`large`), `presentation` (`modal`, `side-sheet`, or `fullscreen`), and independent
|
|
10
|
+
`bodyInset` / `footerInset` (`none`, `compact`, or `comfortable`). The defaults
|
|
11
|
+
match Kerf's medium modal, 8px body, and 16px footer rhythm.
|
|
12
|
+
|
|
13
|
+
Treat a dialog body as a `List` by default. When its rows or sections own their
|
|
14
|
+
standard list geometry, set `bodyInset="none"` so the dialog does not add a
|
|
15
|
+
second inset. Bare prose is still a list child: wrap it in `ListInsetText` so its
|
|
16
|
+
text edge receives the standard 8px margin + 1px transparent border + 8px
|
|
17
|
+
padding and aligns with bordered siblings. Reserve `compact` or `comfortable`
|
|
18
|
+
body insets for exceptional content that does not already have list/content-item
|
|
19
|
+
geometry.
|
|
20
|
+
|
|
21
|
+
Wrap one `wa-dropdown` with `PopupSurface`. `inset="list-zero"` removes menu
|
|
22
|
+
padding for a child that already owns row insets, `compact` uses 4px, and
|
|
23
|
+
`standard` uses the shared 8px surface inset. Keep the dropdown trigger named and
|
|
24
|
+
preserve `data-morph-skip-children` when its upgraded light-DOM items must retain
|
|
25
|
+
identity across Kerf rerenders.
|
|
@@ -0,0 +1,52 @@
|
|
|
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. Its single-line labels preserve their full line box and
|
|
50
|
+
truncate horizontally with an ellipsis when a destination name exceeds its
|
|
51
|
+
share of the bar. On larger device classes, promote the tab set to a
|
|
52
|
+
`Workbench` rail or a persistent sidebar instead of a bottom bar.
|