@kerfjs/ui 5.0.0-beta.98 → 5.0.0-beta.99
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 +42 -1
- package/ai/application-ui-diagnostic-ids-v1.json +1 -0
- package/ai/component-catalog.json +487 -66
- package/ai/component-catalog.schema.json +1 -1
- package/ai/component-composition-rules.json +22 -2
- package/ai/component-composition.json +360 -33
- package/ai/component-composition.schema.json +1 -1
- package/ai/components/README.md +18 -17
- package/ai/components/anchored-surface.md +13 -0
- package/ai/components/application-tabs.md +1 -1
- package/ai/components/chip.md +9 -5
- package/ai/components/choice-chips.md +15 -8
- package/ai/components/collapsible-panel.md +3 -2
- package/ai/components/composer.md +83 -0
- package/ai/components/content-item.md +2 -2
- package/ai/components/corner-status.md +1 -1
- package/ai/components/document-baseline.md +3 -3
- package/ai/components/foundation.md +2 -2
- package/ai/components/layout.md +1 -1
- package/ai/components/list-action-row.md +5 -1
- package/ai/components/list-item.md +5 -1
- package/ai/components/list.md +19 -3
- package/ai/components/nav-stack.md +11 -2
- package/ai/components/pane.md +13 -4
- package/ai/components/popup-menu.md +4 -4
- package/ai/components/recipe-app-shell.md +3 -3
- package/ai/components/recipe-collapsible-sidebar.md +3 -3
- package/ai/components/recipe-compact-toolbar.md +1 -1
- package/ai/components/recipe-composer-form.md +3 -3
- package/ai/components/recipe-list-detail-dialog.md +2 -2
- package/ai/components/recipe-list-workspace-states.md +3 -3
- package/ai/components/recipe-loading-inspector.md +1 -1
- package/ai/components/recipe-navigation-sidebar.md +3 -3
- package/ai/components/recipe-navigation-stack.md +3 -3
- package/ai/components/recipe-workspace-header.md +3 -3
- package/ai/components/resize.md +2 -2
- package/ai/components/split-view.md +4 -2
- package/ai/components/sunken-panel.md +2 -2
- package/ai/components/surface-scaffold.md +15 -3
- package/ai/components/tab-bar.md +1 -0
- package/ai/components/tab-navigator.md +4 -0
- package/ai/components/toolbar-control-group.md +2 -0
- package/ai/components/toolbar-text.md +1 -0
- package/ai/components/toolbar.md +1 -0
- package/ai/components/workbench.md +1 -0
- package/ai/public-api-signatures-v1.md +487 -77
- package/ai/skill.md +40 -13
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/analyzer/component-ownership.mjs +1 -1
- package/analyzer/index.mjs +41 -14
- package/dist/anchored-surface.d.ts +30 -2
- package/dist/anchored-surface.js +1 -1
- package/dist/app-tab.js +1 -1
- package/dist/browser/composer.js +4 -0
- package/dist/catalog-resources.d.ts +3 -2
- package/dist/catalog-resources.js +4 -4
- package/dist/catalog-resources.js.map +1 -1
- package/dist/catalog.d.ts +22 -5
- package/dist/catalog.js +50 -31
- package/dist/catalog.js.map +1 -1
- package/dist/chip.d.ts +41 -9
- package/dist/chip.js +1 -1
- package/dist/choice-chips.d.ts +40 -16
- package/dist/choice-chips.js +1 -1
- package/dist/chunk-34U4ANDJ.js +119 -0
- package/dist/chunk-34U4ANDJ.js.map +1 -0
- package/dist/{chunk-36TL6BCR.js → chunk-3GLYIIFR.js} +34 -6
- package/dist/chunk-3GLYIIFR.js.map +1 -0
- package/dist/{chunk-HWY4BSLS.js → chunk-3VXDSIBJ.js} +2 -2
- package/dist/{chunk-HWY4BSLS.js.map → chunk-3VXDSIBJ.js.map} +1 -1
- package/dist/chunk-3WYREXW4.js +120 -0
- package/dist/chunk-3WYREXW4.js.map +1 -0
- package/dist/chunk-AV7CPAPT.js +82 -0
- package/dist/chunk-AV7CPAPT.js.map +1 -0
- package/dist/{chunk-XK4E225T.js → chunk-B53PS2LE.js} +2 -2
- package/dist/{chunk-XK4E225T.js.map → chunk-B53PS2LE.js.map} +1 -1
- package/dist/{chunk-R42D2BAO.js → chunk-CA7LNX3J.js} +2 -2
- package/dist/{chunk-R42D2BAO.js.map → chunk-CA7LNX3J.js.map} +1 -1
- package/dist/{chunk-AZWE2FB5.js → chunk-CMETHPKR.js} +12 -15
- package/dist/chunk-CMETHPKR.js.map +1 -0
- package/dist/{chunk-Y43B6DS3.js → chunk-CVD5IZGB.js} +2 -2
- package/dist/{chunk-Y43B6DS3.js.map → chunk-CVD5IZGB.js.map} +1 -1
- package/dist/{chunk-6STQXRT4.js → chunk-CXZXHZTR.js} +121 -12
- package/dist/chunk-CXZXHZTR.js.map +1 -0
- package/dist/{chunk-GZE2XVKC.js → chunk-DZJ4GBSP.js} +3 -3
- package/dist/{chunk-GZE2XVKC.js.map → chunk-DZJ4GBSP.js.map} +1 -1
- package/dist/chunk-ELCMSQNJ.js +49 -0
- package/dist/chunk-ELCMSQNJ.js.map +1 -0
- package/dist/{chunk-P74VXMB6.js → chunk-EOO6PYGF.js} +7 -14
- package/dist/chunk-EOO6PYGF.js.map +1 -0
- package/dist/{chunk-PRG4CL3Q.js → chunk-FR76ZJQC.js} +21 -19
- package/dist/chunk-FR76ZJQC.js.map +1 -0
- package/dist/{chunk-QQ3TTWFH.js → chunk-G3J7JDP6.js} +1 -1
- package/dist/{chunk-QQ3TTWFH.js.map → chunk-G3J7JDP6.js.map} +1 -1
- package/dist/{chunk-IGTWXPTN.js → chunk-HGV7J4T6.js} +3 -3
- package/dist/{chunk-IGTWXPTN.js.map → chunk-HGV7J4T6.js.map} +1 -1
- package/dist/{chunk-YWQGSUF2.js → chunk-HLDRH23D.js} +3 -3
- package/dist/{chunk-YWQGSUF2.js.map → chunk-HLDRH23D.js.map} +1 -1
- package/dist/{chunk-PBT3CPPY.js → chunk-HQZYKJ56.js} +24 -4
- package/dist/chunk-HQZYKJ56.js.map +1 -0
- package/dist/{chunk-V6W56V3N.js → chunk-HZPHCSCS.js} +16 -3
- package/dist/chunk-HZPHCSCS.js.map +1 -0
- package/dist/{chunk-GJYQ27C2.js → chunk-INI4WDWC.js} +8 -1
- package/dist/chunk-INI4WDWC.js.map +1 -0
- package/dist/{chunk-U657IHFB.js → chunk-IQSRHXZR.js} +10 -4
- package/dist/chunk-IQSRHXZR.js.map +1 -0
- package/dist/{chunk-KQDU7Z7P.js → chunk-K4AT3V3J.js} +2 -1
- package/dist/chunk-K4AT3V3J.js.map +1 -0
- package/dist/{chunk-QIAPOWHH.js → chunk-KCS6NJ3O.js} +2 -2
- package/dist/{chunk-QIAPOWHH.js.map → chunk-KCS6NJ3O.js.map} +1 -1
- package/dist/{chunk-UKKRPP56.js → chunk-LOSRXZAH.js} +2 -2
- package/dist/{chunk-UKKRPP56.js.map → chunk-LOSRXZAH.js.map} +1 -1
- package/dist/{chunk-ROZLJULF.js → chunk-MF6ZUUJZ.js} +8 -3
- package/dist/chunk-MF6ZUUJZ.js.map +1 -0
- package/dist/{chunk-6IVNMC6R.js → chunk-MHMWQ7RB.js} +2 -2
- package/dist/{chunk-6IVNMC6R.js.map → chunk-MHMWQ7RB.js.map} +1 -1
- package/dist/{chunk-JYD7G4RG.js → chunk-PIVY32AE.js} +15 -5
- package/dist/chunk-PIVY32AE.js.map +1 -0
- package/dist/{chunk-527IAEWG.js → chunk-PYBBYSZ3.js} +10 -17
- package/dist/chunk-PYBBYSZ3.js.map +1 -0
- package/dist/chunk-QDSZTQXL.js +28 -0
- package/dist/chunk-QDSZTQXL.js.map +1 -0
- package/dist/{chunk-YXOMI7ES.js → chunk-QIG4IGUP.js} +2 -2
- package/dist/{chunk-YXOMI7ES.js.map → chunk-QIG4IGUP.js.map} +1 -1
- package/dist/{chunk-6BRRUPJY.js → chunk-QROYYGWU.js} +3 -3
- package/dist/chunk-QROYYGWU.js.map +1 -0
- package/dist/{chunk-FOQK4F3L.js → chunk-RHD7RKGI.js} +28 -7
- package/dist/chunk-RHD7RKGI.js.map +1 -0
- package/dist/{chunk-BBU2SPRE.js → chunk-T4WJ5VGP.js} +4 -4
- package/dist/{chunk-BBU2SPRE.js.map → chunk-T4WJ5VGP.js.map} +1 -1
- package/dist/{chunk-6NOLL5KG.js → chunk-TGLUDQP2.js} +3 -3
- package/dist/{chunk-6NOLL5KG.js.map → chunk-TGLUDQP2.js.map} +1 -1
- package/dist/{chunk-HO6FU3G7.js → chunk-TLMMFQWM.js} +4 -4
- package/dist/{chunk-HO6FU3G7.js.map → chunk-TLMMFQWM.js.map} +1 -1
- package/dist/chunk-UMKV7YVQ.js +20 -0
- package/dist/chunk-UMKV7YVQ.js.map +1 -0
- package/dist/{chunk-M2NRCFRO.js → chunk-VFYO3UGZ.js} +2 -2
- package/dist/{chunk-M2NRCFRO.js.map → chunk-VFYO3UGZ.js.map} +1 -1
- package/dist/{chunk-YOKEL75M.js → chunk-WVS6CUCT.js} +2 -2
- package/dist/{chunk-YOKEL75M.js.map → chunk-WVS6CUCT.js.map} +1 -1
- package/dist/{chunk-MN3POJFN.js → chunk-YMUIQRWK.js} +2 -2
- package/dist/{chunk-MN3POJFN.js.map → chunk-YMUIQRWK.js.map} +1 -1
- package/dist/{chunk-Y3LC7JQO.js → chunk-YOA5W5ZH.js} +2 -2
- package/dist/{chunk-Y3LC7JQO.js.map → chunk-YOA5W5ZH.js.map} +1 -1
- package/dist/{chunk-44A6LP54.js → chunk-YQ2AC4E2.js} +2 -2
- package/dist/{chunk-44A6LP54.js.map → chunk-YQ2AC4E2.js.map} +1 -1
- package/dist/chunk-ZNROKDSZ.js +54 -0
- package/dist/chunk-ZNROKDSZ.js.map +1 -0
- package/dist/collapsible-panel.d.ts +3 -2
- package/dist/collapsible-panel.js +4 -3
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/composer.d.ts +62 -0
- package/dist/composer.js +5 -0
- package/dist/composer.js.map +1 -0
- package/dist/corner-status.d.ts +1 -1
- package/dist/corner-status.js +1 -1
- package/dist/css-values.d.ts +9 -1
- package/dist/css-values.js +1 -1
- package/dist/disclosure-arrow.js +1 -1
- package/dist/empty-state.js +1 -1
- package/dist/grid.js +1 -1
- package/dist/icon-button.js +1 -1
- package/dist/icon-toggle-button.js +1 -1
- package/dist/index.d.ts +10 -5
- package/dist/index.js +40 -37
- package/dist/list-action-row.d.ts +10 -2
- package/dist/list-action-row.js +1 -1
- package/dist/list-header.js +1 -1
- package/dist/list-icon-tile-B8dr97OW.d.ts +18 -0
- package/dist/list-item.d.ts +10 -3
- package/dist/list-item.js +1 -1
- package/dist/list.d.ts +36 -4
- package/dist/list.js +1 -1
- package/dist/loading-spinner.js +1 -1
- package/dist/lucide-icon.js +1 -1
- package/dist/{nav-stack-Cp3Y8kJ9.d.ts → nav-stack-VXTumzKa.d.ts} +48 -3
- package/dist/nav-stack.d.ts +3 -2
- package/dist/nav-stack.js +2 -1
- package/dist/pane.d.ts +40 -3
- package/dist/pane.js +1 -1
- package/dist/{panel-toolbar-CL9tYOkh.d.ts → panel-toolbar-vg6Ir5Dy.d.ts} +6 -1
- package/dist/popup-menu-register.d.ts +2 -1
- package/dist/popup-menu-register.js +97 -22
- package/dist/popup-menu-register.js.map +1 -1
- package/dist/popup-menu.js +1 -1
- package/dist/row.js +1 -1
- package/dist/scroll-clearance-BikHljTc.d.ts +14 -0
- package/dist/select.js +1 -1
- package/dist/skeleton.js +1 -1
- package/dist/spacer.js +1 -1
- package/dist/split-button.js +1 -1
- package/dist/split-view.d.ts +7 -5
- package/dist/split-view.js +6 -3
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.d.ts +6 -1
- package/dist/state-banner.js +1 -1
- package/dist/styles/anchored-surface.css +27 -1
- package/dist/styles/catalog/components/catalog-example.css +33 -0
- package/dist/styles/catalog/components/catalog-stage.css +7 -0
- package/dist/styles/chip.css +70 -0
- package/dist/styles/components/actions/popup-menu/popup-menu.css +14 -4
- package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +75 -1
- package/dist/styles/components/actions/toolbar-text/toolbar-text.css +66 -0
- package/dist/styles/components/collections/list/list.css +77 -7
- package/dist/styles/components/collections/list-action-row/list-action-row.css +177 -22
- package/dist/styles/components/collections/list-item/list-item.css +158 -11
- package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +27 -1
- package/dist/styles/components/feedback/chip/chip.css +70 -0
- package/dist/styles/components/feedback/state-banner/state-banner.css +24 -13
- package/dist/styles/components/forms/composer/composer.css +100 -0
- package/dist/styles/components/forms/token-search-field/token-search-field.css +13 -2
- package/dist/styles/components/layout/pane/pane.css +81 -2
- package/dist/styles/components/navigation/nav-stack/nav-stack.css +84 -0
- package/dist/styles/components/navigation/tab-bar/tab-bar.css +15 -4
- package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +79 -4
- package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +195 -0
- package/dist/styles/composer.css +100 -0
- package/dist/styles/foundation.css +40 -0
- package/dist/styles/list-action-row.css +177 -22
- package/dist/styles/list-item.css +158 -11
- package/dist/styles/list.css +77 -7
- package/dist/styles/nav-stack.css +84 -0
- package/dist/styles/pane.css +81 -2
- package/dist/styles/popup-menu.css +14 -4
- package/dist/styles/state-banner.css +24 -13
- package/dist/styles/styles.css +1 -0
- package/dist/styles/surface-scaffold.css +195 -0
- package/dist/styles/tab-bar.css +15 -4
- package/dist/styles/tab-navigator.css +79 -4
- package/dist/styles/token-search-field.css +13 -2
- package/dist/styles/toolbar-control-group.css +75 -1
- package/dist/styles/toolbar-text.css +66 -0
- package/dist/surface-scaffold-register.d.ts +6 -1
- package/dist/surface-scaffold-register.js +89 -3
- package/dist/surface-scaffold-register.js.map +1 -1
- package/dist/surface-scaffold.d.ts +66 -7
- package/dist/surface-scaffold.js +1 -1
- package/dist/tab-navigator.d.ts +23 -2
- package/dist/tab-navigator.js +2 -0
- package/dist/tab-navigator.js.map +1 -1
- package/dist/token-search-field.d.ts +1 -1
- package/dist/token-search-field.js +1 -1
- package/dist/{token-search-model-DTcTXWMS.d.ts → token-search-model-aYR0YAei.d.ts} +32 -2
- package/dist/token-search-model.d.ts +1 -1
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.js +1 -1
- package/dist/toolbar.d.ts +1 -0
- package/dist/value-table.js +1 -1
- package/dist/wire-catalog.d.ts +3 -2
- package/dist/wire-catalog.js +2 -2
- package/dist/wire-composers.d.ts +16 -0
- package/dist/wire-composers.js +3 -0
- package/dist/wire-composers.js.map +1 -0
- package/dist/wire-list-focus-scroll.d.ts +16 -0
- package/dist/wire-list-focus-scroll.js +3 -0
- package/dist/wire-list-focus-scroll.js.map +1 -0
- package/dist/wire-nav-stack.js +135 -0
- package/dist/wire-nav-stack.js.map +1 -1
- package/dist/wire-scroll-dividers.d.ts +9 -0
- package/dist/wire-scroll-dividers.js +1 -1
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-tab-navigator.d.ts +3 -1
- package/dist/wire-tab-navigator.js +133 -1
- package/dist/wire-tab-navigator.js.map +1 -1
- package/dist/wire-token-search-fields.d.ts +1 -1
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/workbench.d.ts +8 -3
- package/dist/workbench.js +2 -1
- package/docs/accessibility.md +132 -3
- package/docs/anchored-surface.md +46 -4
- package/docs/app-layouts.md +23 -2
- package/docs/apple-hig.md +1 -0
- package/docs/catalog.md +54 -8
- package/docs/collapsible-panel.md +9 -6
- package/docs/component-contract.md +41 -12
- package/docs/component-selection.md +143 -19
- package/docs/composer.md +143 -0
- package/docs/design/templates/choice-chips/multiple-dark.svg +1 -0
- package/docs/design/templates/choice-chips/multiple.svg +1 -0
- package/docs/design/templates/choice-chips-dark.svg +1 -1
- package/docs/design/templates/choice-chips.svg +1 -1
- package/docs/design/templates/composer/chat-dark.svg +1 -0
- package/docs/design/templates/composer/chat.svg +1 -0
- package/docs/design/templates/composer/prompt-dark.svg +1 -0
- package/docs/design/templates/composer/prompt.svg +1 -0
- package/docs/design/templates/composer-dark.svg +1 -0
- package/docs/design/templates/composer.svg +1 -0
- package/docs/design/templates/empty-state/filtered-dark.svg +1 -1
- package/docs/design/templates/empty-state/filtered.svg +1 -1
- package/docs/design/templates/empty-state/with-action-dark.svg +1 -1
- package/docs/design/templates/empty-state/with-action.svg +1 -1
- package/docs/design/templates/empty-state-dark.svg +1 -1
- package/docs/design/templates/empty-state.svg +1 -1
- package/docs/design/templates/list/compact-dark.svg +1 -1
- package/docs/design/templates/list/compact.svg +1 -1
- package/docs/design/templates/list-action-row/default-dark.svg +1 -1
- package/docs/design/templates/list-action-row/default.svg +1 -1
- package/docs/design/templates/list-action-row/icon-tile-dark.svg +1 -0
- package/docs/design/templates/list-action-row/icon-tile.svg +1 -0
- package/docs/design/templates/list-action-row/selected-dark.svg +1 -1
- package/docs/design/templates/list-action-row/selected.svg +1 -1
- package/docs/design/templates/list-action-row/trailing-menu-dark.svg +1 -1
- package/docs/design/templates/list-action-row/trailing-menu.svg +1 -1
- package/docs/design/templates/list-action-row-dark.svg +1 -1
- package/docs/design/templates/list-action-row.svg +1 -1
- package/docs/design/templates/list-dark.svg +1 -1
- package/docs/design/templates/list-header/size-l-dark.svg +1 -1
- package/docs/design/templates/list-header/size-l.svg +1 -1
- package/docs/design/templates/list-header/size-m-dark.svg +1 -1
- package/docs/design/templates/list-header/size-m.svg +1 -1
- package/docs/design/templates/list-header/size-xl-dark.svg +1 -1
- package/docs/design/templates/list-header/size-xl.svg +1 -1
- package/docs/design/templates/list-header-dark.svg +1 -1
- package/docs/design/templates/list-header.svg +1 -1
- package/docs/design/templates/list-item/icon-tile-dark.svg +1 -0
- package/docs/design/templates/list-item/icon-tile.svg +1 -0
- package/docs/design/templates/list-item/selected-dark.svg +1 -1
- package/docs/design/templates/list-item/selected.svg +1 -1
- package/docs/design/templates/list-item-dark.svg +1 -1
- package/docs/design/templates/list-item.svg +1 -1
- package/docs/design/templates/list.svg +1 -1
- package/docs/design/templates/segmented-control-dark.svg +1 -1
- package/docs/design/templates/segmented-control.svg +1 -1
- package/docs/design/templates/skeleton-dark.svg +1 -1
- package/docs/design/templates/skeleton.svg +1 -1
- package/docs/design/templates/state-banner/danger-dark.svg +1 -1
- package/docs/design/templates/state-banner/danger.svg +1 -1
- package/docs/design/templates/state-banner/info-badge-dark.svg +1 -1
- package/docs/design/templates/state-banner/info-badge.svg +1 -1
- package/docs/design/templates/state-banner/info-dark.svg +1 -1
- package/docs/design/templates/state-banner/info.svg +1 -1
- package/docs/design/templates/state-banner/strip-dark.svg +1 -1
- package/docs/design/templates/state-banner/strip.svg +1 -1
- package/docs/design/templates/state-banner/success-dark.svg +1 -1
- package/docs/design/templates/state-banner/success.svg +1 -1
- package/docs/design/templates/state-banner/warning-dark.svg +1 -1
- package/docs/design/templates/state-banner/warning.svg +1 -1
- package/docs/design/templates/state-banner-dark.svg +1 -1
- package/docs/design/templates/state-banner.svg +1 -1
- package/docs/design/templates/tab-bar-dark.svg +1 -1
- package/docs/design/templates/tab-bar.svg +1 -1
- package/docs/design/templates/token-search-field-dark.svg +1 -1
- package/docs/design/templates/token-search-field.svg +1 -1
- package/docs/design/templates/toolbar-control-group-dark.svg +1 -1
- package/docs/design/templates/toolbar-control-group.svg +1 -1
- package/docs/design/templates/toolbar-dark.svg +1 -1
- package/docs/design/templates/toolbar-text/default-dark.svg +1 -1
- package/docs/design/templates/toolbar-text/default.svg +1 -1
- package/docs/design/templates/toolbar-text/large-dark.svg +1 -1
- package/docs/design/templates/toolbar-text/large.svg +1 -1
- package/docs/design/templates/toolbar-text-dark.svg +1 -1
- package/docs/design/templates/toolbar-text.svg +1 -1
- package/docs/design/templates/toolbar.svg +1 -1
- package/docs/design/templates/value-table/details-dark.svg +1 -1
- package/docs/design/templates/value-table/details.svg +1 -1
- package/docs/design/templates/value-table-dark.svg +1 -1
- package/docs/design/templates/value-table.svg +1 -1
- package/docs/design-philosophy.md +9 -1
- package/docs/examples/command-palette-adapter.tsx +3 -3
- package/docs/layout.md +147 -14
- package/docs/nav-stack.md +112 -2
- package/docs/recipes.md +9 -9
- package/docs/split-view.md +3 -3
- package/docs/sunken-panel.md +2 -2
- package/docs/surface-scaffold.md +138 -7
- package/docs/tab-navigator.md +38 -1
- package/docs/type-contracts.md +16 -16
- package/docs/ui-analyzer.md +3 -1
- package/docs/ui-doctor.md +71 -0
- package/docs/ux-demo.md +6 -6
- package/docs/workbench.md +10 -6
- package/docs/writing.md +152 -0
- package/doctor/copy-capitalization-checks.mjs +591 -0
- package/doctor/index.mjs +17 -8
- package/llms.txt +105 -11
- package/package.json +15 -1
- package/ux-demo/recipes/app-shell.tsx +11 -11
- package/ux-demo/recipes/collapsible-sidebar.tsx +12 -11
- package/ux-demo/recipes/compact-toolbar.tsx +10 -10
- package/ux-demo/recipes/composer-form.tsx +8 -8
- package/ux-demo/recipes/list-detail-dialog.tsx +13 -13
- package/ux-demo/recipes/list-workspace-states.tsx +13 -13
- package/ux-demo/recipes/loading-inspector.tsx +10 -10
- package/ux-demo/recipes/navigation-sidebar.tsx +7 -7
- package/ux-demo/recipes/navigation-stack.tsx +5 -5
- package/ux-demo/recipes/workspace-header.tsx +5 -5
- package/dist/chunk-36TL6BCR.js.map +0 -1
- package/dist/chunk-4LOK4634.js +0 -21
- package/dist/chunk-4LOK4634.js.map +0 -1
- package/dist/chunk-527IAEWG.js.map +0 -1
- package/dist/chunk-6BRRUPJY.js.map +0 -1
- package/dist/chunk-6STQXRT4.js.map +0 -1
- package/dist/chunk-76PWEN5Y.js +0 -64
- package/dist/chunk-76PWEN5Y.js.map +0 -1
- package/dist/chunk-AZWE2FB5.js.map +0 -1
- package/dist/chunk-DQDTZE4W.js +0 -70
- package/dist/chunk-DQDTZE4W.js.map +0 -1
- package/dist/chunk-FOQK4F3L.js.map +0 -1
- package/dist/chunk-GJYQ27C2.js.map +0 -1
- package/dist/chunk-JYD7G4RG.js.map +0 -1
- package/dist/chunk-KQDU7Z7P.js.map +0 -1
- package/dist/chunk-P74VXMB6.js.map +0 -1
- package/dist/chunk-PBT3CPPY.js.map +0 -1
- package/dist/chunk-PRG4CL3Q.js.map +0 -1
- package/dist/chunk-ROZLJULF.js.map +0 -1
- package/dist/chunk-U657IHFB.js.map +0 -1
- package/dist/chunk-V6W56V3N.js.map +0 -1
package/docs/accessibility.md
CHANGED
|
@@ -18,7 +18,7 @@ These are normative contracts for package components and consuming applications.
|
|
|
18
18
|
- Interactive `ContentItem` cards are keyboard reachable. Set `selectionMode="toggle"` for a pressed button, `selectionMode="single"` for an option inside a listbox, or `selectionMode="multiple"` for a selectable row with a gridcell that can contain native controls. Place multiple-mode rows inside a labeled `List` or `Grid` with `selectionMode="multiple"`; the app owns the selection set and range gestures. Wire `wireContentItems` once so Enter and Space on the card activate its delegated action without activating it from a nested control. Disabled cards expose `aria-disabled` and leave the Tab order.
|
|
19
19
|
- An unlabeled `Skeleton` (including the value slots a component's `placeholder` prop renders) is decorative loading chrome: it is `aria-hidden`, conveys no information a user needs to act on, and is deliberately exempt from the WCAG 3:1 non-text-contrast target. Inside a solid `Badge` it stays subtle (about 2.1–4.4:1 in light, 2.4–2.8:1 in dark, depending on tone); `tests/browser/tone-contrast.spec.ts` holds those values as a regression floor rather than an accessibility threshold. Announce loading through a labeled `Skeleton`, `LoadingSpinner`, or `aria-busy` on the owning region, never through the skeleton's visibility.
|
|
20
20
|
- `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure. Its optional `badge` is persistent inline status/count content beside the title and is announced as part of the banner; keep it terse and do not rely on color alone for its meaning.
|
|
21
|
-
- A removable `Chip` has a native button with a specific `removeLabel`, such as "Remove Urgent tag". The button is absent for a plain chip and disabled for a disabled chip. Its leading `icon` is decorative; the visible label names the tag. `truncate` retains the full plain-text label as a native tooltip. Tag text remains visible because tone does not convey meaning alone.
|
|
21
|
+
- A removable `Chip` has a native button with a specific `removeLabel`, such as "Remove Urgent tag". The button is absent for a plain chip and disabled for a disabled chip. Its leading `icon` is decorative; the visible label names the tag. `truncate` retains the full plain-text label as a native tooltip. Tag text remains visible because tone does not convey meaning alone. A link `Chip` (`href`) is a native anchor named by its visible label, with no remove button; `external` adds `target="_blank"`, `rel="noopener noreferrer"`, and an "(opens in new tab)" name suffix for a text label, and a disabled link chip drops `href`, sets `aria-disabled`, and leaves the tab order. `tone="inherit"` paints no fill, so its label keeps the surrounding text's contrast (a brand-loud bubble's white text included), and its focus ring is drawn in the surrounding foreground so it stays visible on a brand-colored surface.
|
|
22
22
|
- `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
|
|
23
23
|
- `Select` follows the Web Awesome host's standard `input`/`change` events. Application tests verify the live `value`, focus, and events—not attributes alone. Its decorative option icons and value-dependent selected content remain present after controlled rerenders, so the visible choice does not silently lose its non-text cue.
|
|
24
24
|
- `DisclosureArrow` is an 18px root-scaled decorative visual by default, not an interaction target. Its owning native control supplies a stable accessible name, pointer and keyboard interaction, and `aria-expanded`; multiple arrows keep independently controlled state. Consumers may resize it with the typed `size` prop without changing that ownership. Configured directions animate over the shortest path; a 180-degree closed-to-open tie uses counterclockwise rotation.
|
|
@@ -220,7 +220,18 @@ name from its content and ignores an `aria-label` on the host, so an icon-only
|
|
|
220
220
|
trigger requires `label`, which renders as visually hidden text inside the
|
|
221
221
|
button; a trigger with visible `text` is named by that text. An icon-only
|
|
222
222
|
trigger also shows its `label` as an `aria-hidden` help tag on hover and
|
|
223
|
-
keyboard focus (see [Help tags on icon-only triggers](#help-tags-on-icon-only-triggers)).
|
|
223
|
+
keyboard focus (see [Help tags on icon-only triggers](#help-tags-on-icon-only-triggers)).
|
|
224
|
+
The open menu carries that same name. Web Awesome renders the `role="menu"`
|
|
225
|
+
panel inside the dropdown's shadow root, where no ID reference can reach the
|
|
226
|
+
trigger (the context anchor is `aria-hidden` light DOM, and a button trigger's
|
|
227
|
+
real `<button>` sits in `wa-button`'s own shadow root). So `PopupMenu` renders
|
|
228
|
+
the name it already knows (a context or icon-only `label`, or the visible
|
|
229
|
+
`text`) as `data-menu-label` on the dropdown, and the register copies it to the
|
|
230
|
+
panel as `aria-label` on first render and on every open. A submenu panel shares
|
|
231
|
+
a shadow root with its parent item's label and is `aria-labelledby` it, so it
|
|
232
|
+
is announced as, for example, "More Actions, menu".
|
|
233
|
+
`tests/browser/popup-menu-accessible-name.spec.ts` finds each panel with
|
|
234
|
+
`getByRole('menu', { name })` in Chromium, Firefox, and WebKit. Group headings
|
|
224
235
|
render as the same uppercase group titles a grouped `Select` shows, and dividers
|
|
225
236
|
are presentational. A keyboard-focused item is the menu's current row, painted
|
|
226
237
|
with the Select's current-option fill rather than a second focus ring inside the
|
|
@@ -466,6 +477,41 @@ focused radio, and the checked pill adds a second pixel of border so the
|
|
|
466
477
|
selection does not rely on hue alone; forced-colors mode paints it with
|
|
467
478
|
`Highlight`.
|
|
468
479
|
|
|
480
|
+
With `multiple`, the group renders `role="group"` with the same naming, and
|
|
481
|
+
each choice is a native `<input type="checkbox">` inside its pill. Every pill
|
|
482
|
+
is its own Tab stop, Space toggles it, assistive technology reads its native
|
|
483
|
+
checked state, and every checked box submits its value under `name`
|
|
484
|
+
(`FormData.getAll(name)`). Native `required` on a checkbox demands that one
|
|
485
|
+
box, so a multiple group takes no `required`; validate an "at least one" rule
|
|
486
|
+
in the application. Every chip color is a theme token resolved through
|
|
487
|
+
`light-dark()`, so inside a dark appearance scope (`.wa-dark`, `.wa-invert`,
|
|
488
|
+
or any `color-scheme: dark` ancestor) the pills, labels, checked tint, and
|
|
489
|
+
focus ring read on inverse chrome without overrides.
|
|
490
|
+
|
|
491
|
+
## Composer
|
|
492
|
+
|
|
493
|
+
`Composer`'s message field is a native `<textarea>` named by `label`
|
|
494
|
+
(`aria-label`, title style). With an `id`, the `hint` renders with the id
|
|
495
|
+
`<id>-hint` and becomes the field's `aria-describedby`; without an `id`, the
|
|
496
|
+
hint is visible only. The field draws no border or outline of its own: the
|
|
497
|
+
frame is its boundary, and while focus is anywhere within the composer the
|
|
498
|
+
frame's border turns brand (`Highlight` in forced-colors mode, with a 2px
|
|
499
|
+
outline). Each trailing action keeps its own name and focus ring. `disabled`
|
|
500
|
+
disables the field only; the app disables its actions. Sending and
|
|
501
|
+
announcing a sent message stay with the app.
|
|
502
|
+
|
|
503
|
+
With `submitOnEnter` and `wireComposers`, the field carries
|
|
504
|
+
`aria-keyshortcuts="Enter"`, so assistive technology announces the shortcut.
|
|
505
|
+
Enter and Cmd/Ctrl+Enter activate the first trailing action, the same control
|
|
506
|
+
a pointer or Tab-and-Space user reaches; Shift+Enter inserts a newline. An
|
|
507
|
+
Enter that commits an IME composition (`isComposing`, keyCode 229, or between
|
|
508
|
+
`compositionstart` and `compositionend`) is left to the input method, so
|
|
509
|
+
people composing Japanese, Chinese, or Korean text never send a half-written
|
|
510
|
+
message. An empty or whitespace-only message, an auto-repeating key, a
|
|
511
|
+
disabled field, or a disabled, loading, or `aria-disabled` action sends
|
|
512
|
+
nothing. A visible hint may also state the shortcut when the audience may not
|
|
513
|
+
expect it.
|
|
514
|
+
|
|
469
515
|
## TokenSearchField
|
|
470
516
|
|
|
471
517
|
Use `presentation="form-field"` when a structured search query sits beside
|
|
@@ -481,7 +527,10 @@ form-control tokens, so the two controls read as one form.
|
|
|
481
527
|
|
|
482
528
|
`TokenSearchField` exposes the editable surface as a named `searchbox`. Each
|
|
483
529
|
chip is atomic (`contenteditable="false"`) and contains separately named edit
|
|
484
|
-
and remove buttons; the clear action is also named.
|
|
530
|
+
and remove buttons; the clear action is also named. A `trailingAction` is a
|
|
531
|
+
named button whose `label` is also its tooltip; a help toggle sets `expanded`
|
|
532
|
+
(`aria-expanded`, with `haspopup` and `controls` for the surface it opens) and
|
|
533
|
+
an on/off toggle sets `pressed` (`aria-pressed`). Disabled fields publish
|
|
485
534
|
`aria-disabled` and stop editing without hiding the current expression. The
|
|
486
535
|
application owns query parsing and must announce result-count or loading
|
|
487
536
|
changes separately when that feedback is useful. Use `readTokenSearchField()`
|
|
@@ -612,6 +661,86 @@ opened it, else the restore control, else a control whose `aria-controls` names
|
|
|
612
661
|
panel; an open overlay panel takes focus and keeps Tab inside it (the ARIA dialog
|
|
613
662
|
pattern). See [`workbench.md`](workbench.md).
|
|
614
663
|
|
|
664
|
+
## Chrome material
|
|
665
|
+
|
|
666
|
+
Translucent chrome, such as a `Pane` header with
|
|
667
|
+
`headerPresentation="overlay"`, a `NavStack` top bar with
|
|
668
|
+
`chromePresentation="overlay"`, or a `NavStack` bottom toolbar with
|
|
669
|
+
`bottomToolbarPresentation="overlay"`, or a `TabNavigator` tab bar with
|
|
670
|
+
`barPresentation="overlay"`, paints `--kui-chrome-material`: the surface at
|
|
671
|
+
80% opacity behind a blur-and-saturate backdrop filter
|
|
672
|
+
(`--kui-chrome-material-filter`). Content scrolls visibly beneath it, so the
|
|
673
|
+
backdrop under its text is not a fixed color. The contrast rule measures the
|
|
674
|
+
worst case: the material composited over a solid black backdrop in the light
|
|
675
|
+
theme and a solid white backdrop in the dark theme. The blur only averages the
|
|
676
|
+
content, so it can never be darker or lighter than that.
|
|
677
|
+
|
|
678
|
+
Chrome on a raised surface, such as a `DialogSurface` title bar with
|
|
679
|
+
`headerPresentation="floating"`, paints `--kui-chrome-material-raised`
|
|
680
|
+
instead: the raised surface at the same 80%, so the bar matches the dialog
|
|
681
|
+
panel behind it. The same rule and fallbacks apply.
|
|
682
|
+
|
|
683
|
+
- **Normal text and icons pass AA over any content.** `--kui-color-text`
|
|
684
|
+
measures about 10.5:1 (light) and 8.3:1 (dark) on that worst case, and
|
|
685
|
+
about 10.5:1 (light) and 6.7:1 (dark) over the raised material. 80% is
|
|
686
|
+
above the minimum opacity that keeps 4.5:1 there (about 55% light and 60%
|
|
687
|
+
dark). A theme that overrides `--kui-chrome-material` or
|
|
688
|
+
`--kui-chrome-material-raised` must keep its text at 4.5:1 or better
|
|
689
|
+
against the same backdrops.
|
|
690
|
+
- **Quiet text does not, so `ToolbarText` lifts it.** `--kui-color-text-quiet`
|
|
691
|
+
falls to about 3.2:1 (light) and 4.1:1 (dark) over the worst case, and the
|
|
692
|
+
default-size title color to about 3.8:1 (light). Inside an overlay `Pane`
|
|
693
|
+
header or overlay `NavStack` chrome or bottom toolbar, or slotted as the `label` (or into
|
|
694
|
+
`header-actions`) of a `DialogSurface` floating header, an untoned
|
|
695
|
+
`ToolbarText` therefore takes
|
|
696
|
+
`--kui-color-text` for its title at every size, and its `description` mixes
|
|
697
|
+
the quiet color halfway toward the normal one, about 5.9:1 in both themes
|
|
698
|
+
(about 4.9:1 dark over the raised material). That keeps AA while the
|
|
699
|
+
description still reads as secondary. Controls lift the same way in an
|
|
700
|
+
overlay `Pane` header, overlay `NavStack` chrome or bottom toolbar, and a
|
|
701
|
+
`DialogSurface` floating header's `header-actions`: a default-tone
|
|
702
|
+
`ToolbarControlGroup`'s quiet control color over its own translucent fill
|
|
703
|
+
measures about 3.0:1 (light) and 3.5:1 (dark) over the worst case, so its
|
|
704
|
+
controls take `--kui-color-text` there. An overlay `TabNavigator` bar's
|
|
705
|
+
unselected labels lift halfway toward `--kui-color-text`, and its selected
|
|
706
|
+
label mixes 40% of it into the brand color: the plain brand color measured
|
|
707
|
+
about 3.2:1 (light) over the worst case. `wa-dialog`'s own close button is an
|
|
708
|
+
icon, held to the 3:1 non-text bar, and measures about 3.8:1 (light) and
|
|
709
|
+
4.1:1 (dark) over the raised material. Any other quiet copy you place in
|
|
710
|
+
overlay chrome yourself has no such lift; keep it in the content.
|
|
711
|
+
- **The material turns opaque when transparency would hurt.** Under
|
|
712
|
+
`prefers-reduced-transparency: reduce` or `prefers-contrast: more`, and where
|
|
713
|
+
`backdrop-filter` is unsupported, it resolves to the opaque surface. In forced
|
|
714
|
+
colors, the header (or NavStack chrome) paints `Canvas` with a `CanvasText`
|
|
715
|
+
bottom edge, and an overlay NavStack bottom toolbar paints `Canvas` with a
|
|
716
|
+
`CanvasText` top edge, so content passing beneath it never shows through.
|
|
717
|
+
|
|
718
|
+
`tests/browser/overlay-header.spec.ts` (Pane),
|
|
719
|
+
`tests/browser/overlay-nav-stack.spec.ts` (NavStack top chrome),
|
|
720
|
+
`tests/browser/overlay-nav-stack-bottom.spec.ts` (NavStack bottom toolbar,
|
|
721
|
+
which also measures its description, a control, the top chrome's back
|
|
722
|
+
control, and increased contrast), and
|
|
723
|
+
`tests/browser/dialog-floating-header.spec.ts` (DialogSurface) measure the
|
|
724
|
+
title over the material on both worst-case backdrops in Chromium, Firefox, and
|
|
725
|
+
WebKit, and the Pane and DialogSurface specs also measure the description and
|
|
726
|
+
a header control group (DialogSurface also its close button). In
|
|
727
|
+
Chromium they also check the opaque fallbacks for reduced transparency and
|
|
728
|
+
forced colors, and the Pane and DialogSurface specs check increased contrast.
|
|
729
|
+
|
|
730
|
+
**Firefox and WebKit screenshots omit the blur.** In Playwright's Firefox and
|
|
731
|
+
WebKit, `page.screenshot()` (headless or headed) shows the content beneath the
|
|
732
|
+
material crisp, as if `backdrop-filter` did nothing. Stock Firefox's own
|
|
733
|
+
`--headless --screenshot` does the same. The filter is still applied: a
|
|
734
|
+
`recordVideo` frame of the same page in the same session, which records the
|
|
735
|
+
compositor's output, shows the content blurred. Firefox and WebKit draw
|
|
736
|
+
`backdrop-filter` only when compositing (WebRender in Firefox, Core Animation
|
|
737
|
+
in WebKit), and their snapshot paths repaint the page without it. Chromium's
|
|
738
|
+
screenshot shows the blur. Plain test pages with no kerf CSS behave the same
|
|
739
|
+
way, so no ancestor style in these components is blocking the filter. When
|
|
740
|
+
you review the blur in Firefox or WebKit, use a `recordVideo` frame, not a
|
|
741
|
+
screenshot. A crisp screenshot is not a defect, and contrast is unaffected
|
|
742
|
+
because the rule above already assumes no blur.
|
|
743
|
+
|
|
615
744
|
## Verification matrix
|
|
616
745
|
|
|
617
746
|
For each changed component, inspect default, hover, focus, disabled, selected/pressed, busy/error, long-content, wide, narrow, light, dark, increased-contrast, reduced-motion, keyboard-only, and 200%-zoom states where applicable. DOM order must match reading and focus order, with no clipping or unreachable action.
|
package/docs/anchored-surface.md
CHANGED
|
@@ -23,10 +23,15 @@ focusable anchor on close (or the previously focused element for a nonfocusable
|
|
|
23
23
|
anchor). Pass `initialFocus: false` for read-only
|
|
24
24
|
help that leaves focus on its trigger. Escape and an outside press dismiss it;
|
|
25
25
|
`dismiss`, `onDismiss`, and `outsideIgnore` can customize that behavior.
|
|
26
|
-
`placement` (`bottom` or `top`), `align` (`start` or `end`),
|
|
27
|
-
shared `kerfjs/overlay` positioning core: it flips
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
`placement` (`bottom` or `top`), `align` (`start` or `end`), `gap`, and
|
|
27
|
+
`viewportMargin` feed the shared `kerfjs/overlay` positioning core: it flips
|
|
28
|
+
vertically when the other side fits, keeps the surface `viewportMargin` (default
|
|
29
|
+
8px) inside the viewport edges, and follows scroll and resize. The surface fits
|
|
30
|
+
its height to the room beside the anchor: when neither side holds it, it opens
|
|
31
|
+
on the roomier side, caps its height to that room, and scrolls its content.
|
|
32
|
+
The room is exposed to the content as `--kui-anchored-surface-available-height`
|
|
33
|
+
and refreshed on every reposition, so a tall help surface fits beside a field on
|
|
34
|
+
a phone without application media queries or `max-height` overrides.
|
|
30
35
|
|
|
31
36
|
```tsx
|
|
32
37
|
openAnchoredSurfaceAt(
|
|
@@ -52,6 +57,43 @@ Closing the anchor's modal dialog also closes the surface. Escape closes only
|
|
|
52
57
|
the surface; the dialog beneath stays open. The temporary pointer anchor is
|
|
53
58
|
removed when the surface closes.
|
|
54
59
|
|
|
60
|
+
## Dark tone for media chrome
|
|
61
|
+
|
|
62
|
+
A popup anchored to dark media chrome (a volume control in a
|
|
63
|
+
`ToolbarControlGroup tone="dark"` over a video or image preview) passes
|
|
64
|
+
`tone: 'dark'`:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
openAnchoredSurface(volumeButton, () => <VolumeControls />, {
|
|
68
|
+
label: 'Volume',
|
|
69
|
+
tone: 'dark',
|
|
70
|
+
placement: 'top',
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The tone is the same loud, inverse tone as a dark `ToolbarControlGroup` or
|
|
75
|
+
`ToolbarText`: dark beside a light page and light beside a dark one, so the
|
|
76
|
+
popup matches the group that opened it. When the surface opens it takes the
|
|
77
|
+
opposite of the anchor's color scheme (an explicit `color-scheme` on the anchor
|
|
78
|
+
or an ancestor wins; `light dark` follows the user's preference), and the
|
|
79
|
+
surface is marked `data-tone="dark"`. Nothing else changes. Every semantic
|
|
80
|
+
color token is a `light-dark()` pair, so the surface fill, border, and shadow,
|
|
81
|
+
and the text, borders, and focus rings of the Kerf UI and Web Awesome
|
|
82
|
+
components inside it (a `ListItem` mute row, a `wa-slider`), resolve to the
|
|
83
|
+
other side by inheritance. The content needs no tone props and no overrides.
|
|
84
|
+
Text and focus rings keep WCAG AA in both page schemes. A theme that replaces a
|
|
85
|
+
semantic token with a single color instead of a `light-dark()` pair opts that
|
|
86
|
+
token out of the flip. While the surface is open it follows a live switch: an
|
|
87
|
+
OS preference change, or a theme class, attribute, or inline `color-scheme`
|
|
88
|
+
changed on the anchor or one of its ancestors, flips it to the new opposite
|
|
89
|
+
(the content's own color transitions run, as the page's do). A scheme changed
|
|
90
|
+
only by swapping a stylesheet is not observed. `openAnchoredSurfaceAt` reads the
|
|
91
|
+
scheme from its `context` (the element under the pointer), so a region with its
|
|
92
|
+
own `color-scheme` is honored; without a connected `context` it reads the modal
|
|
93
|
+
scope's (or the page's).
|
|
94
|
+
|
|
95
|
+
## Theming
|
|
96
|
+
|
|
55
97
|
Override `--kui-anchored-surface-inset` and
|
|
56
98
|
`--kui-anchored-surface-radius` for a theme; content can compose any Kerf UI
|
|
57
99
|
components without the surface applying action policy. Prefer `PopupMenu` for
|
package/docs/app-layouts.md
CHANGED
|
@@ -41,8 +41,19 @@ the layouts handle the insets for you. There is nothing to configure:
|
|
|
41
41
|
status bar or home indicator but its first and last items can always be
|
|
42
42
|
scrolled clear.
|
|
43
43
|
- **Interior edges get nothing.** A `Workbench` center next to an expanded rail,
|
|
44
|
-
a `SplitView` detail beside its list, or a `NavStack` view under its
|
|
45
|
-
not inset on that edge. Collapse the rail and the center picks up
|
|
44
|
+
a `SplitView` detail beside its list, or a `NavStack` view under its docked
|
|
45
|
+
chrome is not inset on that edge. Collapse the rail and the center picks up
|
|
46
|
+
the edge. A `NavStack` with `chromePresentation="overlay"` is the exception
|
|
47
|
+
by design: its views reach under the translucent chrome, and the measured
|
|
48
|
+
chrome height (top safe area included) becomes their block-start inset, so
|
|
49
|
+
content scrolls beneath the bar yet rests clear of it (see
|
|
50
|
+
[Navigation stack › Overlay chrome](nav-stack.md#overlay-chrome)). Its
|
|
51
|
+
`bottomToolbarPresentation="overlay"` does the same at the bottom: the
|
|
52
|
+
measured toolbar height (bottom safe area included) becomes the views'
|
|
53
|
+
block-end inset, so the last item rests just above the toolbar. A
|
|
54
|
+
`TabNavigator` with `barPresentation="overlay"` does the same for its tab
|
|
55
|
+
bar: the measured bar height (bottom safe area included) becomes the
|
|
56
|
+
scenes' block-end inset.
|
|
46
57
|
- **No double inset.** Whichever region applies an inset clears it for its
|
|
47
58
|
descendants. A layout region whose only child is a `Pane` or another layout
|
|
48
59
|
lets that child own the insets. That child fills the region — a `Workbench`
|
|
@@ -130,6 +141,16 @@ single scrolling slot; the negative margins change only its inline reach. The
|
|
|
130
141
|
[`Pane` UX catalog example](../ux-demo/demos/pane.tsx) shows both `deepInset`
|
|
131
142
|
states with the same table component.
|
|
132
143
|
|
|
144
|
+
### Scrolling beneath overlaying chrome
|
|
145
|
+
|
|
146
|
+
`Pane.scrollClearance` (`"block-start"`, `"block-end"`, or `"both"`) lets the
|
|
147
|
+
scrolling slot pass beneath chrome laid over the pane's edge, such as a
|
|
148
|
+
`DialogSurface` floating footer over a filling Pane. The slot adds the
|
|
149
|
+
inherited `--kui-scroll-clearance-block-*` context that the chrome publishes to
|
|
150
|
+
its block padding and `scroll-padding`, alongside the safe-area and
|
|
151
|
+
`deepInset` insets, so its first and last items still rest clear of the
|
|
152
|
+
chrome. See `docs/layout.md` "Scroll clearance".
|
|
153
|
+
|
|
133
154
|
## Decision matrix
|
|
134
155
|
|
|
135
156
|
| Situation | Layout | Device threshold |
|
package/docs/apple-hig.md
CHANGED
|
@@ -12,6 +12,7 @@ Native Apple applications should use native platform controls and follow the Hum
|
|
|
12
12
|
- Use one icon family and one metaphor per action. Directional icons may mirror in RTL; checkmarks, logos, and real-world objects do not.
|
|
13
13
|
- Motion is brief, causal, and optional. Honor reduced-motion and reduced-transparency preferences; repeating indicators stop moving under reduced motion.
|
|
14
14
|
- Writing is concise, specific, and action-led. Labels describe the result, not implementation details.
|
|
15
|
+
- Capitalize by element type, consistently: names of commands, controls, views, and fields use title style; descriptions, messages, tooltips, and option text use sentence style. See [Writing interface copy](./writing.md#capitalization).
|
|
15
16
|
|
|
16
17
|
## Interaction
|
|
17
18
|
|
package/docs/catalog.md
CHANGED
|
@@ -85,7 +85,7 @@ action. Filtering does not change the selected
|
|
|
85
85
|
entry or its preview.
|
|
86
86
|
|
|
87
87
|
Per-entry `resources` render as "open in new tab" links in the footer, and
|
|
88
|
-
`related` renders a "Related
|
|
88
|
+
`related` renders a "Related Entries" `PopupMenu` in a `single`
|
|
89
89
|
`ToolbarControlGroup` (grouped by each entry's `group`), so register its elements
|
|
90
90
|
with `@kerfjs/ui/popup-menu/register` when you use it.
|
|
91
91
|
|
|
@@ -142,18 +142,64 @@ entry's `tags` array.
|
|
|
142
142
|
classes. A supplied `label` also names the row's `section`, so each specimen
|
|
143
143
|
is a named region that assistive technology and
|
|
144
144
|
`getByRole("region", { name })` can address.
|
|
145
|
+
- Example and stack labels are headings, so they use title style ("Heading
|
|
146
|
+
Identity Stays Whole", "Icon Trigger in a Toolbar Group"); the `note` that
|
|
147
|
+
explains a specimen stays sentence style. Write a label as a name, not a
|
|
148
|
+
sentence. See [Writing interface copy](./writing.md#capitalization);
|
|
149
|
+
`kerf-ui-doctor` reports a sentence-case label as `KUI-D040`.
|
|
145
150
|
- Focused demos do not import local stylesheets, use inline `style`, or invent
|
|
146
151
|
styling-only `demo-*` classes. Prefer a public Kerf component, then a Web
|
|
147
152
|
Awesome surface when Kerf has no equivalent, then ordinary semantic HTML.
|
|
148
153
|
Use `CatalogExample.viewport` for bounded specimen width, height, frame,
|
|
149
154
|
surface, overflow, responsive visibility, and public custom-property values;
|
|
150
155
|
keep those constraints catalog-owned instead of recreating a demo stylesheet.
|
|
156
|
+
Widths run `sidebar` (272px, a production rail, for sidebar-pane content),
|
|
157
|
+
`compact` (440px), `medium` (640px), and `wide` (864px), each capped at the
|
|
158
|
+
stage, plus `full`; `text` and `control` size a single label or control.
|
|
151
159
|
Component heights run `short` (220px) through `tall` (360px); frame an app
|
|
152
160
|
shell, whole-screen layout, or other application-sized recipe with
|
|
153
161
|
`height: "app"` (592px), and give its root `fill` rather than a frame `Pane`.
|
|
154
162
|
An app frame stands in for the screen: it is the containing block for
|
|
155
163
|
screen-fixed chrome, so a compact `CollapsiblePanel` overlay, its backdrop,
|
|
156
164
|
and restore controls dock to the frame's edges rather than the page's.
|
|
165
|
+
`minHeight` (`short`, `medium`, or `app` at 592px) is a floor the frame
|
|
166
|
+
may grow past rather than a definite height.
|
|
167
|
+
- A top-layer specimen (a `popover`, a modal) that production anchors to a
|
|
168
|
+
named region with CSS anchor positioning anchors to the frame instead when
|
|
169
|
+
the frame takes that name: `viewport.anchorName` is a CSS dashed-ident
|
|
170
|
+
(`"--app-shell-main"`, validated; anything else throws) that becomes the
|
|
171
|
+
frame's `anchor-name`, so the specimen's own `position-anchor` rule resolves
|
|
172
|
+
to the frame. Do not wrap the specimen in a stage element to name it. Anchor
|
|
173
|
+
names are document-wide, so give each frame on a page its own. The frame
|
|
174
|
+
only names the anchor: the specimen keeps owning its placement, and as a
|
|
175
|
+
top-layer element it is not clipped by the frame or the stage. Chromium,
|
|
176
|
+
Firefox, and WebKit with CSS anchor positioning place it against the frame;
|
|
177
|
+
an engine without anchor positioning ignores the name, and the specimen
|
|
178
|
+
falls back to whatever placement its own CSS gives it there.
|
|
179
|
+
- For a centered reading column inside a wider frame, set
|
|
180
|
+
`viewport.contentWidth` (`compact` 440px, `medium` 640px, or `wide` 864px,
|
|
181
|
+
each capped at the frame) and `contentAlign: "center"` (the default is
|
|
182
|
+
`start`). Each in-flow child fills that column; `[popover]` children keep
|
|
183
|
+
their own geometry. The column applies to block, `grid`, and `flex-column`
|
|
184
|
+
frames, and `contentAlign` does nothing without `contentWidth`.
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
<CatalogExample
|
|
188
|
+
label="Request Docked to the Main Column"
|
|
189
|
+
viewport={{
|
|
190
|
+
layout: "flex-column",
|
|
191
|
+
width: "full",
|
|
192
|
+
minHeight: "app",
|
|
193
|
+
anchorName: "--app-shell-main",
|
|
194
|
+
contentWidth: "medium",
|
|
195
|
+
contentAlign: "center",
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
<Transcript />
|
|
199
|
+
<PermissionRequestPopup /> {/* its CSS: position-anchor: --app-shell-main */}
|
|
200
|
+
</CatalogExample>
|
|
201
|
+
```
|
|
202
|
+
|
|
157
203
|
- The specimen is an immediate child of `CatalogExample`. A focused component
|
|
158
204
|
row should place the component root there, without a decorative card or
|
|
159
205
|
spacing wrapper. A composition row may place the composition root there.
|
|
@@ -171,7 +217,7 @@ import { CatalogExample, CatalogExampleStack } from "@kerfjs/ui/catalog";
|
|
|
171
217
|
|
|
172
218
|
const buttonPreview = (
|
|
173
219
|
<CatalogExampleStack
|
|
174
|
-
label="Button
|
|
220
|
+
label="Button Variants"
|
|
175
221
|
rootAttributes={{ "data-demo": "button" }}
|
|
176
222
|
>
|
|
177
223
|
<CatalogExample label="Icon" note="A bare glyph." align="glyph">
|
|
@@ -180,7 +226,7 @@ const buttonPreview = (
|
|
|
180
226
|
<CatalogExample label="Control" align="inline-control">
|
|
181
227
|
<SegmentedControl id="view" label="View" value="list" choices={choices} />
|
|
182
228
|
</CatalogExample>
|
|
183
|
-
<CatalogExample label="Authoring
|
|
229
|
+
<CatalogExample label="Authoring Note" note="Explain the specimen below.">
|
|
184
230
|
<p>Use the public helper contract.</p>
|
|
185
231
|
</CatalogExample>
|
|
186
232
|
</CatalogExampleStack>
|
|
@@ -367,23 +413,23 @@ const sections: CatalogSection[] = [
|
|
|
367
413
|
const renderers: Record<string, () => SafeHtml> = {
|
|
368
414
|
button: () => (
|
|
369
415
|
<CatalogExampleStack
|
|
370
|
-
label="Button
|
|
416
|
+
label="Button States"
|
|
371
417
|
rootAttributes={{ "data-demo": "button" }}
|
|
372
418
|
>
|
|
373
419
|
<CatalogExample label="Default" align="inline-control">
|
|
374
420
|
<Button label="Save" />
|
|
375
421
|
</CatalogExample>
|
|
376
|
-
<CatalogExample label="Authoring
|
|
422
|
+
<CatalogExample label="Authoring Note" note="Explain the specimen below.">
|
|
377
423
|
<p>The application owns product copy and actions.</p>
|
|
378
424
|
</CatalogExample>
|
|
379
425
|
</CatalogExampleStack>
|
|
380
426
|
),
|
|
381
427
|
"profile-form": () => (
|
|
382
428
|
<CatalogExampleStack
|
|
383
|
-
label="Profile
|
|
429
|
+
label="Profile Form Composition"
|
|
384
430
|
rootAttributes={{ "data-demo": "profile-form" }}
|
|
385
431
|
>
|
|
386
|
-
<CatalogExample label="Complete
|
|
432
|
+
<CatalogExample label="Complete Composition">
|
|
387
433
|
<ProfileForm />
|
|
388
434
|
</CatalogExample>
|
|
389
435
|
</CatalogExampleStack>
|
|
@@ -499,7 +545,7 @@ revealCatalogEntry(app, initial, { block: "center" });
|
|
|
499
545
|
|
|
500
546
|
## UX Review capture
|
|
501
547
|
|
|
502
|
-
On a local Vite dev or preview server the catalog can offer a **Capture
|
|
548
|
+
On a local Vite dev or preview server the catalog can offer a **Capture Region**
|
|
503
549
|
header link that starts a UX Review region screenshot, preset with the project
|
|
504
550
|
and the active entry. Static hosting has no local project, so the link never
|
|
505
551
|
appears there. Three pieces wire it:
|
|
@@ -147,7 +147,9 @@ top toolbar over a `Pane`:
|
|
|
147
147
|
place groups wherever the open panel needs them;
|
|
148
148
|
- a `ToolbarControlGroup` marked `relocateOnCollapse` stays available in the
|
|
149
149
|
work area when the panel closes; the standard `toggle: { action, name,
|
|
150
|
-
showLabel?, hideLabel? }` is always the final group.
|
|
150
|
+
showLabel?, hideLabel? }` is always the final group. Give `name` as the
|
|
151
|
+
panel's title-style name ("Navigator"), which labels the toggle group and
|
|
152
|
+
completes "Show Navigator" / "Hide Navigator".
|
|
151
153
|
|
|
152
154
|
Busy groups keep their inert control, spinner, and adjacent live status together
|
|
153
155
|
when relocated. Clearing `busy` restores the same action in its current toolbar;
|
|
@@ -166,7 +168,8 @@ divider of its own unless `dividerSides` says so. The `Pane` under it draws
|
|
|
166
168
|
the line under the toolbar only while its content is scrolled (see
|
|
167
169
|
[Scroll dividers](./layout.md#scroll-dividers)), and takes the panel's `pane`
|
|
168
170
|
(`PaneConfig`: `contentElement`, `contentLabel`, `separators`,
|
|
169
|
-
`safeAreaEdges`, `chromeDividers`
|
|
171
|
+
`safeAreaEdges`, `chromeDividers`, `appearance`, `deepInset`,
|
|
172
|
+
`scrollClearance`, `headerPresentation`, `tabIndex`, `outlined`) — for example `{ contentElement: "nav",
|
|
170
173
|
contentLabel: "Sections" }` for a navigation rail. An omitted or `undefined`
|
|
171
174
|
field keeps the `Pane` default; without a `toolbar` there is no `Pane`, so
|
|
172
175
|
`pane` is ignored.
|
|
@@ -187,8 +190,8 @@ inert; avoid duplicate `id` attributes on marked groups.
|
|
|
187
190
|
const toolbar: CollapsiblePanelToolbar = {
|
|
188
191
|
label: "Navigator",
|
|
189
192
|
leading: <NewFileGroup />,
|
|
190
|
-
trailing: <ToolbarControlGroup relocateOnCollapse label="Search
|
|
191
|
-
toggle: { action: "toggle-nav", name: "
|
|
193
|
+
trailing: <ToolbarControlGroup relocateOnCollapse label="Search Files"><SearchButton /></ToolbarControlGroup>,
|
|
194
|
+
toggle: { action: "toggle-nav", name: "Navigator" },
|
|
192
195
|
};
|
|
193
196
|
|
|
194
197
|
<Row fill gap="none">
|
|
@@ -307,7 +310,7 @@ mount(app, () => (
|
|
|
307
310
|
side="left"
|
|
308
311
|
collapsed
|
|
309
312
|
action="toggle-nav"
|
|
310
|
-
label="Show
|
|
313
|
+
label="Show Navigator"
|
|
311
314
|
/>
|
|
312
315
|
)}
|
|
313
316
|
{/* content */}
|
|
@@ -362,7 +365,7 @@ sizes, content, and any per-project persistence — exactly as with the other la
|
|
|
362
365
|
|
|
363
366
|
## Recipe and coverage
|
|
364
367
|
|
|
365
|
-
The catalog ships a runnable **Collapsible
|
|
368
|
+
The catalog ships a runnable **Collapsible Sidebar** recipe — a left rail and a
|
|
366
369
|
bottom drawer with the standard toggles, the compact overlay, and per-panel
|
|
367
370
|
persistence: [open it](../ux-demo/?component=recipe-collapsible-sidebar) or read
|
|
368
371
|
[`recipes.md`](recipes.md#collapsible-sidebar) · [TSX source](../ux-demo/recipes/collapsible-sidebar.tsx).
|
|
@@ -89,8 +89,12 @@ only cataloged `descendantElements` and composed JSX evidence.
|
|
|
89
89
|
An entry's optional `cssValueProps` makes CSS-adjacent prop grammar equally
|
|
90
90
|
machine-evaluable. Each path records the property grammar, finite shorthands,
|
|
91
91
|
canonical versus exceptional spacing choices, standalone and expression-only
|
|
92
|
-
helpers, raw/unsafe policy, and serialization examples. Nested paths
|
|
93
|
-
|
|
92
|
+
helpers, raw/unsafe policy, and serialization examples. Nested paths are
|
|
93
|
+
dot-separated object members, each optionally suffixed with `[]` to visit
|
|
94
|
+
every array element: `items[].property` for a value inside each array item,
|
|
95
|
+
`iconTile.color` for a member of an object-valued prop. A member that is not a
|
|
96
|
+
literal object or array (a tone string such as `iconTile="brand"`, a variable)
|
|
97
|
+
is skipped rather than guessed. Consumer catalogs can publish the same facts;
|
|
94
98
|
ESLint, `kerf-ui-analyze`, and `kerf-ui-doctor` then issue the same stable
|
|
95
99
|
`KUI-L013`–`KUI-L017` feedback for application and third-party components.
|
|
96
100
|
|
|
@@ -246,10 +250,10 @@ product that genuinely needs one of these elements records a narrow `KUI-L301`
|
|
|
246
250
|
profile exception for the file that renders it.
|
|
247
251
|
|
|
248
252
|
Catalog detail footers use one standard resource vocabulary and order. Build
|
|
249
|
-
them with `catalogResources()` from `@kerfjs/ui/catalog-resources`: `Demo
|
|
250
|
-
first, optional `Component
|
|
253
|
+
them with `catalogResources()` from `@kerfjs/ui/catalog-resources`: `Demo Source`
|
|
254
|
+
first, optional `Component Source` and `Design Template`, then `Guidance`.
|
|
251
255
|
Third-party entries use the typed `integrationGuidance` kind, which renders
|
|
252
|
-
`Integration
|
|
256
|
+
`Integration Guidance`, and omit `Component Source` when the implementation is
|
|
253
257
|
not owned by the catalog's project. Prefer these semantic kinds over local labels
|
|
254
258
|
such as “View source” or “Read UI guidance”; the helper makes that convention a
|
|
255
259
|
compile-time choice while still returning ordinary `CatalogResource[]` values.
|
|
@@ -333,6 +337,8 @@ visual anchor.
|
|
|
333
337
|
|
|
334
338
|
`ToolbarControlGroup.tileTone` selects `neutral`, `brand`, `success`, `warning`, or `danger` quiet fill, matching quiet border, and on-quiet foreground for a non-interactive icon tile. Compose a contained `single` group whose only child is a direct decorative LucideIcon (`aria-hidden="true"`). Omission preserves the normal group palette. The prop does not recolor interactive, mixed, multi-control, or borderless groups; keep real actions in controls with their existing focus/hover/pressed treatments.
|
|
335
339
|
|
|
340
|
+
`ListItem`, `ListItemLink`, and `ListActionRow` take `iconTile` to present their leading `icon` in a filled 32px rounded tile holding an 18px glyph, for settings or command rows that identify each entry by color. Pass a tone (`neutral`, `brand`, `success`, `warning`, or `danger`) for the same quiet fill, matching quiet border, and on-quiet foreground as `ToolbarControlGroup.tileTone`, or `{ color, foreground? }` for an application color: `color` is a `CssColor` (`uiColor()`, `colorVar()`, or `fillColor('#e5484d')`) and `foreground` a `CssForegroundColor`; without one, the glyph is black or white, whichever contrasts with the fill. The tile sits 6px clear of a standard 44px row (a compact row is at least 42px tall), moves the label to the tile's edge, and keeps its colors when the row is hovered or selected. Never wrap the icon in your own tile or resize the row's icon slot.
|
|
341
|
+
|
|
336
342
|
### Public CSS anatomy
|
|
337
343
|
|
|
338
344
|
The `publicClasses` array on each entry in
|
|
@@ -396,7 +402,12 @@ The documented exceptions live with their reasons in
|
|
|
396
402
|
check, so the list only shrinks. The layout regions' inherited edge-inset
|
|
397
403
|
context (`--kui-edge-inset-*` handed to a sole Pane or layout child and cleared
|
|
398
404
|
for any other content, and a Toolbar clearing it for its zones) is the one
|
|
399
|
-
design exception; the others name the open fix ticket.
|
|
405
|
+
design exception; the others name the open fix ticket. The scroll-clearance
|
|
406
|
+
context (`--kui-scroll-clearance-block-*`) needs no exception: overlaying
|
|
407
|
+
chrome publishes it on its own element (a `DialogSurface` on its dialog body,
|
|
408
|
+
a `Pane` overlay header, through `wireScrollDividers`, on its own Pane root).
|
|
409
|
+
A scroll owner reads it only through its own `scrollClearance` prop, or, for an
|
|
410
|
+
overlay-header Pane's content, through its own `headerPresentation`.
|
|
400
411
|
|
|
401
412
|
Applications and component packages built on the package follow the same
|
|
402
413
|
rules, and the downstream tools enforce them against the composition catalog
|
|
@@ -628,10 +639,14 @@ contract in `foundation.css`. A capped popup that scrolls (a long listbox, a
|
|
|
628
639
|
menu, or a phone submenu) shows a soft shadow along each edge with more
|
|
629
640
|
choices beyond it, so overlay scrollbars never leave it looking complete; the
|
|
630
641
|
cue is CSS-only, appears only toward hidden rows, and a popup whose choices
|
|
631
|
-
all fit draws none. Checkbox items reserve a check column
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
642
|
+
all fit draws none. Checkbox items reserve a check column, and so does every
|
|
643
|
+
plain command in a level (the top menu or one submenu) that holds a checkbox
|
|
644
|
+
item, through an `aria-hidden` icon-slot spacer, so check, icon, and label
|
|
645
|
+
columns align across the level. A level without a checkbox item keeps the
|
|
646
|
+
compact start: plain commands begin at the checkmark's leading edge, beside the
|
|
647
|
+
group title rather than the farther-in Select option label. Checked submenu
|
|
648
|
+
items keep their checkmark inside the submenu surface with the same check-column
|
|
649
|
+
advance as the top level. Compact mixed controls retain the standard
|
|
635
650
|
item padding, omit an internal separator, and let the raised selected item paint
|
|
636
651
|
over the outer border instead of shrinking to an inset highlight. The Web
|
|
637
652
|
Awesome trigger's shadow base owns max-content sizing, so icons, localized text,
|
|
@@ -743,7 +758,12 @@ propagating before the window, or a press without a click, collapses in the
|
|
|
743
758
|
next task instead.
|
|
744
759
|
`TokenSearchField.trailingAction` supplies an icon, accessible `label`, delegated
|
|
745
760
|
`action`, and optional `id`; the field renders a 24px wide button that fills its
|
|
746
|
-
34px line slot, including the hit target, disabled
|
|
761
|
+
34px line slot (32px in `toolbar-group`), including the hit target, disabled
|
|
762
|
+
state, hover, focus ring, and the clear button's radius in each presentation.
|
|
763
|
+
`expanded` renders `aria-expanded` and `pressed` renders `aria-pressed` (one or
|
|
764
|
+
the other, never both); either `true` state keeps the hover fill at full text
|
|
765
|
+
strength. `controls` and `haspopup` render `aria-controls` and `aria-haspopup`,
|
|
766
|
+
and `attributes` carries domain `data-*` metadata but never `data-action`.
|
|
747
767
|
Use `trailing` for passive content. The two props are mutually exclusive.
|
|
748
768
|
For a collapsible search in a stretched Toolbar center, `fill` lets the expanded
|
|
749
769
|
field and its enclosing search `ToolbarControlGroup` consume the available width;
|
|
@@ -786,7 +806,16 @@ When a leading AppTab is pinned, the leading scroll divider appears at that
|
|
|
786
806
|
tab's trailing edge while peer tabs scroll beneath it, including in RTL.
|
|
787
807
|
With `snapTabs`, `wireTabBars` measures the pinned tab and settles peers at
|
|
788
808
|
whole-tab starts after scroll and selection changes, reserving end room so the
|
|
789
|
-
last peer can align there.
|
|
809
|
+
last peer can align there. That room is scrollable content after the last tab,
|
|
810
|
+
inside the strip, so it lengthens the scroll range without widening the strip:
|
|
811
|
+
the strip still shrinks to its allocation, and `trailing` and `end` stay inside
|
|
812
|
+
the bar. The room is still in-flow content, so it counts toward the strip's
|
|
813
|
+
content-based flex basis: when another zone also shrinks (a trailing zone
|
|
814
|
+
given `--kui-tab-bar-trailing-flex: 0 1 auto` with shrinkable content), the
|
|
815
|
+
strip keeps a proportionally larger share. The share converges within a few
|
|
816
|
+
frames and never pushes `trailing` or `end` out of the bar; keep the trailing
|
|
817
|
+
zone non-shrinking with `snapTabs` to avoid it. The application still owns
|
|
818
|
+
selection and tab order.
|
|
790
819
|
ResizeObserver notifications coalesce snap measurements into the next animation
|
|
791
820
|
frame, so inset writes cannot resize the observed strip during observer delivery.
|
|
792
821
|
`wireTabBars` watches the whole root it is given, but a mutation re-measures only
|