@kerfjs/ui 5.0.0-beta.10 → 5.0.0-beta.101
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 +632 -132
- package/ai/application-ui-diagnostic-ids-v1.json +114 -0
- package/ai/application-ui-profile-sync.cjs +548 -0
- package/ai/application-ui-profile.d.mts +1 -0
- package/ai/application-ui-profile.d.ts +106 -0
- package/ai/application-ui-profile.defaults.json +96 -0
- package/ai/application-ui-profile.mjs +653 -0
- package/ai/application-ui-profile.schema.json +140 -0
- package/ai/catalog-authoring.json +34 -0
- package/ai/catalog-authoring.schema.json +105 -0
- package/ai/compile-time-contracts-v1.json +149 -0
- package/ai/compile-time-contracts-v1.schema.json +52 -0
- package/ai/component-catalog-extension.schema.json +104 -0
- package/ai/component-catalog.json +6668 -1002
- package/ai/component-catalog.schema.json +94 -1
- package/ai/component-class-owners.cjs +83 -0
- package/ai/component-composition-extension.d.ts +7 -0
- package/ai/component-composition-extension.schema.json +7 -0
- package/ai/component-composition-rules.json +551 -0
- package/ai/component-composition.d.ts +132 -0
- package/ai/component-composition.json +8937 -0
- package/ai/component-composition.schema.json +412 -0
- package/ai/components/README.md +187 -0
- package/ai/components/anchored-surface.md +99 -0
- package/ai/components/application-tabs.md +65 -0
- package/ai/components/backdrop-image.md +84 -0
- package/ai/components/badge.md +76 -0
- package/ai/components/chip.md +76 -0
- package/ai/components/choice-chips.md +87 -0
- package/ai/components/collapsible-panel.md +103 -0
- package/ai/components/column-track.md +78 -0
- package/ai/components/combobox.md +88 -0
- package/ai/components/composer.md +83 -0
- package/ai/components/content-item.md +98 -0
- package/ai/components/content-swap.md +78 -0
- package/ai/components/corner-status.md +88 -0
- package/ai/components/disclosure-arrow.md +77 -0
- package/ai/components/document-baseline.md +77 -0
- package/ai/components/edge-activity-indicator.md +81 -0
- package/ai/components/empty-state.md +80 -0
- package/ai/components/feedback.md +71 -0
- package/ai/components/file-picker.md +88 -0
- package/ai/components/floating-toolbar.md +82 -0
- package/ai/components/foundation.md +65 -0
- package/ai/components/framed-list.md +80 -0
- package/ai/components/grid.md +101 -0
- package/ai/components/headers.md +63 -0
- package/ai/components/icon-button.md +82 -0
- package/ai/components/icon-toggle-button.md +80 -0
- package/ai/components/layout.md +75 -0
- package/ai/components/list-action-row.md +108 -0
- package/ai/components/list-header.md +91 -0
- package/ai/components/list-inset-control.md +74 -0
- package/ai/components/list-inset-text.md +72 -0
- package/ai/components/list-item.md +108 -0
- package/ai/components/list.md +125 -0
- package/ai/components/loading-spinner.md +73 -0
- package/ai/components/lucide-icon.md +97 -0
- package/ai/components/nav-stack.md +120 -0
- package/ai/components/pane.md +125 -0
- package/ai/components/popup-menu.md +171 -0
- package/ai/components/recipe-app-shell.md +57 -0
- package/ai/components/recipe-collapsible-sidebar.md +57 -0
- package/ai/components/recipe-compact-toolbar.md +57 -0
- package/ai/components/recipe-composer-form.md +57 -0
- package/ai/components/recipe-list-detail-dialog.md +57 -0
- package/ai/components/recipe-list-workspace-states.md +57 -0
- package/ai/components/recipe-loading-inspector.md +57 -0
- package/ai/components/recipe-media-detail.md +57 -0
- package/ai/components/recipe-navigation-sidebar.md +57 -0
- package/ai/components/recipe-navigation-stack.md +57 -0
- package/ai/components/recipe-workspace-header.md +57 -0
- package/ai/components/resize.md +106 -0
- package/ai/components/row.md +96 -0
- package/ai/components/segmented-control.md +82 -0
- package/ai/components/select.md +116 -0
- package/ai/components/skeleton.md +84 -0
- package/ai/components/spacer.md +86 -0
- package/ai/components/split-button.md +80 -0
- package/ai/components/split-view.md +105 -0
- package/ai/components/state-banner.md +96 -0
- package/ai/components/summary-row.md +85 -0
- package/ai/components/sunken-panel.md +79 -0
- package/ai/components/surface-scaffold.md +112 -0
- package/ai/components/tab-bar.md +114 -0
- package/ai/components/tab-navigator.md +105 -0
- package/ai/components/tabs.md +95 -0
- package/ai/components/text.md +90 -0
- package/ai/components/token-search-field.md +107 -0
- package/ai/components/toolbar-control-group.md +126 -0
- package/ai/components/toolbar-text.md +100 -0
- package/ai/components/toolbar.md +103 -0
- package/ai/components/value-table.md +80 -0
- package/ai/components/wa-accordion-item.md +55 -0
- package/ai/components/wa-accordion.md +65 -0
- package/ai/components/wa-animated-image.md +55 -0
- package/ai/components/wa-animation.md +55 -0
- package/ai/components/wa-avatar.md +55 -0
- package/ai/components/wa-badge.md +55 -0
- package/ai/components/wa-breadcrumb-item.md +53 -0
- package/ai/components/wa-breadcrumb.md +55 -0
- package/ai/components/wa-button-group.md +60 -0
- package/ai/components/wa-button.md +59 -0
- package/ai/components/wa-callout.md +58 -0
- package/ai/components/wa-card.md +59 -0
- package/ai/components/wa-carousel-item.md +53 -0
- package/ai/components/wa-carousel.md +55 -0
- package/ai/components/wa-checkbox-group.md +55 -0
- package/ai/components/wa-checkbox.md +55 -0
- package/ai/components/wa-color-picker.md +55 -0
- package/ai/components/wa-comparison.md +55 -0
- package/ai/components/wa-copy-button.md +55 -0
- package/ai/components/wa-details.md +61 -0
- package/ai/components/wa-dialog.md +59 -0
- package/ai/components/wa-divider.md +53 -0
- package/ai/components/wa-drawer.md +55 -0
- package/ai/components/wa-dropdown-item.md +60 -0
- package/ai/components/wa-dropdown.md +60 -0
- package/ai/components/wa-format-bytes.md +53 -0
- package/ai/components/wa-format-date.md +53 -0
- package/ai/components/wa-format-number.md +53 -0
- package/ai/components/wa-icon.md +58 -0
- package/ai/components/wa-include.md +53 -0
- package/ai/components/wa-input.md +59 -0
- package/ai/components/wa-intersection-observer.md +53 -0
- package/ai/components/wa-known-date.md +53 -0
- package/ai/components/wa-markdown.md +53 -0
- package/ai/components/wa-mutation-observer.md +53 -0
- package/ai/components/wa-number-input.md +55 -0
- package/ai/components/wa-option.md +59 -0
- package/ai/components/wa-otp-input.md +53 -0
- package/ai/components/wa-page.md +53 -0
- package/ai/components/wa-pagination.md +55 -0
- package/ai/components/wa-popover.md +55 -0
- package/ai/components/wa-popup.md +53 -0
- package/ai/components/wa-progress-bar.md +53 -0
- package/ai/components/wa-progress-ring.md +53 -0
- package/ai/components/wa-qr-code.md +53 -0
- package/ai/components/wa-radio-group.md +59 -0
- package/ai/components/wa-radio.md +55 -0
- package/ai/components/wa-random-content.md +53 -0
- package/ai/components/wa-rating.md +55 -0
- package/ai/components/wa-relative-time.md +53 -0
- package/ai/components/wa-resize-observer.md +53 -0
- package/ai/components/wa-scroller.md +53 -0
- package/ai/components/wa-select.md +63 -0
- package/ai/components/wa-skeleton.md +53 -0
- package/ai/components/wa-slider.md +55 -0
- package/ai/components/wa-spinner.md +58 -0
- package/ai/components/wa-split-panel.md +58 -0
- package/ai/components/wa-switch.md +53 -0
- package/ai/components/wa-tab-group.md +69 -0
- package/ai/components/wa-tab-panel.md +60 -0
- package/ai/components/wa-tab.md +59 -0
- package/ai/components/wa-tag.md +59 -0
- package/ai/components/wa-textarea.md +53 -0
- package/ai/components/wa-time-input.md +55 -0
- package/ai/components/wa-toast-item.md +60 -0
- package/ai/components/wa-toast.md +60 -0
- package/ai/components/wa-tooltip.md +55 -0
- package/ai/components/wa-tree-item.md +55 -0
- package/ai/components/wa-tree.md +55 -0
- package/ai/components/wa-zoomable-frame.md +55 -0
- package/ai/components/workbench.md +120 -0
- package/ai/public-api-signatures-v1.md +4564 -438
- package/ai/skill.md +490 -64
- package/ai/webawesome-jsx-signatures-v1.md +9 -2
- package/analyzer/cli.mjs +49 -0
- package/analyzer/component-ownership.d.mts +72 -0
- package/analyzer/component-ownership.mjs +429 -0
- package/analyzer/index.d.mts +53 -0
- package/analyzer/index.mjs +2375 -0
- package/analyzer/loud-pairs.d.mts +49 -0
- package/analyzer/loud-pairs.mjs +357 -0
- package/analyzer/report.schema.json +66 -0
- package/analyzer/selectors.d.mts +21 -0
- package/analyzer/selectors.mjs +212 -0
- package/catalog-projection-cli.mjs +96 -0
- package/catalog-projection.d.mts +55 -0
- package/catalog-projection.mjs +62 -0
- package/dist/anchored-surface.d.ts +69 -0
- package/dist/anchored-surface.js +3 -0
- package/dist/anchored-surface.js.map +1 -0
- package/dist/app-tab.d.ts +39 -4
- package/dist/app-tab.js +2 -1
- package/dist/backdrop-image.d.ts +132 -0
- package/dist/backdrop-image.js +5 -0
- package/dist/backdrop-image.js.map +1 -0
- package/dist/badge.d.ts +83 -0
- package/dist/badge.js +4 -0
- package/dist/badge.js.map +1 -0
- package/dist/browser/anchored-surface.js +3 -0
- package/dist/browser/backdrop-image.js +3 -0
- package/dist/browser/badge.js +3 -0
- package/dist/browser/catalog.js +28 -0
- package/dist/browser/chip.js +3 -0
- package/dist/browser/choice-chips.js +3 -0
- package/dist/browser/collapsible-panel.js +9 -0
- package/dist/browser/column-track.js +3 -0
- package/dist/browser/combobox.js +3 -0
- package/dist/browser/composer.js +4 -0
- package/dist/browser/content-item.js +3 -0
- package/dist/browser/content-swap.js +3 -0
- package/dist/browser/corner-status.js +3 -0
- package/dist/browser/edge-activity-indicator.js +3 -0
- package/dist/browser/file-picker.js +3 -0
- package/dist/browser/floating-toolbar.js +3 -0
- package/dist/browser/framed-list.js +3 -0
- package/dist/browser/grid.js +3 -0
- package/dist/browser/icon-button.js +4 -0
- package/dist/browser/icon-toggle-button.js +4 -0
- package/dist/browser/list-action-row.js +2 -0
- package/dist/browser/list-header.js +3 -1
- package/dist/browser/list-inset-control.js +3 -0
- package/dist/browser/list-inset-text.js +3 -0
- package/dist/browser/list-item.js +1 -0
- package/dist/browser/list.js +3 -0
- package/dist/browser/nav-stack.js +11 -0
- package/dist/browser/pane.js +3 -0
- package/dist/browser/popup-menu.js +3 -0
- package/dist/browser/row.js +3 -0
- package/dist/browser/segmented-control.js +0 -1
- package/dist/browser/select.js +2 -1
- package/dist/browser/spacer.js +3 -0
- package/dist/browser/split-button.js +5 -0
- package/dist/browser/split-view.js +13 -0
- package/dist/browser/state-banner.js +1 -0
- package/dist/browser/summary-row.js +3 -0
- package/dist/browser/sunken-panel.js +3 -0
- package/dist/browser/surface-scaffold.js +3 -0
- package/dist/browser/tab-navigator.js +4 -0
- package/dist/browser/text.js +3 -0
- package/dist/browser/toolbar-control-group.js +1 -0
- package/dist/browser/workbench.js +14 -0
- package/dist/catalog-resources.d.ts +50 -0
- package/dist/catalog-resources.js +22 -0
- package/dist/catalog-resources.js.map +1 -0
- package/dist/catalog.d.ts +134 -101
- package/dist/catalog.js +669 -105
- package/dist/catalog.js.map +1 -1
- package/dist/chip.d.ts +69 -0
- package/dist/chip.js +3 -0
- package/dist/chip.js.map +1 -0
- package/dist/choice-chips.d.ts +82 -0
- package/dist/choice-chips.js +3 -0
- package/dist/choice-chips.js.map +1 -0
- package/dist/chunk-2GOCOLQL.js +270 -0
- package/dist/chunk-2GOCOLQL.js.map +1 -0
- package/dist/chunk-2MSPNRWJ.js +82 -0
- package/dist/chunk-2MSPNRWJ.js.map +1 -0
- package/dist/chunk-2PXZBFEC.js +141 -0
- package/dist/chunk-2PXZBFEC.js.map +1 -0
- package/dist/chunk-34U4ANDJ.js +119 -0
- package/dist/chunk-34U4ANDJ.js.map +1 -0
- package/dist/chunk-3SKAEPPZ.js +165 -0
- package/dist/chunk-3SKAEPPZ.js.map +1 -0
- package/dist/{chunk-56RJXPHG.js → chunk-3VXDSIBJ.js} +7 -4
- package/dist/chunk-3VXDSIBJ.js.map +1 -0
- package/dist/chunk-3WYREXW4.js +120 -0
- package/dist/chunk-3WYREXW4.js.map +1 -0
- package/dist/chunk-4UB5Z3ZZ.js +68 -0
- package/dist/chunk-4UB5Z3ZZ.js.map +1 -0
- package/dist/chunk-5NQF35VO.js +67 -0
- package/dist/chunk-5NQF35VO.js.map +1 -0
- package/dist/chunk-6C6DLP3W.js +31 -0
- package/dist/chunk-6C6DLP3W.js.map +1 -0
- package/dist/chunk-6E2H2E5A.js +196 -0
- package/dist/chunk-6E2H2E5A.js.map +1 -0
- package/dist/chunk-7IUEBNCO.js +108 -0
- package/dist/chunk-7IUEBNCO.js.map +1 -0
- package/dist/chunk-AN5IWMTM.js +24 -0
- package/dist/chunk-AN5IWMTM.js.map +1 -0
- package/dist/chunk-AV7CPAPT.js +82 -0
- package/dist/chunk-AV7CPAPT.js.map +1 -0
- package/dist/chunk-B53PS2LE.js +34 -0
- package/dist/chunk-B53PS2LE.js.map +1 -0
- package/dist/chunk-BYFTHA4I.js +153 -0
- package/dist/chunk-BYFTHA4I.js.map +1 -0
- package/dist/chunk-CA7LNX3J.js +44 -0
- package/dist/chunk-CA7LNX3J.js.map +1 -0
- package/dist/chunk-CEJ5FK6G.js +288 -0
- package/dist/chunk-CEJ5FK6G.js.map +1 -0
- package/dist/chunk-CMLPFIC5.js +46 -0
- package/dist/chunk-CMLPFIC5.js.map +1 -0
- package/dist/chunk-CVD5IZGB.js +66 -0
- package/dist/chunk-CVD5IZGB.js.map +1 -0
- package/dist/chunk-D5RGM3XA.js +38 -0
- package/dist/chunk-D5RGM3XA.js.map +1 -0
- package/dist/chunk-DZJ4GBSP.js +59 -0
- package/dist/chunk-DZJ4GBSP.js.map +1 -0
- package/dist/chunk-EIET4QLK.js +51 -0
- package/dist/chunk-EIET4QLK.js.map +1 -0
- package/dist/chunk-EKHDLBRP.js +86 -0
- package/dist/chunk-EKHDLBRP.js.map +1 -0
- package/dist/chunk-ELCMSQNJ.js +49 -0
- package/dist/chunk-ELCMSQNJ.js.map +1 -0
- package/dist/chunk-ETCWQLJV.js +85 -0
- package/dist/chunk-ETCWQLJV.js.map +1 -0
- package/dist/chunk-FDZNYD4C.js +138 -0
- package/dist/chunk-FDZNYD4C.js.map +1 -0
- package/dist/chunk-FNZQOUZ5.js +52 -0
- package/dist/chunk-FNZQOUZ5.js.map +1 -0
- package/dist/chunk-FR76ZJQC.js +90 -0
- package/dist/chunk-FR76ZJQC.js.map +1 -0
- package/dist/chunk-G3J7JDP6.js +29 -0
- package/dist/chunk-G3J7JDP6.js.map +1 -0
- package/dist/chunk-GS2QD3N7.js +14 -0
- package/dist/chunk-GS2QD3N7.js.map +1 -0
- package/dist/chunk-HLDRH23D.js +84 -0
- package/dist/chunk-HLDRH23D.js.map +1 -0
- package/dist/chunk-HQZYKJ56.js +302 -0
- package/dist/chunk-HQZYKJ56.js.map +1 -0
- package/dist/chunk-HZPHCSCS.js +95 -0
- package/dist/chunk-HZPHCSCS.js.map +1 -0
- package/dist/chunk-IKS72C6P.js +53 -0
- package/dist/chunk-IKS72C6P.js.map +1 -0
- package/dist/chunk-IM3GKYOG.js +202 -0
- package/dist/chunk-IM3GKYOG.js.map +1 -0
- package/dist/chunk-IQSRHXZR.js +164 -0
- package/dist/chunk-IQSRHXZR.js.map +1 -0
- package/dist/chunk-IXLUZHGT.js +162 -0
- package/dist/chunk-IXLUZHGT.js.map +1 -0
- package/dist/chunk-JOR5LGYE.js +55 -0
- package/dist/chunk-JOR5LGYE.js.map +1 -0
- package/dist/chunk-K4AT3V3J.js +545 -0
- package/dist/chunk-K4AT3V3J.js.map +1 -0
- package/dist/chunk-KCS6NJ3O.js +82 -0
- package/dist/chunk-KCS6NJ3O.js.map +1 -0
- package/dist/chunk-KH4KEMSP.js +71 -0
- package/dist/chunk-KH4KEMSP.js.map +1 -0
- package/dist/chunk-LL45C7BB.js +122 -0
- package/dist/chunk-LL45C7BB.js.map +1 -0
- package/dist/chunk-LOSRXZAH.js +100 -0
- package/dist/chunk-LOSRXZAH.js.map +1 -0
- package/dist/chunk-LPHU44S3.js +24 -0
- package/dist/chunk-LPHU44S3.js.map +1 -0
- package/dist/chunk-M5KZ26VV.js +148 -0
- package/dist/chunk-M5KZ26VV.js.map +1 -0
- package/dist/chunk-MF6ZUUJZ.js +300 -0
- package/dist/chunk-MF6ZUUJZ.js.map +1 -0
- package/dist/chunk-MHMWQ7RB.js +19 -0
- package/dist/chunk-MHMWQ7RB.js.map +1 -0
- package/dist/chunk-MJ5RU6XT.js +173 -0
- package/dist/chunk-MJ5RU6XT.js.map +1 -0
- package/dist/chunk-MXNIVEVL.js +48 -0
- package/dist/chunk-MXNIVEVL.js.map +1 -0
- package/dist/{chunk-YGN7OMLT.js → chunk-NS2TBYM4.js} +8 -5
- package/dist/chunk-NS2TBYM4.js.map +1 -0
- package/dist/chunk-NS762OVM.js +222 -0
- package/dist/chunk-NS762OVM.js.map +1 -0
- package/dist/chunk-OZ6X76G5.js +547 -0
- package/dist/chunk-OZ6X76G5.js.map +1 -0
- package/dist/chunk-PKL6QJKK.js +835 -0
- package/dist/chunk-PKL6QJKK.js.map +1 -0
- package/dist/chunk-QDSZTQXL.js +28 -0
- package/dist/chunk-QDSZTQXL.js.map +1 -0
- package/dist/chunk-QH5KOFNS.js +39 -0
- package/dist/chunk-QH5KOFNS.js.map +1 -0
- package/dist/chunk-QLA25V2K.js +493 -0
- package/dist/chunk-QLA25V2K.js.map +1 -0
- package/dist/chunk-QTSMHH5J.js +85 -0
- package/dist/chunk-QTSMHH5J.js.map +1 -0
- package/dist/chunk-QYMJHS4K.js +36 -0
- package/dist/chunk-QYMJHS4K.js.map +1 -0
- package/dist/chunk-S2XULQEK.js +52 -0
- package/dist/chunk-S2XULQEK.js.map +1 -0
- package/dist/chunk-S3ECMF23.js +33 -0
- package/dist/chunk-S3ECMF23.js.map +1 -0
- package/dist/chunk-S4Y3YTAI.js +35 -0
- package/dist/chunk-S4Y3YTAI.js.map +1 -0
- package/dist/chunk-S4ZGTDSG.js +18 -0
- package/dist/chunk-S4ZGTDSG.js.map +1 -0
- package/dist/chunk-SCDWURBO.js +136 -0
- package/dist/chunk-SCDWURBO.js.map +1 -0
- package/dist/chunk-SIW64V65.js +176 -0
- package/dist/chunk-SIW64V65.js.map +1 -0
- package/dist/chunk-SQ5DH66U.js +37 -0
- package/dist/chunk-SQ5DH66U.js.map +1 -0
- package/dist/chunk-TDMHA7C3.js +251 -0
- package/dist/chunk-TDMHA7C3.js.map +1 -0
- package/dist/chunk-TGLUDQP2.js +138 -0
- package/dist/chunk-TGLUDQP2.js.map +1 -0
- package/dist/chunk-U6QYTMDB.js +85 -0
- package/dist/chunk-U6QYTMDB.js.map +1 -0
- package/dist/chunk-VFYO3UGZ.js +45 -0
- package/dist/chunk-VFYO3UGZ.js.map +1 -0
- package/dist/chunk-W4IK7AWD.js +73 -0
- package/dist/chunk-W4IK7AWD.js.map +1 -0
- package/dist/chunk-WVS6CUCT.js +48 -0
- package/dist/chunk-WVS6CUCT.js.map +1 -0
- package/dist/chunk-Y6KT7ZTR.js +109 -0
- package/dist/chunk-Y6KT7ZTR.js.map +1 -0
- package/dist/chunk-YBWQR2OK.js +526 -0
- package/dist/chunk-YBWQR2OK.js.map +1 -0
- package/dist/chunk-YMUIQRWK.js +44 -0
- package/dist/chunk-YMUIQRWK.js.map +1 -0
- package/dist/chunk-YQ2AC4E2.js +59 -0
- package/dist/chunk-YQ2AC4E2.js.map +1 -0
- package/dist/chunk-ZKKO4MHC.js +25 -0
- package/dist/chunk-ZKKO4MHC.js.map +1 -0
- package/dist/chunk-ZNROKDSZ.js +54 -0
- package/dist/chunk-ZNROKDSZ.js.map +1 -0
- package/dist/chunk-ZYU5WRC3.js +230 -0
- package/dist/chunk-ZYU5WRC3.js.map +1 -0
- package/dist/collapsible-panel.d.ts +96 -17
- package/dist/collapsible-panel.js +133 -14
- package/dist/collapsible-panel.js.map +1 -1
- package/dist/column-track.d.ts +41 -0
- package/dist/column-track.js +4 -0
- package/dist/column-track.js.map +1 -0
- package/dist/combobox.d.ts +40 -0
- package/dist/combobox.js +3 -0
- package/dist/combobox.js.map +1 -0
- package/dist/composer.d.ts +62 -0
- package/dist/composer.js +5 -0
- package/dist/composer.js.map +1 -0
- package/dist/content-item.d.ts +116 -0
- package/dist/content-item.js +4 -0
- package/dist/content-item.js.map +1 -0
- package/dist/content-swap.d.ts +34 -0
- package/dist/content-swap.js +3 -0
- package/dist/content-swap.js.map +1 -0
- package/dist/corner-status.d.ts +36 -0
- package/dist/corner-status.js +3 -0
- package/dist/corner-status.js.map +1 -0
- package/dist/css-values.d.ts +127 -0
- package/dist/css-values.js +3 -0
- package/dist/css-values.js.map +1 -0
- package/dist/device-class.js +34 -6
- package/dist/device-class.js.map +1 -1
- package/dist/disclosure-arrow.d.ts +6 -1
- package/dist/disclosure-arrow.js +2 -1
- package/dist/edge-activity-indicator.d.ts +28 -0
- package/dist/edge-activity-indicator.js +3 -0
- package/dist/edge-activity-indicator.js.map +1 -0
- package/dist/empty-state.d.ts +5 -2
- package/dist/empty-state.js +2 -1
- package/dist/extension-attributes-mu8MFUl0.d.ts +13 -0
- package/dist/file-picker.d.ts +47 -0
- package/dist/file-picker.js +3 -0
- package/dist/file-picker.js.map +1 -0
- package/dist/flex-alignment-4ms8ZbV8.d.ts +5 -0
- package/dist/floating-toolbar.d.ts +51 -0
- package/dist/floating-toolbar.js +3 -0
- package/dist/floating-toolbar.js.map +1 -0
- package/dist/framed-list.d.ts +47 -0
- package/dist/framed-list.js +4 -0
- package/dist/framed-list.js.map +1 -0
- package/dist/grid.d.ts +58 -0
- package/dist/grid.js +6 -0
- package/dist/grid.js.map +1 -0
- package/dist/help-tags-register.d.ts +13 -0
- package/dist/help-tags-register.js +9 -0
- package/dist/help-tags-register.js.map +1 -0
- package/dist/icon-button.d.ts +46 -0
- package/dist/icon-button.js +6 -0
- package/dist/icon-button.js.map +1 -0
- package/dist/icon-toggle-button.d.ts +42 -0
- package/dist/icon-toggle-button.js +6 -0
- package/dist/icon-toggle-button.js.map +1 -0
- package/dist/index.d.ts +65 -17
- package/dist/index.js +71 -24
- package/dist/list-action-row.d.ts +69 -12
- package/dist/list-action-row.js +5 -1
- package/dist/list-header.d.ts +65 -10
- package/dist/list-header.js +4 -1
- package/dist/list-icon-tile-B8dr97OW.d.ts +18 -0
- package/dist/list-inset-control.d.ts +22 -0
- package/dist/list-inset-control.js +3 -0
- package/dist/list-inset-control.js.map +1 -0
- package/dist/list-inset-text.d.ts +30 -0
- package/dist/list-inset-text.js +4 -0
- package/dist/list-inset-text.js.map +1 -0
- package/dist/list-item.d.ts +49 -5
- package/dist/list-item.js +4 -1
- package/dist/list.d.ts +128 -0
- package/dist/list.js +7 -0
- package/dist/list.js.map +1 -0
- package/dist/loading-spinner.d.ts +12 -2
- package/dist/loading-spinner.js +2 -1
- package/dist/lucide-icon.d.ts +15 -3
- package/dist/lucide-icon.js +2 -1
- package/dist/nav-stack-CU_0dKtI.d.ts +194 -0
- package/dist/nav-stack.d.ts +15 -38
- package/dist/nav-stack.js +14 -1
- package/dist/pane.d.ts +225 -0
- package/dist/pane.js +6 -0
- package/dist/pane.js.map +1 -0
- package/dist/panel-toolbar-vg6Ir5Dy.d.ts +77 -0
- package/dist/popup-menu-register.d.ts +10 -0
- package/dist/popup-menu-register.js +336 -0
- package/dist/popup-menu-register.js.map +1 -0
- package/dist/popup-menu.d.ts +125 -0
- package/dist/popup-menu.js +3 -0
- package/dist/popup-menu.js.map +1 -0
- package/dist/resizable-region.d.ts +32 -3
- package/dist/resizable-region.js +1 -1
- package/dist/row.d.ts +53 -0
- package/dist/row.js +7 -0
- package/dist/row.js.map +1 -0
- package/dist/scroll-clearance-BikHljTc.d.ts +14 -0
- package/dist/segmented-control.d.ts +12 -9
- package/dist/segmented-control.js +1 -2
- package/dist/select-register.js +255 -3
- package/dist/select-register.js.map +1 -1
- package/dist/select.d.ts +103 -8
- package/dist/select.js +4 -1
- package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
- package/dist/semantic-tone-CdW013KF.d.ts +4 -0
- package/dist/sides-BPSWde0A.d.ts +4 -0
- package/dist/skeleton.d.ts +14 -7
- package/dist/skeleton.js +2 -1
- package/dist/spacer.d.ts +18 -0
- package/dist/spacer.js +4 -0
- package/dist/spacer.js.map +1 -0
- package/dist/split-button.d.ts +57 -0
- package/dist/split-button.js +7 -0
- package/dist/split-button.js.map +1 -0
- package/dist/split-view.d.ts +63 -6
- package/dist/split-view.js +150 -10
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.d.ts +41 -7
- package/dist/state-banner.js +4 -1
- package/dist/styles/anchored-surface.css +41 -0
- package/dist/styles/app-tab.css +232 -0
- package/dist/styles/backdrop-image.css +241 -0
- package/dist/styles/badge.css +145 -0
- package/dist/styles/catalog/components/catalog-example-stack.css +14 -0
- package/dist/styles/catalog/components/catalog-example.css +204 -0
- package/dist/styles/catalog/components/catalog-stage.css +61 -0
- package/dist/styles/catalog/components/catalog.css +26 -0
- package/dist/styles/catalog.css +5 -498
- package/dist/styles/chip.css +208 -0
- package/dist/styles/choice-chips.css +154 -0
- package/dist/styles/collapsible-panel.css +450 -16
- package/dist/styles/column-track.css +57 -0
- package/dist/styles/combobox.css +113 -0
- package/dist/styles/components/actions/floating-toolbar/floating-toolbar.css +119 -0
- package/dist/styles/components/actions/icon-button/icon-button.css +22 -0
- package/dist/styles/components/actions/icon-toggle-button/icon-toggle-button.css +83 -0
- package/dist/styles/components/actions/popup-menu/popup-menu.css +370 -0
- package/dist/styles/components/actions/split-button/split-button.css +95 -0
- package/dist/styles/components/actions/toolbar/toolbar.css +452 -0
- package/dist/styles/components/actions/toolbar-control-group/toolbar-control-group.css +1197 -0
- package/dist/styles/components/actions/toolbar-text/toolbar-text.css +269 -0
- package/dist/styles/components/collections/framed-list/framed-list.css +47 -0
- package/dist/styles/components/collections/list/list.css +250 -0
- package/dist/styles/components/collections/list-action-row/list-action-row.css +466 -0
- package/dist/styles/components/collections/list-header/list-header.css +229 -0
- package/dist/styles/components/collections/list-inset-control/list-inset-control.css +30 -0
- package/dist/styles/components/collections/list-inset-text/list-inset-text.css +29 -0
- package/dist/styles/components/collections/list-item/list-item.css +461 -0
- package/dist/styles/components/collections/summary-row/summary-row.css +161 -0
- package/dist/styles/components/data-display/value-table/value-table.css +183 -0
- package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +41 -0
- package/dist/styles/components/feedback/badge/badge.css +145 -0
- package/dist/styles/components/feedback/chip/chip.css +208 -0
- package/dist/styles/components/feedback/corner-status/corner-status.css +60 -0
- package/dist/styles/components/feedback/edge-activity-indicator/edge-activity-indicator.css +127 -0
- package/dist/styles/components/feedback/empty-state/empty-state.css +47 -0
- package/dist/styles/components/feedback/loading-spinner/loading-spinner.css +25 -0
- package/dist/styles/components/feedback/skeleton/skeleton.css +92 -0
- package/dist/styles/components/feedback/state-banner/state-banner.css +297 -0
- package/dist/styles/components/forms/choice-chips/choice-chips.css +154 -0
- package/dist/styles/components/forms/combobox/combobox.css +113 -0
- package/dist/styles/components/forms/composer/composer.css +100 -0
- package/dist/styles/components/forms/file-picker/file-picker.css +81 -0
- package/dist/styles/components/forms/segmented-control/segmented-control.css +196 -0
- package/dist/styles/components/forms/select/select.css +455 -0
- package/dist/styles/components/forms/token-search-field/token-search-field.css +432 -0
- package/dist/styles/components/layout/collapsible-panel/collapsible-panel.css +580 -0
- package/dist/styles/components/layout/column-track/column-track.css +57 -0
- package/dist/styles/components/layout/content-swap/content-swap.css +154 -0
- package/dist/styles/components/layout/grid/grid.css +88 -0
- package/dist/styles/components/layout/pane/pane.css +502 -0
- package/dist/styles/components/layout/resizable-region/resizable-region.css +545 -0
- package/dist/styles/components/layout/row/row.css +114 -0
- package/dist/styles/components/layout/spacer/spacer.css +11 -0
- package/dist/styles/components/layout/split-view/split-view.css +146 -0
- package/dist/styles/components/layout/workbench/workbench.css +1086 -0
- package/dist/styles/components/media/lucide-icon/lucide-icon.css +15 -0
- package/dist/styles/components/navigation/app-tab/app-tab.css +384 -0
- package/dist/styles/components/navigation/disclosure-arrow/disclosure-arrow.css +23 -0
- package/dist/styles/components/navigation/nav-stack/nav-stack.css +543 -0
- package/dist/styles/components/navigation/tab-bar/tab-bar.css +197 -0
- package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +443 -0
- package/dist/styles/components/surfaces/backdrop-image/backdrop-image.css +241 -0
- package/dist/styles/components/surfaces/content-item/content-item.css +138 -0
- package/dist/styles/components/surfaces/sunken-panel/sunken-panel.css +31 -0
- package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +589 -0
- package/dist/styles/components/typography/text/text.css +220 -0
- package/dist/styles/composer.css +100 -0
- package/dist/styles/content-item.css +138 -0
- package/dist/styles/content-swap.css +154 -0
- package/dist/styles/corner-status.css +60 -0
- package/dist/styles/document.css +98 -0
- package/dist/styles/edge-activity-indicator.css +127 -0
- package/dist/styles/file-picker.css +81 -0
- package/dist/styles/floating-toolbar.css +119 -0
- package/dist/styles/foundation.css +419 -46
- package/dist/styles/framed-list.css +47 -0
- package/dist/styles/grid.css +88 -0
- package/dist/styles/icon-button.css +22 -0
- package/dist/styles/icon-toggle-button.css +83 -0
- package/dist/styles/layout.css +3 -41
- package/dist/styles/list-action-row.css +324 -18
- package/dist/styles/list-header.css +128 -59
- package/dist/styles/list-inset-control.css +30 -0
- package/dist/styles/list-inset-text.css +29 -0
- package/dist/styles/list-item.css +371 -33
- package/dist/styles/list.css +250 -0
- package/dist/styles/loading-spinner.css +4 -0
- package/dist/styles/lucide-icon.css +9 -0
- package/dist/styles/nav-stack.css +479 -47
- package/dist/styles/overlay-sheet.css +211 -0
- package/dist/styles/pane.css +502 -0
- package/dist/styles/popup-menu.css +370 -0
- package/dist/styles/resizable-region.css +438 -4
- package/dist/styles/row.css +114 -0
- package/dist/styles/segmented-control.css +36 -9
- package/dist/styles/select.css +338 -21
- package/dist/styles/skeleton.css +64 -3
- package/dist/styles/spacer.css +11 -0
- package/dist/styles/split-button.css +95 -0
- package/dist/styles/split-view.css +114 -3
- package/dist/styles/state-banner.css +146 -22
- package/dist/styles/styles.css +24 -1
- package/dist/styles/summary-row.css +161 -0
- package/dist/styles/sunken-panel.css +31 -0
- package/dist/styles/surface-scaffold.css +589 -0
- package/dist/styles/tab-bar.css +161 -9
- package/dist/styles/tab-navigator.css +443 -0
- package/dist/styles/text.css +220 -0
- package/dist/styles/token-search-field.css +202 -59
- package/dist/styles/toolbar-control-group.css +1022 -36
- package/dist/styles/toolbar-text.css +236 -7
- package/dist/styles/toolbar.css +402 -11
- package/dist/styles/value-table.css +115 -7
- package/dist/styles/webawesome.css +420 -17
- package/dist/styles/workbench.css +999 -7
- package/dist/summary-row.d.ts +56 -0
- package/dist/summary-row.js +4 -0
- package/dist/summary-row.js.map +1 -0
- package/dist/sunken-panel.d.ts +38 -0
- package/dist/sunken-panel.js +4 -0
- package/dist/sunken-panel.js.map +1 -0
- package/dist/surface-scaffold-register.d.ts +10 -0
- package/dist/surface-scaffold-register.js +254 -0
- package/dist/surface-scaffold-register.js.map +1 -0
- package/dist/surface-scaffold.d.ts +203 -0
- package/dist/surface-scaffold.js +4 -0
- package/dist/surface-scaffold.js.map +1 -0
- package/dist/tab-bar.d.ts +33 -7
- package/dist/tab-bar.js +1 -1
- package/dist/tab-navigator.d.ts +154 -0
- package/dist/tab-navigator.js +131 -0
- package/dist/tab-navigator.js.map +1 -0
- package/dist/text.d.ts +98 -0
- package/dist/text.js +3 -0
- package/dist/text.js.map +1 -0
- package/dist/token-search-field.d.ts +3 -43
- package/dist/token-search-field.js +2 -1
- package/dist/token-search-model-aYR0YAei.d.ts +184 -0
- package/dist/token-search-model.d.ts +3 -0
- package/dist/token-search-model.js +3 -0
- package/dist/token-search-model.js.map +1 -0
- package/dist/toolbar-control-group.d.ts +107 -7
- package/dist/toolbar-control-group.js +3 -1
- package/dist/toolbar-text.d.ts +63 -9
- package/dist/toolbar-text.js +2 -1
- package/dist/toolbar.d.ts +72 -7
- package/dist/toolbar.js +1 -1
- package/dist/types-CVgIwSdq.d.ts +189 -0
- package/dist/value-table.d.ts +30 -4
- package/dist/value-table.js +2 -1
- package/dist/webawesome.d.ts +8 -1
- package/dist/wire-catalog.d.ts +111 -5
- package/dist/wire-catalog.js +302 -14
- package/dist/wire-catalog.js.map +1 -1
- package/dist/wire-comboboxes.d.ts +26 -0
- package/dist/wire-comboboxes.js +3 -0
- package/dist/wire-comboboxes.js.map +1 -0
- 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-content-items.d.ts +11 -0
- package/dist/wire-content-items.js +139 -0
- package/dist/wire-content-items.js.map +1 -0
- package/dist/wire-corner-status.d.ts +11 -0
- package/dist/wire-corner-status.js +3 -0
- package/dist/wire-corner-status.js.map +1 -0
- package/dist/wire-file-pickers.d.ts +26 -0
- package/dist/wire-file-pickers.js +3 -0
- package/dist/wire-file-pickers.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-list-reorder.d.ts +41 -0
- package/dist/wire-list-reorder.js +3 -0
- package/dist/wire-list-reorder.js.map +1 -0
- package/dist/wire-nav-stack.d.ts +4 -1
- package/dist/wire-nav-stack.js +352 -15
- package/dist/wire-nav-stack.js.map +1 -1
- package/dist/wire-overlay-sheets.d.ts +20 -0
- package/dist/wire-overlay-sheets.js +4 -0
- package/dist/wire-overlay-sheets.js.map +1 -0
- package/dist/wire-resizable-regions.d.ts +2 -1
- package/dist/wire-resizable-regions.js +2 -1
- package/dist/wire-scroll-dividers.d.ts +87 -0
- package/dist/wire-scroll-dividers.js +3 -0
- package/dist/wire-scroll-dividers.js.map +1 -0
- package/dist/wire-sidebar.d.ts +24 -4
- package/dist/wire-sidebar.js +159 -45
- package/dist/wire-sidebar.js.map +1 -1
- package/dist/wire-tab-bars.d.ts +6 -2
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-tab-navigator.d.ts +13 -0
- package/dist/wire-tab-navigator.js +153 -0
- package/dist/wire-tab-navigator.js.map +1 -0
- package/dist/wire-token-search-fields.d.ts +28 -10
- package/dist/wire-token-search-fields.js +2 -1
- package/dist/wire-toolbar-visibility.d.ts +13 -0
- package/dist/wire-toolbar-visibility.js +3 -0
- package/dist/wire-toolbar-visibility.js.map +1 -0
- package/dist/wire-workbench.d.ts +116 -0
- package/dist/wire-workbench.js +6 -0
- package/dist/wire-workbench.js.map +1 -0
- package/dist/workbench.d.ts +253 -10
- package/dist/workbench.js +21 -15
- package/dist/workbench.js.map +1 -1
- package/docs/accessibility.md +675 -33
- package/docs/anchored-surface.md +101 -0
- package/docs/app-layouts.md +172 -10
- package/docs/apple-hig.md +2 -0
- package/docs/backdrop-image.md +128 -0
- package/docs/catalog.md +565 -63
- package/docs/collapsible-panel.md +308 -25
- package/docs/component-contract.md +818 -53
- package/docs/component-integration.md +75 -0
- package/docs/component-selection.md +971 -99
- package/docs/composer.md +143 -0
- package/docs/design/templates/choice-chips/color-palette-dark.svg +1 -0
- package/docs/design/templates/choice-chips/color-palette.svg +1 -0
- package/docs/design/templates/choice-chips/compact-icons-dark.svg +1 -0
- package/docs/design/templates/choice-chips/compact-icons.svg +1 -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 -0
- package/docs/design/templates/choice-chips.svg +1 -0
- 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 -0
- package/docs/design/templates/empty-state/filtered.svg +1 -0
- package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
- package/docs/design/templates/empty-state/with-action.svg +1 -0
- package/docs/design/templates/empty-state-dark.svg +1 -0
- package/docs/design/templates/empty-state.svg +1 -0
- package/docs/design/templates/list/compact-dark.svg +1 -0
- package/docs/design/templates/list/compact.svg +1 -0
- package/docs/design/templates/list/stack-dark.svg +1 -0
- package/docs/design/templates/list/stack.svg +1 -0
- package/docs/design/templates/list-action-row/default-dark.svg +1 -0
- package/docs/design/templates/list-action-row/default.svg +1 -0
- package/docs/design/templates/list-action-row/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 -0
- package/docs/design/templates/list-action-row/selected.svg +1 -0
- package/docs/design/templates/list-action-row/trailing-menu-dark.svg +1 -0
- package/docs/design/templates/list-action-row/trailing-menu.svg +1 -0
- package/docs/design/templates/list-action-row-dark.svg +1 -0
- package/docs/design/templates/list-action-row.svg +1 -0
- package/docs/design/templates/list-dark.svg +1 -0
- package/docs/design/templates/list-header/action-dark.svg +1 -0
- package/docs/design/templates/list-header/action.svg +1 -0
- package/docs/design/templates/list-header/count-dark.svg +1 -0
- package/docs/design/templates/list-header/count.svg +1 -0
- package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
- package/docs/design/templates/list-header/inline-action.svg +1 -0
- package/docs/design/templates/list-header/size-l-dark.svg +1 -0
- package/docs/design/templates/list-header/size-l.svg +1 -0
- package/docs/design/templates/list-header/size-m-dark.svg +1 -0
- package/docs/design/templates/list-header/size-m.svg +1 -0
- package/docs/design/templates/list-header/size-xl-dark.svg +1 -0
- package/docs/design/templates/list-header/size-xl.svg +1 -0
- package/docs/design/templates/list-header/size-xs-dark.svg +1 -0
- package/docs/design/templates/list-header/size-xs.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
- package/docs/design/templates/list-header-dark.svg +1 -0
- package/docs/design/templates/list-header.svg +1 -0
- package/docs/design/templates/list-item/default-dark.svg +1 -0
- package/docs/design/templates/list-item/default.svg +1 -0
- package/docs/design/templates/list-item/icon-tile-dark.svg +1 -0
- package/docs/design/templates/list-item/icon-tile.svg +1 -0
- package/docs/design/templates/list-item/multiline-dark.svg +1 -0
- package/docs/design/templates/list-item/multiline.svg +1 -0
- package/docs/design/templates/list-item/selected-dark.svg +1 -0
- package/docs/design/templates/list-item/selected.svg +1 -0
- package/docs/design/templates/list-item/trailing-dark.svg +1 -0
- package/docs/design/templates/list-item/trailing.svg +1 -0
- package/docs/design/templates/list-item-dark.svg +1 -0
- package/docs/design/templates/list-item.svg +1 -0
- package/docs/design/templates/list.svg +1 -0
- package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
- package/docs/design/templates/segmented-control/equal.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small.svg +1 -0
- package/docs/design/templates/segmented-control-dark.svg +1 -0
- package/docs/design/templates/segmented-control.svg +1 -0
- package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
- package/docs/design/templates/skeleton/avatar.svg +1 -0
- package/docs/design/templates/skeleton/block-dark.svg +1 -0
- package/docs/design/templates/skeleton/block.svg +1 -0
- package/docs/design/templates/skeleton/lines-dark.svg +1 -0
- package/docs/design/templates/skeleton/lines.svg +1 -0
- package/docs/design/templates/skeleton-dark.svg +1 -0
- package/docs/design/templates/skeleton.svg +1 -0
- package/docs/design/templates/state-banner/danger-dark.svg +1 -0
- package/docs/design/templates/state-banner/danger.svg +1 -0
- package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
- package/docs/design/templates/state-banner/info-badge.svg +1 -0
- package/docs/design/templates/state-banner/info-dark.svg +1 -0
- package/docs/design/templates/state-banner/info.svg +1 -0
- package/docs/design/templates/state-banner/strip-dark.svg +1 -0
- package/docs/design/templates/state-banner/strip.svg +1 -0
- package/docs/design/templates/state-banner/success-dark.svg +1 -0
- package/docs/design/templates/state-banner/success.svg +1 -0
- package/docs/design/templates/state-banner/warning-dark.svg +1 -0
- package/docs/design/templates/state-banner/warning.svg +1 -0
- package/docs/design/templates/state-banner-dark.svg +1 -0
- package/docs/design/templates/state-banner.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
- package/docs/design/templates/tab-bar-dark.svg +1 -0
- package/docs/design/templates/tab-bar.svg +1 -0
- package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
- package/docs/design/templates/token-search-field/empty.svg +1 -0
- package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
- package/docs/design/templates/token-search-field/tokens.svg +1 -0
- package/docs/design/templates/token-search-field-dark.svg +1 -0
- package/docs/design/templates/token-search-field.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
- package/docs/design/templates/toolbar/width-visibility-320-dark.svg +1 -0
- package/docs/design/templates/toolbar/width-visibility-320.svg +1 -0
- package/docs/design/templates/toolbar/width-visibility-600-dark.svg +1 -0
- package/docs/design/templates/toolbar/width-visibility-600.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -1
- package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -1
- package/docs/design/templates/toolbar-control-group/glass-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/glass.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -1
- package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -1
- package/docs/design/templates/toolbar-control-group/icon-tile-brand-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-tile-brand.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-tile-danger-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-tile-danger.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-tile-neutral-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-tile-neutral.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-tile-success-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-tile-success.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-tile-warning-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-tile-warning.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -1
- package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -1
- package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -1
- package/docs/design/templates/toolbar-control-group/with-text.svg +1 -1
- package/docs/design/templates/toolbar-control-group-dark.svg +1 -11
- package/docs/design/templates/toolbar-control-group.svg +1 -11
- package/docs/design/templates/toolbar-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/dark-tone-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/dark-tone.svg +1 -0
- package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/default.svg +1 -0
- package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/large.svg +1 -0
- package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/small.svg +1 -0
- package/docs/design/templates/toolbar-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-text.svg +1 -0
- package/docs/design/templates/toolbar.svg +1 -0
- package/docs/design/templates/value-table/details-dark.svg +1 -0
- package/docs/design/templates/value-table/details.svg +1 -0
- package/docs/design/templates/value-table-dark.svg +1 -0
- package/docs/design/templates/value-table.svg +1 -0
- package/docs/design/templates.md +83 -18
- package/docs/design-philosophy.md +65 -20
- package/docs/device-class.md +10 -9
- package/docs/document-baseline.md +93 -0
- package/docs/examples/application-ui-profile.json +60 -0
- package/docs/examples/command-palette-adapter.tsx +69 -17
- package/docs/examples/component-catalog-extension.json +52 -0
- package/docs/examples/component-composition-extension.json +80 -0
- package/docs/layout.md +926 -53
- package/docs/manual-test-plan.md +29 -0
- package/docs/nav-stack.md +323 -14
- package/docs/recipes.md +153 -73
- package/docs/split-view.md +96 -7
- package/docs/sunken-panel.md +106 -0
- package/docs/surface-scaffold.md +416 -0
- package/docs/tab-navigator.md +291 -0
- package/docs/type-contracts.md +137 -0
- package/docs/ui-analyzer.md +406 -0
- package/docs/ui-doctor.md +281 -0
- package/docs/ui-evaluator.md +139 -0
- package/docs/ux-demo.md +58 -17
- package/docs/webawesome-theme.md +215 -16
- package/docs/workbench.md +627 -12
- package/docs/writing.md +152 -0
- package/doctor/cli.mjs +120 -0
- package/doctor/component-style-checks.mjs +457 -0
- package/doctor/config.schema.json +94 -0
- package/doctor/copy-capitalization-checks.mjs +591 -0
- package/doctor/eslint-diagnostics.d.mts +24 -0
- package/doctor/eslint-diagnostics.mjs +24 -0
- package/doctor/index.d.mts +122 -0
- package/doctor/index.mjs +1424 -0
- package/doctor/report.schema.json +118 -0
- package/evaluator/cli.mjs +89 -0
- package/evaluator/index.d.mts +90 -0
- package/evaluator/index.mjs +1203 -0
- package/evaluator/report.schema.json +152 -0
- package/llms.txt +808 -50
- package/package.json +357 -27
- package/scripts/component-catalog-extension-validation.mjs +28 -0
- package/traversal-exclusions.mjs +86 -0
- package/ux-demo/recipes/app-shell.tsx +204 -16
- package/ux-demo/recipes/collapsible-sidebar.tsx +206 -45
- package/ux-demo/recipes/compact-toolbar.tsx +135 -14
- package/ux-demo/recipes/composer-form.tsx +133 -21
- package/ux-demo/recipes/list-detail-dialog.tsx +255 -14
- package/ux-demo/recipes/list-workspace-states.tsx +183 -11
- package/ux-demo/recipes/loaders.ts +11 -3
- package/ux-demo/recipes/loading-inspector.tsx +162 -39
- package/ux-demo/recipes/media-detail.tsx +272 -0
- package/ux-demo/recipes/mount-recipe.ts +26 -10
- package/ux-demo/recipes/navigation-sidebar.tsx +137 -11
- package/ux-demo/recipes/navigation-stack.tsx +79 -24
- package/ux-demo/recipes/types.ts +17 -2
- package/ux-demo/recipes/workspace-header.tsx +86 -5
- package/vite-ux-review.d.mts +83 -0
- package/vite-ux-review.mjs +120 -0
- package/dist/browser/panel-header.js +0 -7
- package/dist/chunk-2Z5TWHKQ.js +0 -34
- package/dist/chunk-2Z5TWHKQ.js.map +0 -1
- package/dist/chunk-37T3VNIZ.js +0 -19
- package/dist/chunk-37T3VNIZ.js.map +0 -1
- package/dist/chunk-55DATV3A.js +0 -10
- package/dist/chunk-55DATV3A.js.map +0 -1
- package/dist/chunk-56RJXPHG.js.map +0 -1
- package/dist/chunk-7BR3DO2A.js +0 -31
- package/dist/chunk-7BR3DO2A.js.map +0 -1
- package/dist/chunk-7JB7VPRI.js +0 -34
- package/dist/chunk-7JB7VPRI.js.map +0 -1
- package/dist/chunk-7LDCTU7D.js +0 -134
- package/dist/chunk-7LDCTU7D.js.map +0 -1
- package/dist/chunk-AQNKJRFH.js +0 -79
- package/dist/chunk-AQNKJRFH.js.map +0 -1
- package/dist/chunk-AVZHJN6C.js +0 -14
- package/dist/chunk-AVZHJN6C.js.map +0 -1
- package/dist/chunk-B24C4A6T.js +0 -22
- package/dist/chunk-B24C4A6T.js.map +0 -1
- package/dist/chunk-BLK3UEEI.js +0 -23
- package/dist/chunk-BLK3UEEI.js.map +0 -1
- package/dist/chunk-CVZYNJJ5.js +0 -265
- package/dist/chunk-CVZYNJJ5.js.map +0 -1
- package/dist/chunk-DAFFDO33.js +0 -14
- package/dist/chunk-DAFFDO33.js.map +0 -1
- package/dist/chunk-I7INRJO4.js +0 -17
- package/dist/chunk-I7INRJO4.js.map +0 -1
- package/dist/chunk-L4OKFDJY.js +0 -24
- package/dist/chunk-L4OKFDJY.js.map +0 -1
- package/dist/chunk-LC3YSAZC.js +0 -39
- package/dist/chunk-LC3YSAZC.js.map +0 -1
- package/dist/chunk-M5JRI6MN.js +0 -27
- package/dist/chunk-M5JRI6MN.js.map +0 -1
- package/dist/chunk-MFKMHK5W.js +0 -10
- package/dist/chunk-MFKMHK5W.js.map +0 -1
- package/dist/chunk-RUNWEPR4.js +0 -10
- package/dist/chunk-RUNWEPR4.js.map +0 -1
- package/dist/chunk-W2FK7ZNY.js +0 -26
- package/dist/chunk-W2FK7ZNY.js.map +0 -1
- package/dist/chunk-W5EQ3HYQ.js +0 -42
- package/dist/chunk-W5EQ3HYQ.js.map +0 -1
- package/dist/chunk-WVPYVDXY.js +0 -15
- package/dist/chunk-WVPYVDXY.js.map +0 -1
- package/dist/chunk-XXMOARTH.js +0 -229
- package/dist/chunk-XXMOARTH.js.map +0 -1
- package/dist/chunk-Y5MRDTLC.js +0 -20
- package/dist/chunk-Y5MRDTLC.js.map +0 -1
- package/dist/chunk-YGN7OMLT.js.map +0 -1
- package/dist/panel-header.d.ts +0 -36
- package/dist/panel-header.js +0 -7
- package/dist/panel-header.js.map +0 -1
- package/dist/styles/panel-header.css +0 -64
- package/dist/styles/tab-scaffold.css +0 -84
- package/dist/tab-scaffold.d.ts +0 -30
- package/dist/tab-scaffold.js +0 -16
- package/dist/tab-scaffold.js.map +0 -1
- package/dist/wire-tab-scaffold.d.ts +0 -11
- package/dist/wire-tab-scaffold.js +0 -16
- package/dist/wire-tab-scaffold.js.map +0 -1
- package/docs/design/templates/panel-header/icon-actions-dark.svg +0 -1
- package/docs/design/templates/panel-header/icon-actions.svg +0 -1
- package/docs/design/templates/panel-header/icon-summary-actions-dark.svg +0 -1
- package/docs/design/templates/panel-header/icon-summary-actions.svg +0 -1
- package/docs/design/templates/panel-header/no-icon-dark.svg +0 -1
- package/docs/design/templates/panel-header/no-icon.svg +0 -1
- package/docs/design/templates/panel-header/page-heading-dark.svg +0 -1
- package/docs/design/templates/panel-header/page-heading.svg +0 -1
- package/docs/design/templates/panel-header/title-only-dark.svg +0 -1
- package/docs/design/templates/panel-header/title-only.svg +0 -1
- package/docs/design/templates/panel-header-dark.svg +0 -13
- package/docs/design/templates/panel-header.svg +0 -13
- package/docs/tab-scaffold.md +0 -41
- package/ux-demo/recipes/recipes.css +0 -448
package/docs/accessibility.md
CHANGED
|
@@ -5,15 +5,251 @@ These are normative contracts for package components and consuming applications.
|
|
|
5
5
|
## Shared rules
|
|
6
6
|
|
|
7
7
|
- All controls have accessible names and visible `:focus-visible` treatment.
|
|
8
|
-
- Decorative `LucideIcon` output is `aria-hidden`; pass `label` only when the SVG itself conveys meaning.
|
|
9
|
-
- `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
|
|
10
|
-
- `
|
|
11
|
-
-
|
|
8
|
+
- Decorative `LucideIcon` output is `aria-hidden`; pass `label` only when the SVG itself conveys meaning. `size="xs|s|m|l|xl"` selects the 12/16/20/24/32px root-scaled icon steps, and a positive numeric `size` converts pixels to rem; omitting `size` keeps 1em. Size changes the visual only, never the owning control's hit target or accessible name.
|
|
9
|
+
- `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Selected rows keep the normal foreground over their brand-tinted fill so text retains WCAG AA contrast in light and dark themes. Its leading icon visual is 18px while the row retains its 44px minimum target; multiline icons align to the first text line. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
|
|
10
|
+
- `ListItemLink` shares that row content and geometry but uses native anchor Enter behavior and requires `href`. It has no `action` or `pressed` state. `external` opens a new tab with `rel="noopener noreferrer"`; the default accessible name announces the new tab for a string label, so an explicit `accessibleLabel` should do the same. Disabled and loading links omit `href`, expose `aria-disabled`, and leave the tab order. Its trailing content remains dormant.
|
|
11
|
+
- `ListHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Optional `badge` and `status` content can appear together and should each carry meaningful text. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
|
|
12
|
+
- Dispose `wireTabBars` and `wireTokenSearchFields` with their owning view. Disposal cancels queued controlled-render focus restoration, so a torn-down view cannot reclaim focus from its successor.
|
|
13
|
+
- `wireNavStack` moves focus into every newly active top view and remembers the last focused descendant of each mounted view for pop restoration. Mark a preferred initial heading or control with `data-nav-focus` (and `tabindex="-1"` when it is not normally tabbable); otherwise the helper uses the first enabled focusable descendant, then a programmatically focusable view-container fallback.
|
|
14
|
+
- In any `.kui-pane`, menu rows, `ListHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `ListHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-list-header-action-icon-size` inside a fitted 36px hover square; a transparent hit layer extends the pointer target to at least 44 × 44 around that square without moving layout or enlarging the painted surface. Compact headers are 36px tall like the rest of the compact List family, so their action target is 44px wide and clamped to the 36px header height instead of reaching into a neighboring row. Do not reduce the target to the visible icon, and do not force the button itself to 44px.
|
|
12
15
|
- `LoadingSpinner` is either labeled (`role="img"`) or hidden. Its rotation stops for reduced motion.
|
|
13
|
-
- `
|
|
16
|
+
- A busy `ToolbarControlGroup` makes its controls inert and exposes `aria-busy`; its `busyLabel` is announced by an adjacent status node outside the inert group. Clear `busy` when the operation finishes so the controls rejoin the focus order.
|
|
17
|
+
- A bottom `Toolbar` uses `position="footer"` for footer semantics; top toolbars keep the default `header`. Workbench's main bottom toolbar sets this automatically.
|
|
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
|
+
- 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
|
+
- `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. 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.
|
|
14
22
|
- `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
|
|
15
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.
|
|
16
|
-
- `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
|
|
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.
|
|
25
|
+
- `List` is layout-only by default and adds no `list` role. With `selectionMode="multiple"` and a required `ariaLabel`, it becomes a multi-select grid for `ContentItem` rows. Give a scrollable List a bounded block size, avoid nested scroll owners, and keep focused children visible while scrolling.
|
|
26
|
+
- `Grid` is layout-only by default. With `selectionMode="multiple"` and a required `ariaLabel`, it becomes a multi-select grid for `ContentItem` tiles; each tile remains a row with a gridcell, including when the CSS grid wraps.
|
|
27
|
+
|
|
28
|
+
In a multi-select ContentItem grid, `wireContentItems` moves focus between
|
|
29
|
+
enabled rows on Up/Down and to the first/last enabled row on Home/End. Native
|
|
30
|
+
controls inside a row retain those keys. In a `Grid`, Left/Right moves between
|
|
31
|
+
tiles on the same rendered line and Up/Down moves to the nearest tile in the
|
|
32
|
+
adjacent rendered line; Home/End reaches the first/last enabled tile. The
|
|
33
|
+
navigation reads live geometry, so responsive column changes need no rewiring.
|
|
34
|
+
The application changes selection
|
|
35
|
+
on card activation and handles its own Shift/Cmd range gestures.
|
|
36
|
+
With delegated click actions, use `isContentItemActivation(event, card)` in the
|
|
37
|
+
card's selection handler so a nested menu trigger or other control does not
|
|
38
|
+
also select the card.
|
|
39
|
+
|
|
40
|
+
## Select
|
|
41
|
+
|
|
42
|
+
Supply a visible `label`, or `ariaLabel` when the surrounding interface already
|
|
43
|
+
provides visual context. A nonempty `label` takes precedence when both are
|
|
44
|
+
supplied. Kerf forwards that name through Web Awesome's internal label contract;
|
|
45
|
+
an `aria-label` on the custom-element host alone cannot name its shadow
|
|
46
|
+
combobox. With `ariaLabel` alone, Kerf visually hides the internal label without
|
|
47
|
+
adding height or spacing. This also applies to `renderSelected`: selected content
|
|
48
|
+
stays separate from the control's stable accessible name. Applications do not
|
|
49
|
+
need shadow-DOM patches or extra label styling.
|
|
50
|
+
|
|
51
|
+
Set `multiple` (with an array `value`) to let the person choose any number
|
|
52
|
+
of choices. The popup stays open while they toggle choices with the pointer,
|
|
53
|
+
Enter, or Space, and closes on an outside press, Escape, or focus leaving,
|
|
54
|
+
returning focus to the combobox. The listbox is `aria-multiselectable`, each
|
|
55
|
+
chosen option shows its check, and the closed control reads the chosen labels
|
|
56
|
+
in choice order as one short localized list (the `placeholderText` when none
|
|
57
|
+
are chosen) instead of a removable tag per choice. `change` and `input` report
|
|
58
|
+
the value array; in a form, every chosen value is submitted under `name`.
|
|
59
|
+
Disabled choices remain visible with `aria-disabled` and cannot be selected;
|
|
60
|
+
`disabledReason` adds a native tooltip. For a multiple Select, opt-in
|
|
61
|
+
`selectAllLabel` and `clearLabel` render named footer buttons. They keep the
|
|
62
|
+
popup open, select only enabled choices or clear the selection, and emit the
|
|
63
|
+
ordinary `input` and `change` events only when the value changes. Their labels
|
|
64
|
+
come from the application so they can be localized.
|
|
65
|
+
|
|
66
|
+
A toolbar filter menu (a "Filter by label" funnel) is a multiple Select with
|
|
67
|
+
`selectedPresentation="icon-only"` and a fixed `triggerIcon`; the types accept
|
|
68
|
+
`triggerIcon` only there, and require it there, because no single chosen
|
|
69
|
+
choice has an icon to show. The trigger keeps the icon-only pill geometry
|
|
70
|
+
below: the fixed icon, then, while any choice is chosen, a compact brand
|
|
71
|
+
`Badge` with the count 4px after it, then the caret. The badge is hidden from
|
|
72
|
+
assistive technology; instead the combobox's accessible name ends with the
|
|
73
|
+
chosen labels in choice order ("Filter by label: Bug, Docs", just "Filter by
|
|
74
|
+
label" when none are chosen), and its hidden value text stays empty so screen
|
|
75
|
+
readers do not announce the list twice. With a visible `label` the same summary
|
|
76
|
+
reads in the label, standing in for the hidden value.
|
|
77
|
+
|
|
78
|
+
Use `presentation="toolbar-borderless"` inside a `ToolbarControlGroup`, with
|
|
79
|
+
`size="compact"` in a compact group; set `focusRingOwner="group"` when that
|
|
80
|
+
parent paints the composed focus ring. `selectedPresentation="icon-only"` hides
|
|
81
|
+
only the visible selected label—the required `label` or `ariaLabel` still names
|
|
82
|
+
the combobox. An icon-only trigger keeps its disclosure caret: the selected icon
|
|
83
|
+
and caret form one pill with the same geometry as a `PopupMenu`
|
|
84
|
+
trigger (8px inline padding on both sides, 12px from the icon to the caret, a
|
|
85
|
+
40px default or 32px compact control height), so the caret still signals a
|
|
86
|
+
choice list. Inside a group the trigger is inset evenly on every side (2px in a
|
|
87
|
+
default group, 1px in a compact one) with a concentric radius; the group grows
|
|
88
|
+
to the trigger's width, so a single group is wider than it is tall and its
|
|
89
|
+
focus ring follows the pill while focused. While the listbox is open the
|
|
90
|
+
trigger drops its ring, as a `PopupMenu` trigger does: the open popup and its
|
|
91
|
+
current option show where focus is, and the ring returns when the listbox
|
|
92
|
+
closes with focus still on the control. A group that owns the ring
|
|
93
|
+
(`focusRing="outline"` or `"halo"`) drops it the same way while a Select
|
|
94
|
+
listbox or `PopupMenu` inside the group is open.
|
|
95
|
+
Beside other controls in one group, the icon-only trigger is one more segment:
|
|
96
|
+
it keeps the group's gap and its siblings' block inset, height, item radius,
|
|
97
|
+
and hover pill. In a group with per-control rings (`focusRing="control"`, the
|
|
98
|
+
default) it paints the same ring as its sibling buttons, even when
|
|
99
|
+
`focusRingOwner="group"` was set, because no group ring would show focus.
|
|
100
|
+
Navigation selects can use intrinsic `presentation="navigation"` plus
|
|
101
|
+
`labelMaxWidth` for component-owned ellipsis. These props own the control's
|
|
102
|
+
appearance; its parent continues to own outer placement.
|
|
103
|
+
|
|
104
|
+
Use `presentation="inline"` with a single-value `renderSelected` when the
|
|
105
|
+
selected content, such as a `Badge`, already provides the trigger chrome. The
|
|
106
|
+
closed combobox has no minimum height, inline padding, or border width, so the
|
|
107
|
+
rendered content defines its box. The Select still owns the focus ring,
|
|
108
|
+
accessible combobox name, and listbox interaction; keep `label` or `ariaLabel`
|
|
109
|
+
even when the selected content shows its own short text.
|
|
110
|
+
|
|
111
|
+
Use `hint` for persistent supporting text below the control; use
|
|
112
|
+
`placeholderText` only for the empty value shown inside the closed control.
|
|
113
|
+
Kerf passes hint text through Web Awesome's form-control contract, which renders
|
|
114
|
+
the hint part and connects the shadow combobox to it with `aria-describedby`.
|
|
115
|
+
The accessible control is that shadow combobox, not the `wa-select` wrapper;
|
|
116
|
+
inspect or test the element returned by the `combobox` role. Both the `hint`
|
|
117
|
+
attribute used by Kerf and Web Awesome's explicit `hint` slot produce a computed
|
|
118
|
+
accessible description in Chromium, Firefox, and WebKit, so applications do not
|
|
119
|
+
need an `aria-description` mirror or a shadow-DOM patch.
|
|
120
|
+
Loading placeholders keep the same visible hint while replacing the interactive
|
|
121
|
+
control with inert chrome.
|
|
122
|
+
|
|
123
|
+
`tests/unit/components.test.tsx` covers name projection and visible/hidden label
|
|
124
|
+
variants. `tests/browser/select-accessibility.spec.ts` verifies actual accessible
|
|
125
|
+
names, unchanged unlabeled geometry, keyboard selection, controlled rerenders,
|
|
126
|
+
native hint wiring, and wide/narrow presentation in Chromium, Firefox, and WebKit.
|
|
127
|
+
|
|
128
|
+
The explicit `@kerfjs/ui/select/register` import also installs the canonical
|
|
129
|
+
Select lifecycle adapter. The latest open/close request owns animation completion
|
|
130
|
+
and deferred option focus. A stale close cannot hide a reopened popup after
|
|
131
|
+
resize; stale completion events are not emitted. Preventing `wa-show` or
|
|
132
|
+
`wa-hide` retains the accepted closed/open state, and removal prevents deferred
|
|
133
|
+
activation. Native option selection, keyboard behavior, dismissal listeners,
|
|
134
|
+
and popup anchor placement remain with Web Awesome. Raw `wa-select` elements
|
|
135
|
+
without the canonical `data-component="select"` marker are unchanged.
|
|
136
|
+
`tests/unit/select-lifecycle.test.ts` and `animate-select-popup.test.ts` cover
|
|
137
|
+
repeated and out-of-order completions, cancellation, instance isolation, and
|
|
138
|
+
removal. `tests/browser/select-lifecycle.spec.ts` repeats animation/resize/reopen
|
|
139
|
+
sequences at wide and narrow widths in Chromium, Firefox, and WebKit.
|
|
140
|
+
|
|
141
|
+
### Help tags on icon-only triggers
|
|
142
|
+
|
|
143
|
+
An icon-only control has no visible title, so sighted pointer and keyboard users
|
|
144
|
+
would otherwise see only its icon (and a filter's count). Following the Apple
|
|
145
|
+
HIG convention for toolbar items, three kinds of icon-only control show a help
|
|
146
|
+
tag: a Web Awesome `wa-tooltip` in the theme's tooltip palette, below the
|
|
147
|
+
control.
|
|
148
|
+
|
|
149
|
+
- an icon-only `Select` (`selectedPresentation="icon-only"`, single or
|
|
150
|
+
`multiple`);
|
|
151
|
+
- an icon-only `PopupMenu` (named by `label` rather than visible `text`);
|
|
152
|
+
- an icon-only `<button>` or link (`<a href>`) inside a `ToolbarControlGroup`,
|
|
153
|
+
named by a non-empty `aria-label`, with no visible text, no native `title`,
|
|
154
|
+
and no Web Awesome tooltip `for` it.
|
|
155
|
+
|
|
156
|
+
Tags are on by default and take no props. One document-level installer covers
|
|
157
|
+
all three kinds, and any of `@kerfjs/ui/select/register`,
|
|
158
|
+
`@kerfjs/ui/popup-menu/register`, or `@kerfjs/ui/help-tags/register` installs
|
|
159
|
+
it (once per document, however many are imported). An application whose
|
|
160
|
+
toolbars have icon-only buttons but no registered `Select` or `PopupMenu`
|
|
161
|
+
imports `@kerfjs/ui/help-tags/register`, which registers only the tooltip
|
|
162
|
+
element. Pure `SafeHtml` rendering stays zero-JS: without an installer, nothing
|
|
163
|
+
shows a tag.
|
|
164
|
+
|
|
165
|
+
- **Text.** The tag says what the accessible name says: a filter's name and
|
|
166
|
+
chosen labels ("Filter by label: Bug, Docs", or "Filter by label" when none
|
|
167
|
+
are chosen), a single Select's name and current choice ("Rendering balance:
|
|
168
|
+
Balanced"), a menu's `label` ("Sort tickets"), or a toolbar button's
|
|
169
|
+
`aria-label` ("Pin view"). It updates with the selection.
|
|
170
|
+
- **Showing.** It appears after the pointer rests on the control for 500ms, at
|
|
171
|
+
once when a neighboring tag closed moments ago (so scanning along a toolbar
|
|
172
|
+
stays quick, across buttons, Selects, and menus alike), and immediately on
|
|
173
|
+
keyboard focus (`:focus-visible`). A pointer press that focuses the control
|
|
174
|
+
and touch input never show it.
|
|
175
|
+
- **Hiding.** It hides when the pointer and keyboard focus have both left the
|
|
176
|
+
control, on a press, on Escape, and whenever a popup opens. It does not come
|
|
177
|
+
back when the popup closes and focus returns to the trigger, or while the
|
|
178
|
+
pointer stays on a pressed button; it returns after the pointer moves onto
|
|
179
|
+
something else or focus leaves and comes back.
|
|
180
|
+
- **Assistive technology.** The tag is `aria-hidden` and never joins the
|
|
181
|
+
control's `aria-labelledby` or `aria-describedby` (kerf anchors it directly
|
|
182
|
+
rather than through `for`), so the accessible name is unchanged and announced
|
|
183
|
+
once; the accessibility tree is identical with the tag open or closed.
|
|
184
|
+
- **Isolation.** A Select's or PopupMenu's tag lives in the control's own
|
|
185
|
+
shadow root, so the morph never touches it and light-DOM `:has([open])`
|
|
186
|
+
selectors do not see it. A plain button cannot host a shadow root, so its tag
|
|
187
|
+
lives in one `aria-hidden`, `data-morph-skip` layer at the end of `<body>`
|
|
188
|
+
(fixed and zero-sized, so it never joins a grid or flex body's flow, and
|
|
189
|
+
re-attached if a body re-render drops it) and leaves that layer once hidden.
|
|
190
|
+
It copies the button's `color-scheme`, so it matches a subtree theme, and its
|
|
191
|
+
popup renders in the browser's top layer, so it shows above a modal dialog
|
|
192
|
+
that holds the button. Tags' `wa-show` / `wa-hide` lifecycle events stop at
|
|
193
|
+
the tag, so a listener on the control (or a delegated one above it) hears only
|
|
194
|
+
the popup; a document capture-phase listener still observes them. A long tag
|
|
195
|
+
wraps at the tooltip's maximum width and keeps 8px clear of the viewport
|
|
196
|
+
edge.
|
|
197
|
+
|
|
198
|
+
A labeled Select, a `PopupMenu` with visible `text`, and a toolbar button with
|
|
199
|
+
visible text get no tag: their visible text already names them. A button that
|
|
200
|
+
carries a native `title`, or that the application has already given a
|
|
201
|
+
`wa-tooltip` `for` its `id`, keeps that and gets no second tag, so nothing is
|
|
202
|
+
shown twice. Zero-JS components that name an icon control with a native `title`
|
|
203
|
+
(the `ListHeader` action, the `AppTab` close button, the `TokenSearchField`
|
|
204
|
+
reveal and clear buttons, and `SegmentedControl` choice titles) keep the
|
|
205
|
+
browser's own tooltip, which needs no script; the installer deliberately skips
|
|
206
|
+
them rather than stacking a second tag on the first. Icon-only buttons outside
|
|
207
|
+
a `ToolbarControlGroup` are application markup and keep whatever naming the
|
|
208
|
+
application gives them.
|
|
209
|
+
`tests/unit/help-tag.test.ts` walks the hover, focus, press, Escape, popup, and
|
|
210
|
+
warm-window transitions for every kind; `tests/browser/select-help-tag.spec.ts`
|
|
211
|
+
and `tests/browser/toolbar-help-tag.spec.ts` check the real elements in
|
|
212
|
+
Chromium.
|
|
213
|
+
|
|
214
|
+
## PopupMenu
|
|
215
|
+
|
|
216
|
+
`PopupMenu` renders a Web Awesome dropdown, so the menu roles, arrow-key and
|
|
217
|
+
typeahead navigation, Escape, and focus return to the trigger come from
|
|
218
|
+
`wa-dropdown`. Kerf's part is the trigger's name. A Web Awesome button takes its
|
|
219
|
+
name from its content and ignores an `aria-label` on the host, so an icon-only
|
|
220
|
+
trigger requires `label`, which renders as visually hidden text inside the
|
|
221
|
+
button; a trigger with visible `text` is named by that text. An icon-only
|
|
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)).
|
|
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
|
|
235
|
+
render as the same uppercase group titles a grouped `Select` shows, and dividers
|
|
236
|
+
are presentational. A keyboard-focused item is the menu's current row, painted
|
|
237
|
+
with the Select's current-option fill rather than a second focus ring inside the
|
|
238
|
+
open popup. A disabled item stays in the menu but cannot be
|
|
239
|
+
chosen; disable the whole trigger only when every command is unavailable.
|
|
240
|
+
Clicking outside dismisses a context `PopupMenu`, including one reopened while
|
|
241
|
+
its previous hide animation is still finishing.
|
|
242
|
+
Nested items use Web Awesome's submenu pointer and Left/Right arrow routing;
|
|
243
|
+
headings and dividers can separate nested groups without becoming keyboard
|
|
244
|
+
choices. A rapid ArrowDown or ArrowUp after opening a submenu keeps the chosen
|
|
245
|
+
item focused when its show animation finishes. If a submenu is reopened during
|
|
246
|
+
its close animation, it remains visible; pointer and ArrowRight actions also
|
|
247
|
+
recover a submenu left hidden with an open state. Set `checked` to `true` or `false` for checkbox menu semantics; omit it
|
|
248
|
+
for a plain command. `details` supplies optional
|
|
249
|
+
trailing visual content without changing a command's accessible label. A
|
|
250
|
+
`context` menu's invisible anchor is outside the accessibility tree and normal
|
|
251
|
+
Tab order; opening the menu moves focus into its commands and Escape dismisses
|
|
252
|
+
it. Keep a separate visible row action named even when it is disabled.
|
|
17
253
|
|
|
18
254
|
## ListActionRow
|
|
19
255
|
|
|
@@ -25,56 +261,182 @@ the row's item id for application delegation, while the root deliberately has
|
|
|
25
261
|
no action or role. `label`, `icon`, and `trailingActionIcon` are dormant visual
|
|
26
262
|
content and must not contain buttons, links, interactive roles, or other
|
|
27
263
|
controls. A trailing click, double click, context menu, Enter, or Space
|
|
28
|
-
must never activate the primary control.
|
|
264
|
+
must never activate the primary control. Selected rows keep the normal
|
|
265
|
+
foreground over their brand-tinted fill so text retains WCAG AA contrast in
|
|
266
|
+
light and dark themes. The application owns controlled
|
|
29
267
|
selection and the lifecycle, focus, and dismissal policy of any related
|
|
30
268
|
popover or context menu.
|
|
31
269
|
|
|
270
|
+
With `trailingMenu`, the trailing control is a `PopupMenu` trigger instead of
|
|
271
|
+
the native trailing button: an icon-only Web Awesome button named by
|
|
272
|
+
`trailingMenu.label`, announced with `aria-haspopup="menu"` and
|
|
273
|
+
`aria-expanded`, in the same primary-then-trailing order. Opening it moves
|
|
274
|
+
focus to the first command; Escape or a choice closes the menu and returns
|
|
275
|
+
focus to the trigger. An interaction-revealed trigger stays visible while its
|
|
276
|
+
menu is open. A pointer opening through `openPopupMenuAt` (for example from
|
|
277
|
+
the row's `contextmenu`) keeps the same keyboard and dismissal behavior.
|
|
278
|
+
|
|
279
|
+
## Reorderable list rows
|
|
280
|
+
|
|
281
|
+
A `draggable` `ListItem`, `ListItemLink`, or `ListActionRow` is a native HTML
|
|
282
|
+
drag source (a `ListActionRow` is dragged by its noninteractive root, so its two
|
|
283
|
+
buttons keep their own activation). Pointer dragging is never the only way to
|
|
284
|
+
reorder: `wireListReorder()` moves the focused row (or, when it is selected or
|
|
285
|
+
pressed, every selected row of its list) with Alt+Shift+ArrowUp and
|
|
286
|
+
Alt+Shift+ArrowDown, and returns focus to the moved row's focusable control
|
|
287
|
+
after the application re-renders. The dragged rows dim and the drop target
|
|
288
|
+
draws a 2px brand insertion line (system `Highlight` in forced colors), so the
|
|
289
|
+
landing position never relies on color alone. A loading placeholder is never
|
|
290
|
+
draggable. The application owns order, selection, and any announcement of the
|
|
291
|
+
completed move.
|
|
292
|
+
|
|
32
293
|
## ResizableRegion
|
|
33
294
|
|
|
295
|
+
Set `responsiveFillAt="narrow"` or `"compact"` when a responsive composition
|
|
296
|
+
shows the region as its only inline track. The component then owns its
|
|
297
|
+
full-width geometry and removes its dormant separator at that container
|
|
298
|
+
breakpoint; the application owns which pane is visible.
|
|
299
|
+
|
|
34
300
|
The handle exposes separator role, orientation, name, minimum, maximum, and current values. `wireResizableRegions()` adds:
|
|
35
301
|
|
|
36
302
|
- the axis arrow keys in 16 px steps by default;
|
|
37
303
|
- Shift+Arrow in 64 px steps;
|
|
38
304
|
- Home and End for minimum and maximum;
|
|
39
305
|
- primary-pointer drag with clamping;
|
|
40
|
-
-
|
|
306
|
+
- a visible maximum: when a parent clamps the track below the requested size
|
|
307
|
+
(a `max-width` stage, a narrow container), pointer and keyboard resizing stop
|
|
308
|
+
at the size actually shown, and the handle announces that size in
|
|
309
|
+
`aria-valuenow` and that bound in `aria-valuemax`. Focusing the handle
|
|
310
|
+
reports a stale larger committed size as the shown size without committing
|
|
311
|
+
it, so the first key press moves the visible separator. The reported values
|
|
312
|
+
also stay current at rest: a `ResizeObserver` on each region and its parent
|
|
313
|
+
re-clamps them when the space changes, and a `MutationObserver` re-clamps
|
|
314
|
+
them after a re-render writes the rendered props back, so assistive
|
|
315
|
+
technology never reads an unclamped size between interactions (the
|
|
316
|
+
disposer disconnects both observers);
|
|
317
|
+
- a truthful value below the minimum: when the container shows the track at
|
|
318
|
+
less than `min` (a narrowing `Workbench` squeezes its resizable rails), the
|
|
319
|
+
handle reports that shown size in `aria-valuenow` and pins `aria-valuemin`
|
|
320
|
+
and `aria-valuemax` to it — WAI-ARIA requires the value to lie inside the
|
|
321
|
+
range, and the separator cannot move a track its container holds. A pinned
|
|
322
|
+
separator — squeezed, or with a reachable range collapsed to one size —
|
|
323
|
+
commits nothing: arrow keys, Home/End, and drags leave the app's size (and
|
|
324
|
+
its storage) as it was, so the track returns to it, with the rendered range,
|
|
325
|
+
once it has room;
|
|
326
|
+
- a whole hit target and focus ring at a clamped edge: when the separator has
|
|
327
|
+
less room past it than the handle's 10px overhang (a parent clamps the track
|
|
328
|
+
to its own edge), the region carries `data-handle-inset` and the handle sits
|
|
329
|
+
wholly inside it, so a clipping ancestor cannot cut off half the handle or
|
|
330
|
+
its focus ring. The ring is drawn inside the handle's box, so the region's
|
|
331
|
+
ends meeting a clipping edge never shave it either;
|
|
332
|
+
- preview callbacks while dragging and one commit callback on release;
|
|
333
|
+
- a live size that also resizes slide-motion content, so the content tracks the
|
|
334
|
+
separator during a drag or key press instead of waiting for the app to
|
|
335
|
+
re-render the committed size.
|
|
41
336
|
|
|
42
337
|
The application owns persistence and collapsed/expanded policy. Keep the last expanded size outside the component and restore it when reopening. An optional `handleIcon` replaces only decorative dormant content; it must not contain controls or interactive roles because the separator remains the sole focus and interaction owner.
|
|
43
338
|
|
|
44
|
-
##
|
|
339
|
+
## Toolbar headings
|
|
340
|
+
|
|
341
|
+
For headings and paragraphs outside toolbar identity/title zones, use `Text`.
|
|
342
|
+
It renders the selected native `h1`–`h6` or `p` element, so heading navigation
|
|
343
|
+
follows the variant directly; choose a level that preserves the document
|
|
344
|
+
outline. Native global, `data-*`, and `aria-*` attributes pass through.
|
|
45
345
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
`ToolbarText
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
346
|
+
Compose panel, dialog, and page headings as a plain `Toolbar`. Its leading zone
|
|
347
|
+
holds an optional icon `ToolbarControlGroup` and a direct extra-large
|
|
348
|
+
`ToolbarText`; controls belong in a trailing `ToolbarControlGroup`. By default
|
|
349
|
+
the title carries no native heading role, so for a **dialog or panel** the
|
|
350
|
+
application connects the title id and optional supporting-copy id to the owning
|
|
351
|
+
host through `aria-labelledby` and `aria-describedby`. For a **page or view**
|
|
352
|
+
title, pass `headingLevel` (usually `1`) to `ToolbarText`: it then exposes
|
|
53
353
|
`role="heading"` with a matching `aria-level`, giving the view a heading landmark
|
|
54
354
|
so screen-reader heading navigation and "main heading" semantics work — the same
|
|
55
|
-
role/level pair
|
|
56
|
-
|
|
355
|
+
role/level pair used throughout Kerf UI. Keep the levels meaningful and
|
|
356
|
+
non-skipping within a view. Pass trailing
|
|
57
357
|
controls as a labeled `ToolbarControlGroup` when that group needs an accessible
|
|
58
|
-
name.
|
|
358
|
+
name. A short supporting line belongs in the title's `description`: with it,
|
|
359
|
+
`id` and `headingLevel` move to the title span, so `aria-labelledby` names only
|
|
360
|
+
the title, and `descriptionId` gives the description its own id for
|
|
361
|
+
`aria-describedby`. Keep longer supporting copy as app-owned content below the
|
|
362
|
+
toolbar.
|
|
363
|
+
An editable title can use `ToolbarText action="edit-title"`; it renders a native
|
|
364
|
+
button whose full text remains its accessible name even when visually truncated.
|
|
365
|
+
Click, Enter, and Space activate the delegated action. The app owns the input
|
|
366
|
+
shown while editing and its focus lifecycle. Because the button cannot also be
|
|
367
|
+
a heading landmark, expose a heading elsewhere when the view needs one.
|
|
368
|
+
|
|
369
|
+
## FloatingToolbar
|
|
370
|
+
|
|
371
|
+
`FloatingToolbar` is a `role="toolbar"` region with a **required** accessible
|
|
372
|
+
`label`; it floats over the content of its nearest positioned ancestor and is
|
|
373
|
+
forced to a dark color scheme, but it is **not** in the top layer, so it never
|
|
374
|
+
covers dialogs, popovers, or other overlays. Its children are the app's controls
|
|
375
|
+
(normally `ToolbarControlGroup`s), which keep their own names, focus, and
|
|
376
|
+
keyboard behavior; the application owns their actions, the toolbar's visibility,
|
|
377
|
+
and — via `position` and the typed `inset` prop — where it sits. Inside a `Workbench`, `CollapsiblePanel`, or
|
|
378
|
+
`ResizableRegion` `restoreControl` the restore corner owns the inset, so the
|
|
379
|
+
toolbar floats from that corner. While one of those layouts has a side overlay
|
|
380
|
+
open over the toolbar's region (a Workbench rail overlay, a `wireSidebar`
|
|
381
|
+
compact overlay, an overlay `CollapsiblePanel` or horizontal
|
|
382
|
+
`ResizableRegion`), the toolbar is hidden (`visibility: hidden`), so it is
|
|
383
|
+
unfocusable and out of the accessibility tree rather than a control stranded
|
|
384
|
+
behind the overlay's focus trap; it returns when the overlay closes. The
|
|
385
|
+
overlay's own floating toolbars stay.
|
|
59
386
|
|
|
60
387
|
## Tabs
|
|
61
388
|
|
|
62
389
|
`AppTab` renders one controlled tab. `TabBar` supplies the containing tab list,
|
|
63
|
-
fixed leading/trailing regions, and a horizontally scrollable strip.
|
|
390
|
+
fixed leading/trailing/end regions, and a horizontally scrollable strip.
|
|
391
|
+
Use `TabBar.presentation` for rail, segmented, or inspector chrome,
|
|
392
|
+
`allocation="fill"` when peers should divide the strip, and
|
|
393
|
+
`trailingPlacement="adjacent"` when an action belongs beside the final tab.
|
|
394
|
+
When that local action must coexist with a workspace-level action, put the
|
|
395
|
+
workspace action in `end`; TabBar keeps the adjacent action immediately after
|
|
396
|
+
the shrinkable/scrolling tabs and pins `end` at the far edge. Leading,
|
|
397
|
+
trailing, and end actions remain visible and do not shrink. The component owns
|
|
398
|
+
that geometry; applications must not override its anatomy to create the split.
|
|
399
|
+
`trailing` and `end` each accept one `ToolbarControlGroup` for grouped controls
|
|
400
|
+
or one standalone `wa-button` when a primary action needs its own Web Awesome
|
|
401
|
+
chrome. Do not add a wrapper or flex margins to position the action.
|
|
402
|
+
`AppTab` provides compact 32px, segmented, truncating-label, and icon-only
|
|
403
|
+
presentations. Icon-only tabs keep the required `name` as the tab button's
|
|
404
|
+
accessible name while visually hiding the duplicate label. The default name
|
|
405
|
+
overflow is an ellipsis; `nameOverflow="visible"` lets an inline loading
|
|
406
|
+
treatment show the whole name, without changing the accessible name. The app
|
|
407
|
+
sets `dropTarget` during a content drag-over to paint a brand highlight;
|
|
408
|
+
selection and tab focus stay independent of that visual state. A `pending` tab (known,
|
|
409
|
+
still opening) keeps its visible name as the tab button's accessible name, so a
|
|
410
|
+
trailing spinner's own label is not folded into it. It is `aria-busy` and stays
|
|
411
|
+
selectable (in the roving tab order like a live tab), so the application can
|
|
412
|
+
show placeholder content in its panel until loading completes; its close
|
|
413
|
+
button is disabled and hidden and it is not draggable. A `placeholder` tab
|
|
414
|
+
(unknown) is instead disabled, out of the tab order, and not draggable. These props own
|
|
415
|
+
component appearance only; the application still owns the bar's outer placement.
|
|
64
416
|
`AppTab.rootAttributes` accepts application `data-*` metadata only; runtime
|
|
65
417
|
filtering rejects roles plus case variants of component- or wiring-owned
|
|
66
418
|
action, identity, selection, drag, drop, and component attributes. An optional
|
|
67
419
|
`closeIcon` is decorative dormant content inside the already named close button
|
|
68
420
|
and must not contain interactive descendants.
|
|
421
|
+
The first `AppTab` can set `pinned` to stay at the inline start while its peers
|
|
422
|
+
scroll. It remains a normal tab within the same tablist and roving-focus order;
|
|
423
|
+
focus and controlled selection reveal peers beyond the pinned tab. Pin only the
|
|
424
|
+
leading tab of a strip.
|
|
425
|
+
WebKit currently moves a pinned tab outside an RTL strip as it scrolls;
|
|
426
|
+
`KF-MYFQAS` tracks the browser correction.
|
|
69
427
|
`wireTabBars()` adds Left/Right wrapping, Home/End, Delete/Backspace close
|
|
70
428
|
activation, same-bar pointer reordering, `Alt+Shift+ArrowLeft/ArrowRight`
|
|
71
429
|
reordering, focus restoration, scroll-into-view, and pointer-proximity
|
|
72
430
|
autoscroll at either horizontal edge. Edge autoscroll is direct manipulation,
|
|
73
|
-
stops on drop/drag end/disposal, and does not change keyboard behavior.
|
|
74
|
-
|
|
431
|
+
stops on drop/drag end/disposal, and does not change keyboard behavior. When
|
|
432
|
+
the application changes a bar's selected tab without focus (adding and
|
|
433
|
+
selecting a tab), the wiring scrolls that tab's strip — never the page — so
|
|
434
|
+
the new selection is fully visible; a re-render that keeps the same selection
|
|
435
|
+
leaves a strip the user scrolled where it is. It returns a disposer.
|
|
75
436
|
|
|
76
437
|
Arrow / Home / End follow the ARIA Tabs **automatic-activation** pattern by
|
|
77
|
-
default: they move roving focus and select the focused tab.
|
|
438
|
+
default: they move roving focus and select the focused tab. If selection synchronously
|
|
439
|
+
replaces the strip, focus returns to the replacement with the same bar and tab IDs. Pass
|
|
78
440
|
`activation: 'manual'` (or set `data-tab-activation="manual"` on a strip via the
|
|
79
441
|
`TabBar` `activation` prop, which overrides the option per bar) for
|
|
80
442
|
**manual activation**: arrow keys move roving focus only and the user selects
|
|
@@ -95,14 +457,80 @@ stays in sequential Tab order, matching Hot Sheet 2's compact view and inspector
|
|
|
95
457
|
controls; Enter and Space use native button activation. The application handles
|
|
96
458
|
the supplied `data-action`, reads `data-segment-value`, updates `value`, and
|
|
97
459
|
re-renders. Keep labels unique and meaningful even when `content` shows only an
|
|
98
|
-
icon.
|
|
460
|
+
icon. The filled track uses the normal neutral text role for readable unselected
|
|
461
|
+
labels in light and dark themes. Use tabs—not a segmented control—when choices switch page regions that
|
|
99
462
|
need `tab`/`tabpanel` semantics.
|
|
100
463
|
|
|
464
|
+
## ChoiceChips
|
|
465
|
+
|
|
466
|
+
`ChoiceChips` renders `role="radiogroup"`, named by `label` (`aria-label`) or
|
|
467
|
+
by `labelledBy` (`aria-labelledby`, typically a visible `FieldLabel`). Each
|
|
468
|
+
choice is a native `<input type="radio">` inside its pill `<label>`, so the
|
|
469
|
+
browser owns the interaction: the group is a single Tab stop on the checked
|
|
470
|
+
radio (or the first enabled one when nothing is selected), arrow keys move and
|
|
471
|
+
select, Space selects, and the checked radio submits its value under `name`.
|
|
472
|
+
`required` makes an unselected group fail native form validation, and
|
|
473
|
+
`disabled` (group) or a choice's `disabled` removes it from interaction. The
|
|
474
|
+
radio's accessible name is the visible label; a `swatch` or `icon` is
|
|
475
|
+
`aria-hidden` decoration. The focus ring is drawn on the pill around the
|
|
476
|
+
focused radio, and the checked pill adds a second pixel of border so the
|
|
477
|
+
selection does not rely on hue alone; forced-colors mode paints it with
|
|
478
|
+
`Highlight`.
|
|
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
|
+
|
|
101
515
|
## TokenSearchField
|
|
102
516
|
|
|
517
|
+
Use `presentation="form-field"` when a structured search query sits beside
|
|
518
|
+
Web Awesome form controls. It renders the visible `label` with the same inset
|
|
519
|
+
and typography, an optional `hint`, and an optional `required` marker. The
|
|
520
|
+
searchbox references the label and hint with `aria-labelledby` and
|
|
521
|
+
`aria-describedby`, and `required` sets `aria-required`; the application
|
|
522
|
+
validates the value before submission. `wireTokenSearchFields()` also moves
|
|
523
|
+
focus to the editor when the visible label is clicked. The field stands outside
|
|
524
|
+
a Toolbar and fills the same inline space as a neighboring `wa-input`.
|
|
525
|
+
Its control radius and required-marker color and offset follow Web Awesome's
|
|
526
|
+
form-control tokens, so the two controls read as one form.
|
|
527
|
+
|
|
103
528
|
`TokenSearchField` exposes the editable surface as a named `searchbox`. Each
|
|
104
529
|
chip is atomic (`contenteditable="false"`) and contains separately named edit
|
|
105
|
-
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
|
|
106
534
|
`aria-disabled` and stop editing without hiding the current expression. The
|
|
107
535
|
application owns query parsing and must announce result-count or loading
|
|
108
536
|
changes separately when that feedback is useful. Use `readTokenSearchField()`
|
|
@@ -111,44 +539,258 @@ and `placeTokenSearchCaret()` to restore a text caret without landing inside a
|
|
|
111
539
|
chip. Call `wireTokenSearchFields()` once at a stable root so Enter submits
|
|
112
540
|
without inserting a contenteditable line break and keyboard chip deletion
|
|
113
541
|
restores focus plus the text-relative caret after controlled rendering replaces
|
|
114
|
-
the editor
|
|
542
|
+
the editor. Restoration finishes after synchronous input listeners and before
|
|
543
|
+
another keystroke; it does not wait for an animation frame that could overwrite
|
|
544
|
+
a later selection. Select All + Backspace/Delete shares that replacement path
|
|
545
|
+
and keeps the adopted expanded signal open while managed focus is enabled.
|
|
546
|
+
Real outside focus, empty Escape, disposal, and removed fields retain their usual
|
|
547
|
+
behavior. The editor still wraps text visually at its inline edge. Editable text is DOM-owned between token changes; a clear handler empties
|
|
115
548
|
the editor's `textContent` before updating application state. Leading and
|
|
116
549
|
trailing controls share the first text line's fixed vertical center and remain
|
|
117
550
|
there as the editor wraps. In `collapsible` mode, the closed state is one named
|
|
118
551
|
iconic search button and the open state is the same named searchbox, whether
|
|
119
552
|
the field stands alone or is composed inside `ToolbarControlGroup`.
|
|
553
|
+
|
|
554
|
+
On coarse touch devices, the editor's computed text size has a 16px floor. This
|
|
555
|
+
avoids iOS focus magnification of the small-text editor while preserving the
|
|
556
|
+
user's ability to pinch zoom; other field chrome and desktop density are unchanged.
|
|
557
|
+
|
|
558
|
+
For an app-driven query replacement with an unmanaged field, change its
|
|
559
|
+
`revision` along with `query` so the editor text rebuilds. For a model-managed
|
|
560
|
+
field, call `model.replace({ query, tokens })`: it parses the supplied state and
|
|
561
|
+
advances `editorRevision` even when the tokens did not change.
|
|
562
|
+
|
|
120
563
|
Applications set `expanded` while the field is focused, move focus from the
|
|
121
564
|
trigger into the revealed editor, and clear `expanded` only after focus leaves
|
|
122
565
|
the complete component. Text or tokens keep the field expanded even when that
|
|
123
566
|
transient state is false. Width animation is disabled under reduced motion.
|
|
567
|
+
Clearing with ✕ follows where focus was when it was pressed: from inside the
|
|
568
|
+
field (the editor, or ✕ reached by keyboard) the caret returns to the emptied
|
|
569
|
+
editor; with focus elsewhere, `wireTokenSearchFields` leaves focus where it is
|
|
570
|
+
and collapses the emptied field, as it would on blur.
|
|
124
571
|
|
|
125
572
|
When the field is composed with a caller-owned surface rendered outside it — a
|
|
126
573
|
suggestions listbox, date picker, or help popover — mark that surface (or wrap
|
|
127
574
|
it) with `data-token-search-keep-open`, or pass `collapsible.keepOpenOn(target)`,
|
|
128
|
-
so focus moving into it does not collapse an empty field.
|
|
575
|
+
so focus or pointer activation moving into it does not collapse an empty field.
|
|
576
|
+
The helper preserves the pointer target when a browser omits
|
|
577
|
+
`focusout.relatedTarget`, preventing the field or target from disappearing
|
|
578
|
+
between pointerdown and click. For an empty field losing focus during a pointer
|
|
579
|
+
press, managed collapse waits until that press's click has dispatched (or the
|
|
580
|
+
press ends without a click), then rechecks focus and content before closing.
|
|
581
|
+
This keeps a shifting search row from moving the pressed control mid-click.
|
|
582
|
+
`wireTokenSearchFields`
|
|
129
583
|
can also, opt-in via `keyboard`, own atomic-chip editing keys: from a collapsed
|
|
130
584
|
caret with no selection, Backspace removes the token before the caret and Delete
|
|
131
585
|
the token after it (reported through `onRemoveToken` for the app to apply to its
|
|
132
586
|
controlled state), and ArrowRight moves the caret past a trailing chip so typed
|
|
133
|
-
text lands after it. An optional `onEdit({ id, editor })`
|
|
587
|
+
text lands after it. An optional `onEdit({ id, editor, event })` — with the
|
|
588
|
+
originating `InputEvent` so a caller can gate on `inputType`/`data` — fires on every editor
|
|
134
589
|
`input`, letting a caller drop its own `input` listener; the application still
|
|
135
|
-
owns query parsing and result-count/loading announcements.
|
|
590
|
+
owns query parsing and result-count/loading announcements. When the app's `onEdit` or `onRemoveToken` changes tokens and the controlled render replaces the focused editor, `wireTokenSearchFields` moves focus and caret to the replacement at the same mutation checkpoint (a caret at the end of the query stays at the end; any other caret keeps its place in the remaining text), so keys typed at full speed are not lost.
|
|
591
|
+
|
|
592
|
+
With a `createTokenSearchModel` passed to the field and registered in
|
|
593
|
+
`wireTokenSearchFields(root, { models })`, grammar parsing, chips, clear/edit/
|
|
594
|
+
remove actions, and suggestions are managed. Arrow Down moves from the editor
|
|
595
|
+
to suggestions, arrow keys move between suggestions, Enter selects one, and
|
|
596
|
+
Escape returns focus to the editor. The application still announces results
|
|
597
|
+
when needed and can supply an `evaluate` callback to compute them. Omitting the
|
|
598
|
+
model leaves the manual behavior above intact. After a chip commit, suggestion
|
|
599
|
+
choice, chip edit, or Backspace/Delete chip removal, focus restoration runs at
|
|
600
|
+
the controlled render checkpoint and keeps the query-relative caret offset;
|
|
601
|
+
it leaves focus on a different control if the user moved there in the meantime.
|
|
602
|
+
Suggestions use Floating UI positioning, following the same viewport-aware
|
|
603
|
+
popup behavior as the Web Awesome controls behind Select and PopupMenu. They
|
|
604
|
+
follow the field while scrolling, flip above it at the bottom edge, and shift
|
|
605
|
+
within narrow viewports without growing its toolbar row. One
|
|
606
|
+
suggestion keeps a pill outline; lists with multiple choices use the standard
|
|
607
|
+
rounded surface and scroll once they reach 240px. The clear button's hover and focus
|
|
608
|
+
highlight follows the field's pill shape or its toolbar group's rounded shape.
|
|
609
|
+
|
|
610
|
+
Managed clear captures the action before application handlers run, keeps the adopted
|
|
611
|
+
expanded signal open for a collapsible field, and restores the editor focus for
|
|
612
|
+
both collapsible and plain model fields at the actual
|
|
613
|
+
mutation checkpoint before the next input task. It also returns keyboard activation
|
|
614
|
+
from the clear button to a surviving editor immediately. No animation frame owns
|
|
615
|
+
clear focus, so fast typing cannot escape to page shortcuts or lose its first letter. The app still owns clearing query/tokens and emptying
|
|
616
|
+
DOM-owned text. This focus step respects `manageFocus: false`, disposal, removed fields,
|
|
617
|
+
and focus deliberately moved to another control; no app-level reopen callback is needed.
|
|
136
618
|
|
|
137
619
|
## CollapsiblePanel / sidebar
|
|
138
620
|
|
|
139
|
-
`CollapsiblePanel` is a labeled `aside` region that
|
|
140
|
-
collapsed
|
|
621
|
+
`CollapsiblePanel` is a labeled `aside` region that renders `inert` and
|
|
622
|
+
`aria-hidden` while collapsed, straight from the app's `collapsed` flag, so its
|
|
623
|
+
optional `tabIndex={0}` leaves the Tab order while closed. `tabIndex={-1}` permits
|
|
624
|
+
programmatic focus while open; `outlined` keeps the region's focus ring visible
|
|
625
|
+
only while expanded and visible. A toolbar panel's inner `Pane` can take the
|
|
626
|
+
same options through `pane` when that is the intended focus boundary. Its
|
|
627
|
+
controls leave the Tab order and the accessibility tree even while the content
|
|
628
|
+
is still sliding out, and focusing one can never scroll clipped content back into
|
|
629
|
+
view (`aria-hidden` alone left them focusable inside a hidden subtree). Its
|
|
630
|
+
content slides via transform (disabled under reduced motion); `inert` does not
|
|
631
|
+
affect rendering, so the slide still plays. A `restoreControl` renders outside
|
|
632
|
+
the panel and stays reachable. A collapsed `ResizableRegion` renders `inert` and
|
|
633
|
+
`aria-hidden` on the region itself the same way (its content wrapper is `inert`
|
|
634
|
+
too, and its separator is already hidden and not displayed), so no empty
|
|
635
|
+
labeled region landmark stays behind; its `restoreControl` stays outside the
|
|
636
|
+
region.
|
|
141
637
|
`CollapsiblePanelToggle` is a named button carrying `aria-expanded` and the
|
|
142
638
|
standard per-side collapse/expand glyph (`PanelLeft*` / `PanelRight*` /
|
|
143
639
|
`PanelBottom*`); place a collapse toggle inside the panel and an expand toggle in
|
|
144
640
|
an always-visible location so it is reachable while collapsed. `wireSidebar()`
|
|
145
641
|
moves focus into the panel when it opens and restores it to the trigger when it
|
|
146
|
-
closes
|
|
642
|
+
closes; a trigger inside the now-inert panel hands focus to the panel's toggle
|
|
643
|
+
outside it instead (and focus stranded there by a wide → compact crossing is
|
|
644
|
+
rescued the same way), so an app may render the expand toggle only while the panel
|
|
645
|
+
is collapsed. When a `deviceClass()` reports `compact`, an open panel becomes an overlay
|
|
147
646
|
with a dismissable backdrop, Escape and backdrop-click collapse it, and Tab is
|
|
148
|
-
trapped within it (the ARIA dialog pattern).
|
|
647
|
+
trapped within it (the ARIA dialog pattern). That modal state only ever begins
|
|
648
|
+
from a user action: at wire-up on a compact device and on a wide → compact
|
|
649
|
+
crossing, every panel starts collapsed, so no page load or resize traps focus. The application owns each `collapsed`
|
|
149
650
|
signal, the panels, their sizes, and content; the wire may persist the collapsed
|
|
150
651
|
state per panel.
|
|
151
652
|
|
|
653
|
+
## Workbench
|
|
654
|
+
|
|
655
|
+
Each `Workbench` rail and drawer is a labeled region (`aside` for a rail, `section`
|
|
656
|
+
for the drawer). While a panel is collapsed the region itself renders `inert` and
|
|
657
|
+
`aria-hidden="true"`, and so does not linger as an empty landmark, and its content
|
|
658
|
+
wrapper is `inert`, all rendered from the app's `collapsed` flag, so its controls
|
|
659
|
+
leave the Tab order and the accessibility tree even while the content is still
|
|
660
|
+
sliding out, and focusing one can never scroll clipped content back into view. The panel's `restoreControl` renders
|
|
661
|
+
outside that content and stays reachable; a resizable panel's separator also leaves
|
|
662
|
+
the Tab order while collapsed or not inline. `wireWorkbench`, given a panel's
|
|
663
|
+
`collapsed` signal, returns focus stranded in a closing panel to the control that
|
|
664
|
+
opened it, else the restore control, else a control whose `aria-controls` names the
|
|
665
|
+
panel; an open overlay panel takes focus and keeps Tab inside it (the ARIA dialog
|
|
666
|
+
pattern). See [`workbench.md`](workbench.md).
|
|
667
|
+
|
|
668
|
+
## Chrome material
|
|
669
|
+
|
|
670
|
+
Translucent chrome, such as a `Pane` header with
|
|
671
|
+
`headerPresentation="overlay"`, a `NavStack` top bar with
|
|
672
|
+
`chromePresentation="overlay"`, or a `NavStack` bottom toolbar with
|
|
673
|
+
`bottomToolbarPresentation="overlay"`, or a `TabNavigator` tab bar with
|
|
674
|
+
`barPresentation="overlay"`, paints `--kui-chrome-material`: the surface at
|
|
675
|
+
80% opacity behind a blur-and-saturate backdrop filter
|
|
676
|
+
(`--kui-chrome-material-filter`). Content scrolls visibly beneath it, so the
|
|
677
|
+
backdrop under its text is not a fixed color. The contrast rule measures the
|
|
678
|
+
worst case: the material composited over a solid black backdrop in the light
|
|
679
|
+
theme and a solid white backdrop in the dark theme. The blur only averages the
|
|
680
|
+
content, so it can never be darker or lighter than that.
|
|
681
|
+
|
|
682
|
+
Chrome on a raised surface, such as a `DialogSurface` title bar with
|
|
683
|
+
`headerPresentation="floating"`, paints `--kui-chrome-material-raised`
|
|
684
|
+
instead: the raised surface at the same 80%, so the bar matches the dialog
|
|
685
|
+
panel behind it. The same rule and fallbacks apply.
|
|
686
|
+
|
|
687
|
+
- **Normal text and icons pass AA over any content.** `--kui-color-text`
|
|
688
|
+
measures about 10.5:1 (light) and 8.3:1 (dark) on that worst case, and
|
|
689
|
+
about 10.5:1 (light) and 6.7:1 (dark) over the raised material. 80% is
|
|
690
|
+
above the minimum opacity that keeps 4.5:1 there (about 55% light and 60%
|
|
691
|
+
dark). A theme that overrides `--kui-chrome-material` or
|
|
692
|
+
`--kui-chrome-material-raised` must keep its text at 4.5:1 or better
|
|
693
|
+
against the same backdrops.
|
|
694
|
+
- **Quiet text does not, so `ToolbarText` lifts it.** `--kui-color-text-quiet`
|
|
695
|
+
falls to about 3.2:1 (light) and 4.1:1 (dark) over the worst case, and the
|
|
696
|
+
default-size title color to about 3.8:1 (light). Inside an overlay `Pane`
|
|
697
|
+
header or overlay `NavStack` chrome or bottom toolbar, or slotted as the `label` (or into
|
|
698
|
+
`header-actions`) of a `DialogSurface` floating header, an untoned
|
|
699
|
+
`ToolbarText` therefore takes
|
|
700
|
+
`--kui-color-text` for its title at every size, and its `description` mixes
|
|
701
|
+
the quiet color halfway toward the normal one, about 5.9:1 in both themes
|
|
702
|
+
(about 4.9:1 dark over the raised material). That keeps AA while the
|
|
703
|
+
description still reads as secondary. Controls lift the same way in an
|
|
704
|
+
overlay `Pane` header, overlay `NavStack` chrome or bottom toolbar, and a
|
|
705
|
+
`DialogSurface` floating header's `header-actions`: a default-tone
|
|
706
|
+
`ToolbarControlGroup`'s quiet control color over its own translucent fill
|
|
707
|
+
measures about 3.0:1 (light) and 3.5:1 (dark) over the worst case, so its
|
|
708
|
+
controls take `--kui-color-text` there. An overlay `TabNavigator` bar's
|
|
709
|
+
unselected labels lift halfway toward `--kui-color-text`, and its selected
|
|
710
|
+
label mixes 40% of it into the brand color: the plain brand color measured
|
|
711
|
+
about 3.2:1 (light) over the worst case. `wa-dialog`'s own close button is an
|
|
712
|
+
icon, held to the 3:1 non-text bar, and measures about 3.8:1 (light) and
|
|
713
|
+
4.1:1 (dark) over the raised material. Any other quiet copy you place in
|
|
714
|
+
overlay chrome yourself has no such lift; keep it in the content.
|
|
715
|
+
- **The material turns opaque when transparency would hurt.** Under
|
|
716
|
+
`prefers-reduced-transparency: reduce` or `prefers-contrast: more`, and where
|
|
717
|
+
`backdrop-filter` is unsupported, it resolves to the opaque surface. In forced
|
|
718
|
+
colors, the header (or NavStack chrome) paints `Canvas` with a `CanvasText`
|
|
719
|
+
bottom edge, and an overlay NavStack bottom toolbar paints `Canvas` with a
|
|
720
|
+
`CanvasText` top edge, so content passing beneath it never shows through.
|
|
721
|
+
- **Clear chrome has no material of its own.** A `clear` Pane header,
|
|
722
|
+
NavStack bar, or TabNavigator bar is transparent, so nothing guarantees its
|
|
723
|
+
text over arbitrary content: keep its controls in glass control groups,
|
|
724
|
+
which carry the material and AA lift with them, and give a title over
|
|
725
|
+
imagery a scrim (or keep it in the content). `scrollEdge="material"` (or
|
|
726
|
+
`"surface"`) fades a backing in gradually as content scrolls beneath the
|
|
727
|
+
bar. Clear chrome turns
|
|
728
|
+
opaque under reduced transparency or increased contrast and paints
|
|
729
|
+
`Canvas` in forced colors, like the material.
|
|
730
|
+
- **An app can turn every effect static.** There is no reliable CSS signal
|
|
731
|
+
for a low-power device, so an app that detects one sets
|
|
732
|
+
`data-kui-effects="static"` on the root element
|
|
733
|
+
(`document.documentElement.dataset.kuiEffects = 'static'`). The material
|
|
734
|
+
then resolves to the opaque surface, and every scroll-driven animation a
|
|
735
|
+
component owns stops at its resting state. A display that cannot animate
|
|
736
|
+
smoothly (`@media (update: slow)`, such as e-ink) gets the same result on
|
|
737
|
+
its own. Removing the attribute restores the effects.
|
|
738
|
+
- **A glass `ToolbarControlGroup` (`material="glass"`) carries the material
|
|
739
|
+
with it.** A group floating over imagery or a clear toolbar paints
|
|
740
|
+
`--kui-chrome-material` behind its own controls. The same quiet-color shortfall
|
|
741
|
+
applies, so its controls always take `--kui-color-text`. A raised or outline
|
|
742
|
+
selection paints an opaque surface pill, so the brand selected color keeps AA:
|
|
743
|
+
over the bare 80% material it measured about 3.2:1 (light, over black). The
|
|
744
|
+
group's hover mixes a text-color tint into the material, so a lone control's
|
|
745
|
+
whole-group hover never drops the fill. The specular edge gives way to the
|
|
746
|
+
normal group border wherever the material turns opaque, including static
|
|
747
|
+
effects. A dark-toned glass group's loud fill turns opaque there too. In
|
|
748
|
+
forced colors the group paints `Canvas` with a `CanvasText` border and drops
|
|
749
|
+
its filter and shadow. `tests/browser/tone-contrast.spec.ts` measures rest,
|
|
750
|
+
hover, and selected controls on both worst-case backdrops in all three
|
|
751
|
+
engines, and checks the opaque and forced-color fallbacks in Chromium.
|
|
752
|
+
|
|
753
|
+
`tests/browser/overlay-header.spec.ts` (Pane),
|
|
754
|
+
`tests/browser/overlay-nav-stack.spec.ts` (NavStack top chrome),
|
|
755
|
+
`tests/browser/overlay-nav-stack-bottom.spec.ts` (NavStack bottom toolbar,
|
|
756
|
+
which also measures its description, a control, the top chrome's back
|
|
757
|
+
control, and increased contrast), and
|
|
758
|
+
`tests/browser/dialog-floating-header.spec.ts` (DialogSurface) measure the
|
|
759
|
+
title over the material on both worst-case backdrops in Chromium, Firefox, and
|
|
760
|
+
WebKit, and the Pane and DialogSurface specs also measure the description and
|
|
761
|
+
a header control group (DialogSurface also its close button). In
|
|
762
|
+
Chromium they also check the opaque fallbacks for reduced transparency and
|
|
763
|
+
forced colors, and the Pane and DialogSurface specs check increased contrast.
|
|
764
|
+
|
|
765
|
+
**Firefox and WebKit screenshots omit the blur.** In Playwright's Firefox and
|
|
766
|
+
WebKit, `page.screenshot()` (headless or headed) shows the content beneath the
|
|
767
|
+
material crisp, as if `backdrop-filter` did nothing. Stock Firefox's own
|
|
768
|
+
`--headless --screenshot` does the same. The filter is still applied: a
|
|
769
|
+
`recordVideo` frame of the same page in the same session, which records the
|
|
770
|
+
compositor's output, shows the content blurred. Firefox and WebKit draw
|
|
771
|
+
`backdrop-filter` only when compositing (WebRender in Firefox, Core Animation
|
|
772
|
+
in WebKit), and their snapshot paths repaint the page without it. Chromium's
|
|
773
|
+
screenshot shows the blur. Plain test pages with no kerf CSS behave the same
|
|
774
|
+
way, so no ancestor style in these components is blocking the filter. When
|
|
775
|
+
you review the blur in Firefox or WebKit, use a `recordVideo` frame, not a
|
|
776
|
+
screenshot. A crisp screenshot is not a defect, and contrast is unaffected
|
|
777
|
+
because the rule above already assumes no blur.
|
|
778
|
+
|
|
152
779
|
## Verification matrix
|
|
153
780
|
|
|
154
781
|
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.
|
|
782
|
+
|
|
783
|
+
Text contrast checks must measure the painted solid-color backdrop, compositing
|
|
784
|
+
translucent fills from the document canvas through every ancestor and the
|
|
785
|
+
component itself before comparing the foreground. Raw RGB channels from an
|
|
786
|
+
`rgba()` value are not the rendered background. The shared browser helper in
|
|
787
|
+
`tests/browser/painted-contrast.ts` resolves CSS color syntax through canvas and
|
|
788
|
+
uses the evaluator's WCAG ratio calculation; its browser regressions cover
|
|
789
|
+
nested fills, transparent foregrounds, shadow-host backdrops, theme changes,
|
|
790
|
+
and genuinely insufficient contrast. Background images (including the catalog
|
|
791
|
+
checkerboard), filters, and group opacity remain separate visual-review concerns;
|
|
792
|
+
this helper measures only the solid CSS color layers.
|
|
793
|
+
|
|
794
|
+
Separator checks resolve the documented semantic border token in the component's
|
|
795
|
+
context instead of pinning an old opaque palette value, while still verifying
|
|
796
|
+
that the line has its intended width and a visible color.
|