@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/layout.md
CHANGED
|
@@ -185,6 +185,62 @@ Firefox, scroll-state container queries only in Chromium, and neither lets
|
|
|
185
185
|
the pinned chrome — a sibling of the scroller — read the scroller's state on
|
|
186
186
|
its own. One wiring keeps the behavior identical in every engine.
|
|
187
187
|
|
|
188
|
+
### Overlay header
|
|
189
|
+
|
|
190
|
+
`headerPresentation="overlay"` lays a Pane's header over the top of its
|
|
191
|
+
scrolling content instead of giving it its own row, the way platform
|
|
192
|
+
navigation bars let content scroll visibly beneath a translucent bar:
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<Pane
|
|
196
|
+
label="Inbox"
|
|
197
|
+
headerPresentation="overlay"
|
|
198
|
+
header={<Toolbar label="Inbox" leading={<ToolbarText text="Inbox" size="xlarge" />} />}
|
|
199
|
+
>
|
|
200
|
+
{messages}
|
|
201
|
+
</Pane>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The header paints the shared chrome material, `--kui-chrome-material` (the
|
|
205
|
+
surface at 80% opacity) behind `--kui-chrome-material-filter` (a blur and
|
|
206
|
+
saturation backdrop filter), so content passing beneath it reads as softened
|
|
207
|
+
color, not as competing text. The material becomes the opaque surface under
|
|
208
|
+
`prefers-reduced-transparency: reduce` or `prefers-contrast: more`, and where
|
|
209
|
+
the browser lacks `backdrop-filter`. In forced colors the header paints
|
|
210
|
+
`Canvas` with a `CanvasText` bottom edge.
|
|
211
|
+
|
|
212
|
+
`wireScrollDividers` measures each overlay header (its border-box height,
|
|
213
|
+
safe-area padding included) with a `ResizeObserver` and publishes it on the
|
|
214
|
+
Pane as the `--kui-scroll-clearance-block-start` context (see
|
|
215
|
+
[Scroll clearance](#scroll-clearance)). The Pane's content clears it
|
|
216
|
+
automatically, with no `scrollClearance` prop. At rest the first item sits just
|
|
217
|
+
below the header. Mid-scroll, content passes beneath it, and anything focused
|
|
218
|
+
or scrolled into view still comes to rest clear of it. The clearance follows
|
|
219
|
+
the header as its title wraps or its text grows. The header draws its scroll
|
|
220
|
+
divider as a docked header does, once content is scrolled beneath it. The
|
|
221
|
+
wiring also marks the Pane `data-header-overlay="measured"`, and only then
|
|
222
|
+
does the header move over the content. Until the wiring runs, the header stays
|
|
223
|
+
docked in its own row, so it can never cover content it has not measured. A
|
|
224
|
+
re-render that drops the measurement gets it back before paint.
|
|
225
|
+
|
|
226
|
+
The overlay needs pinned chrome. With `chromePlacement="auto"`, the header
|
|
227
|
+
stays docked. `PaneConfig` carries `headerPresentation`, so a Workbench
|
|
228
|
+
`mainPane` or panel `pane`, a `CollapsiblePanel` `pane`, or a `NavStack` view's
|
|
229
|
+
`pane` can lay its header over its content the same way. Overlay-header text
|
|
230
|
+
must stay at AA over whatever passes beneath: an untoned `ToolbarText` there
|
|
231
|
+
takes the normal text color for its title and lifts its `description` toward
|
|
232
|
+
it, and other quiet secondary copy belongs in the content (see
|
|
233
|
+
[Accessibility › Chrome material](accessibility.md#chrome-material)).
|
|
234
|
+
|
|
235
|
+
An overlay scrollbar (macOS by default) passes beneath the header like the
|
|
236
|
+
content. A classic, space-taking scrollbar (Windows and Linux, or macOS with
|
|
237
|
+
"always show scrollbars") stays uncovered: the wiring also measures the
|
|
238
|
+
content's vertical scrollbar (`offsetWidth` beyond `clientWidth`) and publishes
|
|
239
|
+
its inline size on the Pane as `--kui-pane-scrollbar-inline-size`, and the
|
|
240
|
+
header ends where the scrollbar's track begins, so its thumb and arrow buttons
|
|
241
|
+
remain visible and usable. The measurement follows the scrollbar as content
|
|
242
|
+
starts or stops overflowing.
|
|
243
|
+
|
|
188
244
|
A pane is safe-area aware. Its background and separators paint through a
|
|
189
245
|
device's unsafe areas while its header, content, and footer pad for each side
|
|
190
246
|
the pane still reaches; the content's block insets are scroll padding, so its
|
|
@@ -313,19 +369,24 @@ component's context stays allowed (see `KUI-L019`).
|
|
|
313
369
|
|
|
314
370
|
## Public roles and tokens
|
|
315
371
|
|
|
316
|
-
| Need | Class
|
|
317
|
-
| ---------------------------------------- |
|
|
318
|
-
| Unpadded header/content/footer structure | `Pane`, `.kui-pane`
|
|
319
|
-
| Scrolling pane content | `.kui-pane__content`
|
|
320
|
-
| Optional logical-edge separators | `Pane.separators`
|
|
321
|
-
| Safe-area sides a pane may pad | `Pane.safeAreaEdges`
|
|
322
|
-
|
|
|
323
|
-
|
|
|
324
|
-
|
|
|
325
|
-
|
|
|
326
|
-
|
|
|
327
|
-
|
|
|
328
|
-
|
|
|
372
|
+
| Need | Class | Token / default |
|
|
373
|
+
| ---------------------------------------- | ------------------------------------ | ------------------------------------------------------------------ |
|
|
374
|
+
| Unpadded header/content/footer structure | `Pane`, `.kui-pane` | — |
|
|
375
|
+
| Scrolling pane content | `.kui-pane__content` | — |
|
|
376
|
+
| Optional logical-edge separators | `Pane.separators` | `--kui-pane-separator-width: 1px` |
|
|
377
|
+
| Safe-area sides a pane may pad | `Pane.safeAreaEdges` | `--kui-safe-area-*: env(safe-area-inset-*)`, routed by each layout |
|
|
378
|
+
| Header laid over scrolling content | `Pane.headerPresentation` | `--kui-chrome-material`, `--kui-chrome-material-filter` |
|
|
379
|
+
| Dialog title bar laid over its body | `DialogSurface.headerPresentation` | `--kui-chrome-material-raised`, `--kui-chrome-material-filter` |
|
|
380
|
+
| Stack top bar laid over the active view | `NavStack.chromePresentation` | `--kui-chrome-material`, `--kui-chrome-material-filter` |
|
|
381
|
+
| Stack bottom toolbar laid over the view | `NavStack.bottomToolbarPresentation` | `--kui-chrome-material`, `--kui-chrome-material-filter` |
|
|
382
|
+
| Tab bar laid over the active scene | `TabNavigator.barPresentation` | `--kui-chrome-material`, `--kui-chrome-material-filter` |
|
|
383
|
+
| Major vertical rhythm | `.kui-content` | `--kui-layout-content-gap: 24px` |
|
|
384
|
+
| Self-contained child geometry | `ContentItem` | 8px margin + 1px border + 8px padding |
|
|
385
|
+
| Pill child | `ContentItem shape="pill"` | `--kui-layout-pill-radius: 22px` |
|
|
386
|
+
| Visibly framed child | `ContentItem frame="framed"` | neutral 1px border, same geometry |
|
|
387
|
+
| Related controls | `.kui-control-cluster` | `--kui-layout-control-gap: 8px` |
|
|
388
|
+
| Inline metadata | `.kui-inline-metadata` | `--kui-layout-metadata-gap: 4px` |
|
|
389
|
+
| Explicit scroll owner outside a pane | `.kui-scroll-owner` | `overflow: auto` |
|
|
329
390
|
|
|
330
391
|
The component layer applies the same contract to `Toolbar`, `ListHeader`,
|
|
331
392
|
`ListItem`, `Toolbar`, `StateBanner`, `ValueTable`,
|
|
@@ -409,7 +470,7 @@ import { FieldLabel } from "@kerfjs/ui/text";
|
|
|
409
470
|
|
|
410
471
|
<div role="group" aria-labelledby="needed-by-label">
|
|
411
472
|
<ListInsetControl sides="trl">
|
|
412
|
-
<FieldLabel id="needed-by-label">Needed
|
|
473
|
+
<FieldLabel id="needed-by-label">Needed By</FieldLabel>
|
|
413
474
|
</ListInsetControl>
|
|
414
475
|
<ContentItem>October 16, 2026</ContentItem>
|
|
415
476
|
</div>;
|
|
@@ -646,6 +707,78 @@ When it has focus it draws the theme focus ring inset by the ring width and
|
|
|
646
707
|
rounded with `--kui-radius-m`, so a rounded or `overflow: hidden` parent
|
|
647
708
|
never clips it. Do not style `.kui-list` focus yourself.
|
|
648
709
|
|
|
710
|
+
### Scroll clearance
|
|
711
|
+
|
|
712
|
+
Chrome that lies over a scroll owner's block edge (a `DialogSurface` floating
|
|
713
|
+
footer over a transcript, or translucent chrome over a scrolling region)
|
|
714
|
+
publishes how far it covers that edge through an inherited context:
|
|
715
|
+
`--kui-scroll-clearance-block-start` and `--kui-scroll-clearance-block-end`.
|
|
716
|
+
A scroll owner opts in per edge with `scrollClearance` (`"block-start"`,
|
|
717
|
+
`"block-end"`, or `"both"`) on a `scrollable` `List` or a `Pane`. It adds the
|
|
718
|
+
published clearance to its own block padding and `scroll-padding`, so content
|
|
719
|
+
scrolls visibly beneath the chrome while the first and last items (and any
|
|
720
|
+
item focused or scrolled into view) still come to rest clear of it. Without
|
|
721
|
+
the prop the context is ignored. `PaneConfig` carries `scrollClearance` too,
|
|
722
|
+
so a composed Pane opts in the same way: a Workbench `mainPane` or panel
|
|
723
|
+
`pane`, a `CollapsiblePanel` `pane`, or a `NavStack` view's `pane` (for
|
|
724
|
+
example a NavStack view inside a dialog with a floating composer).
|
|
725
|
+
|
|
726
|
+
Opt in only the one scroller that reaches the covered edge; a nested scroller
|
|
727
|
+
must not opt in again. `DialogSurface` publishes block-end for its floating
|
|
728
|
+
footer and block-start for its floating header (see
|
|
729
|
+
`docs/surface-scaffold.md`), and clears the context in every dialog body. A `Pane` with `headerPresentation="overlay"` publishes block-start for
|
|
730
|
+
its own header, once `wireScrollDividers` has measured it, and its content
|
|
731
|
+
clears it without the prop (see [Overlay header](#overlay-header)). When an application overlays its own chrome on a region (for example a
|
|
732
|
+
translucent toolbar laid over the top of a scrolling list), it publishes the
|
|
733
|
+
chrome's block size on that region's container, the one place where an app
|
|
734
|
+
sets this context:
|
|
735
|
+
|
|
736
|
+
```tsx
|
|
737
|
+
<section style="position:relative;display:grid;--kui-scroll-clearance-block-start:48px">
|
|
738
|
+
<List scrollable scrollClearance="block-start">{rows}</List>
|
|
739
|
+
{translucentToolbar}
|
|
740
|
+
</section>;
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### Framed result pickers
|
|
744
|
+
|
|
745
|
+
For a bounded, framed list of `ListItem` rows (search results, duplicate
|
|
746
|
+
candidates, a recent-items picker), configure the List instead of wrapping it
|
|
747
|
+
in an app-owned scroller:
|
|
748
|
+
|
|
749
|
+
```tsx
|
|
750
|
+
<List scrollable frame gap="2xs" minHeight={rem(4.25)} maxHeight={rem(14)}>
|
|
751
|
+
{candidates.map((candidate) => (
|
|
752
|
+
<ListItem action="pick-duplicate" itemId={candidate.id} label={candidate.title} description={candidate.detail} multiline />
|
|
753
|
+
))}
|
|
754
|
+
</List>;
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
`frame` draws a 1px `--kui-color-neutral-border-normal` border with the
|
|
758
|
+
`--kui-radius-m` control radius and a 4px inset on every side (replacing
|
|
759
|
+
`inset`, `textInsets`, and `controlInsets`). That inset is exactly a row's
|
|
760
|
+
focus-ring reach, so the first and last rows' rings stay inside the clip at
|
|
761
|
+
the scroll extremes. Every `scrollable` List, framed or not, carries block
|
|
762
|
+
scroll padding of that reach plus 2px of slack (6px; nothing moves at rest),
|
|
763
|
+
so a middle row focused or scrolled into view also stops with its ring inside
|
|
764
|
+
the clip, even where Chromium's focus scroll stops a pixel short of the padded
|
|
765
|
+
target. An unframed scrollable List has no inset, so its first and last rows
|
|
766
|
+
still meet the clip at the scroll extremes; use `frame` when those rows must
|
|
767
|
+
show a whole ring.
|
|
768
|
+
How far `focus()` scrolls a partly visible row is up to the engine: Chromium
|
|
769
|
+
brings it to the nearest edge, Firefox leaves it until about half of it is
|
|
770
|
+
hidden and then centers it, and WebKit leaves a partly visible row where it
|
|
771
|
+
is. Call `wireListFocusScroll(root)` from `@kerfjs/ui/wire-list-focus-scroll`
|
|
772
|
+
once at the app root (keep its disposer) to make keyboard focus consistent:
|
|
773
|
+
when a `:focus-visible` element inside a `scrollable` List has a ring that
|
|
774
|
+
crosses the clip, it calls `scrollIntoView({ block: 'nearest' })`, which
|
|
775
|
+
honors the List's scroll padding in every engine. It leaves pointer focus
|
|
776
|
+
alone, so a click never scrolls its row from under the pointer, and it is a
|
|
777
|
+
no-op where Chromium has already scrolled. `maxHeight` and `minHeight` take typed
|
|
778
|
+
`CssLength`s from `@kerfjs/ui/css-values` and bound the List directly, with no
|
|
779
|
+
flex-column ancestor; pair `maxHeight` with `scrollable`. A frame of settings
|
|
780
|
+
rows with dividers between them is a `FramedList`, not a framed List.
|
|
781
|
+
|
|
649
782
|
For long reading content, such as a reader view, pass `inset="document"`: 24px
|
|
650
783
|
block and 32px inline on all four sides, narrowing to 16px inline below 576px.
|
|
651
784
|
It replaces any `textInsets`/`controlInsets` on that List.
|
package/docs/nav-stack.md
CHANGED
|
@@ -66,7 +66,8 @@ the view renders a `Pane`: its header and footer stay pinned around the view's
|
|
|
66
66
|
scrolling content, and slide with that view during navigation. The header and
|
|
67
67
|
footer list configuration forwards to their `List`s; `pane` forwards
|
|
68
68
|
`contentElement`, `contentLabel`, `separators`, `safeAreaEdges`,
|
|
69
|
-
`chromeDividers`,
|
|
69
|
+
`chromeDividers`, `appearance`, `deepInset`, `scrollClearance`, `headerPresentation`, `tabIndex`,
|
|
70
|
+
and `outlined` to the Pane. Without those fields, the view
|
|
70
71
|
keeps its simple direct scroll area. Use `pane: { appearance: "sunken" }` for a
|
|
71
72
|
lowered scrolling work surface.
|
|
72
73
|
The back control appears automatically once the stack has more than one entry;
|
|
@@ -136,7 +137,7 @@ Configure the toolbar with `toolbarConfig` instead of styling it:
|
|
|
136
137
|
view's top toolbar. `Workbench` uses it for a navigation panel's standard
|
|
137
138
|
collapse toggle; the active view still owns the preceding toolbar groups.
|
|
138
139
|
|
|
139
|
-
The catalog's **Configured
|
|
140
|
+
The catalog's **Configured Toolbar** example shows these together: a level-2
|
|
140
141
|
heading title over a bottom divider, leading and center groups on the root
|
|
141
142
|
view, a trailing group on the pushed view, and `backText` set to the previous
|
|
142
143
|
view's title.
|
|
@@ -186,6 +187,115 @@ top Toolbar's own `dividerSides`.
|
|
|
186
187
|
<NavStack id="settings" label="Settings" views={views.value} chromeDividers="always" />
|
|
187
188
|
```
|
|
188
189
|
|
|
190
|
+
## Overlay chrome
|
|
191
|
+
|
|
192
|
+
`chromePresentation="overlay"` lays the top chrome over the active view's top
|
|
193
|
+
edge, like an iOS navigation bar, instead of docking it above the view
|
|
194
|
+
(`"docked"`, the default). The chrome paints the shared chrome material,
|
|
195
|
+
`--kui-chrome-material` behind `--kui-chrome-material-filter`, so content
|
|
196
|
+
scrolls visibly, blurred, beneath it.
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
<NavStack id="mail" label="Mail" views={views.value} chromePresentation="overlay" />
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
- **`wireNavStack` measures it.** The helper you already call for transitions
|
|
203
|
+
reads the live chrome's border-box height with a `ResizeObserver`, publishes
|
|
204
|
+
it on the stack root, and marks the root `data-chrome-overlay="measured"`.
|
|
205
|
+
Only then does the chrome leave its band. Unwired, or with `hideToolbar`, the
|
|
206
|
+
chrome stays docked, so it never covers content it has not measured. The
|
|
207
|
+
measurement follows the chrome as its title wraps, text grows, or a pushed
|
|
208
|
+
view changes its toolbar. A re-render that strips the measurement gets it
|
|
209
|
+
back before paint.
|
|
210
|
+
- **Every view clears it automatically.** The measured height becomes the
|
|
211
|
+
viewport's block-start edge context (`--kui-edge-inset-block-start`), the
|
|
212
|
+
same context that carries safe areas. A plain view pads and scroll-pads by
|
|
213
|
+
it, a sole `Pane` adds it to its content's (or its header's) block-start
|
|
214
|
+
inset, and a nested layout hands it on. The first item rests just below the
|
|
215
|
+
chrome, and anything focused or scrolled into view stops clear of it. No
|
|
216
|
+
`scrollClearance` prop is involved, so a Pane that also opts into
|
|
217
|
+
`scrollClearance` does not clear the chrome twice.
|
|
218
|
+
- **Safe area.** The chrome keeps claiming the top safe area: its toolbar pads
|
|
219
|
+
for it while the material paints through it. The measured height already
|
|
220
|
+
includes that padding, so views clear the chrome and the safe area exactly
|
|
221
|
+
once.
|
|
222
|
+
- **Transitions.** Only the live chrome is measured, never a cross-fading
|
|
223
|
+
copy. The copy keeps painting over the top edge with the same material while
|
|
224
|
+
it fades, and the incoming view settles at the incoming chrome's clearance.
|
|
225
|
+
- **Classic scrollbars.** Where the active view's scroller (the view, or a
|
|
226
|
+
sole header-less Pane's content) shows a classic, space-taking scrollbar,
|
|
227
|
+
`wireNavStack` measures it and the chrome ends where its track begins, so
|
|
228
|
+
the thumb and arrows stay visible and usable. An overlay scrollbar takes no
|
|
229
|
+
space and passes beneath the material like the content.
|
|
230
|
+
- **Dividers.** With `wireScrollDividers`, the line under the chrome still
|
|
231
|
+
appears only while content is scrolled beneath it.
|
|
232
|
+
- **Contrast.** The material keeps normal text at AA over any content. An
|
|
233
|
+
untoned `ToolbarText` in the chrome lifts itself: its title takes the normal
|
|
234
|
+
text color at every size and its `description` mixes the quiet color halfway
|
|
235
|
+
toward it, and a default-tone `ToolbarControlGroup` takes the normal text
|
|
236
|
+
color for its controls. Other quiet copy you place in the chrome has no such
|
|
237
|
+
lift, so keep it in the view (see
|
|
238
|
+
[Accessibility › Chrome material](accessibility.md#chrome-material)). The
|
|
239
|
+
material turns opaque under `prefers-reduced-transparency`,
|
|
240
|
+
`prefers-contrast: more`, or without `backdrop-filter`, and forced colors
|
|
241
|
+
paint `Canvas` with a `CanvasText` bottom edge.
|
|
242
|
+
|
|
243
|
+
`SplitView`'s `compactStack.chromePresentation` and a Workbench navigation
|
|
244
|
+
panel's `navStack.chromePresentation` forward the same option.
|
|
245
|
+
`chromePresentation` governs only the top chrome; the bottom toolbar has its
|
|
246
|
+
own option below.
|
|
247
|
+
|
|
248
|
+
### Overlay bottom toolbar
|
|
249
|
+
|
|
250
|
+
`bottomToolbarPresentation="overlay"` does the same for the bottom toolbar,
|
|
251
|
+
like an iOS toolbar: it lies over the active view's bottom edge on the same
|
|
252
|
+
chrome material instead of docking below the view (`"docked"`, the default).
|
|
253
|
+
It is a separate prop, so either bar can be overlaid alone, and it mirrors
|
|
254
|
+
`Pane.headerPresentation` and `DialogSurface.footerPresentation` in naming one
|
|
255
|
+
edge.
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
<NavStack
|
|
259
|
+
id="mail"
|
|
260
|
+
label="Mail"
|
|
261
|
+
views={views.value}
|
|
262
|
+
chromePresentation="overlay"
|
|
263
|
+
bottomToolbarPresentation="overlay"
|
|
264
|
+
bottomToolbar={{ label: 'Mail Actions', trailing: composeGroup }}
|
|
265
|
+
/>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
- **`wireNavStack` measures it.** It reads the live bottom toolbar's
|
|
269
|
+
border-box height (never a cross-fading copy's), publishes it on the stack
|
|
270
|
+
root, and marks the root `data-bottom-toolbar-overlay="measured"`. Only then
|
|
271
|
+
does the toolbar leave its band. Unwired, or while no bottom toolbar is
|
|
272
|
+
shown (a view without one and no stack-level fallback), it stays docked. The
|
|
273
|
+
measurement follows a view-owned toolbar as views push and pop, and a
|
|
274
|
+
re-render that strips it gets it back before paint.
|
|
275
|
+
- **Every view clears it automatically.** The measured height becomes the
|
|
276
|
+
viewport's block-end edge context (`--kui-edge-inset-block-end`). A plain
|
|
277
|
+
view pads and scroll-pads by it, a sole `Pane` adds it to its content's
|
|
278
|
+
block-end inset (or its footer's, so the footer rests just above the
|
|
279
|
+
toolbar), and a nested layout hands it on. The last item rests just above
|
|
280
|
+
the toolbar, and anything focused or scrolled into view stops clear of it.
|
|
281
|
+
- **Safe area.** The toolbar keeps claiming the bottom safe area (the home
|
|
282
|
+
indicator), and the measured height includes it, so views clear both exactly
|
|
283
|
+
once. Inside a `TabNavigator` scene the tab bar owns that edge, so the
|
|
284
|
+
stack's toolbar sits just above the tab bar and counts no safe area.
|
|
285
|
+
- **Classic scrollbars and dividers.** The toolbar ends where a classic
|
|
286
|
+
scrollbar's track begins (a sole Pane with a footer ends its scroller above
|
|
287
|
+
the toolbar, so nothing needs leaving there). With `wireScrollDividers`, the
|
|
288
|
+
toolbar's top line still appears only while more content lies below.
|
|
289
|
+
- **Contrast.** An untoned `ToolbarText` lifts itself as in the top chrome,
|
|
290
|
+
and a default-tone `ToolbarControlGroup` in either overlaid bar takes the
|
|
291
|
+
normal text color for its controls, so labels and icons keep AA over the
|
|
292
|
+
content beneath. The material has the same opaque fallbacks; in forced
|
|
293
|
+
colors the toolbar paints `Canvas` and its always-present 1px top border
|
|
294
|
+
becomes a `CanvasText` edge, which the measurement includes.
|
|
295
|
+
|
|
296
|
+
`SplitView`'s `compactStack.bottomToolbarPresentation` and a Workbench
|
|
297
|
+
navigation panel's `navStack.bottomToolbarPresentation` forward it too.
|
|
298
|
+
|
|
189
299
|
## Safe areas
|
|
190
300
|
|
|
191
301
|
The stack paints through a device's unsafe areas. Its chrome pads for the top
|
package/docs/recipes.md
CHANGED
|
@@ -26,7 +26,7 @@ The adapter is delivered as reference source, not a new package runtime export.
|
|
|
26
26
|
`delegate()` remains a valid alternative when an application needs selector-
|
|
27
27
|
specific dispatch; either way, wire once at a stable root and retain disposal.
|
|
28
28
|
|
|
29
|
-
## Desktop
|
|
29
|
+
## Desktop Application Shell
|
|
30
30
|
|
|
31
31
|
[Open the recipe](../ux-demo/?component=recipe-app-shell) · [TSX source](../ux-demo/recipes/app-shell.tsx)
|
|
32
32
|
|
|
@@ -52,7 +52,7 @@ whole width). The app owns each rail's collapsed and size signals — which
|
|
|
52
52
|
routing, persistence, and data. Adapt only public `--kui-layout-*` and
|
|
53
53
|
component variables.
|
|
54
54
|
|
|
55
|
-
## Navigation
|
|
55
|
+
## Navigation Sidebar
|
|
56
56
|
|
|
57
57
|
[Open the recipe](../ux-demo/?component=recipe-navigation-sidebar) · [TSX source](../ux-demo/recipes/navigation-sidebar.tsx)
|
|
58
58
|
|
|
@@ -65,7 +65,7 @@ permissions, labels, selection, valid section counts and their localized
|
|
|
65
65
|
content. A toggled `ListHeader` supplies the production `DisclosureArrow` when
|
|
66
66
|
no custom `actionIcon` is needed; ordinary navigation rows stay chevron-free.
|
|
67
67
|
|
|
68
|
-
## Workspace
|
|
68
|
+
## Workspace Header
|
|
69
69
|
|
|
70
70
|
[Open the recipe](../ux-demo/?component=recipe-workspace-header) · [TSX source](../ux-demo/recipes/workspace-header.tsx)
|
|
71
71
|
|
|
@@ -99,7 +99,7 @@ application owns open state, selection, the pushed detail, dismissal policy,
|
|
|
99
99
|
and record actions. Do not put the primary title in a header spanning both
|
|
100
100
|
columns, rebuild the dialog, or reach into private shadow parts.
|
|
101
101
|
|
|
102
|
-
## Composer
|
|
102
|
+
## Composer Form
|
|
103
103
|
|
|
104
104
|
[Open the recipe](../ux-demo/?component=recipe-composer-form) · [TSX source](../ux-demo/recipes/composer-form.tsx)
|
|
105
105
|
|
|
@@ -118,7 +118,7 @@ Because upgraded Web Awesome fields retain live value properties, controlled
|
|
|
118
118
|
resets synchronize both those properties and the rendered value attributes;
|
|
119
119
|
the Reset action also announces `Draft reset` through the catalog live region.
|
|
120
120
|
|
|
121
|
-
## List
|
|
121
|
+
## List Workspace States
|
|
122
122
|
|
|
123
123
|
[Open the recipe](../ux-demo/?component=recipe-list-workspace-states) · [TSX source](../ux-demo/recipes/list-workspace-states.tsx)
|
|
124
124
|
|
|
@@ -127,7 +127,7 @@ populated, stale/background refresh, and error/retry states. The recipe owns
|
|
|
127
127
|
feedback placement; the app owns fetching, cache age, retry policy, and domain
|
|
128
128
|
rows.
|
|
129
129
|
|
|
130
|
-
## Compact
|
|
130
|
+
## Compact Toolbar Choices and Actions
|
|
131
131
|
|
|
132
132
|
[Open the recipe](../ux-demo/?component=recipe-compact-toolbar) · [TSX source](../ux-demo/recipes/compact-toolbar.tsx)
|
|
133
133
|
|
|
@@ -140,7 +140,7 @@ widths the toolbar stacks its zones and wraps whole trailing groups onto another
|
|
|
140
140
|
row, so the last action is never clipped. The app owns values, actions,
|
|
141
141
|
persistence, and responsive priority.
|
|
142
142
|
|
|
143
|
-
## Navigation
|
|
143
|
+
## Navigation Stack
|
|
144
144
|
|
|
145
145
|
[Open the recipe](../ux-demo/?component=recipe-navigation-stack) · [TSX source](../ux-demo/recipes/navigation-stack.tsx)
|
|
146
146
|
|
|
@@ -152,7 +152,7 @@ layout guide
|
|
|
152
152
|
[`app-layouts.md`](app-layouts.md) for choosing among `NavStack`, `SplitView`,
|
|
153
153
|
`Workbench`, and `TabNavigator`.
|
|
154
154
|
|
|
155
|
-
## Loading
|
|
155
|
+
## Loading Inspector
|
|
156
156
|
|
|
157
157
|
[Open the recipe](../ux-demo/?component=recipe-loading-inspector) · [TSX source](../ux-demo/recipes/loading-inspector.tsx)
|
|
158
158
|
|
|
@@ -166,7 +166,7 @@ the populated record — no separate skeleton markup. The composition is the poi
|
|
|
166
166
|
record title instead of truncating it. The app owns the loading lifecycle and
|
|
167
167
|
which values are still unknown.
|
|
168
168
|
|
|
169
|
-
## Collapsible
|
|
169
|
+
## Collapsible Sidebar
|
|
170
170
|
|
|
171
171
|
[Open the recipe](../ux-demo/?component=recipe-collapsible-sidebar) · [TSX source](../ux-demo/recipes/collapsible-sidebar.tsx)
|
|
172
172
|
|
package/docs/split-view.md
CHANGED
|
@@ -114,12 +114,12 @@ compactStack={{
|
|
|
114
114
|
list: { toolbar: { trailing: <ComposeGroup /> } },
|
|
115
115
|
detail: {
|
|
116
116
|
toolbar: { trailing: <ReplyGroup /> },
|
|
117
|
-
bottomToolbar: { label: "Message
|
|
117
|
+
bottomToolbar: { label: "Message Actions", leading: <MessageActions /> },
|
|
118
118
|
},
|
|
119
119
|
}}
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
The catalog's **Interactive
|
|
122
|
+
The catalog's **Interactive Compact Drill-Down** example exercises this exact
|
|
123
123
|
controlled flow: a list action sets the selected message, `detailActive` pushes
|
|
124
124
|
its detail, and the wired Back action clears the selection to reveal the
|
|
125
125
|
preserved list. It also forwards `compactStack`: a level-2 heading title over
|
|
@@ -128,7 +128,7 @@ bottom toolbar on the detail. Use the same structure in application demos so
|
|
|
128
128
|
compact examples show the interaction rather than rendering a disconnected
|
|
129
129
|
detail state.
|
|
130
130
|
|
|
131
|
-
The catalog's **Resizable,
|
|
131
|
+
The catalog's **Resizable, Collapsible List** example forwards `resizable`: a
|
|
132
132
|
hidden separator that still resizes the list (its committed width comes back
|
|
133
133
|
through `wireResizableRegions`' `onCommit` into the app's size signal), a Hide
|
|
134
134
|
control in the list's own toolbar, and a `FloatingToolbar` restore control in
|
package/docs/sunken-panel.md
CHANGED
|
@@ -8,7 +8,7 @@ issue tracker or component catalog.
|
|
|
8
8
|
```tsx
|
|
9
9
|
import { SunkenPanel } from '@kerfjs/ui/sunken-panel';
|
|
10
10
|
|
|
11
|
-
<SunkenPanel ariaLabel="Release
|
|
11
|
+
<SunkenPanel ariaLabel="Release Workspace">
|
|
12
12
|
<ReleaseSummary />
|
|
13
13
|
<ReleaseChecks />
|
|
14
14
|
</SunkenPanel>;
|
|
@@ -33,7 +33,7 @@ can shrink with its parent. Like `Grid`, `flex` accepts `true` (equivalent to
|
|
|
33
33
|
|
|
34
34
|
```tsx
|
|
35
35
|
<List fill>
|
|
36
|
-
<Toolbar label="Work
|
|
36
|
+
<Toolbar label="Work Area" />
|
|
37
37
|
<SunkenPanel flex>
|
|
38
38
|
<WorkArea />
|
|
39
39
|
</SunkenPanel>
|
package/docs/surface-scaffold.md
CHANGED
|
@@ -54,7 +54,7 @@ import { remify } from '@kerfjs/ui/css-values';
|
|
|
54
54
|
import { DialogSurface } from '@kerfjs/ui/surface-scaffold';
|
|
55
55
|
|
|
56
56
|
<DialogSurface preferredWidth={remify(832)} viewportGutter={remify(8)} maxHeight="viewport" bodyInset="none">
|
|
57
|
-
<wa-dialog label="Close
|
|
57
|
+
<wa-dialog label="Close Workspace">{/* Application-owned content and footer */}</wa-dialog>
|
|
58
58
|
</DialogSurface>;
|
|
59
59
|
```
|
|
60
60
|
|
|
@@ -86,12 +86,129 @@ itself stops scrolling. Combine it with `bodyInset="none"`.
|
|
|
86
86
|
|
|
87
87
|
```tsx
|
|
88
88
|
<DialogSurface size="large" height={remify(640)} viewportGutter={remify(8)} bodyInset="none" bodyLayout="fill">
|
|
89
|
-
<wa-dialog label="Repository
|
|
89
|
+
<wa-dialog label="Repository Status">
|
|
90
90
|
<SplitView id="repo-split" label="Changes" listWidth={{ size: 432, maxFraction: 0.5 }} list={list} detail={detail} />
|
|
91
91
|
</wa-dialog>
|
|
92
92
|
</DialogSurface>;
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
### Chat dialogs: a floating footer over a filling transcript
|
|
96
|
+
|
|
97
|
+
A chat-style dialog keeps a heading or notices above one scrolling transcript
|
|
98
|
+
and floats its composer over the body's block end. Two props compose it:
|
|
99
|
+
|
|
100
|
+
- `bodyLayout="fill-last"` stacks the body's children: the leading children
|
|
101
|
+
keep their natural height, and the last one takes the remaining height and
|
|
102
|
+
owns its scrolling (a `scrollable` `List` transcript). The body itself stops
|
|
103
|
+
scrolling. `PopoverDialogSurface` accepts the same layout.
|
|
104
|
+
- `footerPresentation="floating"` lays the footer over the body's block end,
|
|
105
|
+
`footerOffset` (a branded `CssLength`, default the 16px container inset)
|
|
106
|
+
from the panel's inline and block-end edges, on a transparent background.
|
|
107
|
+
The modal panel is its positioning context. The default `docked` footer
|
|
108
|
+
sits below the body as before.
|
|
109
|
+
|
|
110
|
+
The register measures the footer (it follows a composer that grows, wraps, or
|
|
111
|
+
hides) and the body reserves that height plus the offset after its own inset,
|
|
112
|
+
with a matching `scroll-padding`. Flowing body content scrolls beneath the
|
|
113
|
+
floating footer and its end stays reachable above it; with `fill` or
|
|
114
|
+
`fill-last` the filling child's scrollport ends at the footer's top. Content
|
|
115
|
+
is never hidden beneath the footer.
|
|
116
|
+
|
|
117
|
+
To let a filling transcript scroll visibly beneath the floating footer, opt
|
|
118
|
+
its scroller in with `scrollClearance="block-end"` (a `scrollable` `List`, or a
|
|
119
|
+
`Pane`, as the `fill` or last `fill-last` child). The body then stops
|
|
120
|
+
reserving the footer below that child, so the child reaches the panel's block
|
|
121
|
+
end, and publishes the footer's clearance (measured height plus offset) as the
|
|
122
|
+
inherited `--kui-scroll-clearance-block-end` context. The child adds it to its
|
|
123
|
+
own block-end padding and `scroll-padding`: messages pass beneath the
|
|
124
|
+
transparent footer mid-scroll, the last message rests above the composer at
|
|
125
|
+
the end, and focus or `scrollIntoView` never leaves an item hidden beneath it.
|
|
126
|
+
The context is general: chrome that overlays a region's block-start or
|
|
127
|
+
block-end edge publishes `--kui-scroll-clearance-block-start` / `-end`, and a
|
|
128
|
+
scroll owner opts in per edge (see `docs/layout.md` "Scroll clearance"). A
|
|
129
|
+
dialog body clears any clearance inherited from the page.
|
|
130
|
+
|
|
131
|
+
Choose the offset like any other responsive value: the scaffold adds no
|
|
132
|
+
breakpoint. An 8px offset lines a composer up with the transcript's
|
|
133
|
+
content-item edges when the body has `bodyInset="none"` and list geometry;
|
|
134
|
+
`viewportGutter` and `footerOffset` can follow `deviceClass()` when a phone
|
|
135
|
+
needs tighter edges. Put a [`Composer`](./composer.md) in the floating footer
|
|
136
|
+
with `footerInset="none"`: its raised frame and shadow lift it over the
|
|
137
|
+
transcript, and its field grows the measured footer. With `submitOnEnter` and
|
|
138
|
+
`wireComposers(root)`, Enter sends through the same action as the Send button.
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
<DialogSurface height={remify(560)} viewportGutter={remify(8)} bodyInset="none" bodyLayout="fill-last" footerPresentation="floating" footerOffset={remify(8)} footerInset="none">
|
|
142
|
+
<wa-dialog label="Assistant">
|
|
143
|
+
<StateBanner title="Answers use the latest nightly build." tone="neutral" />
|
|
144
|
+
<List gap="xs" controlInsets="tb" scrollable scrollClearance="block-end">
|
|
145
|
+
{messages.map((message) => (
|
|
146
|
+
<ContentItem bubble={message.mine ? 'end' : 'start'} appearance={message.mine ? 'brand-loud' : 'neutral'}>
|
|
147
|
+
<Text variant="span">{message.text}</Text>
|
|
148
|
+
</ContentItem>
|
|
149
|
+
))}
|
|
150
|
+
</List>
|
|
151
|
+
<Composer submitOnEnter slot="footer" id="assistant-message" name="message" label="Message" placeholder="Ask about this build" actions={<wa-button variant="brand" data-action="send-message">Send</wa-button>} />
|
|
152
|
+
</wa-dialog>
|
|
153
|
+
</DialogSurface>;
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### A floating title bar
|
|
157
|
+
|
|
158
|
+
`headerPresentation="floating"` lays the dialog's header (its title and close
|
|
159
|
+
control) over the body's block-start edge on the translucent chrome material,
|
|
160
|
+
`--kui-chrome-material-raised` (the raised panel surface at 80% opacity)
|
|
161
|
+
behind `--kui-chrome-material-filter` (a blur and saturation backdrop filter).
|
|
162
|
+
Content passing beneath the title bar reads as softened color, not as
|
|
163
|
+
competing text. The default `docked` header sits above the body as before.
|
|
164
|
+
|
|
165
|
+
The register measures the header the same way it measures the footer (it
|
|
166
|
+
follows a title that wraps, text that grows, or a `without-header` toggle).
|
|
167
|
+
The body reserves the measured height before its own inset, with a matching
|
|
168
|
+
`scroll-padding`, so flowing content starts below the title bar and scrolls
|
|
169
|
+
beneath it. A dialog without the register has no measurement, so its header
|
|
170
|
+
stays docked rather than covering content.
|
|
171
|
+
|
|
172
|
+
To let a filling transcript scroll visibly beneath the title bar, make it the
|
|
173
|
+
body's only child (`bodyLayout="fill"`, or `fill-last` with no leading rows)
|
|
174
|
+
and opt its scroller in with block-start `scrollClearance`. The body then
|
|
175
|
+
stops reserving the header, so the child reaches the panel's block-start
|
|
176
|
+
edge, and publishes the header's height as `--kui-scroll-clearance-block-start`.
|
|
177
|
+
Leading `fill-last` rows (a notice above the transcript) keep the body's
|
|
178
|
+
reservation: they never sit beneath the title bar. Combined with a floating
|
|
179
|
+
composer and `scrollClearance="both"`, the transcript scrolls beneath both:
|
|
180
|
+
|
|
181
|
+
```tsx
|
|
182
|
+
<DialogSurface height={remify(560)} viewportGutter={remify(8)} bodyInset="none" bodyLayout="fill" headerPresentation="floating" footerPresentation="floating" footerOffset={remify(8)} footerInset="none">
|
|
183
|
+
<wa-dialog label="Release Thread">
|
|
184
|
+
<ToolbarText slot="label" text="Release Thread" size="xlarge" description="Engineering · 5.2 candidate" />
|
|
185
|
+
<List gap="xs" controlInsets="tb" scrollable scrollClearance="both">
|
|
186
|
+
{bubbles}
|
|
187
|
+
</List>
|
|
188
|
+
<Composer submitOnEnter slot="footer" id="thread-message" name="message" label="Message" placeholder="Reply to the thread" actions={<wa-button variant="brand" data-action="send-message">Send</wa-button>} />
|
|
189
|
+
</wa-dialog>
|
|
190
|
+
</DialogSurface>;
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Like a `Pane`'s overlay header, the title bar draws its divider as scroll
|
|
194
|
+
state: a 1px line along its bottom edge only while the body, or a filling
|
|
195
|
+
child that reaches beneath it, is scrolled away from its top, and none at
|
|
196
|
+
rest. The register follows those scrollers (a nested scroller that starts
|
|
197
|
+
below the title bar never counts), and the line is an inset shadow, so it
|
|
198
|
+
never moves the header or the content.
|
|
199
|
+
|
|
200
|
+
The material turns opaque under `prefers-reduced-transparency: reduce` or
|
|
201
|
+
`prefers-contrast: more`, and where the browser lacks `backdrop-filter`; in
|
|
202
|
+
forced colors the header paints `Canvas` with a `CanvasText` bottom edge.
|
|
203
|
+
|
|
204
|
+
For a title with a short supporting line, slot a `ToolbarText` as the label
|
|
205
|
+
(`<ToolbarText slot="label" text="Release Thread" size="xlarge" description="Engineering · 5.2 candidate" />`).
|
|
206
|
+
On the floating title bar an untoned `ToolbarText` keeps AA by itself: its
|
|
207
|
+
title takes the normal text color and its description lifts halfway toward
|
|
208
|
+
it. Any other quiet copy you place there can fall below AA over the content
|
|
209
|
+
passing beneath it, so keep it in the body (see
|
|
210
|
+
[Accessibility › Chrome material](accessibility.md#chrome-material)).
|
|
211
|
+
|
|
95
212
|
Treat a dialog body as a `List` by default. When its rows or sections own their
|
|
96
213
|
standard list geometry, set `bodyInset="none"` so the dialog does not add a
|
|
97
214
|
second inset. Bare prose is still a list child: wrap it in `ListInsetText` so its
|
|
@@ -109,8 +226,22 @@ hand-built popover with application chrome CSS. It is a native
|
|
|
109
226
|
`popover="auto"` section with `role="dialog"` that owns Kerf's dialog chrome
|
|
110
227
|
(raised surface, border, large radius and shadow, and a backdrop) and the
|
|
111
228
|
same geometry props as a modal `DialogSurface`: `size`, `preferredWidth`,
|
|
112
|
-
`viewportGutter` (16px by default), `height` / `maxHeight`, and `bodyLayout
|
|
113
|
-
|
|
229
|
+
`viewportGutter` (16px by default), `height` / `maxHeight`, and `bodyLayout`
|
|
230
|
+
(`flow`, `fill`, or `fill-last`).
|
|
231
|
+
Name it with `label` or `labelledBy`. To give it an accessible description,
|
|
232
|
+
pass `describedBy`, the id of the element that holds the summary. It renders as
|
|
233
|
+
`aria-describedby` on the `role="dialog"` root and usually names a title
|
|
234
|
+
`ToolbarText`'s `descriptionId`:
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
<PopoverDialogSurface id="connection-details" labelledBy="connection-title" describedBy="connection-summary" bodyLayout="fill-last">
|
|
238
|
+
<Toolbar label="Connection Details Heading" leading={<ToolbarText text="Connection Details" size="xlarge" id="connection-title" description="Connected to the build server." descriptionId="connection-summary" />} />
|
|
239
|
+
{details}
|
|
240
|
+
</PopoverDialogSurface>;
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`DialogSurface` has no `describedBy`. It wraps a `wa-dialog` that the app
|
|
244
|
+
authors, so the app names and describes that dialog on its own markup.
|
|
114
245
|
|
|
115
246
|
Open it with a control's `popoverTarget` (or `showPopover()`); the browser
|
|
116
247
|
places it centered in the top layer, light-dismisses it, and restores nothing
|
|
@@ -121,8 +252,8 @@ layer and never wider than its container, for a catalog specimen or an inline
|
|
|
121
252
|
preview.
|
|
122
253
|
|
|
123
254
|
```tsx
|
|
124
|
-
<button type="button" popoverTarget="repository-status">Repository
|
|
125
|
-
<PopoverDialogSurface id="repository-status" label="Repository
|
|
255
|
+
<button type="button" popoverTarget="repository-status">Repository Status</button>
|
|
256
|
+
<PopoverDialogSurface id="repository-status" label="Repository Status" preferredWidth={remify(1024)} height={remify(640)} viewportGutter={remify(8)} bodyLayout="fill">
|
|
126
257
|
<SplitView id="repo-split" label="Changes" list={list} detail={detail} />
|
|
127
258
|
</PopoverDialogSurface>;
|
|
128
259
|
```
|
|
@@ -136,4 +267,4 @@ preserve `data-morph-skip-children` when its upgraded light-DOM items must retai
|
|
|
136
267
|
identity across Kerf rerenders. Import `@kerfjs/ui/popup-menu/register` once for
|
|
137
268
|
the focus handoff fix when wrapping a raw `wa-dropdown`.
|
|
138
269
|
|
|
139
|
-
Import `@kerfjs/ui/surface-scaffold/register` once when using `DialogSurface`: it registers `wa-dialog
|
|
270
|
+
Import `@kerfjs/ui/surface-scaffold/register` once when using `DialogSurface`: it registers `wa-dialog`, measures each surface's footer for a floating footer's body clearance, draws a floating header's divider while content is scrolled beneath it, and repairs Web Awesome 3.12 native dialog names before the first modal opening. Headed dialogs reference the rendered title, including live slotted labels; headerless dialogs use the public `label`. Explicit host `aria-label` and pre-existing native names are preserved. A host that is itself an explicit dialog (`role="dialog"` or `"alertdialog"` with its own name) keeps that name alone: the native panel is left unnamed, so one dialog carries the name. Geometry-only markup stays CSS-free and does not register Web Awesome implicitly.
|