@kerfjs/ui 5.0.0-beta.6 → 5.0.0-beta.61
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 +494 -138
- package/ai/application-ui-diagnostic-ids-v1.json +109 -0
- package/ai/application-ui-profile-sync.cjs +529 -0
- package/ai/application-ui-profile.d.mts +1 -0
- package/ai/application-ui-profile.d.ts +102 -0
- package/ai/application-ui-profile.defaults.json +96 -0
- package/ai/application-ui-profile.mjs +626 -0
- package/ai/application-ui-profile.schema.json +130 -0
- package/ai/catalog-authoring.json +36 -0
- package/ai/catalog-authoring.schema.json +109 -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 +79 -0
- package/ai/component-catalog.json +4041 -591
- package/ai/component-catalog.schema.json +78 -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 +524 -0
- package/ai/component-composition.d.ts +125 -0
- package/ai/component-composition.json +7074 -0
- package/ai/component-composition.schema.json +375 -0
- package/ai/components/README.md +171 -0
- package/ai/components/application-tabs.md +65 -0
- package/ai/components/badge.md +72 -0
- package/ai/components/chip.md +72 -0
- package/ai/components/collapsible-panel.md +102 -0
- package/ai/components/content-item.md +90 -0
- package/ai/components/disclosure-arrow.md +77 -0
- package/ai/components/document-baseline.md +75 -0
- package/ai/components/empty-state.md +80 -0
- package/ai/components/feedback.md +71 -0
- package/ai/components/floating-toolbar.md +82 -0
- package/ai/components/foundation.md +65 -0
- package/ai/components/grid.md +96 -0
- package/ai/components/headers.md +63 -0
- package/ai/components/layout.md +75 -0
- package/ai/components/list-action-row.md +91 -0
- package/ai/components/list-header.md +89 -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 +94 -0
- package/ai/components/list.md +103 -0
- package/ai/components/loading-spinner.md +72 -0
- package/ai/components/lucide-icon.md +66 -0
- package/ai/components/nav-stack.md +103 -0
- package/ai/components/pane.md +100 -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-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 +112 -0
- package/ai/components/skeleton.md +84 -0
- package/ai/components/spacer.md +86 -0
- package/ai/components/split-view.md +102 -0
- package/ai/components/state-banner.md +94 -0
- package/ai/components/sunken-panel.md +79 -0
- package/ai/components/surface-scaffold.md +78 -0
- package/ai/components/tab-bar.md +112 -0
- package/ai/components/tab-scaffold.md +94 -0
- package/ai/components/tabs.md +95 -0
- package/ai/components/text.md +83 -0
- package/ai/components/token-search-field.md +103 -0
- package/ai/components/toolbar-control-group.md +102 -0
- package/ai/components/toolbar-text.md +78 -0
- package/ai/components/toolbar.md +97 -0
- package/ai/components/value-table.md +73 -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 +59 -0
- package/ai/components/wa-button.md +55 -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 +114 -0
- package/ai/public-api-signatures-v1.md +2597 -190
- package/ai/skill.md +259 -65
- package/ai/webawesome-jsx-signatures-v1.md +9 -2
- package/analyzer/cli.mjs +49 -0
- package/analyzer/component-ownership.d.mts +56 -0
- package/analyzer/component-ownership.mjs +184 -0
- package/analyzer/index.d.mts +49 -0
- package/analyzer/index.mjs +1458 -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/dist/app-tab.d.ts +41 -4
- package/dist/app-tab.js +3 -1
- package/dist/badge.d.ts +52 -0
- package/dist/badge.js +3 -0
- package/dist/badge.js.map +1 -0
- package/dist/browser/app-tab.js +1 -0
- package/dist/browser/badge.js +3 -0
- package/dist/browser/catalog.js +25 -0
- package/dist/browser/chip.js +3 -0
- package/dist/browser/collapsible-panel.js +7 -0
- package/dist/browser/content-item.js +3 -0
- package/dist/browser/floating-toolbar.js +3 -0
- package/dist/browser/grid.js +3 -0
- package/dist/browser/list-action-row.js +5 -0
- package/dist/browser/list-header.js +8 -0
- package/dist/browser/list-inset-control.js +3 -0
- package/dist/browser/list-inset-text.js +3 -0
- package/dist/browser/list-item.js +5 -0
- package/dist/browser/list.js +3 -0
- package/dist/browser/{panel-header.js → nav-stack.js} +4 -2
- 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/select.js +2 -0
- package/dist/browser/skeleton.js +3 -0
- package/dist/browser/spacer.js +3 -0
- package/dist/browser/split-view.js +10 -0
- package/dist/browser/state-banner.js +2 -0
- package/dist/browser/sunken-panel.js +3 -0
- package/dist/browser/surface-scaffold.js +3 -0
- package/dist/browser/tab-scaffold.js +4 -0
- package/dist/browser/text.js +3 -0
- package/dist/browser/toolbar-text.js +1 -0
- package/dist/browser/value-table.js +1 -0
- package/dist/browser/workbench.js +10 -0
- package/dist/catalog-resources.d.ts +46 -0
- package/dist/catalog-resources.js +22 -0
- package/dist/catalog-resources.js.map +1 -0
- package/dist/catalog.d.ts +90 -0
- package/dist/catalog.js +501 -0
- package/dist/catalog.js.map +1 -0
- package/dist/chip.d.ts +29 -0
- package/dist/chip.js +3 -0
- package/dist/chip.js.map +1 -0
- package/dist/chunk-2G2ZJQYK.js +152 -0
- package/dist/chunk-2G2ZJQYK.js.map +1 -0
- package/dist/chunk-3SXMEK3B.js +43 -0
- package/dist/chunk-3SXMEK3B.js.map +1 -0
- package/dist/{chunk-W2FK7ZNY.js → chunk-4R777DUZ.js} +10 -2
- package/dist/chunk-4R777DUZ.js.map +1 -0
- package/dist/chunk-4SQK27MR.js +202 -0
- package/dist/chunk-4SQK27MR.js.map +1 -0
- package/dist/chunk-5I7QGQVT.js +24 -0
- package/dist/chunk-5I7QGQVT.js.map +1 -0
- package/dist/chunk-5PESG2VP.js +115 -0
- package/dist/chunk-5PESG2VP.js.map +1 -0
- package/dist/chunk-5Q3N6EV7.js +156 -0
- package/dist/chunk-5Q3N6EV7.js.map +1 -0
- package/dist/chunk-6ZXOQEAN.js +27 -0
- package/dist/chunk-6ZXOQEAN.js.map +1 -0
- package/dist/chunk-7JUBXOIV.js +103 -0
- package/dist/chunk-7JUBXOIV.js.map +1 -0
- package/dist/chunk-A3CKOW2R.js +622 -0
- package/dist/chunk-A3CKOW2R.js.map +1 -0
- package/dist/chunk-AHEG27M7.js +66 -0
- package/dist/chunk-AHEG27M7.js.map +1 -0
- package/dist/chunk-AUKT3KMG.js +37 -0
- package/dist/chunk-AUKT3KMG.js.map +1 -0
- package/dist/chunk-AYUHMAI6.js +48 -0
- package/dist/chunk-AYUHMAI6.js.map +1 -0
- package/dist/chunk-BFPBS5Y4.js +250 -0
- package/dist/chunk-BFPBS5Y4.js.map +1 -0
- package/dist/{chunk-MQIZAD4A.js → chunk-C4OYOKWX.js} +138 -22
- package/dist/chunk-C4OYOKWX.js.map +1 -0
- package/dist/chunk-DG6G5EPF.js +35 -0
- package/dist/chunk-DG6G5EPF.js.map +1 -0
- package/dist/chunk-E36EVCKG.js +34 -0
- package/dist/chunk-E36EVCKG.js.map +1 -0
- package/dist/chunk-EAC4CDY7.js +57 -0
- package/dist/chunk-EAC4CDY7.js.map +1 -0
- package/dist/chunk-EZVNC4J6.js +104 -0
- package/dist/chunk-EZVNC4J6.js.map +1 -0
- package/dist/chunk-F4QMLOXJ.js +135 -0
- package/dist/chunk-F4QMLOXJ.js.map +1 -0
- package/dist/chunk-GLDWRCAM.js +49 -0
- package/dist/chunk-GLDWRCAM.js.map +1 -0
- package/dist/chunk-I3Q7KKYM.js +33 -0
- package/dist/chunk-I3Q7KKYM.js.map +1 -0
- package/dist/chunk-IA7TMD4Y.js +288 -0
- package/dist/chunk-IA7TMD4Y.js.map +1 -0
- package/dist/chunk-JCV4GXWB.js +249 -0
- package/dist/chunk-JCV4GXWB.js.map +1 -0
- package/dist/chunk-K47P7QZC.js +17 -0
- package/dist/chunk-K47P7QZC.js.map +1 -0
- package/dist/chunk-KLJKU6ZL.js +109 -0
- package/dist/chunk-KLJKU6ZL.js.map +1 -0
- package/dist/{chunk-56RJXPHG.js → chunk-KROD7KZK.js} +7 -4
- package/dist/chunk-KROD7KZK.js.map +1 -0
- package/dist/chunk-MA73TA6D.js +42 -0
- package/dist/chunk-MA73TA6D.js.map +1 -0
- package/dist/chunk-MERWRTBG.js +45 -0
- package/dist/chunk-MERWRTBG.js.map +1 -0
- package/dist/chunk-MEY33VWB.js +44 -0
- package/dist/chunk-MEY33VWB.js.map +1 -0
- package/dist/chunk-MP2GDBQZ.js +50 -0
- package/dist/chunk-MP2GDBQZ.js.map +1 -0
- package/dist/chunk-N3GKMPDW.js +51 -0
- package/dist/chunk-N3GKMPDW.js.map +1 -0
- package/dist/chunk-NAPE7WUS.js +75 -0
- package/dist/chunk-NAPE7WUS.js.map +1 -0
- package/dist/chunk-NN6GYOA2.js +47 -0
- package/dist/chunk-NN6GYOA2.js.map +1 -0
- package/dist/chunk-NNMLEDMD.js +399 -0
- package/dist/chunk-NNMLEDMD.js.map +1 -0
- package/dist/chunk-NYL63PSD.js +95 -0
- package/dist/chunk-NYL63PSD.js.map +1 -0
- package/dist/chunk-P7D3EMXM.js +52 -0
- package/dist/chunk-P7D3EMXM.js.map +1 -0
- package/dist/chunk-PSHUSWQH.js +18 -0
- package/dist/chunk-PSHUSWQH.js.map +1 -0
- package/dist/chunk-Q6YU3BT7.js +138 -0
- package/dist/chunk-Q6YU3BT7.js.map +1 -0
- package/dist/chunk-RRB26JRJ.js +247 -0
- package/dist/chunk-RRB26JRJ.js.map +1 -0
- package/dist/{chunk-7BR3DO2A.js → chunk-SRSJO5QE.js} +13 -4
- package/dist/chunk-SRSJO5QE.js.map +1 -0
- package/dist/chunk-SWVW56CV.js +188 -0
- package/dist/chunk-SWVW56CV.js.map +1 -0
- package/dist/chunk-TEYARRAK.js +216 -0
- package/dist/chunk-TEYARRAK.js.map +1 -0
- package/dist/chunk-UCKKT6KO.js +71 -0
- package/dist/chunk-UCKKT6KO.js.map +1 -0
- package/dist/chunk-UIDKY4UB.js +14 -0
- package/dist/chunk-UIDKY4UB.js.map +1 -0
- package/dist/chunk-VCBTX62E.js +135 -0
- package/dist/chunk-VCBTX62E.js.map +1 -0
- package/dist/chunk-VZOFZPSO.js +71 -0
- package/dist/chunk-VZOFZPSO.js.map +1 -0
- package/dist/{chunk-TRNBHPNW.js → chunk-X5QSICJ3.js} +13 -7
- package/dist/chunk-X5QSICJ3.js.map +1 -0
- package/dist/chunk-Y74NWGQW.js +418 -0
- package/dist/chunk-Y74NWGQW.js.map +1 -0
- package/dist/chunk-ZLWQZUGF.js +27 -0
- package/dist/chunk-ZLWQZUGF.js.map +1 -0
- package/dist/collapsible-panel.d.ts +122 -0
- package/dist/collapsible-panel.js +115 -0
- package/dist/collapsible-panel.js.map +1 -0
- package/dist/content-item.d.ts +46 -0
- package/dist/content-item.js +4 -0
- package/dist/content-item.js.map +1 -0
- package/dist/css-values.d.ts +107 -0
- package/dist/css-values.js +3 -0
- package/dist/css-values.js.map +1 -0
- package/dist/device-class.js +33 -5
- package/dist/device-class.js.map +1 -1
- package/dist/disclosure-arrow.d.ts +6 -1
- package/dist/disclosure-arrow.js +1 -1
- package/dist/empty-state.d.ts +5 -2
- package/dist/empty-state.js +1 -1
- 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/grid.d.ts +42 -0
- package/dist/grid.js +5 -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/index.d.ts +35 -14
- package/dist/index.js +43 -22
- package/dist/list-action-row.d.ts +64 -0
- package/dist/list-action-row.js +8 -0
- package/dist/list-action-row.js.map +1 -0
- package/dist/list-header.d.ts +99 -0
- package/dist/list-header.js +10 -0
- package/dist/list-header.js.map +1 -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 +62 -0
- package/dist/list-item.js +8 -0
- package/dist/list-item.js.map +1 -0
- package/dist/list.d.ts +59 -0
- package/dist/list.js +6 -0
- package/dist/list.js.map +1 -0
- package/dist/loading-spinner.d.ts +3 -1
- package/dist/loading-spinner.js +1 -1
- package/dist/lucide-icon.d.ts +3 -1
- package/dist/lucide-icon.js +1 -1
- package/dist/nav-stack.d.ts +60 -10
- package/dist/nav-stack.js +6 -1
- package/dist/pane.d.ts +102 -0
- package/dist/pane.js +4 -0
- package/dist/pane.js.map +1 -0
- package/dist/panel-toolbar-DqwtBxQL.d.ts +63 -0
- package/dist/popup-menu-register.d.ts +9 -0
- package/dist/popup-menu-register.js +31 -0
- package/dist/popup-menu-register.js.map +1 -0
- package/dist/popup-menu.d.ts +99 -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 +48 -0
- package/dist/row.js +6 -0
- package/dist/row.js.map +1 -0
- package/dist/segmented-control.d.ts +13 -8
- package/dist/segmented-control.js +1 -1
- package/dist/select-register.d.ts +5 -1
- package/dist/select-register.js +243 -2
- package/dist/select-register.js.map +1 -1
- package/dist/select.d.ts +101 -8
- package/dist/select.js +4 -1
- package/dist/semantic-content-BbzjvSu9.d.ts +14 -0
- package/dist/sides-BPSWde0A.d.ts +4 -0
- package/dist/skeleton.d.ts +31 -0
- package/dist/skeleton.js +4 -0
- package/dist/skeleton.js.map +1 -0
- package/dist/spacer.d.ts +18 -0
- package/dist/spacer.js +4 -0
- package/dist/spacer.js.map +1 -0
- package/dist/split-view.d.ts +39 -7
- package/dist/split-view.js +120 -10
- package/dist/split-view.js.map +1 -1
- package/dist/state-banner.d.ts +19 -5
- package/dist/state-banner.js +4 -1
- package/dist/styles/app-tab.css +104 -0
- package/dist/styles/badge.css +118 -0
- package/dist/styles/catalog/components/catalog-example-stack.css +9 -0
- package/dist/styles/catalog/components/catalog-example.css +164 -0
- package/dist/styles/catalog/components/catalog-stage.css +55 -0
- package/dist/styles/catalog/components/catalog.css +12 -0
- package/dist/styles/catalog.css +5 -0
- package/dist/styles/chip.css +118 -0
- package/dist/styles/collapsible-panel.css +575 -0
- package/dist/styles/content-item.css +30 -0
- package/dist/styles/document.css +23 -0
- package/dist/styles/floating-toolbar.css +119 -0
- package/dist/styles/foundation.css +178 -40
- package/dist/styles/grid.css +28 -0
- package/dist/styles/layout.css +3 -41
- package/dist/styles/list-action-row.css +257 -0
- package/dist/styles/list-header.css +228 -0
- package/dist/styles/list-inset-control.css +30 -0
- package/dist/styles/list-inset-text.css +29 -0
- package/dist/styles/list-item.css +245 -0
- package/dist/styles/list.css +133 -0
- package/dist/styles/nav-stack.css +158 -50
- package/dist/styles/pane.css +229 -0
- package/dist/styles/popup-menu.css +271 -0
- package/dist/styles/resizable-region.css +429 -4
- package/dist/styles/row.css +111 -0
- package/dist/styles/segmented-control.css +33 -9
- package/dist/styles/select.css +262 -9
- package/dist/styles/skeleton.css +92 -0
- package/dist/styles/spacer.css +11 -0
- package/dist/styles/split-view.css +108 -3
- package/dist/styles/state-banner.css +94 -19
- package/dist/styles/styles.css +14 -4
- package/dist/styles/sunken-panel.css +22 -0
- package/dist/styles/surface-scaffold.css +56 -0
- package/dist/styles/tab-bar.css +132 -9
- package/dist/styles/tab-scaffold.css +113 -3
- package/dist/styles/text.css +65 -0
- package/dist/styles/token-search-field.css +49 -43
- package/dist/styles/toolbar-control-group.css +465 -28
- package/dist/styles/toolbar-text.css +88 -4
- package/dist/styles/toolbar.css +322 -11
- package/dist/styles/value-table.css +34 -4
- package/dist/styles/webawesome.css +348 -10
- package/dist/styles/workbench.css +828 -7
- package/dist/sunken-panel.d.ts +22 -0
- package/dist/sunken-panel.js +3 -0
- package/dist/sunken-panel.js.map +1 -0
- package/dist/surface-scaffold.d.ts +30 -0
- package/dist/surface-scaffold.js +3 -0
- package/dist/surface-scaffold.js.map +1 -0
- package/dist/tab-bar.d.ts +31 -6
- package/dist/tab-bar.js +1 -1
- package/dist/tab-scaffold.d.ts +54 -7
- package/dist/tab-scaffold.js +82 -9
- package/dist/tab-scaffold.js.map +1 -1
- package/dist/text.d.ts +38 -0
- package/dist/text.js +3 -0
- package/dist/text.js.map +1 -0
- package/dist/token-search-field-DvgFLhLL.d.ts +130 -0
- package/dist/token-search-field.d.ts +3 -43
- package/dist/token-search-field.js +1 -1
- 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 +65 -7
- package/dist/toolbar-control-group.js +1 -1
- package/dist/toolbar-text.d.ts +37 -4
- package/dist/toolbar-text.js +3 -1
- package/dist/toolbar.d.ts +56 -7
- package/dist/toolbar.js +1 -1
- package/dist/types-BuHELdXR.d.ts +117 -0
- package/dist/value-table.d.ts +13 -3
- package/dist/value-table.js +3 -1
- package/dist/webawesome.d.ts +8 -1
- package/dist/wire-catalog.d.ts +82 -0
- package/dist/wire-catalog.js +327 -0
- package/dist/wire-catalog.js.map +1 -0
- package/dist/wire-nav-stack.d.ts +4 -1
- package/dist/wire-nav-stack.js +147 -7
- package/dist/wire-nav-stack.js.map +1 -1
- package/dist/wire-resizable-regions.d.ts +2 -1
- package/dist/wire-resizable-regions.js +2 -1
- package/dist/wire-scroll-dividers.d.ts +69 -0
- package/dist/wire-scroll-dividers.js +3 -0
- package/dist/wire-scroll-dividers.js.map +1 -0
- package/dist/wire-sidebar.d.ts +63 -0
- package/dist/wire-sidebar.js +231 -0
- package/dist/wire-sidebar.js.map +1 -0
- package/dist/wire-tab-bars.d.ts +19 -2
- package/dist/wire-tab-bars.js +1 -1
- package/dist/wire-tab-scaffold.js +9 -4
- package/dist/wire-tab-scaffold.js.map +1 -1
- package/dist/wire-token-search-fields.d.ts +65 -3
- package/dist/wire-token-search-fields.js +1 -1
- package/dist/wire-workbench.d.ts +109 -0
- package/dist/wire-workbench.js +6 -0
- package/dist/wire-workbench.js.map +1 -0
- package/dist/workbench.d.ts +218 -7
- package/dist/workbench.js +13 -15
- package/dist/workbench.js.map +1 -1
- package/docs/accessibility.md +413 -23
- package/docs/app-layouts.md +106 -8
- package/docs/catalog.md +440 -0
- package/docs/collapsible-panel.md +363 -0
- package/docs/component-contract.md +560 -52
- package/docs/component-integration.md +75 -0
- package/docs/component-selection.md +421 -89
- package/docs/design/templates/empty-state/filtered-dark.svg +1 -0
- package/docs/design/templates/empty-state/filtered.svg +1 -0
- package/docs/design/templates/empty-state/with-action-dark.svg +1 -0
- package/docs/design/templates/empty-state/with-action.svg +1 -0
- package/docs/design/templates/empty-state-dark.svg +1 -0
- package/docs/design/templates/empty-state.svg +1 -0
- package/docs/design/templates/list/compact-dark.svg +1 -0
- package/docs/design/templates/list/compact.svg +1 -0
- package/docs/design/templates/list/stack-dark.svg +1 -0
- package/docs/design/templates/list/stack.svg +1 -0
- package/docs/design/templates/list-action-row/default-dark.svg +1 -0
- package/docs/design/templates/list-action-row/default.svg +1 -0
- package/docs/design/templates/list-action-row/selected-dark.svg +1 -0
- package/docs/design/templates/list-action-row/selected.svg +1 -0
- package/docs/design/templates/list-action-row-dark.svg +1 -0
- package/docs/design/templates/list-action-row.svg +1 -0
- package/docs/design/templates/list-dark.svg +1 -0
- package/docs/design/templates/list-header/action-dark.svg +1 -0
- package/docs/design/templates/list-header/action.svg +1 -0
- package/docs/design/templates/list-header/count-dark.svg +1 -0
- package/docs/design/templates/list-header/count.svg +1 -0
- package/docs/design/templates/list-header/inline-action-dark.svg +1 -0
- package/docs/design/templates/list-header/inline-action.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-collapsed.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded-dark.svg +1 -0
- package/docs/design/templates/list-header/toggle-expanded.svg +1 -0
- package/docs/design/templates/list-header-dark.svg +1 -0
- package/docs/design/templates/list-header.svg +1 -0
- package/docs/design/templates/list-item/default-dark.svg +1 -0
- package/docs/design/templates/list-item/default.svg +1 -0
- package/docs/design/templates/list-item/multiline-dark.svg +1 -0
- package/docs/design/templates/list-item/multiline.svg +1 -0
- package/docs/design/templates/list-item/selected-dark.svg +1 -0
- package/docs/design/templates/list-item/selected.svg +1 -0
- package/docs/design/templates/list-item/trailing-dark.svg +1 -0
- package/docs/design/templates/list-item/trailing.svg +1 -0
- package/docs/design/templates/list-item-dark.svg +1 -0
- package/docs/design/templates/list-item.svg +1 -0
- package/docs/design/templates/list.svg +1 -0
- package/docs/design/templates/segmented-control/equal-dark.svg +1 -0
- package/docs/design/templates/segmented-control/equal.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small-dark.svg +1 -0
- package/docs/design/templates/segmented-control/pill-small.svg +1 -0
- package/docs/design/templates/segmented-control-dark.svg +1 -0
- package/docs/design/templates/segmented-control.svg +1 -0
- package/docs/design/templates/skeleton/avatar-dark.svg +1 -0
- package/docs/design/templates/skeleton/avatar.svg +1 -0
- package/docs/design/templates/skeleton/block-dark.svg +1 -0
- package/docs/design/templates/skeleton/block.svg +1 -0
- package/docs/design/templates/skeleton/lines-dark.svg +1 -0
- package/docs/design/templates/skeleton/lines.svg +1 -0
- package/docs/design/templates/skeleton-dark.svg +1 -0
- package/docs/design/templates/skeleton.svg +1 -0
- package/docs/design/templates/state-banner/danger-dark.svg +1 -0
- package/docs/design/templates/state-banner/danger.svg +1 -0
- package/docs/design/templates/state-banner/info-badge-dark.svg +1 -0
- package/docs/design/templates/state-banner/info-badge.svg +1 -0
- package/docs/design/templates/state-banner/info-dark.svg +1 -0
- package/docs/design/templates/state-banner/info.svg +1 -0
- package/docs/design/templates/state-banner/success-dark.svg +1 -0
- package/docs/design/templates/state-banner/success.svg +1 -0
- package/docs/design/templates/state-banner/warning-dark.svg +1 -0
- package/docs/design/templates/state-banner/warning.svg +1 -0
- package/docs/design/templates/state-banner-dark.svg +1 -0
- package/docs/design/templates/state-banner.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs-dark.svg +1 -0
- package/docs/design/templates/tab-bar/workspace-tabs.svg +1 -0
- package/docs/design/templates/tab-bar-dark.svg +1 -0
- package/docs/design/templates/tab-bar.svg +1 -0
- package/docs/design/templates/token-search-field/empty-dark.svg +1 -0
- package/docs/design/templates/token-search-field/empty.svg +1 -0
- package/docs/design/templates/token-search-field/tokens-dark.svg +1 -0
- package/docs/design/templates/token-search-field/tokens.svg +1 -0
- package/docs/design/templates/token-search-field-dark.svg +1 -0
- package/docs/design/templates/token-search-field.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions-dark.svg +1 -0
- package/docs/design/templates/toolbar/title-and-actions.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/borderless-single.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/icon-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/push-buttons.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group/with-text.svg +1 -0
- package/docs/design/templates/toolbar-control-group-dark.svg +1 -0
- package/docs/design/templates/toolbar-control-group.svg +1 -0
- package/docs/design/templates/toolbar-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/default-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/default.svg +1 -0
- package/docs/design/templates/toolbar-text/large-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/large.svg +1 -0
- package/docs/design/templates/toolbar-text/small-dark.svg +1 -0
- package/docs/design/templates/toolbar-text/small.svg +1 -0
- package/docs/design/templates/toolbar-text-dark.svg +1 -0
- package/docs/design/templates/toolbar-text.svg +1 -0
- package/docs/design/templates/toolbar.svg +1 -0
- package/docs/design/templates/value-table/details-dark.svg +1 -0
- package/docs/design/templates/value-table/details.svg +1 -0
- package/docs/design/templates/value-table-dark.svg +1 -0
- package/docs/design/templates/value-table.svg +1 -0
- package/docs/design/templates.md +138 -0
- package/docs/design-philosophy.md +48 -20
- package/docs/device-class.md +10 -9
- package/docs/document-baseline.md +38 -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 +45 -0
- package/docs/examples/component-composition-extension.json +80 -0
- package/docs/layout.md +409 -59
- package/docs/nav-stack.md +126 -14
- package/docs/recipes.md +137 -54
- package/docs/split-view.md +69 -6
- package/docs/sunken-panel.md +58 -0
- package/docs/surface-scaffold.md +25 -0
- package/docs/tab-scaffold.md +94 -10
- package/docs/type-contracts.md +134 -0
- package/docs/ui-analyzer.md +306 -0
- package/docs/ui-doctor.md +107 -0
- package/docs/ui-evaluator.md +139 -0
- package/docs/ux-demo.md +40 -16
- package/docs/webawesome-theme.md +175 -16
- package/docs/workbench.md +534 -12
- package/doctor/cli.mjs +115 -0
- package/doctor/config.schema.json +68 -0
- package/doctor/eslint-diagnostics.d.mts +24 -0
- package/doctor/eslint-diagnostics.mjs +24 -0
- package/doctor/index.d.mts +116 -0
- package/doctor/index.mjs +1277 -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 +423 -42
- package/package.json +241 -37
- package/traversal-exclusions.mjs +27 -0
- package/ux-demo/recipes/app-shell.tsx +205 -17
- package/ux-demo/recipes/collapsible-sidebar.tsx +260 -0
- 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 +282 -0
- package/ux-demo/recipes/list-workspace-states.tsx +183 -11
- package/ux-demo/recipes/loaders.ts +15 -5
- package/ux-demo/recipes/loading-inspector.tsx +204 -0
- package/ux-demo/recipes/mount-recipe.ts +28 -10
- package/ux-demo/recipes/navigation-sidebar.tsx +139 -13
- package/ux-demo/recipes/navigation-stack.tsx +79 -24
- package/ux-demo/recipes/types.ts +23 -2
- package/ux-demo/recipes/workspace-header.tsx +86 -5
- package/dist/browser/menu-action-row.js +0 -3
- package/dist/browser/menu-header.js +0 -5
- package/dist/browser/menu-item.js +0 -3
- package/dist/chunk-24Z2XE6C.js +0 -10
- package/dist/chunk-24Z2XE6C.js.map +0 -1
- package/dist/chunk-2RJBFNB6.js +0 -172
- package/dist/chunk-2RJBFNB6.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.map +0 -1
- package/dist/chunk-7LDCTU7D.js +0 -134
- package/dist/chunk-7LDCTU7D.js.map +0 -1
- package/dist/chunk-AQNKJRFH.js +0 -79
- package/dist/chunk-AQNKJRFH.js.map +0 -1
- package/dist/chunk-CTQKI2XL.js +0 -28
- package/dist/chunk-CTQKI2XL.js.map +0 -1
- package/dist/chunk-DAFFDO33.js +0 -14
- package/dist/chunk-DAFFDO33.js.map +0 -1
- package/dist/chunk-EGHL5S4P.js +0 -33
- package/dist/chunk-EGHL5S4P.js.map +0 -1
- package/dist/chunk-KZKSBUKC.js +0 -19
- package/dist/chunk-KZKSBUKC.js.map +0 -1
- package/dist/chunk-M5JRI6MN.js +0 -27
- package/dist/chunk-M5JRI6MN.js.map +0 -1
- package/dist/chunk-MBR3TYNL.js +0 -14
- package/dist/chunk-MBR3TYNL.js.map +0 -1
- package/dist/chunk-MFKMHK5W.js +0 -10
- package/dist/chunk-MFKMHK5W.js.map +0 -1
- package/dist/chunk-MQIZAD4A.js.map +0 -1
- package/dist/chunk-NPPD2A6O.js +0 -40
- package/dist/chunk-NPPD2A6O.js.map +0 -1
- package/dist/chunk-R6JHHBKQ.js +0 -19
- package/dist/chunk-R6JHHBKQ.js.map +0 -1
- package/dist/chunk-RBTVBGRD.js +0 -23
- package/dist/chunk-RBTVBGRD.js.map +0 -1
- package/dist/chunk-S3TPM2LP.js +0 -23
- package/dist/chunk-S3TPM2LP.js.map +0 -1
- package/dist/chunk-TRNBHPNW.js.map +0 -1
- package/dist/chunk-VHW5YNIB.js +0 -17
- package/dist/chunk-VHW5YNIB.js.map +0 -1
- package/dist/chunk-W2FK7ZNY.js.map +0 -1
- package/dist/chunk-WVPYVDXY.js +0 -15
- package/dist/chunk-WVPYVDXY.js.map +0 -1
- package/dist/chunk-XZXRLY5Q.js +0 -33
- package/dist/chunk-XZXRLY5Q.js.map +0 -1
- package/dist/menu-action-row.d.ts +0 -50
- package/dist/menu-action-row.js +0 -4
- package/dist/menu-action-row.js.map +0 -1
- package/dist/menu-header.d.ts +0 -41
- package/dist/menu-header.js +0 -6
- package/dist/menu-header.js.map +0 -1
- package/dist/menu-item.d.ts +0 -31
- package/dist/menu-item.js +0 -4
- package/dist/menu-item.js.map +0 -1
- package/dist/panel-header.d.ts +0 -26
- package/dist/panel-header.js +0 -6
- package/dist/panel-header.js.map +0 -1
- package/dist/styles/menu-action-row.css +0 -160
- package/dist/styles/menu-header.css +0 -160
- package/dist/styles/menu-item.css +0 -123
- package/dist/styles/panel-header.css +0 -59
- package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
- package/ux-demo/recipes/recipes.css +0 -355
package/docs/tab-scaffold.md
CHANGED
|
@@ -7,11 +7,16 @@ opt-in app layouts (see [`../../docs/23-app-layouts.md`](../../docs/23-app-layou
|
|
|
7
7
|
It is distinct from `TabBar` (document-oriented, reorderable strips).
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
|
-
import { TabScaffold } from
|
|
11
|
-
import { wireTabScaffold } from
|
|
12
|
-
import '@kerfjs/ui/tab-scaffold.css';
|
|
10
|
+
import { TabScaffold } from "@kerfjs/ui/tab-scaffold";
|
|
11
|
+
import { wireTabScaffold } from "@kerfjs/ui/wire-tab-scaffold";
|
|
13
12
|
```
|
|
14
13
|
|
|
14
|
+
In a browser bundler that honors the `browser` export condition (Vite, esbuild, and
|
|
15
|
+
webpack do by default), the import above also
|
|
16
|
+
loads TabScaffold's stylesheet and those of the components it renders internally
|
|
17
|
+
(the tab `Badge`). Without that condition, import the manual stylesheets instead:
|
|
18
|
+
`@kerfjs/ui/tab-scaffold.css` plus those components' CSS, or `@kerfjs/ui/styles.css`.
|
|
19
|
+
|
|
15
20
|
## Controlled selection
|
|
16
21
|
|
|
17
22
|
The app owns the active tab (a signal); `TabScaffold` renders every tab's scene
|
|
@@ -19,23 +24,102 @@ The app owns the active tab (a signal); `TabScaffold` renders every tab's scene
|
|
|
19
24
|
clicks.
|
|
20
25
|
|
|
21
26
|
```tsx
|
|
22
|
-
const active = signal(
|
|
27
|
+
const active = signal("home");
|
|
23
28
|
|
|
24
29
|
<TabScaffold
|
|
25
30
|
id="app"
|
|
26
31
|
label="Sections"
|
|
27
32
|
active={active.value}
|
|
28
33
|
tabs={[
|
|
29
|
-
{ id:
|
|
30
|
-
{
|
|
34
|
+
{ id: "home", label: "Home", icon: <HomeIcon />, content: <HomeStack /> },
|
|
35
|
+
{
|
|
36
|
+
id: "search",
|
|
37
|
+
label: "Search",
|
|
38
|
+
icon: <SearchIcon />,
|
|
39
|
+
content: <SearchStack />,
|
|
40
|
+
},
|
|
31
41
|
]}
|
|
32
42
|
/>;
|
|
33
43
|
|
|
34
44
|
// once, after first render:
|
|
35
|
-
const dispose = wireTabScaffold(root, {
|
|
45
|
+
const dispose = wireTabScaffold(root, {
|
|
46
|
+
onSelect: (id) => {
|
|
47
|
+
active.value = id;
|
|
48
|
+
},
|
|
49
|
+
});
|
|
36
50
|
```
|
|
37
51
|
|
|
38
|
-
Each `TabScaffoldTab` has an `id`, `label`, optional `icon`,
|
|
39
|
-
|
|
40
|
-
|
|
52
|
+
Each `TabScaffoldTab` has an `id`, `label`, optional `icon`, optional `badge`
|
|
53
|
+
and `badgeLabel` (see [Tab badges](#tab-badges)), and `content`. The
|
|
54
|
+
bottom bar paints through the home-indicator safe area and pads for it and for
|
|
55
|
+
the side insets, while each scene pads for the top and side insets (a scene
|
|
56
|
+
whose only child is a `NavStack` or `Pane` lets that child own them; see
|
|
57
|
+
[Choosing an app layout › Safe areas](app-layouts.md#safe-areas)). The bar keeps 44px targets. Its single-line labels preserve their full line box and
|
|
58
|
+
truncate horizontally with an ellipsis when a destination name exceeds its
|
|
59
|
+
share of the bar. On larger device classes, promote the tab set to a
|
|
41
60
|
`Workbench` rail or a persistent sidebar instead of a bottom bar.
|
|
61
|
+
|
|
62
|
+
## Scroll divider
|
|
63
|
+
|
|
64
|
+
The bar has no permanent top border. With `wireScrollDividers(appRoot)`
|
|
65
|
+
installed (see [Layout › Scroll dividers](layout.md#scroll-dividers)), it shows
|
|
66
|
+
a line along its top edge only while the active scene's content continues below
|
|
67
|
+
it — not at the scroll end and not when the content fits. The line keys on
|
|
68
|
+
whatever actually scrolls in the scene: the scene itself or, through a sole
|
|
69
|
+
child, a `NavStack`'s active view or a `Pane`'s content. A `NavStack` with its
|
|
70
|
+
own bottom toolbar sits between them, so that toolbar's line applies instead.
|
|
71
|
+
The line is the bar's always-present 1px top border, colored only while shown,
|
|
72
|
+
so it never moves the bar or a tab.
|
|
73
|
+
|
|
74
|
+
`chromeDividers` configures it, with the same values as a `Pane`'s: `scroll`
|
|
75
|
+
(the default) follows the scroll state and draws nothing until the wiring runs;
|
|
76
|
+
`always` keeps the line, with or without the wiring; `none` drops it even when
|
|
77
|
+
the wiring reports content below.
|
|
78
|
+
|
|
79
|
+
## Tab badges
|
|
80
|
+
|
|
81
|
+
Give a tab a `badge` (a count or short string such as `3`, `"99+"`, or
|
|
82
|
+
`"New"`) to show the iOS tab-bar badge: a compact, solid `danger` `Badge` at the
|
|
83
|
+
top-trailing corner of the tab's icon, growing outward as the text lengthens
|
|
84
|
+
(without an icon it sits above the label). The badge is decorative
|
|
85
|
+
(`aria-hidden`); its meaning reaches assistive technology through `badgeLabel`,
|
|
86
|
+
which the tab folds into its accessible name as `"<label>, <badgeLabel>"`:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
{
|
|
90
|
+
id: "inbox",
|
|
91
|
+
label: "Inbox",
|
|
92
|
+
icon: <LucideIcon icon={Inbox} name="inbox" />,
|
|
93
|
+
badge: unread.value,
|
|
94
|
+
badgeLabel: `${unread.value} unread`,
|
|
95
|
+
content: <InboxStack />,
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`badgeLabel` defaults to the badge text itself, so supply a localized phrase
|
|
100
|
+
whenever the bare text would be ambiguous. Omitting `badge` (or passing an
|
|
101
|
+
empty string or a non-finite number) renders no badge and leaves the tab's name
|
|
102
|
+
as its label; `0` renders, so pass `undefined` when a count should disappear.
|
|
103
|
+
Keep the text short — clamp large counts (`"99+"`) in the app. The tone follows
|
|
104
|
+
the platform convention and is fixed; do not restyle the badge.
|
|
105
|
+
|
|
106
|
+
### Dot badges
|
|
107
|
+
|
|
108
|
+
For new content without a count, pass `badge: true`. The tab shows the iOS
|
|
109
|
+
tab-bar dot — an 8px solid `danger` `Badge` (`size="dot"`) centered on the
|
|
110
|
+
icon's top-trailing corner (above the label without an icon). A dot has no
|
|
111
|
+
text to fall back on, so `badgeLabel` is required by the types:
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
{
|
|
115
|
+
id: "feed",
|
|
116
|
+
label: "Feed",
|
|
117
|
+
icon: <LucideIcon icon={Rss} name="rss" />,
|
|
118
|
+
badge: hasNewPosts.value ? true : undefined,
|
|
119
|
+
badgeLabel: "New activity",
|
|
120
|
+
content: <FeedStack />,
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The tab's accessible name becomes `"Feed, New activity"`. Pass `undefined` to
|
|
125
|
+
clear the dot; `false` is not a badge value.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Compile-time contracts
|
|
2
|
+
|
|
3
|
+
Kerf UI rejects invalid integrations at compile time only when TypeScript can
|
|
4
|
+
prove the relationship from one call. The versioned
|
|
5
|
+
[`compile-time-contracts-v1.json`](../ai/compile-time-contracts-v1.json)
|
|
6
|
+
artifact names those guarantees with stable `KUI-T###` ids. Its source fixture
|
|
7
|
+
is compiled twice: against `src/` and against declarations extracted from the
|
|
8
|
+
actual `npm pack` tarball. `npm run check:catalog` also verifies that every
|
|
9
|
+
contract still points to a real catalog entry/import and a symbol in the emitted
|
|
10
|
+
public-signature artifact.
|
|
11
|
+
|
|
12
|
+
## Strengthened contracts
|
|
13
|
+
|
|
14
|
+
| ID | Compile-time guarantee |
|
|
15
|
+
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| `KUI-T001` | `ListHeader` is a passive heading, a fully named/icon-bearing trailing action, or a controlled disclosure with `action` + `expanded`. Props ignored by the selected mode are rejected. |
|
|
17
|
+
| `KUI-T002` | `Select` has a visible `label` or an `ariaLabel`. |
|
|
18
|
+
| `KUI-T003` | A literal `Select.value` belongs to its literal `choices`. |
|
|
19
|
+
| `KUI-T004` | A literal `SegmentedControl.value` belongs to its literal `choices`. |
|
|
20
|
+
| `KUI-T005` | `expanded`, `expandAction`, and `expandLabel` exist only on `TokenSearchField({ collapsible: true })`. |
|
|
21
|
+
| `KUI-T006` | `TokenSearchEditorAttributes` accepts application `data-*` metadata but rejects component-owned identity, morph, count, and placeholder attributes. |
|
|
22
|
+
| `KUI-T007` | `ToolbarText.maxLines` requires `wrap: true`; it is never silently ignored in typed code. |
|
|
23
|
+
| `KUI-T008` | A literal `TabScaffold.active` id belongs to its literal `tabs`. |
|
|
24
|
+
| `KUI-T009` | Adjacent-token keyboard removal requires `onRemoveToken`; disabling removal rejects the now-meaningless callback. |
|
|
25
|
+
| `KUI-T010` | Finite public variants have named exported union types, including divider sides, tab activation, banner urgency, and all AppTab, TabBar, ToolbarControlGroup, and Select presentation axes; the convenience root barrel re-exports them. |
|
|
26
|
+
| `KUI-T011` | Semantic component zones use the recursive `KerfUiContent` type: `SafeHtml`, runtime-empty booleans/nullish values, and readonly nested arrays are valid; arbitrary strings, numbers, and signals are rejected. Explicit text positions such as `ListInsetText` retain their text exception. |
|
|
27
|
+
| `KUI-T012` | `List.gap` accepts finite `UiSpaceName` shorthands or a complete branded `CssLength`; raw strings and non-standalone `CssLengthExpression` arithmetic are rejected. `calc(plus(...))` promotes a typed expression to a complete value. |
|
|
28
|
+
| `KUI-T013` | CSS-valued props preserve property grammar: `List.flex` uses `CssFlex`, `Skeleton` dimensions use `CssSize`/`CssLength`, and choice icons use `CssForegroundColor` (foreground tokens via `uiColor()` or `foregroundColorVar()`; fill tokens such as `uiColor('success-fill-quiet')` are rejected); row declaration strings are absent. |
|
|
29
|
+
| `KUI-T014` | Stable single-root visual components accept the explicit native `slot` attribute. Multi-root `ResizableRegion` and `CollapsiblePanel` remain excluded because no single returned element can own the assignment, and other arbitrary native attributes remain rejected. |
|
|
30
|
+
|
|
31
|
+
`KerfUiContent` lets conditionals and mapped component collections be direct
|
|
32
|
+
siblings without an otherwise-unnecessary `Fragment`:
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
<List>
|
|
36
|
+
{intro ? <ListInsetText>{intro}</ListInsetText> : null}
|
|
37
|
+
{rows.map((row) => (
|
|
38
|
+
<ListItem label={row.label} />
|
|
39
|
+
))}
|
|
40
|
+
</List>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The type is intentionally narrower than core JSX children. Wrap prose in a
|
|
44
|
+
semantic text component, and read signals into components or structural
|
|
45
|
+
conditionals rather than passing signal objects as content.
|
|
46
|
+
|
|
47
|
+
Literal identity inference is intentionally additive for dynamic applications.
|
|
48
|
+
When choices or ids arrive at runtime and the state signal is correctly typed as
|
|
49
|
+
`string`, widen the component explicitly:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<Select<string> value={loadedValue.value} choices={loadedChoices.value} ... />
|
|
53
|
+
<SegmentedControl<string> value={mode.value} choices={loadedModes.value} ... />
|
|
54
|
+
<TabScaffold<string> active={active.value} tabs={loadedTabs.value} ... />
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The widening is an honest statement that TypeScript cannot know the runtime
|
|
58
|
+
dataset. Do not cast a known literal typo merely to bypass the relationship.
|
|
59
|
+
|
|
60
|
+
## Complete public-surface audit
|
|
61
|
+
|
|
62
|
+
The following existing contracts were already exact and remain covered by the
|
|
63
|
+
ordinary source/type gates:
|
|
64
|
+
|
|
65
|
+
- required identity or accessible-name inputs on `AppTab`, `FloatingToolbar`,
|
|
66
|
+
`NavStack`, `SplitView`, `Workbench`, `TabBar`, `ResizableRegion`,
|
|
67
|
+
`SegmentedControl`, `TokenSearchField`, and `ValueTable`;
|
|
68
|
+
- finite variants exported or represented as literal props on `DisclosureArrow`,
|
|
69
|
+
`FloatingToolbar`, `Pane`, `ResizableRegion`, `SegmentedControl`, `StateBanner`,
|
|
70
|
+
`SunkenPanel`, `ToolbarText`, `CollapsiblePanel`, `Catalog`, and the wire helpers;
|
|
71
|
+
- required controlled-state callbacks on `wireResizableRegions`, `wireTabBars`,
|
|
72
|
+
and `wireTabScaffold`, and the required app-owned size signals on
|
|
73
|
+
`wireWorkbench`;
|
|
74
|
+
- mutually exclusive count/badge metadata on `ListHeader`; and
|
|
75
|
+
- protected application metadata boundaries on `AppTab`, `ListItem`,
|
|
76
|
+
`ListActionRow`, `ListHeader`, `Pane`, `CatalogExample`, and
|
|
77
|
+
`CatalogExampleStack`.
|
|
78
|
+
|
|
79
|
+
The remaining components (`LucideIcon`, `DisclosureArrow`, `Toolbar`,
|
|
80
|
+
`ToolbarControlGroup`, `ListInsetControl`, `ListInsetText`, `LoadingSpinner`,
|
|
81
|
+
`Skeleton`, `EmptyState`, `StateBanner`, `ValueTableRow`, and the
|
|
82
|
+
pure helpers) have independent props with no further conditional invalid state
|
|
83
|
+
that TypeScript can truthfully remove. Optional labels on decorative icons and
|
|
84
|
+
unnamed non-landmark surfaces are intentional variants, not missing contracts.
|
|
85
|
+
|
|
86
|
+
## Boundaries TypeScript cannot prove
|
|
87
|
+
|
|
88
|
+
Types do not claim to validate:
|
|
89
|
+
|
|
90
|
+
- the contents of `SafeHtml` slots (for example, “only AppTab children” or “no
|
|
91
|
+
interactive descendants”);
|
|
92
|
+
- nonempty arrays, unique ids, numeric min/max ordering, or a controlled value
|
|
93
|
+
loaded from dynamic data;
|
|
94
|
+
- equality between ids rendered in separate calls, elements discovered later in
|
|
95
|
+
the DOM, or an ARIA relationship's live target;
|
|
96
|
+
- that a caller retained and invoked a wiring disposer; or
|
|
97
|
+
- CSS class/token use inside arbitrary strings.
|
|
98
|
+
|
|
99
|
+
Those remain catalog `KUI-C###` diagnostics, runtime filtering/guards, lint or
|
|
100
|
+
browser assertions. Encoding them as permissive-looking TypeScript brands would
|
|
101
|
+
move errors into casts without making the integration safer.
|
|
102
|
+
|
|
103
|
+
## Migration from the earlier declarations
|
|
104
|
+
|
|
105
|
+
- Add `actionLabel` and `actionIcon` to a non-toggle `ListHeader` action. For a
|
|
106
|
+
disclosure, set literal `toggle: true`, provide `action` and controlled
|
|
107
|
+
`expanded`, and remove trailing-action-only props.
|
|
108
|
+
- Give every `Select` either `label` or `ariaLabel`. Use an explicit `<string>`
|
|
109
|
+
generic only for genuinely dynamic choice data.
|
|
110
|
+
- Put `expanded`/expand-action props behind `collapsible: true`.
|
|
111
|
+
- Replace `wireTokenSearchFields({ keyboard: true })` with either
|
|
112
|
+
`{ keyboard: { onRemoveToken } }` or
|
|
113
|
+
`{ keyboard: { removeAdjacentToken: false } }` when only caret navigation is
|
|
114
|
+
wanted.
|
|
115
|
+
- Add `wrap: true` anywhere `ToolbarText.maxLines` is used.
|
|
116
|
+
- Replace `Toolbar({ divider: false })` with `dividerSides: ''`, which is now
|
|
117
|
+
the default (a toolbar pinned over scrolling content gets its divider from
|
|
118
|
+
`wireScrollDividers` through its `Pane`); canonical combinations such as `b`,
|
|
119
|
+
`tr`, and `trbl` select permanent physical edges.
|
|
120
|
+
- Replace raw `List.gap` strings with a direct spacing shorthand (`"xs"`,
|
|
121
|
+
`"m"`) or a complete value from `@kerfjs/ui/css-values`. Replace
|
|
122
|
+
`gap="0.25rem"` with `gap={rem(0.25)}` and wrap sums with `calc(plus(...))`.
|
|
123
|
+
- Replace raw `List.flex` shorthands with `flex(grow, shrink, basis)` or a finite
|
|
124
|
+
keyword; replace raw Skeleton dimensions with `px`/`rem`/`em`/`pct` builders;
|
|
125
|
+
and replace raw choice colors with a foreground token such as
|
|
126
|
+
`uiColor('success-on-quiet')` or an app-owned `foregroundColorVar()`.
|
|
127
|
+
`SelectChoice.color` rejects fill tokens (`uiColor('success-fill-quiet')`) and
|
|
128
|
+
unqualified `colorVar()` values because a fill paints a nearly invisible
|
|
129
|
+
icon.
|
|
130
|
+
- Remove `style` from `ListItem` and `ListActionRow`; use `className`, public
|
|
131
|
+
tokens, and cataloged props. These breaking cleanups land before 5.0 stable.
|
|
132
|
+
|
|
133
|
+
JavaScript runtime behavior remains defensive for previously emitted calls, but
|
|
134
|
+
new TypeScript builds report these invalid or ignored combinations.
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# Static CSS and layout ownership analyzer
|
|
2
|
+
|
|
3
|
+
`kerf-ui-analyze` checks integration facts that TypeScript and an individual
|
|
4
|
+
ESLint file cannot see across component markup, stylesheets, the component
|
|
5
|
+
catalog, and the application UI profile.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx kerf-ui-analyze --root . --format text src
|
|
9
|
+
npx kerf-ui-analyze --root . --format json --output artifacts/kerf-ui.json
|
|
10
|
+
npx kerf-ui-analyze --root . --format sarif --output artifacts/kerf-ui.sarif
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The analyzer is opt-in. Add a project script such as
|
|
14
|
+
`"check:kerf-ui-layout": "kerf-ui-analyze --root . src"`; it never executes
|
|
15
|
+
application code. It discovers `.kerf-ui-profile.json`, joins every declared
|
|
16
|
+
composition catalog by `package:id`, parses literal JavaScript/TypeScript
|
|
17
|
+
component calls, TSX/JSX, and CSS, and reports portable repository-relative
|
|
18
|
+
locations.
|
|
19
|
+
|
|
20
|
+
An imported component name is resolved to a catalog entry by `package:id`, not
|
|
21
|
+
by display name: several entries share one (the Kerf `Select` and the Web
|
|
22
|
+
Awesome `wa-select` are both "Select"), so the analyzer joins each composition
|
|
23
|
+
entry to its selection-catalog public exports and import subpaths and picks
|
|
24
|
+
the entry that the import actually names. That keeps the Kerf component's
|
|
25
|
+
CSS-value contracts (`KUI-L013`–`KUI-L017`) in force for `Select` and
|
|
26
|
+
`Skeleton`.
|
|
27
|
+
|
|
28
|
+
Only component exports resolve to an entry. A selection-catalog
|
|
29
|
+
`publicExports` list also carries the helpers a component's props consume
|
|
30
|
+
(`Select` ships `uiColor`, `List` ships `px`, `rem`, and `flex`), and a helper
|
|
31
|
+
never inherits its entry's contracts, so `uiColor({ choices: [...] })` is not
|
|
32
|
+
checked as a `Select` call. A component export is one whose name starts with
|
|
33
|
+
an uppercase letter, the same rule JSX uses to tell a component tag from an
|
|
34
|
+
intrinsic element; `eslint-plugin-kerfjs` applies the identical rule, and the
|
|
35
|
+
package's catalog check fails if a cataloged render function (a value
|
|
36
|
+
returning `SafeHtml`) is lowercase or any other runtime value is uppercase.
|
|
37
|
+
Third-party catalogs should follow the same naming convention.
|
|
38
|
+
|
|
39
|
+
Analysis is scoped per source file. Each TSX/JSX file resolves package,
|
|
40
|
+
workspace, and parent-to-child directory profiles from its own location, then
|
|
41
|
+
follows its relative CSS imports (including nested CSS `@import`s). Stylesheet
|
|
42
|
+
facts are never pooled across unrelated files or sibling packages, and a shared
|
|
43
|
+
stylesheet's diagnostics are evaluated against every importing source profile.
|
|
44
|
+
A violation stays active when any consumer has not narrowly excepted it;
|
|
45
|
+
duplicate source findings collapse to one result. Directly targeted or orphaned
|
|
46
|
+
CSS uses its own directory profile. A direct changed-file target still brings
|
|
47
|
+
its reachable project-local styles into the report, including quoted or
|
|
48
|
+
unquoted `url()` imports; external package styles remain outside the consumer
|
|
49
|
+
boundary.
|
|
50
|
+
|
|
51
|
+
Recursive discovery excludes generated and tool-owned trees, including any
|
|
52
|
+
nested `.claude/worktrees` checkout. Those checkouts are separate repositories,
|
|
53
|
+
not application source, and cannot contribute files, diagnostics, profile
|
|
54
|
+
policy, or analysis inputs to the containing application.
|
|
55
|
+
|
|
56
|
+
## Diagnostics and exit behavior
|
|
57
|
+
|
|
58
|
+
| Rule | Kind | Meaning |
|
|
59
|
+
| ---------- | ------ | ------------------------------------------------------------------------------------- |
|
|
60
|
+
| `KUI-L001` | error | A stylesheet reaches into a private or unknown `.kui-*` selector. |
|
|
61
|
+
| `KUI-L002` | error | A stylesheet references an unknown or private `--kui-*` token. |
|
|
62
|
+
| `KUI-L003` | error | One element combines public classes that claim the same geometry. |
|
|
63
|
+
| `KUI-L004` | review | Nested literal classes both add a content inset. |
|
|
64
|
+
| `KUI-L005` | — | Retired; folded into `KUI-L019` (see below). |
|
|
65
|
+
| `KUI-L006` | review | Literal spacing falls outside Kerf's approved scale. |
|
|
66
|
+
| `KUI-L007` | error | A declared scroll owner is nested inside another scroll owner. |
|
|
67
|
+
| `KUI-L008` | review | A dynamic class expression cannot be classified soundly. |
|
|
68
|
+
| `KUI-L009` | error | A stylesheet cannot be parsed. |
|
|
69
|
+
| `KUI-L010` | error | A selector reaches a private Kerf descendant from a public root. |
|
|
70
|
+
| `KUI-L011` | error | A `::part()` target is not cataloged as a public extension point. |
|
|
71
|
+
| `KUI-L012` | error | CSS assigns a Kerf token without a public configuration contract. |
|
|
72
|
+
| `KUI-L013` | error | A CSS-adjacent prop uses an unknown shorthand or raw literal. |
|
|
73
|
+
| `KUI-L014` | error | A typed helper produces the wrong property grammar. |
|
|
74
|
+
| `KUI-L015` | error | An expression-only helper is passed without a composer. |
|
|
75
|
+
| `KUI-L016` | error | A removed declaration-list escape hatch is used. |
|
|
76
|
+
| `KUI-L017` | review | A valid but exceptional off-scale shorthand needs justification. |
|
|
77
|
+
| `KUI-L018` | review | A loud fill override lacks a governing `on-loud`, or the literal pair is below 4.5:1. |
|
|
78
|
+
| `KUI-L019` | error | A rule's subject is another package's component (class, `data-component`, or tag). |
|
|
79
|
+
| `KUI-L020` | error | CSS reads or writes another component's private `--_*` variable. |
|
|
80
|
+
| `KUI-L021` | error | CSS overrides a component token that a typed prop sets. |
|
|
81
|
+
| `KUI-L022` | error | A class placed on a component's root is styled by the application (a hook class). |
|
|
82
|
+
|
|
83
|
+
Errors are provable contract violations and make the command exit 1. Review
|
|
84
|
+
findings are deliberately heuristic and do not fail by default; pass
|
|
85
|
+
`--fail-on-review` when a project has reviewed its baseline and wants them to
|
|
86
|
+
gate CI. Text, versioned JSON, and SARIF carry the same stable rule ids,
|
|
87
|
+
source locations, evidence, and ownership chain.
|
|
88
|
+
|
|
89
|
+
For staged adoption, pass `--adoption`. Ownership-boundary diagnostics are
|
|
90
|
+
reported as review findings so a team can inventory and migrate its baseline;
|
|
91
|
+
combine it with the exact file-and-rule exceptions below for intentional legacy
|
|
92
|
+
integrations. Remove `--adoption` to make unsupported overrides blocking.
|
|
93
|
+
|
|
94
|
+
Catalog `boundaries.publicClasses` and `boundaries.publicTokens` are explicit
|
|
95
|
+
CSS extension points. A component catalog may additionally list shadow-part
|
|
96
|
+
names in `boundaries.publicParts`; all other `::part()` targets are private.
|
|
97
|
+
Part names are scoped to that entry's `boundaries.rootClass`, so an extension
|
|
98
|
+
point on one component does not authorize the same-named part on another.
|
|
99
|
+
Selectors for application-owned content remain allowed, including in a
|
|
100
|
+
component's context (`.kui-pane > .my-content`): a public class is an
|
|
101
|
+
extension point for naming a context, not for restyling the component itself,
|
|
102
|
+
which `KUI-L019` reports.
|
|
103
|
+
|
|
104
|
+
Catalog entries may also publish `cssValueProps`. Each path names its grammar,
|
|
105
|
+
finite shorthands, canonical and exceptional scale steps, accepted typed
|
|
106
|
+
helpers, expression-only helpers, raw escape policy, and examples. The analyzer
|
|
107
|
+
uses that same metadata for direct component calls and JSX, including nested
|
|
108
|
+
paths such as `choices[].color`; consumer catalogs receive identical checks.
|
|
109
|
+
Dynamic values remain a type-system responsibility rather than being guessed.
|
|
110
|
+
|
|
111
|
+
## Component ownership (`KUI-L019`–`KUI-L022`)
|
|
112
|
+
|
|
113
|
+
Components own their styles and are configured, never overridden. These
|
|
114
|
+
diagnostics apply the package's own `check:css-ownership` rules to
|
|
115
|
+
applications and component packages; the selector parsing is the same module
|
|
116
|
+
(`analyzer/selectors.mjs`), so the package rule and the consumer rule agree on
|
|
117
|
+
which element a selector styles.
|
|
118
|
+
|
|
119
|
+
- **`KUI-L019` — a rule restyles a cataloged component.** The rule's subject
|
|
120
|
+
(the rightmost compound of each selector, ignoring `:has()` and `:not()`
|
|
121
|
+
arguments) names a public class of a cataloged component (`.kui-toolbar`,
|
|
122
|
+
`.kui-list-item__label`, `.acme-meter`), its `[data-component="…"]` root, or
|
|
123
|
+
a Web Awesome tag the package themes (`wa-button`, `wa-select`), and the rule
|
|
124
|
+
sets at least one ordinary property. A `::part()` subject is left to
|
|
125
|
+
`KUI-L011`; a rule that only sets custom properties is configuration, judged
|
|
126
|
+
by the token rules. Forcing a component's dimensions is one such restyle
|
|
127
|
+
and reports here once: `.kui-pane { width: 300px }`.
|
|
128
|
+
- **`KUI-L020` — another component's private variable.** CSS reads or writes a
|
|
129
|
+
`--_<root>-*` variable of a cataloged component (`--_kui-list-gap`), or any
|
|
130
|
+
`--_kui-*` variable.
|
|
131
|
+
- **`KUI-L021` — a token a typed prop sets.** CSS assigns `--<root>-<prop>`
|
|
132
|
+
where the component has that typed prop (`--kui-list-gap` is `List gap`,
|
|
133
|
+
`--kui-floating-toolbar-inset` is `FloatingToolbar inset`). The message
|
|
134
|
+
quotes the prop's cataloged example.
|
|
135
|
+
- **`KUI-L022` — a hook class on a component's root.** A literal class on an
|
|
136
|
+
imported component's JSX (`<Toolbar className="header" />`) is the subject
|
|
137
|
+
of a rule in a stylesheet that file imports, so the application restyles the
|
|
138
|
+
component through it. The evidence names the stylesheet, line, and selector.
|
|
139
|
+
|
|
140
|
+
Every message names the configuration to use (the component's typed props,
|
|
141
|
+
public tokens, and variants from its catalog entry) and ends with the same
|
|
142
|
+
route for a real need that has none: report the component gap to the owning
|
|
143
|
+
package (open a feature request) instead of overriding it.
|
|
144
|
+
|
|
145
|
+
What stays allowed mirrors the package rule, where a child styles itself in a
|
|
146
|
+
parent's context:
|
|
147
|
+
|
|
148
|
+
```css
|
|
149
|
+
/* Your own element in a component's context. */
|
|
150
|
+
.kui-toolbar > .my-widget {
|
|
151
|
+
margin-inline-start: auto;
|
|
152
|
+
}
|
|
153
|
+
/* Keying your own element on a component's state. */
|
|
154
|
+
.shell:has(.kui-select[open]) .my-status {
|
|
155
|
+
opacity: 0.5;
|
|
156
|
+
}
|
|
157
|
+
/* Configuring a component through a public token with no typed prop. */
|
|
158
|
+
.kui-list {
|
|
159
|
+
--kui-list-divider-color: var(--kui-color-neutral-border-quiet);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* KUI-L019, KUI-L020, KUI-L021. */
|
|
163
|
+
.kui-toolbar {
|
|
164
|
+
padding: 0;
|
|
165
|
+
}
|
|
166
|
+
.app {
|
|
167
|
+
--_kui-list-gap: 2px;
|
|
168
|
+
--kui-list-gap: 4px;
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Ownership is decided per package: a stylesheet belongs to the package whose
|
|
173
|
+
`package.json` is nearest it, and only other packages' catalog entries are
|
|
174
|
+
foreign to it. A component package built on Kerf UI therefore styles its own
|
|
175
|
+
cataloged components (including `.kui-toolbar > .acme-meter` from its own
|
|
176
|
+
stylesheet) without findings, while an application that restyles
|
|
177
|
+
`.acme-meter` is reported against `@acme/widgets`. All four ids are
|
|
178
|
+
ownership-boundary diagnostics, so `--adoption` reports them as review
|
|
179
|
+
findings. `eslint-plugin-kerfjs`'s `ui-component-ownership` rule reports
|
|
180
|
+
`KUI-L020` and `KUI-L021` in JavaScript and TypeScript (style strings, style
|
|
181
|
+
objects, and `style.setProperty()`), where this analyzer reads only CSS.
|
|
182
|
+
|
|
183
|
+
### Retired: `KUI-L005` (forced component dimension)
|
|
184
|
+
|
|
185
|
+
`KUI-L005` used to report, as a review finding, any `width`/`height`/`min-*`/
|
|
186
|
+
`max-*` declaration in a rule that mentioned a public component class
|
|
187
|
+
anywhere in its selector. `KUI-L019` now reports every restyle whose subject is
|
|
188
|
+
a cataloged component — dimensions included — so the subject case was
|
|
189
|
+
double-reported (an error and a review finding for one declaration). Its only
|
|
190
|
+
other shape, a public class as an ancestor
|
|
191
|
+
(`.kui-pane .app-sidebar { width: 240px }`), sizes the application's own
|
|
192
|
+
element in the component's context, which the ownership rules explicitly
|
|
193
|
+
allow. With no unique true positive left,
|
|
194
|
+
the rule was retired rather than narrowed. The analyzer no longer emits it;
|
|
195
|
+
the id stays in `ai/application-ui-diagnostic-ids-v1.json` (listed under
|
|
196
|
+
`retired`) so a profile exception that already names it still loads. Remove
|
|
197
|
+
such exceptions at your convenience.
|
|
198
|
+
|
|
199
|
+
## Loud fill / on-loud pairing (`KUI-L018`)
|
|
200
|
+
|
|
201
|
+
The shipped Web Awesome theme pairs every `--wa-color-{tone}-fill-loud` with a
|
|
202
|
+
`--wa-color-{tone}-on-loud` foreground that clears WCAG AA (4.5:1), for the
|
|
203
|
+
`neutral`, `brand`, `success`, `warning`, `danger`, and `pop` tones. A
|
|
204
|
+
stylesheet that overrides a loud fill without also setting that tone's
|
|
205
|
+
`on-loud` keeps the theme's foreground on an unknown fill, so the guarantee no
|
|
206
|
+
longer holds. `KUI-L018` reports such a fill and names each scope that lacks a
|
|
207
|
+
pair, and it reports a pair that is present but measurably fails.
|
|
208
|
+
|
|
209
|
+
**Which `on-loud` governs a fill.** Each individual selector of the fill
|
|
210
|
+
(after resolving CSS nesting and splitting selector lists) inside its chain of
|
|
211
|
+
at-rules is one scope. An `on-loud` of the same tone governs it when declared:
|
|
212
|
+
|
|
213
|
+
- in the same scope, in the same or a separate rule, in either order; or
|
|
214
|
+
- in an ancestor scope: a document-root selector (`:root`, `html`, `:host`,
|
|
215
|
+
`*`), or the leading part of a descendant or child chain (`.shell` governs
|
|
216
|
+
`.shell .panel` and `.frame > .tile`, but not `.shell-alt .panel` or
|
|
217
|
+
`.shell.dense`); and
|
|
218
|
+
- under no at-rule, or the same at-rule chain (an unconditional `on-loud`
|
|
219
|
+
applies inside `@media`, but one under `@media` or `@supports` is not proven
|
|
220
|
+
to apply to an unconditional fill).
|
|
221
|
+
|
|
222
|
+
That `on-loud` may live in the same stylesheet or in another one the same
|
|
223
|
+
entry loads: a source module that imports both stylesheets, or a stylesheet
|
|
224
|
+
that reaches both through `@import`. A stylesheet no entry loads with the fill
|
|
225
|
+
does not count. When several candidates apply, the same scope wins (this
|
|
226
|
+
stylesheet first), then the nearest ancestor.
|
|
227
|
+
|
|
228
|
+
**Contrast, when it can be proven.** When the fill and its governing `on-loud`
|
|
229
|
+
are both literal opaque colors (hex, `rgb()`/`rgba()`, `hsl()`/`hsla()`,
|
|
230
|
+
`white`/`black`, or a `light-dark()` of two of them), the analyzer computes the
|
|
231
|
+
WCAG contrast ratio in each color scheme and reports a pair below 4.5:1, with
|
|
232
|
+
the ratio and scheme in its evidence. An unpaired literal fill is accepted when
|
|
233
|
+
it clears 4.5:1 in both schemes against the theme's default `on-loud` for its
|
|
234
|
+
tone (`light-dark(#fff, #111113)` for `brand` and `danger`, `#1d1d1f` for
|
|
235
|
+
`success` and `warning`); `neutral` follows a surface token and `pop` has no
|
|
236
|
+
Web Awesome default, so their unpaired fills are always reported. Values it
|
|
237
|
+
cannot prove (`var()`, `color-mix()`, translucent colors, keywords) are not
|
|
238
|
+
measured: a present pair is accepted, and an unpaired fill is reported.
|
|
239
|
+
|
|
240
|
+
```css
|
|
241
|
+
/* KUI-L018: brand fill moves, its foreground does not. */
|
|
242
|
+
.billing {
|
|
243
|
+
--wa-color-brand-fill-loud: #7540a8;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/* Paired: both in the same scope. */
|
|
247
|
+
.billing {
|
|
248
|
+
--wa-color-brand-fill-loud: #7540a8;
|
|
249
|
+
--wa-color-brand-on-loud: #ffffff;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/* Paired through an ancestor scope (6.87:1). */
|
|
253
|
+
:root {
|
|
254
|
+
--wa-color-brand-on-loud: #ffffff;
|
|
255
|
+
}
|
|
256
|
+
.billing {
|
|
257
|
+
--wa-color-brand-fill-loud: #7540a8;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* KUI-L018: present, but white on this fill measures 2.14:1. */
|
|
261
|
+
.billing {
|
|
262
|
+
--wa-color-brand-fill-loud: #7fb3ff;
|
|
263
|
+
--wa-color-brand-on-loud: #ffffff;
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
It is a review finding rather than an error because the analyzer cannot see
|
|
268
|
+
every cascade: an `on-loud` set inline, by script, or by a stylesheet it does
|
|
269
|
+
not analyze may still govern the fill. Set the `on-loud` explicitly beside the
|
|
270
|
+
fill, which also documents the pair; use `--fail-on-review` to gate it in CI.
|
|
271
|
+
See [the Web Awesome theme customization notes](./webawesome-theme.md#customization).
|
|
272
|
+
|
|
273
|
+
The JSON report schema is exported as
|
|
274
|
+
`@kerfjs/ui/analyzer/report.schema.json`.
|
|
275
|
+
|
|
276
|
+
Profile discovery, parsing, catalog-loading, and validation diagnostics retain
|
|
277
|
+
their stable `KUI-P###` ids in every output format and count as errors. The
|
|
278
|
+
analyzer therefore cannot silently pass with stale or unreadable policy input.
|
|
279
|
+
|
|
280
|
+
The analyzer is conservative about dynamic class expressions: it emits one
|
|
281
|
+
review finding and does not guess which selectors, tokens, or owners the value
|
|
282
|
+
might contain. CSS values expressed through public variables or `calc()` are
|
|
283
|
+
also left to their owning token contract rather than reverse-engineered.
|
|
284
|
+
|
|
285
|
+
## Narrow exceptions
|
|
286
|
+
|
|
287
|
+
Use the application profile's `exceptions` only for an exact rule and exact
|
|
288
|
+
repository-relative file. Every exception requires an id and rationale; broad
|
|
289
|
+
directories, globs, absolute paths, and traversal are rejected by the profile
|
|
290
|
+
validator.
|
|
291
|
+
|
|
292
|
+
```json
|
|
293
|
+
{
|
|
294
|
+
"id": "legacy-inspector-spacing",
|
|
295
|
+
"rules": ["KUI-L006"],
|
|
296
|
+
"target": "src/legacy/inspector.css",
|
|
297
|
+
"rationale": "The legacy inspector retains its measured spacing until migration."
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Review findings remain visible until explicitly suppressed. Avoid suppressing
|
|
302
|
+
`KUI-L001`, `KUI-L002`, `KUI-L003`, `KUI-L007`, `KUI-L009`, `KUI-L010`,
|
|
303
|
+
`KUI-L011`, `KUI-L012`, `KUI-L013`, `KUI-L014`, `KUI-L015`, `KUI-L016`,
|
|
304
|
+
`KUI-L019`, `KUI-L020`, `KUI-L021`, or `KUI-L022`:
|
|
305
|
+
those indicate a
|
|
306
|
+
definite boundary or parsing failure rather than an aesthetic judgment.
|