@kerfjs/ui 5.0.0-beta.105 → 5.0.0-beta.107
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 +2 -1
- package/ai/application-ui-diagnostic-ids-v1.json +1 -0
- package/ai/application-ui-profile.defaults.json +6 -1
- package/ai/component-catalog.json +160 -27
- package/ai/component-composition-rules.json +20 -2
- package/ai/component-composition.d.ts +11 -0
- package/ai/component-composition.json +118 -19
- package/ai/component-composition.schema.json +32 -0
- package/ai/components/README.md +3 -2
- package/ai/components/button.md +4 -1
- package/ai/components/checkbox-group.md +89 -0
- package/ai/components/checkbox.md +3 -2
- package/ai/components/color-picker.md +10 -7
- package/ai/components/disclosure.md +6 -1
- package/ai/components/foundation.md +1 -1
- package/ai/components/list-detail-dialog.md +1 -1
- package/ai/components/message-composer.md +2 -1
- package/ai/components/radio-group.md +4 -3
- package/ai/components/split-button.md +5 -2
- package/ai/components/surface-scaffold.md +2 -1
- package/ai/components/switch.md +1 -1
- package/ai/components/text.md +2 -0
- package/ai/components/wa-checkbox-group.md +5 -5
- package/ai/components/workbench-dialog.md +1 -1
- package/ai/public-api-signatures-v1.md +198 -26
- package/ai/skill.md +73 -62
- package/ai/webawesome-jsx-signatures-v1.md +1 -1
- package/dist/browser/checkbox-group.js +5 -0
- package/dist/button.d.ts +15 -2
- package/dist/button.js +1 -1
- package/dist/catalog.js +9 -8
- package/dist/catalog.js.map +1 -1
- package/dist/checkbox-group.d.ts +70 -0
- package/dist/checkbox-group.js +7 -0
- package/dist/checkbox-group.js.map +1 -0
- package/dist/{chunk-SQ2Q3OQI.js → chunk-2LM3PYHA.js} +2 -2
- package/dist/{chunk-SQ2Q3OQI.js.map → chunk-2LM3PYHA.js.map} +1 -1
- package/dist/{chunk-HYORKKYP.js → chunk-3VZ6SFD4.js} +1 -1
- package/dist/chunk-3VZ6SFD4.js.map +1 -0
- package/dist/{chunk-EKBMUXFV.js → chunk-3Z7N2NAZ.js} +141 -54
- package/dist/chunk-3Z7N2NAZ.js.map +1 -0
- package/dist/{chunk-FKKJHGN5.js → chunk-AZ3WT77F.js} +2 -2
- package/dist/{chunk-FKKJHGN5.js.map → chunk-AZ3WT77F.js.map} +1 -1
- package/dist/{chunk-FG6U7KQQ.js → chunk-DNWWF6UN.js} +2 -2
- package/dist/{chunk-FG6U7KQQ.js.map → chunk-DNWWF6UN.js.map} +1 -1
- package/dist/{chunk-C3RLKKFQ.js → chunk-EYDXFOSH.js} +13 -8
- package/dist/chunk-EYDXFOSH.js.map +1 -0
- package/dist/{chunk-TAHR457X.js → chunk-G4DRH4A5.js} +3 -1
- package/dist/chunk-G4DRH4A5.js.map +1 -0
- package/dist/{chunk-PE2KPZDW.js → chunk-HDXUHLTQ.js} +7 -2
- package/dist/chunk-HDXUHLTQ.js.map +1 -0
- package/dist/chunk-HU5IHXCI.js +73 -0
- package/dist/chunk-HU5IHXCI.js.map +1 -0
- package/dist/{chunk-DF2YKGIX.js → chunk-JCCN3U6X.js} +2 -2
- package/dist/{chunk-DF2YKGIX.js.map → chunk-JCCN3U6X.js.map} +1 -1
- package/dist/{chunk-GCAK23PU.js → chunk-MVKHSAKE.js} +2 -2
- package/dist/{chunk-GCAK23PU.js.map → chunk-MVKHSAKE.js.map} +1 -1
- package/dist/{chunk-DDSCVEO5.js → chunk-NLMEDZRM.js} +5 -5
- package/dist/{chunk-DDSCVEO5.js.map → chunk-NLMEDZRM.js.map} +1 -1
- package/dist/{chunk-ECFCAKWE.js → chunk-NR6O74ST.js} +5 -1
- package/dist/chunk-NR6O74ST.js.map +1 -0
- package/dist/{chunk-7W3GKMN4.js → chunk-OQP3PFKQ.js} +7 -4
- package/dist/chunk-OQP3PFKQ.js.map +1 -0
- package/dist/{chunk-WJKHLOP5.js → chunk-TI2GV36S.js} +8 -2
- package/dist/chunk-TI2GV36S.js.map +1 -0
- package/dist/{chunk-UTCAOOXU.js → chunk-TXRKCGT3.js} +8 -2
- package/dist/chunk-TXRKCGT3.js.map +1 -0
- package/dist/{chunk-ACUKTVS3.js → chunk-XWNXGFX3.js} +52 -42
- package/dist/chunk-XWNXGFX3.js.map +1 -0
- package/dist/{chunk-ZXZAECHZ.js → chunk-Z4DS77U2.js} +2 -2
- package/dist/{chunk-ZXZAECHZ.js.map → chunk-Z4DS77U2.js.map} +1 -1
- package/dist/collapsible-panel.js +2 -2
- package/dist/color-picker.d.ts +30 -5
- package/dist/color-picker.js +1 -1
- package/dist/disclosure.d.ts +13 -4
- package/dist/disclosure.js +1 -1
- package/dist/disclosure.js.map +1 -1
- package/dist/help-tags-register.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +20 -19
- package/dist/list-action-row.js +1 -1
- package/dist/list-detail-dialog.d.ts +7 -1
- package/dist/list-detail-dialog.js +6 -4
- package/dist/list-detail-dialog.js.map +1 -1
- package/dist/list-header.d.ts +8 -1
- package/dist/list-header.js +1 -1
- package/dist/nav-stack.js +1 -1
- package/dist/popup-menu-register.js +2 -2
- package/dist/popup-menu.d.ts +15 -5
- package/dist/popup-menu.js +1 -1
- package/dist/select-register.js +3 -3
- package/dist/split-button.d.ts +14 -2
- package/dist/split-button.js +1 -1
- package/dist/split-view.js +1 -1
- package/dist/styles/anchored-surface.css +3 -0
- package/dist/styles/button.css +29 -0
- package/dist/styles/catalog/components/catalog-example.css +55 -18
- package/dist/styles/checkbox-group.css +63 -0
- package/dist/styles/checkbox.css +7 -6
- package/dist/styles/color-picker.css +69 -34
- package/dist/styles/components/actions/button/button.css +29 -0
- package/dist/styles/components/actions/split-button/split-button.css +51 -5
- package/dist/styles/components/collections/framed-list/framed-list.css +4 -0
- package/dist/styles/components/collections/list/list.css +6 -0
- package/dist/styles/components/collections/list-header/list-header.css +7 -0
- package/dist/styles/components/feedback/anchored-surface/anchored-surface.css +3 -0
- package/dist/styles/components/forms/checkbox/checkbox.css +7 -6
- package/dist/styles/components/forms/checkbox-group/checkbox-group.css +63 -0
- package/dist/styles/components/forms/color-picker/color-picker.css +69 -34
- package/dist/styles/components/forms/radio-group/radio-group.css +7 -5
- package/dist/styles/components/forms/select/select.css +6 -2
- package/dist/styles/components/forms/switch/switch.css +3 -3
- package/dist/styles/components/layout/pane/pane.css +9 -0
- package/dist/styles/components/layout/workbench/workbench.css +4 -0
- package/dist/styles/components/navigation/nav-stack/nav-stack.css +64 -1
- package/dist/styles/components/navigation/tab-navigator/tab-navigator.css +1 -0
- package/dist/styles/components/surfaces/content-item/content-item.css +30 -0
- package/dist/styles/components/surfaces/disclosure/disclosure.css +38 -0
- package/dist/styles/components/surfaces/sunken-panel/sunken-panel.css +3 -0
- package/dist/styles/components/surfaces/surface-scaffold/surface-scaffold.css +6 -0
- package/dist/styles/components/typography/text/text.css +10 -0
- package/dist/styles/content-item.css +30 -0
- package/dist/styles/disclosure.css +38 -0
- package/dist/styles/foundation.css +21 -0
- package/dist/styles/framed-list.css +4 -0
- package/dist/styles/list-header.css +7 -0
- package/dist/styles/list.css +6 -0
- package/dist/styles/nav-stack.css +64 -1
- package/dist/styles/pane.css +9 -0
- package/dist/styles/radio-group.css +7 -5
- package/dist/styles/select.css +6 -2
- package/dist/styles/split-button.css +51 -5
- package/dist/styles/styles.css +1 -0
- package/dist/styles/sunken-panel.css +3 -0
- package/dist/styles/surface-scaffold.css +6 -0
- package/dist/styles/switch.css +3 -3
- package/dist/styles/tab-navigator.css +1 -0
- package/dist/styles/text.css +10 -0
- package/dist/styles/webawesome.css +45 -9
- package/dist/styles/workbench.css +4 -0
- package/dist/surface-scaffold.d.ts +7 -1
- package/dist/surface-scaffold.js +1 -1
- package/dist/wire-color-fields.js +2 -2
- package/dist/wire-color-pickers.d.ts +4 -3
- package/dist/wire-color-pickers.js +1 -1
- package/dist/wire-comboboxes.js +1 -1
- package/dist/workbench-dialog.d.ts +11 -2
- package/dist/workbench-dialog.js +5 -2
- package/dist/workbench-dialog.js.map +1 -1
- package/dist/workbench.js +1 -1
- package/docs/accessibility.md +40 -1
- package/docs/anchored-surface.md +6 -1
- package/docs/catalog.md +26 -1
- package/docs/color-picker.md +39 -8
- package/docs/component-contract.md +33 -2
- package/docs/component-selection.md +20 -9
- package/docs/design/templates/button/full-width-dark.svg +1 -0
- package/docs/design/templates/button/full-width.svg +1 -0
- package/docs/design/templates/button-dark.svg +1 -1
- package/docs/design/templates/button.svg +1 -1
- package/docs/design/templates/checkbox/error-dark.svg +1 -1
- package/docs/design/templates/checkbox/error.svg +1 -1
- package/docs/design/templates/checkbox/states-dark.svg +1 -1
- package/docs/design/templates/checkbox/states.svg +1 -1
- package/docs/design/templates/checkbox-dark.svg +1 -1
- package/docs/design/templates/checkbox-group/horizontal-dark.svg +1 -0
- package/docs/design/templates/checkbox-group/horizontal.svg +1 -0
- package/docs/design/templates/checkbox-group/vertical-dark.svg +1 -0
- package/docs/design/templates/checkbox-group/vertical.svg +1 -0
- package/docs/design/templates/checkbox-group-dark.svg +1 -0
- package/docs/design/templates/checkbox-group.svg +1 -0
- package/docs/design/templates/checkbox.svg +1 -1
- package/docs/design/templates/color-picker/gray-dark.svg +1 -1
- package/docs/design/templates/color-picker/gray.svg +1 -1
- package/docs/design/templates/color-picker/hsb-dark.svg +1 -0
- package/docs/design/templates/color-picker/hsb.svg +1 -0
- package/docs/design/templates/color-picker/rgb-dark.svg +1 -1
- package/docs/design/templates/color-picker/rgb.svg +1 -1
- package/docs/design/templates/color-picker-dark.svg +1 -1
- package/docs/design/templates/color-picker.svg +1 -1
- package/docs/design/templates/disclosure/embedded-dark.svg +1 -0
- package/docs/design/templates/disclosure/embedded.svg +1 -0
- package/docs/design/templates/disclosure-dark.svg +1 -1
- package/docs/design/templates/disclosure.svg +1 -1
- package/docs/design/templates/radio-group/horizontal-dark.svg +1 -1
- package/docs/design/templates/radio-group/horizontal.svg +1 -1
- package/docs/design/templates/radio-group/vertical-dark.svg +1 -1
- package/docs/design/templates/radio-group/vertical.svg +1 -1
- package/docs/design/templates/radio-group-dark.svg +1 -1
- package/docs/design/templates/radio-group.svg +1 -1
- package/docs/design/templates/split-button/full-width-dark.svg +1 -0
- package/docs/design/templates/split-button/full-width.svg +1 -0
- package/docs/design/templates/split-button/pill-dark.svg +1 -0
- package/docs/design/templates/split-button/pill.svg +1 -0
- package/docs/design/templates/split-button/states-dark.svg +1 -0
- package/docs/design/templates/split-button/states.svg +1 -0
- package/docs/design/templates/split-button/tones-dark.svg +1 -0
- package/docs/design/templates/split-button/tones.svg +1 -0
- package/docs/design/templates/split-button-dark.svg +1 -0
- package/docs/design/templates/split-button.svg +1 -0
- package/docs/design/templates/switch/states-dark.svg +1 -1
- package/docs/design/templates/switch/states.svg +1 -1
- package/docs/design/templates/switch-dark.svg +1 -1
- package/docs/design/templates/switch.svg +1 -1
- package/docs/design/templates.md +10 -3
- package/docs/disclosure.md +34 -0
- package/docs/list-detail-dialog.md +4 -0
- package/docs/nav-stack.md +6 -2
- package/docs/surface-scaffold.md +6 -2
- package/docs/ui-doctor.md +16 -4
- package/docs/webawesome-migration.md +1 -1
- package/docs/webawesome-theme.md +48 -16
- package/docs/workbench-dialog.md +4 -1
- package/docs/writing.md +3 -3
- package/doctor/copy-capitalization-checks.mjs +10 -7
- package/llms.txt +50 -20
- package/package.json +7 -1
- package/dist/chunk-7W3GKMN4.js.map +0 -1
- package/dist/chunk-ACUKTVS3.js.map +0 -1
- package/dist/chunk-C3RLKKFQ.js.map +0 -1
- package/dist/chunk-ECFCAKWE.js.map +0 -1
- package/dist/chunk-EKBMUXFV.js.map +0 -1
- package/dist/chunk-HYORKKYP.js.map +0 -1
- package/dist/chunk-PE2KPZDW.js.map +0 -1
- package/dist/chunk-TAHR457X.js.map +0 -1
- package/dist/chunk-UTCAOOXU.js.map +0 -1
- package/dist/chunk-WJKHLOP5.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/surfaces/workbench-dialog/workbench-dialog.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"sources":["../src/components/surfaces/workbench-dialog/workbench-dialog.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAwGA,SAAS,WAAA,CACP,OACA,OAAA,EAC4B;AAC5B,EAAA,OAAO,SAAS,OAAA,GAAU,EAAE,GAAG,KAAA,EAAO,YAAA,EAAc,WAAU,GAAI,KAAA;AACpE;AAaO,SAAS,eAAA,CAAgB;AAAA,EAC9B,EAAA;AAAA,EACA,KAAA;AAAA,EACA,KAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA;AAAA,EACA,UAAA;AAAA,EACA,iBAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,OAAA,GAAU,KAAA;AAAA,EACV,UAAA,GAAa,OAAA;AAAA,EACb,YAAA,GAAe,KAAA;AAAA,EACf,IAAA,GAAO,OAAA;AAAA,EACP,cAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,cAAA;AAAA,EACA,WAAA;AAAA,EACA,cAAA;AAAA,EACA,SAAA,GAAY,EAAA;AAAA,EACZ;AACF,CAAA,EAAyB;AACvB,EAAA,uBACE,GAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,EAAA;AAAA,MACA,KAAA;AAAA,MACA,aACE,WAAA,KACC,WAAA,KAAgB,MAAA,GAAY,MAAA,GAAY,GAAG,EAAE,CAAA,YAAA,CAAA,CAAA;AAAA,MAEhD,aAAA,EAAa,IAAA;AAAA,MACb,YAAA;AAAA,MACA,UAAA;AAAA,MACA,IAAA;AAAA,MACA,YAAA,EAAc,UAAU,YAAA,GAAe,OAAA;AAAA,MACvC,cAAA;AAAA,MACA,MAAA,EACE,UACI,MAAA,GACA,SAAA,KAAc,SACX,MAAA,IAAU,MAAA,CAAO,GAAG,CAAA,GACrB,MAAA;AAAA,MAER,SAAA,EAAW,UAAU,MAAA,GAAY,SAAA;AAAA,MACjC,cAAA,EAAgB,UAAU,MAAA,GAAY,cAAA;AAAA,MACtC,SAAA,EAAU,MAAA;AAAA,MACV,UAAA,EAAW,MAAA;AAAA,MACX,cAAA,EAAgB;AAAA,QACd,GAAG,cAAA;AAAA,QACH,oBAAA,EAAsB;AAAA,OACxB;AAAA,MACA,SAAA;AAAA,MACA,IAAA;AAAA,MAEA,QAAA,kBAAA,GAAA;AAAA,QAAC,SAAA;AAAA,QAAA;AAAA,UACC,EAAA,EAAI,GAAG,EAAE,CAAA,UAAA,CAAA;AAAA,UACT,KAAA;AAAA,UACA,WAAA,EAAa;AAAA,YACX,KAAA,EAAO,KAAA;AAAA,YACP,KAAA,kBACE,GAAA;AAAA,cAAC,WAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAM,KAAA;AAAA,gBACN,WAAA;AAAA,gBACA,aAAA,EACE,WAAA,KAAgB,MAAA,GAAY,MAAA,GAAY,GAAG,EAAE,CAAA,YAAA,CAAA;AAAA,gBAE/C,IAAA,EAAK,QAAA;AAAA,gBACL,EAAA,EAAI,GAAG,EAAE,CAAA,MAAA,CAAA;AAAA,gBACT,YAAA,EAAc;AAAA;AAAA,aAChB;AAAA,YAEF,OAAA;AAAA,YACA,QAAA,EAAU,OAAA;AAAA;AAAA,YAEV,qBACE,GAAA,CAAC,mBAAA,EAAA,EAAoB,UAAA,EAAW,YAAA,EAAa,QAAM,IAAA,EACjD,QAAA,kBAAA,GAAA;AAAA,cAAC,QAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,QAAA;AAAA,gBACL,YAAA,EAAY,UAAA;AAAA,gBACZ,mBAAA,EAAkB,EAAA;AAAA,gBAElB,QAAA,kBAAA,GAAA,CAAC,UAAA,EAAA,EAAW,IAAA,EAAM,CAAA,EAAG,MAAK,GAAA,EAAI;AAAA;AAAA,aAChC,EACF;AAAA,WAEJ;AAAA,UACA,IAAA;AAAA,UACA,UAAA;AAAA,UACA,iBAAA;AAAA,UACA,QAAA,EAAU,WAAA,CAAY,QAAA,EAAU,OAAO,CAAA;AAAA,UACvC,SAAA,EAAW,WAAA,CAAY,SAAA,EAAW,OAAO,CAAA;AAAA,UACzC,YAAA;AAAA,UACA;AAAA;AAAA;AACF;AAAA,GACF;AAEJ","file":"workbench-dialog.js","sourcesContent":["import { X } from 'lucide';\n\nimport type { KerfUiContent } from '../../../shared/content/semantic-content.js';\nimport type { CssLength } from '../../../shared/styles/css-values.js';\nimport { remify } from '../../../shared/styles/css-values.js';\nimport { ToolbarControlGroup } from '../../actions/toolbar-control-group/toolbar-control-group.js';\nimport { ToolbarText } from '../../actions/toolbar-text/toolbar-text.js';\nimport {\n Workbench,\n type WorkbenchMainBottomToolbar,\n type WorkbenchPanel,\n} from '../../layout/workbench/workbench.js';\nimport { LucideIcon } from '../../media/lucide-icon/lucide-icon.js';\nimport {\n DialogSurface,\n type DialogSurfaceHeight,\n type DialogSurfaceMaxHeight,\n type DialogSurfaceRootAttributes,\n type DialogSurfaceSize,\n} from '../surface-scaffold/surface-scaffold.js';\n\n// Open and close it like any DialogSurface, from the same import.\nexport {\n closeDialog,\n type DialogCloseSource,\n type DialogRequestCloseDetail,\n openDialog,\n} from '../surface-scaffold/surface-scaffold.js';\n\n/**\n * Application `data-*` identity on the dialog root, such as `data-key`; the\n * dialog's own configuration attributes stay protected.\n */\nexport type WorkbenchDialogRootAttributes = DialogSurfaceRootAttributes;\n\nexport interface WorkbenchDialogProps {\n /**\n * The dialog's id, for `openDialog(document.getElementById(id))`. The\n * Workbench inside is `<id>-workbench`, so its rails are\n * `<id>-workbench-left-rail` and `<id>-workbench-right-rail`.\n */\n id: string;\n /** The dialog's accessible name. */\n label: string;\n /** The dialog's primary title: an xl heading in the work area's toolbar. */\n title: string;\n /**\n * A short supporting line under the title. It is the dialog's accessible\n * description (`aria-describedby` to its `<id>-description`) unless\n * `describedBy` names other content.\n */\n description?: string;\n /**\n * Id of the element whose text is the dialog's accessible description, in\n * place of `description`, such as app-owned summary copy in `main`.\n */\n describedBy?: string;\n /** Groups after the title in the work area's toolbar. */\n leading?: KerfUiContent;\n /** Header controls before the close control, such as a `ToolbarControlGroup`. */\n actions?: KerfUiContent;\n /** The work area. */\n main: KerfUiContent;\n /** Fixed content over the work area's bottom toolbar, such as a status line. */\n mainFooter?: KerfUiContent;\n /** The work area's bottom toolbar, usually the dialog's buttons. */\n mainBottomToolbar?: WorkbenchMainBottomToolbar;\n /**\n * Side rails with their own toolbars. On compact devices each presents as\n * an overlay over the work area; the app starts it collapsed and its\n * `toolbar.toggle` shows it.\n */\n leftRail?: WorkbenchPanel;\n rightRail?: WorkbenchPanel;\n /** A bottom drawer under the work area. */\n bottomDrawer?: WorkbenchPanel;\n /**\n * Fill the screen with the rails as overlays: derive from\n * `deviceClass().value.compact`.\n */\n compact?: boolean;\n /** The close control's accessible name (default `Close`). */\n closeLabel?: string;\n /** Close on a press on the backdrop; otherwise the panel pulses. */\n lightDismiss?: boolean;\n /** Roomy preferred width preset (default `large`). */\n size?: DialogSurfaceSize;\n /** Roomy preferred width; takes precedence over `size`. */\n preferredWidth?: CssLength;\n /** Roomy definite height (default 560px), bounded by the viewport. */\n height?: DialogSurfaceHeight;\n /** Roomy height cap in place of a definite height. */\n maxHeight?: DialogSurfaceMaxHeight;\n /** Roomy edge clearance from the viewport. */\n viewportGutter?: CssLength;\n /** Minimum work-area width beside inline rails, in px (Workbench default 320). */\n mainMinSize?: number;\n rootAttributes?: WorkbenchDialogRootAttributes;\n className?: string;\n /** Native named-slot assignment when composed inside a web component. */\n slot?: string;\n}\n\n/** A rail presents as an overlay in a compact dialog, as the app left it otherwise. */\nfunction compactRail(\n panel: WorkbenchPanel | undefined,\n compact: boolean,\n): WorkbenchPanel | undefined {\n return panel && compact ? { ...panel, presentation: 'overlay' } : panel;\n}\n\n/**\n * A workbench dialog: a modal whose body is a `Workbench`. The work area's\n * toolbar carries the dialog's primary title, its actions, and the close\n * control; side rails keep their own toolbars and run the dialog's full\n * height. On compact devices it fills the screen and its rails become\n * overlays over the work area, shown by their toolbar toggles, so the work\n * area keeps its width. Open it with `openDialog()`; the app owns each\n * rail's `collapsed` state, and `wireWorkbench` closes an overlay rail on\n * Escape or a press outside it before the dialog sees the Escape. Built from\n * `DialogSurface` and `Workbench`.\n */\nexport function WorkbenchDialog({\n id,\n label,\n title,\n description,\n describedBy,\n leading,\n actions,\n main,\n mainFooter,\n mainBottomToolbar,\n leftRail,\n rightRail,\n bottomDrawer,\n compact = false,\n closeLabel = 'Close',\n lightDismiss = false,\n size = 'large',\n preferredWidth,\n height,\n maxHeight,\n viewportGutter,\n mainMinSize,\n rootAttributes,\n className = '',\n slot,\n}: WorkbenchDialogProps) {\n return (\n <DialogSurface\n id={id}\n label={label}\n describedBy={\n describedBy ??\n (description === undefined ? undefined : `${id}-description`)\n }\n withoutHeader\n lightDismiss={lightDismiss}\n closeLabel={closeLabel}\n size={size}\n presentation={compact ? 'fullscreen' : 'modal'}\n preferredWidth={preferredWidth}\n height={\n compact\n ? undefined\n : maxHeight === undefined\n ? (height ?? remify(560))\n : height\n }\n maxHeight={compact ? undefined : maxHeight}\n viewportGutter={compact ? undefined : viewportGutter}\n bodyInset=\"none\"\n bodyLayout=\"fill\"\n rootAttributes={{\n ...rootAttributes,\n 'data-dialog-layout': 'workbench',\n }}\n className={className}\n slot={slot}\n >\n <Workbench\n id={`${id}-workbench`}\n label={label}\n mainToolbar={{\n label: title,\n title: (\n <ToolbarText\n text={title}\n description={description}\n descriptionId={\n description === undefined ? undefined : `${id}-description`\n }\n size=\"xlarge\"\n id={`${id}-title`}\n headingLevel={2}\n />\n ),\n leading,\n trailing: actions,\n // After any hidden right rail's toggle, so Close stays outermost.\n end: (\n <ToolbarControlGroup appearance=\"borderless\" single>\n <button\n type=\"button\"\n aria-label={closeLabel}\n data-dialog-close=\"\"\n >\n <LucideIcon icon={X} name=\"x\" />\n </button>\n </ToolbarControlGroup>\n ),\n }}\n main={main}\n mainFooter={mainFooter}\n mainBottomToolbar={mainBottomToolbar}\n leftRail={compactRail(leftRail, compact)}\n rightRail={compactRail(rightRail, compact)}\n bottomDrawer={bottomDrawer}\n mainMinSize={mainMinSize}\n />\n </DialogSurface>\n );\n}\n"]}
|
package/dist/workbench.js
CHANGED
package/docs/accessibility.md
CHANGED
|
@@ -364,7 +364,13 @@ holds an optional icon `ToolbarControlGroup` and a direct extra-large
|
|
|
364
364
|
`ToolbarText`; controls belong in a trailing `ToolbarControlGroup`. By default
|
|
365
365
|
the title carries no native heading role, so for a **dialog or panel** the
|
|
366
366
|
application connects the title id and optional supporting-copy id to the owning
|
|
367
|
-
host through `aria-labelledby` and `aria-describedby`.
|
|
367
|
+
host through `aria-labelledby` and `aria-describedby`. `DialogSurface` and
|
|
368
|
+
`PopoverDialogSurface` take these as `labelledBy` and `describedBy`, so the
|
|
369
|
+
supporting line (or app-owned summary copy in the body) is the dialog's
|
|
370
|
+
accessible description; `WorkbenchDialog` wires its title `description`
|
|
371
|
+
automatically (`<id>-description`), and `ListDetailDialog` takes an explicit
|
|
372
|
+
`describedBy`, since its detail description describes the selection, not the
|
|
373
|
+
dialog. For a **page or view**
|
|
368
374
|
title, pass `headingLevel` (usually `1`) to `ToolbarText`: it then exposes
|
|
369
375
|
`role="heading"` with a matching `aria-level`, giving the view a heading landmark
|
|
370
376
|
so screen-reader heading navigation and "main heading" semantics work — the same
|
|
@@ -542,6 +548,32 @@ disabled or read-only field's clear button is disabled. `showCount` (with
|
|
|
542
548
|
`wireTextFields` keeps current. Compact text grows to 16px on coarse-pointer
|
|
543
549
|
touch screens so focusing a field never zooms the page in iOS.
|
|
544
550
|
|
|
551
|
+
## Button and SplitButton focus
|
|
552
|
+
|
|
553
|
+
`Button` and each `SplitButton` half draw the 3px focus ring 1px outside the
|
|
554
|
+
box, against the surrounding surface, because a loud fill can share the ring's
|
|
555
|
+
hue (the brand fill matches the focus color in both schemes). An ancestor that
|
|
556
|
+
clips overflow would crop that ring, so `focusRingInset` draws it inside the
|
|
557
|
+
box instead, the name and meaning `Select` uses. An inset ring alone would
|
|
558
|
+
touch the fill (1.0:1 on brand, at most 2.3:1 on any loud fill), so the inset
|
|
559
|
+
ring is two-tone: the ring color on the outer 3px and a 3px surface-colored
|
|
560
|
+
line inside it. The ring then meets a surface color on both edges and clears
|
|
561
|
+
3:1 against each on every tone (about 4.0:1 in light, 9.9:1 in dark against
|
|
562
|
+
the line), whatever the fill. Forced-colors mode drops the line and keeps the
|
|
563
|
+
system ring.
|
|
564
|
+
|
|
565
|
+
The line paints the surface the control sits on, not always the base surface:
|
|
566
|
+
a surface that changes the background behind its children publishes the
|
|
567
|
+
surface context (`--kui-color-surface-current`, `-region`, and `-tint`; see
|
|
568
|
+
`docs/component-contract.md`), and the line layers it the same way the page
|
|
569
|
+
does. On a sunken panel, a quiet `ContentItem`, a lowered `FramedList`, a
|
|
570
|
+
raised `List`, popup, or dialog, or a sunken Pane, Workbench area, or
|
|
571
|
+
TabNavigator scene, the line matches the pixels outside the box, so it reads
|
|
572
|
+
as a cut through the fill instead of a light halo. On a doubly sunken surface
|
|
573
|
+
the brand ring still clears 3:1 against the line (about 3.2:1 in light, 6.9:1
|
|
574
|
+
in dark). A sunken surface's grain is not part of the line, and a translucent
|
|
575
|
+
glass material, which has no single color, falls back to the base surface.
|
|
576
|
+
|
|
545
577
|
## Checkbox, RadioGroup, and Switch
|
|
546
578
|
|
|
547
579
|
Each control is a native input inside its `<label>`, positioned invisibly over
|
|
@@ -563,6 +595,13 @@ forced-colors mode paints checked state with `Highlight`.
|
|
|
563
595
|
its `aria-description`; `error` sets `aria-invalid` on the group, `required`
|
|
564
596
|
sets native `required` on each radio plus `aria-required` on the group, and a
|
|
565
597
|
choice's `hint` is that radio's `aria-description`.
|
|
598
|
+
- `CheckboxGroup` is a `<fieldset>` (natively a group) of `Checkbox`es, named
|
|
599
|
+
by its `<legend>` (`label`), `ariaLabel`, or `labelledBy`. Each checkbox
|
|
600
|
+
is its own Tab stop and Space toggles it; every checked value submits under
|
|
601
|
+
`name`. The group's `hint` and `error` form its `aria-description`; `error`
|
|
602
|
+
sets `aria-invalid` on the group and the danger border on each box, and a
|
|
603
|
+
choice's `hint` is that checkbox's `aria-description`. `disabled` disables
|
|
604
|
+
the fieldset and so, natively, every checkbox in it.
|
|
566
605
|
- `Switch` is an `<input type="checkbox" role="switch">`, so assistive
|
|
567
606
|
technology announces on and off; Space toggles it. Its `hint` is its
|
|
568
607
|
`aria-description`. The thumb's slide is skipped when reduced motion is
|
package/docs/anchored-surface.md
CHANGED
|
@@ -26,7 +26,12 @@ help that leaves focus on its trigger. Escape and an outside press dismiss it;
|
|
|
26
26
|
`placement` (`bottom` or `top`), `align` (`start` or `end`), `gap`, and
|
|
27
27
|
`viewportMargin` feed the shared `kerfjs/overlay` positioning core: it flips
|
|
28
28
|
vertically when the other side fits, keeps the surface `viewportMargin` (default
|
|
29
|
-
8px) inside the viewport edges, and follows scroll and
|
|
29
|
+
8px) inside the viewport edges, and follows scroll, resize, and changes to the
|
|
30
|
+
anchor's own size. Anchor help for a field to the whole field rather than to a
|
|
31
|
+
button inside it: the surface then clears every line of a field that wraps, and
|
|
32
|
+
moves as the field grows (a SearchField's trailing help action opens beside
|
|
33
|
+
`[data-component="search-field"]`, which also keeps typing in the field from
|
|
34
|
+
dismissing it). The surface fits
|
|
30
35
|
its height to the room beside the anchor: when neither side holds it, it opens
|
|
31
36
|
on the roomier side, caps its height to that room, and scrolls its content.
|
|
32
37
|
The room is exposed to the content as `--kui-anchored-surface-available-height`
|
package/docs/catalog.md
CHANGED
|
@@ -219,7 +219,32 @@ for a control whose own inline padding contributes about 8px, and
|
|
|
219
219
|
8px inset (such as a plain `PopupMenu` trigger), so its text lines up with the
|
|
220
220
|
example label past the trigger's own padding and border. Use
|
|
221
221
|
`align="none"` (the default) for a content item or composition that owns its
|
|
222
|
-
geometry.
|
|
222
|
+
geometry. Every form field specimen (TextField, Select, ColorField, TagField,
|
|
223
|
+
Slider, Switch, Checkbox, RadioGroup, CheckboxGroup, ColorSwatches) uses
|
|
224
|
+
`inline-control`, so field borders and label text share one column across the
|
|
225
|
+
catalog; a field that already insets itself by 8px (Combobox) stays at `none`.
|
|
226
|
+
A field is a control a person fills in or picks a value with inline, swatch
|
|
227
|
+
pickers included. A field composition (fields in a `Grid` or a form `List`)
|
|
228
|
+
uses `inline-control` too. A width-sized content specimen — one that fills a
|
|
229
|
+
sized `viewport` or its container rather than its content, such as a
|
|
230
|
+
ProgressBar, a group-title `Text`, or Web Awesome's Progress Bar and Skeleton —
|
|
231
|
+
joins the same column with `inline-control`, so a bar's label lines up with the
|
|
232
|
+
example label and note text. Web Awesome integration specimens follow their
|
|
233
|
+
Kerf counterpart's demo: controls and control compositions (Button, Button
|
|
234
|
+
Group, Dropdown, Dropdown Item, the Tab family, Badge, Tag, Avatar, Progress
|
|
235
|
+
Ring, Toast and Toast Item, the dialog, drawer, popover, and tooltip triggers,
|
|
236
|
+
Animation, and the intersection, mutation, and resize observer compositions)
|
|
237
|
+
use `inline-control`, as the Kerf Button demo does, so the control box sits on
|
|
238
|
+
the 8px column; a specimen led by bare text or a bare glyph (Copy Button,
|
|
239
|
+
Breadcrumb and Breadcrumb Item, Divider, Markdown, Relative Time, Pagination's
|
|
240
|
+
summary, Stepper and Step markers, Spinner, Icon) uses `glyph`, as Kerf's flush
|
|
241
|
+
`Text`, `LoadingSpinner`, and `LucideIcon` demos do, so its text lands on the
|
|
242
|
+
text column instead of flush at the example's edge. A panel — a multi-part picker or
|
|
243
|
+
a drop zone such as ColorPicker and FilePicker, or a framed specimen such as
|
|
244
|
+
Web Awesome's Zoomable Frame — owns its geometry and stays flush at `none`, as
|
|
245
|
+
does an edge-to-edge composition (a toolbar, tab bar, or pane). A sized
|
|
246
|
+
`viewport` takes the alignment offset out of its width, so it still ends at the
|
|
247
|
+
example's inline-end edge.
|
|
223
248
|
|
|
224
249
|
```tsx
|
|
225
250
|
import { CatalogExample, CatalogExampleStack } from "@kerfjs/ui/catalog";
|
package/docs/color-picker.md
CHANGED
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
`@kerfjs/ui/color-picker` is a full inline color picker panel:
|
|
4
4
|
|
|
5
5
|
- a saturation and brightness area;
|
|
6
|
-
- a
|
|
7
|
-
|
|
6
|
+
- a slider for each channel of the color model — red, green, and blue (RGB);
|
|
7
|
+
hue, saturation, and lightness (HSL); hue, saturation, and brightness
|
|
8
|
+
(HSB); or one gray level (Grayscale) — each track painting the colors that
|
|
9
|
+
channel produces while the others hold, except the hue track, which always
|
|
10
|
+
shows the full-saturation, full-brightness rainbow so a dark or gray color
|
|
11
|
+
still shows every hue; and, optionally, an opacity slider on a
|
|
12
|
+
checkerboard;
|
|
13
|
+
- a color-model menu (RGB, HSL, HSB, Grayscale) that switches the channel
|
|
14
|
+
sliders and fields together;
|
|
8
15
|
- a hex field (8 digits below full opacity) and an opacity field;
|
|
9
16
|
- a screen sampler where the browser offers the EyeDropper API;
|
|
10
17
|
- optional preset and sample swatches.
|
|
@@ -28,7 +35,8 @@ const stop = wireColorPickers(root, {
|
|
|
28
35
|
- `id` and `label` (required): identity and the picker's accessible name.
|
|
29
36
|
- `value` (required): CSS color text (`#rrggbb[aa]`, `rgb()`, `hsl()`);
|
|
30
37
|
invalid text shows black.
|
|
31
|
-
- `mode`: `rgb` (default), `hsl`, `hsb`, or `gray` — which channel
|
|
38
|
+
- `mode`: `rgb` (default), `hsl`, `hsb`, or `gray` — which channel sliders
|
|
39
|
+
and fields show.
|
|
32
40
|
- `alpha` (default true): the opacity slider and field. Without it the value
|
|
33
41
|
is always opaque.
|
|
34
42
|
- `name`, `form`: a hidden input submits the hex value.
|
|
@@ -49,7 +57,9 @@ picker with a new `value` (or `mode`), the picker adopts it.
|
|
|
49
57
|
|
|
50
58
|
- **The area** takes pointer drags and the arrow keys (Shift for 10% steps);
|
|
51
59
|
Page Up and Down change brightness, Home and End saturation.
|
|
52
|
-
- **
|
|
60
|
+
- **Channel and opacity sliders** are native range inputs (arrow keys, Page
|
|
61
|
+
Up and Down, Home and End). Each changes only its own channel; a slider and
|
|
62
|
+
the field of the same channel always agree.
|
|
53
63
|
- **Fields** commit on Enter or blur; Escape restores the shown value; invalid
|
|
54
64
|
text restores it too.
|
|
55
65
|
- **Swatches** (presets and samples) apply their color.
|
|
@@ -59,13 +69,34 @@ Each change dispatches a bubbling `kui-color-input` (live) or
|
|
|
59
69
|
`onInput` or `onChange`. The value is `#rrggbb`, or `#rrggbbaa` below full
|
|
60
70
|
opacity. The picker keeps hue, saturation, and brightness as its own state,
|
|
61
71
|
so a hue survives while the color is gray, and conversions round-trip without
|
|
62
|
-
drift
|
|
72
|
+
drift: switching models never changes the color, and a channel edit keeps
|
|
73
|
+
every other channel's exact value (an HSL saturation survives lightness
|
|
74
|
+
reaching black or white). The 2D area stays in every model as the primary
|
|
75
|
+
picking surface; the sliders are the precise, per-channel controls.
|
|
63
76
|
|
|
64
77
|
## Accessibility and geometry
|
|
65
78
|
|
|
66
79
|
The picker is a named group. The area is a slider named "Saturation and
|
|
67
|
-
Brightness" whose spoken value gives both
|
|
68
|
-
|
|
69
|
-
|
|
80
|
+
Brightness" whose spoken value gives both. Each channel slider is named for
|
|
81
|
+
its channel (Red, Hue, Lightness, Gray Level) and speaks its unit (`210°`,
|
|
82
|
+
`40%`, or a plain 0–255 byte); the opacity slider speaks a percentage. Every
|
|
83
|
+
field is named the same way (Red, Hue, Hex Color, Opacity). The panel takes
|
|
84
|
+
the shared block-content inset, fills its container up to 320px, and spaces
|
|
85
|
+
its rows 12px apart; the sliders form one group 8px apart.
|
|
86
|
+
|
|
87
|
+
The saturation and brightness area and every slider track draw a 1px inner
|
|
88
|
+
edge halfway between the text color and the color under it, so an area or a
|
|
89
|
+
track as dark as a dark surface (the area's black bottom, a hue at low
|
|
90
|
+
brightness, a faint opacity) or as light as a light one keeps a boundary of at
|
|
91
|
+
least 3:1 against the surface around it. Increased contrast draws the edge in
|
|
92
|
+
the full text color and forced colors in `CanvasText`.
|
|
93
|
+
|
|
94
|
+
Every thumb draws a two-tone ring: a white border inside a dark outline. The
|
|
95
|
+
white holds at least 3:1 against a dark color and the outline against a light
|
|
96
|
+
one, so a thumb stays findable wherever it sits.
|
|
97
|
+
|
|
98
|
+
Every track runs in the writing direction, as the native range does: in
|
|
99
|
+
right-to-left text the gradient starts at the right, so the thumb at a
|
|
100
|
+
channel's minimum (or at no opacity) sits over the color it sets.
|
|
70
101
|
|
|
71
102
|
Use `ColorSwatches` for a small fixed palette.
|
|
@@ -65,6 +65,14 @@ diagnostic even when both the wrapper and a rendered root reject that parent.
|
|
|
65
65
|
When one catalog entry exports another JSX component with different placement,
|
|
66
66
|
`jsxExports` gives that export its own parent contract. The extra export does
|
|
67
67
|
not stand for the entry's root when a parent zone is checked.
|
|
68
|
+
An entry whose own geometry an owner already provides in some configuration
|
|
69
|
+
declares `insetFreeChildren`: `{ prop, equals }` conditions under which its
|
|
70
|
+
direct JSX children must bring no content-item inset (`Disclosure` with
|
|
71
|
+
`appearance="embedded"`). A child brings that inset when its
|
|
72
|
+
`layout.geometry` owns its margin, border, and padding (`self` on all three),
|
|
73
|
+
unless one of its own `insetFreeWhen` conditions holds (`Text` with `flush` or
|
|
74
|
+
`variant="span"`). `ui-composition` reports a child that statically brings the
|
|
75
|
+
inset as `KUI-L204` and names the child's inset-free form when it has one.
|
|
68
76
|
Generated `boundaries.rootClass` prefers the public `kui-<entry-id>` class when
|
|
69
77
|
present, so a sibling export's class cannot become the entry's root class.
|
|
70
78
|
|
|
@@ -411,6 +419,24 @@ a `Pane` overlay header, through `wireScrollDividers`, on its own Pane root).
|
|
|
411
419
|
A scroll owner reads it only through its own `scrollClearance` prop, or, for an
|
|
412
420
|
overlay-header Pane's content, through its own `headerPresentation`.
|
|
413
421
|
|
|
422
|
+
The surface context needs no exception either. A component that changes the
|
|
423
|
+
background behind its children publishes, on its own painted element, the
|
|
424
|
+
foundation tokens `--kui-color-surface-current` (the opaque surface beneath:
|
|
425
|
+
a raised `DialogSurface`, popover dialog, `AnchoredSurface`, or `List`
|
|
426
|
+
`surface`, and a sunken Pane's opaque auto chrome, set it and clear both
|
|
427
|
+
layers), `--kui-color-surface-current-region` (a layout region's translucent
|
|
428
|
+
fill: a sunken `Pane`, `Workbench` area, or `TabNavigator` scene), and
|
|
429
|
+
`--kui-color-surface-current-tint` (a content surface's translucent fill: a
|
|
430
|
+
`SunkenPanel`, a sunken `Disclosure`, a lowered `FramedList`, a quiet,
|
|
431
|
+
hovered, pressed, or selected `ContentItem`). They are undeclared at `:root`
|
|
432
|
+
and a scoped `[data-kui-tint]` resets them. A control that must match what
|
|
433
|
+
lies behind it (the inset focus ring's inner line on `Button` and
|
|
434
|
+
`SplitButton`) layers tint over region over the opaque color, falling back to
|
|
435
|
+
`--kui-color-surface`. Custom properties cannot fold an inherited value into a
|
|
436
|
+
new one, so a content surface inside a sunken region composes exactly while a
|
|
437
|
+
third translucent level keeps only the innermost content tint. An application
|
|
438
|
+
surface that paints its own background sets the same tokens on its own root.
|
|
439
|
+
|
|
414
440
|
Applications and component packages built on the package follow the same
|
|
415
441
|
rules, and the downstream tools enforce them against the composition catalog
|
|
416
442
|
instead of package source: `kerf-ui-analyze` / `kerf-ui-doctor` report a rule
|
|
@@ -624,14 +650,19 @@ compact segment never becomes an oval. An icon-only `Select` joins a group as
|
|
|
624
650
|
one more segment with its siblings' inset, radius, hover, and focus ring; only a
|
|
625
651
|
lone Select grows the group to its icon-and-caret pill.
|
|
626
652
|
|
|
627
|
-
Select menus use native viewport shifting with 10px gutters: default menus grow to content (up to 20rem), while `fitMenu` matches trigger width and long labels wrap. Keep popup sizing and position repairs out of consumer CSS.
|
|
653
|
+
Select menus use native viewport shifting with 10px gutters: default menus grow to content (up to 20rem), while `fitMenu` matches trigger width and long labels wrap. A listbox (like a PopupMenu menu) shifts only along its trigger's edge, never over the trigger: when neither side fits, it opens on the side with more room and scrolls inside its capped height, so a second press on the trigger still closes it. Keep popup sizing and position repairs out of consumer CSS.
|
|
628
654
|
|
|
629
655
|
For a rail view heading, use `Select presentation="title"`: its intrinsic
|
|
630
656
|
36px borderless trigger has a bold large label flush with its leading edge and
|
|
631
657
|
a caret beside that label. Set `focusRingInset` when a NavStack or panel clips
|
|
632
658
|
outside painting; the Select keeps its focus outline 3px inside the trigger
|
|
633
659
|
and drops it while the listbox is open. Both settings belong to Select, not an
|
|
634
|
-
application `::part()` override.
|
|
660
|
+
application `::part()` override. `Button` and `SplitButton` take the same
|
|
661
|
+
`focusRingInset` for a clipping ancestor: the ring moves inside the box (each
|
|
662
|
+
half's, for a SplitButton) with a line inside it in the color of the surface
|
|
663
|
+
it sits on (the surface context), so it stays visible on a loud fill of its
|
|
664
|
+
own hue without a halo on a sunken or tinted surface. Never re-offset their outline from
|
|
665
|
+
application CSS.
|
|
635
666
|
|
|
636
667
|
Set `menuInset` to configure the menu surface
|
|
637
668
|
without application `::part(menu)` CSS; the default `standard` keeps each menu's
|
|
@@ -259,7 +259,7 @@ beside a `ContentItem` value, and connect its id to the preview group's
|
|
|
259
259
|
| Modal dialog, dialog or popup geometry — `DialogSurface`, `openDialog`, `closeDialog`, `PopoverDialogSurface`, `PopupSurface` | A task must block the page in a modal dialog with standard sizes/presentations/body/footer insets, typed modal viewport bounds, a definite height, or a body one child fills; a dialog must stay non-modal and light-dismiss; or a raw dropdown menu needs list-compatible inset ownership. | Do not use these surfaces as generic layout. Do not toggle a `DialogSurface`'s `open` attribute (that opens it non-modally), avoid consumer `::part()` or descendant overrides, and do not stack a dialog body inset around list-owned child geometry. | Render `DialogSurface` with a `label` (or `labelledBy` plus a `heading`), the body as children, and its buttons as `footer`; open it with `openDialog()` and close it with `closeDialog()`, its close button, or a `data-dialog-close` element, and cancel `kui-request-close` to keep it open. Compose a dialog body as a `List` by default, use `bodyInset="none"` when its children own list geometry, and wrap bare prose in `ListInsetText`. Set `height` with `bodyLayout="fill"` when one `SplitView` or `Pane` should fill the body. For a non-modal, light-dismiss dialog render `PopoverDialogSurface` (native `popover="auto"`, opened by a `popoverTarget` control or `showPopover()`), never a hand-built popover with app chrome CSS; `presentation="embedded"` shows it in flow. Wrap a raw `wa-dropdown` in `PopupSurface` for its menu inset. | Open state, whether a close may proceed, dismissal policy, names, content, and outer placement. | `@kerfjs/ui/surface-scaffold` | [Surface scaffolds](./surface-scaffold.md) |
|
|
260
260
|
| Modal list-detail — `ListDetailDialog` | A modal task picks one record from a list and shows its detail beside it, with the dialog's title, actions, and close control belonging to the detail. | Do not use it for a single-pane dialog; use `DialogSurface`. Do not wrap a `SplitView` in a `DialogSurface` by hand for this layout: the surface header spans both columns and stops the divider below it. | Pass `list` (`title`, `toolbar`, `content`, `footer`, `width`) and `detail` (`title`, `description`, `actions`, `content`, `footer`); open it with `openDialog()`; pass `compact` from `deviceClass()` and `detailActive` for the compact drill-down, and pop on `nav-back`. | Open state, selection, the pushed detail, device policy, content, and record actions. | `@kerfjs/ui/list-detail-dialog` | [ListDetailDialog](./list-detail-dialog.md) |
|
|
261
261
|
| Modal workbench — `WorkbenchDialog` | A modal editing task needs a work area with side panels (layers, an inspector) whose rails overlay the work area on compact devices. |
|
|
262
|
-
| Show and hide content — `Disclosure` | Optional or advanced content should stay one press away under a summary (advanced settings, a build log), or several sections should open one at a time. | Do not use it to switch peer views (use `SegmentedControl` or `TabBar`) or for a collapsible rail (use `CollapsiblePanel`). Do not hand-build a details element with its own marker CSS. | Render `Disclosure` with a `title`, an optional `detail`, and content that draws no inset of its own; give Disclosures the same `name` for an accordion; pick `appearance` `plain`, `framed`, or `sunken
|
|
262
|
+
| Show and hide content — `Disclosure` | Optional or advanced content should stay one press away under a summary (advanced settings, a build log), or several sections should open one at a time. | Do not use it to switch peer views (use `SegmentedControl` or `TabBar`) or for a collapsible rail (use `CollapsiblePanel`). Do not hand-build a details element with its own marker CSS. | Render `Disclosure` with a `title`, an optional `detail`, and content that draws no inset of its own; give Disclosures the same `name` for an accordion; pick `appearance` `plain`, `framed`, or `sunken`, or `embedded` inside a `ContentItem` that already owns the inset (no margin, border, or padding of its own, a compact summary row, and `flush` `Text` content). The open state is native: close it from code with `element.open = false`, and listen for the captured `toggle` event through `toggleAction`. | Title, content, initial open state, group name, and toggle handling. | `@kerfjs/ui/disclosure` | [Disclosure](./disclosure.md) |
|
|
263
263
|
| Core overlay bottom sheet — `@kerfjs/ui/overlay-sheet.css`, `wireOverlaySheets` | A `kerfjs/overlay` `overlay()`, `confirm()`, `prompt()`, `form()`, or `choice()` opened with `presentation: 'bottom-sheet'` should look like a `DialogSurface` sheet, and optionally be resizable by the person. | Do not hand-style `.kerf-overlay` or build a grabber; prefer a `DialogSurface` `presentation="bottom-sheet"` when the dialog is a Kerf `DialogSurface` rather than a core overlay. | Import `@kerfjs/ui/overlay-sheet.css` once. Pass `exitClass: 'kui-overlay-sheet-exit'` for the slide out. For a resizable sheet, add the `kui-overlay-sheet-resizable` class (`className: 'kerf-overlay kui-overlay-sheet-resizable'`) and call `wireOverlaySheets()` once per container sheets open in (`document.body` by default). | Opening, results, content, and which sheets are resizable. | `@kerfjs/ui/overlay-sheet.css`, `@kerfjs/ui/wire-overlay-sheets` | [Core overlay bottom sheets](./surface-scaffold.md#core-dialogs-as-bottom-sheets) |
|
|
264
264
|
| Key/value facts — `ValueTable`, `ValueTableRow` | Read-only labels and values form a semantic definition list, optionally with a leading icon. | Do not use it for editable form fields or a row/column data grid; use native form or table semantics. | Compose typed `ValueTableRow` entries; pass `icon` when a 24px leading icon adds useful context. Set `labelWidth` for a minimum label column, `presentation="stacked"` to put each value under its label, or `stackBelow` (`compact` 512px, `narrow` 576px) to stack only while the table is that narrow; never override `--kui-value-table-row-columns` for these. | Values, formatting, icon meaning, and empty/loading policy. | `@kerfjs/ui/value-table` | [Component ownership](./component-contract.md#ownership-boundaries) |
|
|
265
265
|
| Framed rows of arbitrary content — `FramedList`, `FramedListRow` | Settings rows, account or source cards, or a shortcut list group several rows of copy and controls in one rounded frame with dividers between them. | Do not use it for read-only label/value facts; use `ValueTable`. Do not use it for selectable navigation rows or a bounded result picker; use `List` with `ListItem` (a picker adds `frame`, `scrollable`, and `maxHeight`). Do not pad or border rows from application CSS. | Compose any row content (a `Row` of copy, a keycap, icon-only `Button`s) in each `FramedListRow`; mark the row being edited `highlighted`. Name the list with `label` when no heading does. | Row content, controls, which row is highlighted, and the list's name. | `@kerfjs/ui/framed-list` | [Feedback accessibility](./accessibility.md#shared-rules) |
|
|
@@ -273,15 +273,16 @@ beside a `ContentItem` value, and connect its id to the preview group's
|
|
|
273
273
|
| Indeterminate activity — `LoadingSpinner` | A Kerf surface needs compact, labeled or decorative indeterminate progress. | Do not use it for known progress; use a native `<progress>` element, or `wa-progress-bar` / `wa-progress-ring` in an application using the Web Awesome integration. Do not mix spinner systems within one surface. | Pass a label when the spinner conveys status; use size for a named icon step or positive pixel size. | Loading lifecycle and adjacent status copy. | `@kerfjs/ui/loading-spinner` | [Accessibility](./accessibility.md#shared-rules) |
|
|
274
274
|
| Loading placeholder — `Skeleton` + a component's `placeholder` prop | A value or a whole component is still loading and should hold its space as a subtle, unanimated block, keeping the layout stable — an inspector or detail view rendering its real chrome with per-record values absent. | Do not use it for known progress (use `LoadingSpinner`), do not animate it, and do not hand-rebuild a component's empty state — set `placeholder` on the component instead. Prefer it over `wa-skeleton`. | Set `placeholder` on a value-bearing component (`Select`, `ListHeader`, `ListItem`, `ValueTableRow`, `SegmentedControl`, `StateBanner`, `TabBarItem`, `ToolbarText`, `ListActionRow`) to render skeletons in its value slots with interactivity disabled; use the standalone `Skeleton` for a custom slot. | Loading lifecycle, which slots are unknown, and announcing the loading region. | `@kerfjs/ui/skeleton` | [Accessibility](./accessibility.md#shared-rules) |
|
|
275
275
|
| Command menu — `PopupMenu` | A short list of commands sits behind one trigger, such as a toolbar sort or More menu, optionally grouped under headings and dividers with item icons. | Do not use it to choose a persistent value; use `Select`. Do not hand-write `wa-dropdown`/`wa-dropdown-item` markup for an action menu. | Import `@kerfjs/ui/popup-menu/register` once. Place it in a `single` `ToolbarControlGroup` with `nestedDropdown` and a `menuInset`, or wrap it in `PopupSurface` outside a toolbar. Handle item `action`s by delegation. | Commands, enabled state, and reactions to opening or closing. | `@kerfjs/ui/popup-menu` | [Toolbar composition](#toolbar-composition) |
|
|
276
|
-
| Standalone action — `Button` | A dialog footer, a form, or content needs an action button with visible text: `tone="brand"` for the main action, `tone="danger"` for a destructive one, and `emphasis="secondary"` or `"plain"` for the quieter actions beside it. A link that looks like a button takes `href`. | Do not use it inside a `ToolbarControlGroup`; give the group plain `<button>`s. For an icon-only action give it an `icon` and `ariaLabel` and no label, and for an on/off toggle set `pressed`; a menu trigger is a `PopupMenu`. | Pass the label as children and delegate its `action`; a form button sets `type="submit"` or `"reset"` (the default `button` never submits) with `name`, `value`, and `form`. `loading` shows a spinner and `aria-busy`; the app decides what a press does meanwhile. `size="compact"` is 33px. In a `Toolbar` trailing zone or a `TabBar` action zone it keeps the control band.
|
|
276
|
+
| Standalone action — `Button` | A dialog footer, a form, or content needs an action button with visible text: `tone="brand"` for the main action, `tone="danger"` for a destructive one, and `emphasis="secondary"` or `"plain"` for the quieter actions beside it. A link that looks like a button takes `href`. | Do not use it inside a `ToolbarControlGroup`; give the group plain `<button>`s. For an icon-only action give it an `icon` and `ariaLabel` and no label, and for an on/off toggle set `pressed`; a menu trigger is a `PopupMenu`. | Pass the label as children and delegate its `action`; a form button sets `type="submit"` or `"reset"` (the default `button` never submits) with `name`, `value`, and `form`. `loading` shows a spinner and `aria-busy`; the app decides what a press does meanwhile. `size="compact"` is 33px. In a `Toolbar` trailing zone or a `TabBar` action zone it keeps the control band. Set `focusRingInset` when an ancestor clips overflow: the focus ring moves inside the box, with a surface-colored line inside it so it stays visible on a loud fill. `fill` spans a full-width primary action across its parent's inline width, label centered, as on `SplitButton`. | The label, delegated action, and form ownership. | `@kerfjs/ui/button` | [Feedback accessibility](./accessibility.md#shared-rules) |
|
|
277
277
|
| Icon-only action — `Button` with `icon` and `ariaLabel` | One action is clear from its icon alone and stands by itself or beside a form field, such as Browse next to a path input: give `Button` an `icon` and an `ariaLabel` and no label. |
|
|
278
278
|
| Standalone toggle — `Button` with `pressed` | An on/off control (show a panel or a conversation), icon-only or labeled, sits in content or a pane footer outside any Toolbar: `pressed` reports `aria-pressed` and draws the pressed look on top of the tone and emphasis; `shape="pill"` rounds it. |
|
|
279
|
-
| Primary action with options — `SplitButton` | A prominent primary action has a closely related set of options chosen from an attached trigger, such as Drive with a provider menu
|
|
279
|
+
| Primary action with options — `SplitButton` | A prominent primary action has a closely related set of options chosen from an attached trigger, such as Drive with a provider menu; `shape="pill"` rounds both outer ends, as on `Button`. | Do not use it for a plain action; use `Button`. Do not use it for a toolbar command menu; use `PopupMenu`. Do not assemble one from `wa-button-group`, whose hosts do not forward names or `aria-expanded`. | Pass the primary `label` and `action`, the options trigger's `optionsLabel`, `optionsAction`, and `optionsExpanded`; pick a loud `tone`, `fill` for full width, `busy` while work runs, and `disabled`/`optionsDisabled`. Set `focusRingInset` when an ancestor clips overflow, so each half's ring is drawn inside its box. | Both actions, the options menu and its open state, the running state and tone switch, and copy. | `@kerfjs/ui/split-button` | [Feedback accessibility](./accessibility.md#shared-rules) |
|
|
280
280
|
| Choosing or dropping files — `FilePickerTrigger`, `FileDropZone`, `wireFilePickers` | People attach or import files by browsing or by dropping them onto a target. | Do not style a native file input or its label from app CSS. Do not make the drop zone the only way in; it also opens the chooser. | Use `FilePickerTrigger` for a compact Add control and `FileDropZone` for a dashed target; set `accept`, `multiple`, `name`, and `pickerId`. Call `wireFilePickers(root, { onFiles })` once: it reports browsed and dropped files (drops filtered by `accept`, the same check `fileMatchesAccept` exposes) and marks the zone while dragging. | What happens to the files, the chosen-file list, copy, and accepted types. | `@kerfjs/ui/file-picker`, `@kerfjs/ui/wire-file-pickers` | [Feedback accessibility](./accessibility.md#shared-rules) |
|
|
281
281
|
| Value selection — `Select` | A controlled form, toolbar, or compact navigation value comes from a moderate or long choice list, possibly grouped, icon-bearing, icon-only when closed, or truncating. Set `multiple` with an array `value` when the person may choose any number of them; add `selectedPresentation="icon-only"` and a fixed `triggerIcon` for a toolbar filter menu. | Do not use it for commands; use `PopupMenu`. Do not use it for a small visible choice set; use `SegmentedControl`. Prefer it over direct `wa-select`, `wa-option`, or value-like `wa-dropdown`/`wa-dropdown-item` composition. | Import `@kerfjs/ui/select/register` once for registration and current-request animation ownership; listen for standard input/change events. Do not add consumer popup timing or positioning repairs. Use typed presentation props rather than consumer `::part()` overrides; let the parent own placement. | Controlled value, validation, choices, domain mapping, and outer placement. | `@kerfjs/ui/select` | [Web Awesome integration](../README.md#web-awesome-theme) |
|
|
282
282
|
| Free text — `TextField`, `wireTextFields` | A form or dialog collects a line of free text (a name, an address, a title) or a longer note: `label` names it (or `ariaLabel` when nearby content already does), `hint` explains it, and `error` reports a validation problem. `multiline` with `rows` (and `maxRows` to grow) takes a longer note; `maxLength` with `showCount` shows the remaining budget; `icon` and `clearable` suit a filter field. | Do not use it when only existing values are allowed; use `Select`. Do not use it to pick among suggestions or create tags; use `Combobox`. Do not use it for search text with removable filter tokens (`SearchField`) or a message with a send action (`MessageComposer`). Prefer it over `wa-input` and `wa-textarea`. | Call `wireTextFields(root)` once when a field is `clearable` or `showCount`. Read the value from the native `input` or `change` event. | Value, validation and error copy, hint copy, and what a cleared field means. | `@kerfjs/ui/text-field` | [TextField contract](./accessibility.md#textfield) |
|
|
283
|
-
| On/off option — `Checkbox`, `wireCheckboxes`, `Switch` | A form or settings list has an independent yes/no option: `Checkbox` when a form submits it (with `indeterminate` for a parent of partly checked children), `Switch` when the setting applies the moment it changes. | Do not use a `Switch` for an option a form submits later, or a `Checkbox` for an immediate setting. Do not use either for one choice among several (`RadioGroup`)
|
|
283
|
+
| On/off option — `Checkbox`, `wireCheckboxes`, `Switch` | A form or settings list has an independent yes/no option: `Checkbox` when a form submits it (with `indeterminate` for a parent of partly checked children), `Switch` when the setting applies the moment it changes. | Do not use a `Switch` for an option a form submits later, or a `Checkbox` for an immediate setting. Do not use either for one choice among several (`RadioGroup`); a labeled set of related checkboxes is a `CheckboxGroup`. Prefer them over `wa-checkbox` and `wa-switch`. | Call `wireCheckboxes(root)` once when a `Checkbox` is `indeterminate`. Read `checked` from the native `change` event. Stack several standalone Checkboxes in a `List gap="xs"`: 8px apart, the same 28px pitch as `RadioGroup` choices; a labeled set of options is a `CheckboxGroup`. | Checked and mixed state, validation copy, and what a change does. | `@kerfjs/ui/checkbox`, `@kerfjs/ui/switch` | [Checkbox, RadioGroup, and Switch contract](./accessibility.md#checkbox-radiogroup-and-switch) |
|
|
284
284
|
| One of a few labeled options — `RadioGroup` | A form asks for one of two to about six options that people should compare at a glance, each with a readable label and an optional hint. | Do not use it for a long list (`Select`), a view switch (`SegmentedControl`), or pill or color choices (`ChoiceChips`). Prefer it over `wa-radio-group`. | Read the value from the native `change` event and render `value`. | Value, choices and their copy, and validation. | `@kerfjs/ui/radio-group` | [Checkbox, RadioGroup, and Switch contract](./accessibility.md#checkbox-radiogroup-and-switch) |
|
|
285
|
+
| Several of a few labeled options — `CheckboxGroup` | A form asks for any number of two to about eight related options under one label, each with a readable label and an optional hint, and every checked value submits under one name. | Do not use it for one choice among several (`RadioGroup`), one independent option or a mixed parent (`Checkbox`), an immediate setting (`Switch`), or wrapping pill choices (`ChoiceChips` `multiple`). Prefer it over `wa-checkbox-group`. | Pass the checked values as `value` and read each change from the native `change` event. Its choices stack like `RadioGroup` choices: 8px apart, the same 28px pitch. Validate a minimum or maximum in the app and pass `error`. | Checked values, choices and their copy, and validation. | `@kerfjs/ui/checkbox-group` | [Checkbox, RadioGroup, and Switch contract](./accessibility.md#checkbox-radiogroup-and-switch) |
|
|
285
286
|
| Pick or create a value by typing — `Combobox`, `wireComboboxes` | People type to find an existing value among many or create a new one (tags, labels, recipients). | Do not use it when only existing values are allowed; use `Select`. Do not use it for search text with removable filter tokens; use `SearchField`. Do not style a native input with a datalist. | Pass `id`, `label`, and `suggestions` (strings or `{ value, label }`), plus `placeholder`, `hint`, and `createLabel` (or `false` for suggestions only). Call `wireComboboxes(root, { onCommit })` once; it filters, moves the active option with the arrows, and reports `{ id, value, created, input }` on Enter, a comma, or a click. | Suggestions, committed values, normalization, and clearing the field. | `@kerfjs/ui/combobox`, `@kerfjs/ui/wire-comboboxes` | [Feedback accessibility](./accessibility.md#shared-rules) |
|
|
286
287
|
| Small exclusive choice — `SegmentedControl` | A few visible choices switch a compact view or setting, with toolbar, rounded, or pill presentation. | Do not use it for tabpanel semantics; use `TabBar`. Do not use it for many choices; use `ChoiceChips` when they stay visible and wrap, or `Select` for a long list. Prefer it over `wa-button-group` when the controls select one value. | Delegate its action, read `data-segment-value`, update `value`, and rerender. | Controlled value, labels, action, and persistence. | `@kerfjs/ui/segmented-control` | [SegmentedControl contract](./accessibility.md#segmentedcontrol) |
|
|
287
288
|
| Wrapping visible choice — `ChoiceChips` | Several visible options, too many for one segmented track, must wrap onto more rows — a labeled color palette with swatches, a tag color, a category. Set `multiple` with an array `value` when any number may be on, such as label filters or annotation intents. | Do not use it for a few compact choices on one track; use `SegmentedControl`. Do not use it for a long list or tight space; use `Select`. Do not use it for removable tags (`Chip`). Prefer it over `wa-radio-group` with `appearance="button"` radios, which wrap as a ragged joined group. | Pass a form `name`, a `label` or `labelledBy`, and the controlled `value`; delegate the `action` on `change`, read the radio `value` (or, with `multiple`, the checked boxes under `name`), and rerender. Place it in a dark appearance scope for inverse chrome rather than restyling it. Lead choices with a `swatch` (`CssColor` or `'transparent'`) or an `icon`. | Controlled value, choices, swatch colors, change handling, and form submission. | `@kerfjs/ui/choice-chips` | [ChoiceChips contract](./accessibility.md#choicechips) |
|
|
@@ -765,15 +766,22 @@ belongs to — never inside an app wrapper with `position: fixed`, a 1px size,
|
|
|
765
766
|
or a `z-index` (the open menu is in the browser top layer). In the app's
|
|
766
767
|
`contextmenu` handler, prevent the browser menu, store
|
|
767
768
|
`{ x: event.clientX, y: event.clientY }` in the signal the menu renders as
|
|
768
|
-
`anchor`, and call `openPopupMenu` with the menu.
|
|
769
|
+
`anchor`, and call `openPopupMenu` with the menu. Its `placement` applies at
|
|
770
|
+
the point: the default `bottom-start` opens below it, and `top-start` or
|
|
771
|
+
`top-end` opens above it (for a menu anchored to a footer or composer), still
|
|
772
|
+
flipping when that side has no room. Because the point is part of the
|
|
769
773
|
menu's own template, it survives re-renders and a rebuilt menu, so the app
|
|
770
774
|
never re-applies it. `openPopupMenuAt` still opens a menu rendered without `anchor` at a given
|
|
771
|
-
point
|
|
775
|
+
point, at its `placement` unless the call names one for that opening
|
|
776
|
+
(`openPopupMenuAt(menu, x, y, { placement: 'top-start' })`). Call `closePopupMenu` for programmatic dismissal. A pointer-anchored menu
|
|
772
777
|
keeps the same collision placement, focus management, submenu keyboard
|
|
773
778
|
navigation, and outside/Escape dismissal as a trigger-anchored one.
|
|
774
779
|
|
|
775
780
|
The menu opens in the browser top layer, flips and shifts at viewport edges,
|
|
776
|
-
and limits its height to the available space, scrolling past it.
|
|
781
|
+
and limits its height to the available space, scrolling past it. It shifts
|
|
782
|
+
only along its trigger's (or point's) edge, never across it: when neither
|
|
783
|
+
side fits, it opens on the side with more room and scrolls, so a tall menu
|
|
784
|
+
never covers its trigger and a second press on the trigger closes it. The app
|
|
777
785
|
supplies pointer coordinates and command policy; it does not need to calculate
|
|
778
786
|
menu offsets. For other anchored content, use `AnchoredSurface` for help or
|
|
779
787
|
popup content, a native `title` for a plain-text hint, or `kerfjs/overlay`'s
|
|
@@ -813,7 +821,10 @@ right-click, delegate `contextmenu` on the row's primary button, prevent the
|
|
|
813
821
|
browser menu, and call `openPopupMenuAt` with the row's
|
|
814
822
|
`[data-component="popup-menu"]` child and the pointer's `clientX` /
|
|
815
823
|
`clientY`: a button-triggered menu opens `bottom-start` at the pointer for that
|
|
816
|
-
opening and returns to its trigger once it closes.
|
|
824
|
+
opening and returns to its trigger once it closes. Pass a placement for that
|
|
825
|
+
opening as a fourth argument, `openPopupMenuAt(menu, x, y, { placement:
|
|
826
|
+
'top-start' })`, to open it above the point instead (for a row near a footer
|
|
827
|
+
or composer); the viewport flip, shift, and height cap still apply.
|
|
817
828
|
|
|
818
829
|
```tsx
|
|
819
830
|
<ListActionRow
|
|
@@ -1111,7 +1122,7 @@ component below, and file a component gap rather than dropping to the raw tag.
|
|
|
1111
1122
|
| `wa-button`, `wa-dropdown`, `wa-dropdown-item` | Use `Button` (icon-only with `icon` and `ariaLabel`, or a toggle with `pressed`) for actions and `PopupMenu` for a command menu. Use `ListItem` for a navigation row and `Select` for a value. |
|
|
1112
1123
|
| `wa-button-group` | Use `ToolbarControlGroup` or `SplitButton` for grouped actions and `SegmentedControl` for one-of-many selection. |
|
|
1113
1124
|
| `wa-input`, `wa-textarea`, `wa-tag` | Use `TextField` for free text (`multiline` for a textarea) and `Chip` for removable or disabled tags; use `SearchField` only when text and ordered filter tokens form one editor. |
|
|
1114
|
-
| `wa-checkbox`, `wa-checkbox-group`, `wa-switch` | Use `Checkbox` for a submitted option (a
|
|
1125
|
+
| `wa-checkbox`, `wa-checkbox-group`, `wa-switch` | Use `Checkbox` for a submitted option (`CheckboxGroup` for a labeled set of them, or `ChoiceChips` `multiple: 'toggle'` for wrapping pills) and `Switch` for a setting that applies immediately. |
|
|
1115
1126
|
| `wa-radio-group`, `wa-radio` | Use `RadioGroup` for a few labeled options, `ChoiceChips` for wrapping pills, and `SegmentedControl` for a view switch. |
|
|
1116
1127
|
| `wa-select`, `wa-option` | Use `Select`, which owns Kerf spacing, controlled rendering, and form participation. |
|
|
1117
1128
|
| `wa-dialog`, `wa-drawer` | Use `DialogSurface` with `openDialog` / `closeDialog` (`presentation="side-sheet"` or `"bottom-sheet"` for a drawer), or a `kerfjs/overlay` dialog. Use `CollapsiblePanel` for a rail that overlays content on compact devices. |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 84" width="520" height="84"><defs><clipPath id="cp0"><rect x="174.9" y="34" width="16.1" height="16"/></clipPath><clipPath id="cp2"><rect x="198.9" y="33" width="106.3" height="18"/></clipPath><clipPath id="ct3"><rect x="198.9" y="33" width="106.3" height="18"/></clipPath></defs><rect x="20" y="20" width="440" height="44" rx="12" fill="rgb(100,210,255)"/><rect x="20.5" y="20.5" width="439" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g color="rgb(17, 17, 19)"><clipPath id="svgic1"><rect x="174.9" y="34" width="16" height="16"/></clipPath><g clip-path="url(#svgic1)"><g transform="translate(174.86 34)scale(.66667)"><path d="M5 12h14" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(17, 17, 19)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M12 5v14" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(17, 17, 19)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp2)"><g clip-path="url(#ct3)"><text x="198.86" y="48" font-family="system-ui, "-apple-system", sans-serif" font-size="16" font-weight="500" fill="rgb(17,17,19)">Create Project</text></g></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 84" width="520" height="84"><defs><clipPath id="cp0"><rect x="174.9" y="34" width="16.1" height="16"/></clipPath><clipPath id="cp2"><rect x="198.9" y="33" width="106.3" height="18"/></clipPath><clipPath id="ct3"><rect x="198.9" y="33" width="106.3" height="18"/></clipPath></defs><rect x="20" y="20" width="440" height="44" rx="12" fill="rgb(0,117,219)"/><rect x="20.5" y="20.5" width="439" height="43" rx="11.5" fill="none" stroke="rgba(0,0,0,0)" stroke-width="1"/><g clip-path="url(#cp0)"><g color="rgb(255, 255, 255)"><clipPath id="svgic1"><rect x="174.9" y="34" width="16" height="16"/></clipPath><g clip-path="url(#svgic1)"><g transform="translate(174.86 34)scale(.66667)"><path d="M5 12h14" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(255, 255, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/><path d="M12 5v14" style="marker:none;vector-effect:none;color-interpolation:srgb" fill="none" stroke="rgb(255, 255, 255)" stroke-width="2px" stroke-dasharray="none" stroke-linecap="round" stroke-linejoin="round" stroke-opacity="1" fill-opacity="1" opacity="1"/></g></g></g></g><g clip-path="url(#cp2)"><g clip-path="url(#ct3)"><text x="198.86" y="48" font-family="system-ui, "-apple-system", sans-serif" font-size="16" font-weight="500" fill="rgb(255,255,255)">Create Project</text></g></g></svg>
|