@motion-proto/live-tokens 0.96.0 → 0.98.2
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/.claude/skills/live-tokens-check-compliance/SKILL.md +5 -0
- package/.claude/skills/live-tokens-create-component/SKILL.md +4 -3
- package/.claude/skills/live-tokens-create-component/references/contract-tests.md +71 -11
- package/CHANGELOG.md +309 -0
- package/README.md +2 -2
- package/bin/check-component.mjs +8 -0
- package/bin/check-page.mjs +6 -0
- package/bin/cli.mjs +36 -6
- package/bin/contractRunner.mjs +142 -17
- package/bin/lib/findings.mjs +20 -4
- package/bin/rules/a11y.mjs +53 -0
- package/bin/set-colors.mjs +1 -0
- package/bin/setup-claude.mjs +2 -2
- package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
- package/dist-plugin/index.cjs +148 -3
- package/dist-plugin/index.js +250 -105
- package/dist-plugin/setColors/index.cjs +7 -1
- package/dist-plugin/setColors/index.d.cts +9 -4
- package/dist-plugin/setColors/index.d.ts +9 -4
- package/dist-plugin/setColors/index.js +8 -2
- package/dist-plugin/setGeometry/index.d.cts +1 -1
- package/dist-plugin/setGeometry/index.d.ts +1 -1
- package/dist-plugin/setType/index.d.cts +1 -1
- package/dist-plugin/setType/index.d.ts +1 -1
- package/dist-plugin/{themeTypes-B8_Idrp4.d.cts → themeTypes-BaTQUyac.d.cts} +10 -1
- package/dist-plugin/{themeTypes-B8_Idrp4.d.ts → themeTypes-BaTQUyac.d.ts} +10 -1
- package/package.json +49 -2
- package/src/demo/LabeledSelect.svelte +1 -1
- package/src/editor/bootstrap.ts +3 -1
- package/src/editor/component-editor/BadgeEditor.svelte +13 -0
- package/src/editor/component-editor/ButtonEditor.svelte +33 -2
- package/src/editor/component-editor/CalloutEditor.svelte +13 -0
- package/src/editor/component-editor/CardEditor.svelte +12 -2
- package/src/editor/component-editor/CodeSnippetEditor.svelte +11 -1
- package/src/editor/component-editor/CollapsibleSectionEditor.svelte +18 -1
- package/src/editor/component-editor/CornerBadgeEditor.svelte +13 -0
- package/src/editor/component-editor/DialogEditor.svelte +15 -0
- package/src/editor/component-editor/IconButtonEditor.svelte +29 -1
- package/src/editor/component-editor/ImageEditor.svelte +20 -9
- package/src/editor/component-editor/ImageLightboxEditor.svelte +44 -0
- package/src/editor/component-editor/InlineEditActionsEditor.svelte +13 -1
- package/src/editor/component-editor/InputEditor.svelte +23 -0
- package/src/editor/component-editor/MenuSelectEditor.svelte +27 -1
- package/src/editor/component-editor/NotificationEditor.svelte +15 -0
- package/src/editor/component-editor/PanelEditor.svelte +1 -1
- package/src/editor/component-editor/ProgressBarEditor.svelte +13 -0
- package/src/editor/component-editor/RadioButtonEditor.svelte +20 -1
- package/src/editor/component-editor/SectionDividerEditor.svelte +41 -13
- package/src/editor/component-editor/SegmentedControlEditor.svelte +29 -1
- package/src/editor/component-editor/SideNavigationEditor.svelte +40 -1
- package/src/editor/component-editor/SlidePagerEditor.svelte +20 -0
- package/src/editor/component-editor/SliderEditor.svelte +33 -0
- package/src/editor/component-editor/TabBarEditor.svelte +23 -2
- package/src/editor/component-editor/TableEditor.svelte +14 -0
- package/src/editor/component-editor/ToggleEditor.svelte +17 -0
- package/src/editor/component-editor/TooltipEditor.svelte +11 -2
- package/src/editor/component-editor/VideoFrameEditor.svelte +8 -0
- package/src/editor/component-editor/VideoLightboxEditor.svelte +10 -0
- package/src/editor/component-editor/registration.ts +2 -1
- package/src/editor/component-editor/registry.ts +69 -27
- package/src/editor/component-editor/scaffolding/DemoHeader.svelte +2 -0
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +19 -8
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +3 -0
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +70 -10
- package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
- package/src/editor/core/a11y/a11yReport.ts +101 -0
- package/src/editor/core/a11y/a11yReportTypes.ts +69 -0
- package/src/editor/core/a11y/contrastFailures.ts +22 -0
- package/src/editor/core/components/contrastPairRegistry.ts +18 -0
- package/src/editor/core/palettes/contrast.ts +44 -0
- package/src/editor/core/palettes/contrastPairs.ts +79 -0
- package/src/editor/core/palettes/resolveColor.ts +77 -0
- package/src/editor/core/store/editorPersistence.ts +9 -1
- package/src/editor/core/store/editorRenderer.ts +2 -0
- package/src/editor/core/store/editorStore.ts +29 -0
- package/src/editor/core/store/editorTypes.ts +9 -0
- package/src/editor/core/store/editorViewStore.ts +2 -2
- package/src/editor/core/store/unsavedEdits.ts +10 -0
- package/src/editor/core/themes/buildColors.ts +16 -5
- package/src/editor/core/themes/liveStateStream.ts +7 -1
- package/src/editor/core/themes/parsers/gradient.ts +5 -2
- package/src/editor/core/themes/slices/customColors.ts +226 -0
- package/src/editor/core/themes/themeTypes.ts +10 -0
- package/src/editor/docs/chapters.ts +1 -0
- package/src/editor/docs/content/accessibility.md +362 -0
- package/src/editor/docs/content/editing-tokens.md +56 -1
- package/src/editor/docs/content.generated.ts +2 -1
- package/src/editor/pages/ComponentEditorPage.svelte +10 -1
- package/src/editor/pages/EditorShell.svelte +97 -10
- package/src/editor/skill-atlas/skillSources.generated.ts +3 -3
- package/src/editor/skill-atlas/trees/check-compliance.ts +3 -3
- package/src/editor/skill-atlas/trees/create-component.ts +12 -12
- package/src/editor/ui/CustomColorReplaceStep.svelte +118 -0
- package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
- package/src/editor/ui/GradientEditor.svelte +9 -1
- package/src/editor/ui/ThemePanel.svelte +4 -4
- package/src/editor/ui/UIContrastReadout.svelte +186 -0
- package/src/editor/ui/UIInfoPopover.svelte +16 -2
- package/src/editor/ui/UIPaletteSelector.svelte +347 -15
- package/src/editor/ui/UITokenSelector.svelte +33 -14
- package/src/editor/ui/VariablesTab.svelte +3 -0
- package/src/editor/ui/a11y/A11yComponentIssues.svelte +103 -0
- package/src/editor/ui/a11y/A11yFindingRow.svelte +57 -0
- package/src/editor/ui/a11y/A11yIssueRow.svelte +121 -0
- package/src/editor/ui/a11y/A11yOverview.svelte +242 -0
- package/src/editor/ui/a11y/A11yPanel.svelte +128 -0
- package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
- package/src/editor/ui/a11y/A11yRailMark.svelte +34 -0
- package/src/editor/ui/a11y/AccessibilityTab.svelte +334 -0
- package/src/editor/ui/a11y/a11yView.ts +218 -0
- package/src/editor/ui/anchorToTrigger.ts +63 -0
- package/src/editor/ui/contrastReadout.ts +45 -0
- package/src/editor/ui/sections/CustomColorsSection.svelte +254 -0
- package/src/live-tokens/data/themes/autumn.json +20 -20
- package/src/live-tokens/data/themes/halloween.json +20 -20
- package/src/live-tokens/data/themes/midnight-study.json +20 -20
- package/src/live-tokens/data/themes/ocean.json +20 -20
- package/src/live-tokens/data/themes/royal-velvet.json +20 -20
- package/src/live-tokens/data/themes/sketchy.json +20 -20
- package/src/live-tokens/data/themes/spring-meadow.json +20 -20
- package/src/live-tokens/data/themes/sunset.json +20 -20
- package/src/system/components/Badge.svelte +3 -3
- package/src/system/components/Button.svelte +9 -3
- package/src/system/components/CodeSnippet.svelte +8 -1
- package/src/system/components/CornerBadge.svelte +3 -3
- package/src/system/components/Dialog.svelte +43 -14
- package/src/system/components/IconButton.svelte +1 -1
- package/src/system/components/Image.svelte +7 -0
- package/src/system/components/ImageLightbox.svelte +26 -26
- package/src/system/components/Input.svelte +8 -1
- package/src/system/components/MenuSelect.svelte +30 -2
- package/src/system/components/Notification.svelte +1 -1
- package/src/system/components/ProgressBar.svelte +6 -0
- package/src/system/components/RadioButton.svelte +36 -3
- package/src/system/components/SegmentedControl.svelte +36 -2
- package/src/system/components/SlidePager.svelte +2 -2
- package/src/system/components/Slider.svelte +13 -9
- package/src/system/components/TabBar.svelte +33 -3
- package/src/system/components/Toggle.svelte +2 -2
- package/src/system/components/VideoFrame.svelte +1 -1
- package/src/system/components/VideoLightbox.svelte +2 -2
- package/src/system/internal/focusTrap.ts +35 -0
- package/src/testing-js/__aria__/badge.yml +40 -0
- package/src/testing-js/__aria__/button.yml +88 -0
- package/src/testing-js/__aria__/callout.yml +12 -0
- package/src/testing-js/__aria__/card.yml +8 -0
- package/src/testing-js/__aria__/codesnippet.yml +21 -0
- package/src/testing-js/__aria__/collapsiblesection.yml +23 -0
- package/src/testing-js/__aria__/cornerbadge.yml +24 -0
- package/src/testing-js/__aria__/dialog.yml +42 -0
- package/src/testing-js/__aria__/iconbutton.yml +57 -0
- package/src/testing-js/__aria__/image.yml +10 -0
- package/src/testing-js/__aria__/imagelightbox.yml +50 -0
- package/src/testing-js/__aria__/inlineeditactions.yml +16 -0
- package/src/testing-js/__aria__/input.yml +61 -0
- package/src/testing-js/__aria__/menuselect.yml +30 -0
- package/src/testing-js/__aria__/notification.yml +30 -0
- package/src/testing-js/__aria__/panel.yml +2 -0
- package/src/testing-js/__aria__/progressbar.yml +2 -0
- package/src/testing-js/__aria__/radiobutton.yml +20 -0
- package/src/testing-js/__aria__/sectiondivider.yml +9 -0
- package/src/testing-js/__aria__/segmentedcontrol.yml +35 -0
- package/src/testing-js/__aria__/sidenavigation.yml +304 -0
- package/src/testing-js/__aria__/slidepager.yml +72 -0
- package/src/testing-js/__aria__/slider.yml +28 -0
- package/src/testing-js/__aria__/tabbar.yml +35 -0
- package/src/testing-js/__aria__/table.yml +144 -0
- package/src/testing-js/__aria__/toggle.yml +12 -0
- package/src/testing-js/__aria__/tooltip.yml +2 -0
- package/src/testing-js/__aria__/videoframe.yml +6 -0
- package/src/testing-js/__aria__/videolightbox.yml +12 -0
- package/src/testing-js/{chunk-WIZ6W7UT.js → chunk-3WCLMOM7.js} +2 -2
- package/src/testing-js/{chunk-SHBYZUPD.js → chunk-5HNZHIHG.js} +211 -14
- package/src/testing-js/chunk-5HNZHIHG.js.map +1 -0
- package/src/testing-js/{chunk-NB3NZRBM.js → chunk-7CM5D7PF.js} +5 -1
- package/src/testing-js/chunk-7CM5D7PF.js.map +1 -0
- package/src/testing-js/chunk-EWSGE27V.js +168 -0
- package/src/testing-js/chunk-EWSGE27V.js.map +1 -0
- package/src/testing-js/{chunk-GKO27NGN.js → chunk-R54I7YL6.js} +2 -2
- package/src/testing-js/{chunk-X6KHEVIG.js → chunk-RWH46IU5.js} +97 -29
- package/src/testing-js/chunk-RWH46IU5.js.map +1 -0
- package/src/testing-js/chunk-SHLPDNAQ.js +658 -0
- package/src/testing-js/chunk-SHLPDNAQ.js.map +1 -0
- package/src/testing-js/{chunk-3UKGXCDL.js → chunk-YVVR3GJZ.js} +1 -1
- package/src/testing-js/chunk-YVVR3GJZ.js.map +1 -0
- package/src/testing-js/component-a11y.contract.js +283 -0
- package/src/testing-js/component-a11y.contract.js.map +1 -0
- package/src/testing-js/component-alias.contract.js +2 -2
- package/src/testing-js/component-behavior.contract.js +2 -2
- package/src/testing-js/component-editor.contract.js +6 -5
- package/src/testing-js/component-editor.contract.js.map +1 -1
- package/src/testing-js/component-render.contract.js +10 -6
- package/src/testing-js/component-render.contract.js.map +1 -1
- package/src/testing-js/index.d.ts +44 -16
- package/src/testing-js/index.js +9 -9
- package/src/testing-js/page-compliance.contract.js +163 -18
- package/src/testing-js/page-compliance.contract.js.map +1 -1
- package/src/testing-js/vitest.js +3 -3
- package/template/package.json +9 -0
- package/src/editor/ui/keepInViewport.ts +0 -84
- package/src/testing-js/chunk-3UKGXCDL.js.map +0 -1
- package/src/testing-js/chunk-NB3NZRBM.js.map +0 -1
- package/src/testing-js/chunk-SHBYZUPD.js.map +0 -1
- package/src/testing-js/chunk-X6KHEVIG.js.map +0 -1
- /package/src/testing-js/{chunk-WIZ6W7UT.js.map → chunk-3WCLMOM7.js.map} +0 -0
- /package/src/testing-js/{chunk-GKO27NGN.js.map → chunk-R54I7YL6.js.map} +0 -0
|
@@ -2518,7 +2518,7 @@
|
|
|
2518
2518
|
"createdAt": "2026-05-01T08:28:00.345Z",
|
|
2519
2519
|
"updatedAt": "2026-08-19T22:39:32.785Z",
|
|
2520
2520
|
"aliases": {
|
|
2521
|
-
"--badge-brand-surface": "--surface-brand",
|
|
2521
|
+
"--badge-brand-surface": "--surface-brand-low",
|
|
2522
2522
|
"--badge-brand-text": "--text-brand",
|
|
2523
2523
|
"--badge-brand-border": "--border-brand",
|
|
2524
2524
|
"--badge-brand-text-font-family": "--font-sans",
|
|
@@ -2571,7 +2571,7 @@
|
|
|
2571
2571
|
"--badge-alternate-blur": "--blur-none",
|
|
2572
2572
|
"--badge-alternate-icon-size": "--icon-size-sm",
|
|
2573
2573
|
"--badge-canvas-surface": "--surface-canvas",
|
|
2574
|
-
"--badge-canvas-text": "--text-
|
|
2574
|
+
"--badge-canvas-text": "--text-secondary",
|
|
2575
2575
|
"--badge-canvas-border": "--border-canvas",
|
|
2576
2576
|
"--badge-canvas-text-font-family": "--font-sans",
|
|
2577
2577
|
"--badge-canvas-text-font-size": "--font-size-md",
|
|
@@ -2622,7 +2622,7 @@
|
|
|
2622
2622
|
"--badge-warning-shadow": "--shadow-none",
|
|
2623
2623
|
"--badge-warning-blur": "--blur-none",
|
|
2624
2624
|
"--badge-warning-icon-size": "--icon-size-sm",
|
|
2625
|
-
"--badge-danger-surface": "--surface-danger",
|
|
2625
|
+
"--badge-danger-surface": "--surface-danger-low",
|
|
2626
2626
|
"--badge-danger-text": "--text-danger",
|
|
2627
2627
|
"--badge-danger-border": "--border-danger",
|
|
2628
2628
|
"--badge-danger-text-font-family": "--font-sans",
|
|
@@ -2995,7 +2995,7 @@
|
|
|
2995
2995
|
"--cornerbadge-text-font-size": "--font-size-sm",
|
|
2996
2996
|
"--cornerbadge-text-font-weight": "--font-weight-medium",
|
|
2997
2997
|
"--cornerbadge-text-line-height": "--line-height-none",
|
|
2998
|
-
"--cornerbadge-brand-surface": "--surface-brand",
|
|
2998
|
+
"--cornerbadge-brand-surface": "--surface-brand-low",
|
|
2999
2999
|
"--cornerbadge-brand-border": "--border-brand",
|
|
3000
3000
|
"--cornerbadge-brand-text": "--text-brand",
|
|
3001
3001
|
"--cornerbadge-accent-surface": "--surface-accent",
|
|
@@ -3009,7 +3009,7 @@
|
|
|
3009
3009
|
"--cornerbadge-alternate-text": "--text-alternate",
|
|
3010
3010
|
"--cornerbadge-canvas-surface": "--surface-canvas",
|
|
3011
3011
|
"--cornerbadge-canvas-border": "--border-canvas",
|
|
3012
|
-
"--cornerbadge-canvas-text": "--text-
|
|
3012
|
+
"--cornerbadge-canvas-text": "--text-secondary",
|
|
3013
3013
|
"--cornerbadge-special-surface": "--surface-special",
|
|
3014
3014
|
"--cornerbadge-special-border": "--border-special",
|
|
3015
3015
|
"--cornerbadge-special-text": "--text-special",
|
|
@@ -3019,7 +3019,7 @@
|
|
|
3019
3019
|
"--cornerbadge-warning-surface": "--surface-warning",
|
|
3020
3020
|
"--cornerbadge-warning-border": "--border-warning",
|
|
3021
3021
|
"--cornerbadge-warning-text": "--text-warning",
|
|
3022
|
-
"--cornerbadge-danger-surface": "--surface-danger",
|
|
3022
|
+
"--cornerbadge-danger-surface": "--surface-danger-low",
|
|
3023
3023
|
"--cornerbadge-danger-border": "--border-danger",
|
|
3024
3024
|
"--cornerbadge-danger-text": "--text-danger",
|
|
3025
3025
|
"--cornerbadge-info-surface": "--surface-info",
|
|
@@ -3237,7 +3237,7 @@
|
|
|
3237
3237
|
"--input-border-width": "--border-width-1",
|
|
3238
3238
|
"--input-gap": "--space-6",
|
|
3239
3239
|
"--input-default-surface": "--surface-neutral-lower",
|
|
3240
|
-
"--input-default-border": "--border-neutral",
|
|
3240
|
+
"--input-default-border": "--border-neutral-medium",
|
|
3241
3241
|
"--input-default-text": "--text-primary",
|
|
3242
3242
|
"--input-default-text-font-family": "--font-sans",
|
|
3243
3243
|
"--input-default-text-font-size": "--font-size-sm",
|
|
@@ -3396,7 +3396,7 @@
|
|
|
3396
3396
|
"--notification-warning-text-font-size": "--font-size-md",
|
|
3397
3397
|
"--notification-warning-text-font-weight": "--font-weight-normal",
|
|
3398
3398
|
"--notification-warning-text-line-height": "--line-height-normal",
|
|
3399
|
-
"--notification-danger-surface": "--surface-danger",
|
|
3399
|
+
"--notification-danger-surface": "--surface-danger-low",
|
|
3400
3400
|
"--notification-danger-action-surface": "--surface-neutral-lowest",
|
|
3401
3401
|
"--notification-danger-border": "--border-danger",
|
|
3402
3402
|
"--notification-danger-border-width": "--border-width-1",
|
|
@@ -3480,7 +3480,7 @@
|
|
|
3480
3480
|
"createdAt": "2026-05-10T20:44:44.372Z",
|
|
3481
3481
|
"updatedAt": "2026-08-19T22:39:32.785Z",
|
|
3482
3482
|
"aliases": {
|
|
3483
|
-
"--radiobutton-default-dot-border-color": "--border-neutral",
|
|
3483
|
+
"--radiobutton-default-dot-border-color": "--border-neutral-medium",
|
|
3484
3484
|
"--radiobutton-default-dot-border-width": "--border-width-2",
|
|
3485
3485
|
"--radiobutton-default-dot-fill": "--text-secondary",
|
|
3486
3486
|
"--radiobutton-default-dot-size": "--dot-size-0",
|
|
@@ -3489,7 +3489,7 @@
|
|
|
3489
3489
|
"--radiobutton-default-label-font-size": "--font-size-md",
|
|
3490
3490
|
"--radiobutton-default-label-font-weight": "--font-weight-semibold",
|
|
3491
3491
|
"--radiobutton-default-label-line-height": "--line-height-none",
|
|
3492
|
-
"--radiobutton-hover-dot-border-color": "--border-neutral",
|
|
3492
|
+
"--radiobutton-hover-dot-border-color": "--border-neutral-medium",
|
|
3493
3493
|
"--radiobutton-hover-dot-border-width": "--border-width-2",
|
|
3494
3494
|
"--radiobutton-hover-dot-fill": "--text-secondary",
|
|
3495
3495
|
"--radiobutton-hover-dot-size": "--dot-size-50",
|
|
@@ -3498,7 +3498,7 @@
|
|
|
3498
3498
|
"--radiobutton-hover-label-font-size": "--font-size-md",
|
|
3499
3499
|
"--radiobutton-hover-label-font-weight": "--font-weight-semibold",
|
|
3500
3500
|
"--radiobutton-hover-label-line-height": "--line-height-none",
|
|
3501
|
-
"--radiobutton-selected-dot-border-color": "--border-neutral",
|
|
3501
|
+
"--radiobutton-selected-dot-border-color": "--border-neutral-medium",
|
|
3502
3502
|
"--radiobutton-selected-dot-border-width": "--border-width-2",
|
|
3503
3503
|
"--radiobutton-selected-dot-fill": "--text-secondary",
|
|
3504
3504
|
"--radiobutton-selected-dot-size": "--dot-size-50",
|
|
@@ -3607,7 +3607,7 @@
|
|
|
3607
3607
|
"--segmentedcontrol-hover-tint": "--tint",
|
|
3608
3608
|
"--segmentedcontrol-hover-tint-enabled": "--color-transparent",
|
|
3609
3609
|
"--segmentedcontrol-bar-surface": "color-mix(in srgb, var(--surface-neutral-lowest) 75%, transparent)",
|
|
3610
|
-
"--segmentedcontrol-bar-border": "--border-neutral",
|
|
3610
|
+
"--segmentedcontrol-bar-border": "--border-neutral-medium",
|
|
3611
3611
|
"--segmentedcontrol-bar-border-width": "--border-width-1",
|
|
3612
3612
|
"--segmentedcontrol-bar-radius": "--radius-xl",
|
|
3613
3613
|
"--segmentedcontrol-bar-padding": "--space-6",
|
|
@@ -3638,7 +3638,7 @@
|
|
|
3638
3638
|
"--segmentedcontrol-selected-text-font-weight": "--font-weight-normal",
|
|
3639
3639
|
"--segmentedcontrol-selected-text-line-height": "--line-height-normal",
|
|
3640
3640
|
"--segmentedcontrol-selected-icon": "--text-secondary",
|
|
3641
|
-
"--segmentedcontrol-selected-border": "--border-brand",
|
|
3641
|
+
"--segmentedcontrol-selected-border": "--border-brand-medium",
|
|
3642
3642
|
"--segmentedcontrol-selected-border-width": "--border-width-1",
|
|
3643
3643
|
"--segmentedcontrol-selected-radius": "--radius-lg",
|
|
3644
3644
|
"--segmentedcontrol-disabled-surface": "--color-transparent",
|
|
@@ -3865,7 +3865,7 @@
|
|
|
3865
3865
|
"--tabbar-hover-tab-bottom-radius": "--radius-sm",
|
|
3866
3866
|
"--tabbar-hover-indicator-width": "--border-width-2",
|
|
3867
3867
|
"--tabbar-selected-text": "--text-primary",
|
|
3868
|
-
"--tabbar-selected-border": "--
|
|
3868
|
+
"--tabbar-selected-border": "--border-brand-medium",
|
|
3869
3869
|
"--tabbar-selected-surface": "--tint-low",
|
|
3870
3870
|
"--tabbar-selected-text-font-family": "--font-sans",
|
|
3871
3871
|
"--tabbar-selected-text-font-size": "--font-size-md",
|
|
@@ -3935,7 +3935,7 @@
|
|
|
3935
3935
|
"updatedAt": "2026-08-19T22:39:32.785Z",
|
|
3936
3936
|
"aliases": {
|
|
3937
3937
|
"--toggle-track-surface": "--surface-neutral",
|
|
3938
|
-
"--toggle-track-border": "--border-neutral",
|
|
3938
|
+
"--toggle-track-border": "--border-neutral-medium",
|
|
3939
3939
|
"--toggle-track-border-width": "--border-width-1",
|
|
3940
3940
|
"--toggle-track-radius": "--radius-full",
|
|
3941
3941
|
"--toggle-track-padding": "--space-4",
|
|
@@ -3950,7 +3950,7 @@
|
|
|
3950
3950
|
"--toggle-hover-track-surface": "--surface-neutral-high",
|
|
3951
3951
|
"--toggle-hover-thumb-surface": "--text-primary",
|
|
3952
3952
|
"--toggle-on-track-surface": "--surface-brand-high",
|
|
3953
|
-
"--toggle-on-track-border": "--border-brand",
|
|
3953
|
+
"--toggle-on-track-border": "--border-brand-medium",
|
|
3954
3954
|
"--toggle-on-thumb-surface": "--text-primary",
|
|
3955
3955
|
"--toggle-on-thumb-border": "--border-brand-strong",
|
|
3956
3956
|
"--toggle-on-hover-track-surface": "--surface-brand-higher",
|
|
@@ -3986,13 +3986,13 @@
|
|
|
3986
3986
|
"updatedAt": "2026-09-02T10:55:50.831Z",
|
|
3987
3987
|
"aliases": {
|
|
3988
3988
|
"--slider-single-track-surface": "--surface-neutral-lowest",
|
|
3989
|
-
"--slider-single-track-border": "--border-neutral-
|
|
3989
|
+
"--slider-single-track-border": "--border-neutral-medium",
|
|
3990
3990
|
"--slider-single-track-border-width": "--border-width-1",
|
|
3991
3991
|
"--slider-single-track-height": "--space-8",
|
|
3992
3992
|
"--slider-single-track-radius": "--radius-full",
|
|
3993
3993
|
"--slider-single-fill": "--surface-brand-high",
|
|
3994
3994
|
"--slider-single-thumb-surface": "--surface-neutral-highest",
|
|
3995
|
-
"--slider-single-thumb-border": "--border-brand",
|
|
3995
|
+
"--slider-single-thumb-border": "--border-brand-medium",
|
|
3996
3996
|
"--slider-single-thumb-border-width": "--border-width-2",
|
|
3997
3997
|
"--slider-single-thumb-size": "--font-size-lg",
|
|
3998
3998
|
"--slider-single-thumb-radius": "--radius-full",
|
|
@@ -4004,13 +4004,13 @@
|
|
|
4004
4004
|
"--slider-single-disabled-thumb-surface": "--surface-neutral-lower",
|
|
4005
4005
|
"--slider-single-disabled-thumb-border": "--border-neutral",
|
|
4006
4006
|
"--slider-range-track-surface": "--surface-neutral-lowest",
|
|
4007
|
-
"--slider-range-track-border": "--border-neutral-
|
|
4007
|
+
"--slider-range-track-border": "--border-neutral-medium",
|
|
4008
4008
|
"--slider-range-track-border-width": "--border-width-1",
|
|
4009
4009
|
"--slider-range-track-height": "--space-8",
|
|
4010
4010
|
"--slider-range-track-radius": "--radius-full",
|
|
4011
4011
|
"--slider-range-fill": "--surface-brand-high",
|
|
4012
4012
|
"--slider-range-thumb-surface": "--surface-neutral-highest",
|
|
4013
|
-
"--slider-range-thumb-border": "--border-brand",
|
|
4013
|
+
"--slider-range-thumb-border": "--border-brand-medium",
|
|
4014
4014
|
"--slider-range-thumb-border-width": "--border-width-2",
|
|
4015
4015
|
"--slider-range-thumb-size": "--font-size-lg",
|
|
4016
4016
|
"--slider-range-thumb-radius": "--radius-full",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
// -secondary/-tertiary scale); `--text-brand` is the brand-family text.
|
|
76
76
|
:global(:root) {
|
|
77
77
|
/* Brand */
|
|
78
|
-
--badge-brand-surface: var(--surface-brand);
|
|
78
|
+
--badge-brand-surface: var(--surface-brand-low);
|
|
79
79
|
--badge-brand-text: var(--text-brand);
|
|
80
80
|
--badge-brand-border: var(--border-brand);
|
|
81
81
|
--badge-brand-text-font-family: var(--font-sans);
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
|
|
137
137
|
/* Canvas */
|
|
138
138
|
--badge-canvas-surface: var(--surface-canvas);
|
|
139
|
-
--badge-canvas-text: var(--text-
|
|
139
|
+
--badge-canvas-text: var(--text-secondary);
|
|
140
140
|
--badge-canvas-border: var(--border-canvas);
|
|
141
141
|
--badge-canvas-text-font-family: var(--font-sans);
|
|
142
142
|
--badge-canvas-text-font-size: var(--font-size-md);
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
--badge-warning-icon-size: var(--icon-size-sm);
|
|
196
196
|
|
|
197
197
|
/* Danger */
|
|
198
|
-
--badge-danger-surface: var(--surface-danger);
|
|
198
|
+
--badge-danger-surface: var(--surface-danger-low);
|
|
199
199
|
--badge-danger-text: var(--text-danger);
|
|
200
200
|
--badge-danger-border: var(--border-danger);
|
|
201
201
|
--badge-danger-text-font-family: var(--font-sans);
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import type { CatalogueEntry } from '../../editor/component-editor/scaffolding/types';
|
|
3
3
|
|
|
4
4
|
export const catalogue = {
|
|
5
|
-
description: 'A labelled
|
|
6
|
-
whenToUse: '
|
|
5
|
+
description: 'A labelled trigger.',
|
|
6
|
+
whenToUse: 'a trigger that needs a word to be unambiguous. It runs an action or opens another page, such as View details or Create project.',
|
|
7
7
|
whenNotToUse: [
|
|
8
8
|
{ when: 'the glyph alone is plain, and space is short.', use: 'iconbutton' },
|
|
9
9
|
{ when: 'the actions confirm or cancel an inline edit.', use: 'inlineeditactions' },
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
constraints: [
|
|
12
12
|
'One primary Button per page.',
|
|
13
13
|
'A danger Button opens a Dialog to confirm before it runs.',
|
|
14
|
-
'A link
|
|
14
|
+
'A link inside running text is an anchor.',
|
|
15
15
|
],
|
|
16
16
|
props: {
|
|
17
17
|
variant: 'one primary per page; secondary for the rest; outline for a tertiary action; danger for a destructive one.',
|
|
@@ -542,4 +542,10 @@
|
|
|
542
542
|
margin-left: var(--space-4);
|
|
543
543
|
}
|
|
544
544
|
}
|
|
545
|
+
|
|
546
|
+
@media (prefers-reduced-motion: reduce) {
|
|
547
|
+
.button::before {
|
|
548
|
+
display: none;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
545
551
|
</style>
|
|
@@ -46,7 +46,9 @@
|
|
|
46
46
|
</script>
|
|
47
47
|
|
|
48
48
|
<div class="codesnippet {className}">
|
|
49
|
-
|
|
49
|
+
<!-- A long snippet scrolls sideways, so the keyboard needs a stop on it. -->
|
|
50
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
51
|
+
<code class="code" tabindex="0">{code}</code>
|
|
50
52
|
<Tooltip text={copied ? 'Copied' : ''} open={copied} position="top">
|
|
51
53
|
<button
|
|
52
54
|
type="button"
|
|
@@ -118,6 +120,11 @@
|
|
|
118
120
|
scrollbar-color: var(--codesnippet-scrollbar-thumb) transparent;
|
|
119
121
|
}
|
|
120
122
|
|
|
123
|
+
.code:focus-visible {
|
|
124
|
+
outline: none;
|
|
125
|
+
box-shadow: 0 0 0 var(--border-width-2) var(--border-neutral-strong);
|
|
126
|
+
}
|
|
127
|
+
|
|
121
128
|
.code::-webkit-scrollbar {
|
|
122
129
|
height: var(--codesnippet-scrollbar-border-width);
|
|
123
130
|
}
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
/* Per-variant color overrides. Default = inherit the same family/level the
|
|
74
74
|
inner Badge uses; author can swap to a different level (e.g. -low) to
|
|
75
75
|
reduce contrast against the host surface. */
|
|
76
|
-
--cornerbadge-brand-surface: var(--surface-brand);
|
|
76
|
+
--cornerbadge-brand-surface: var(--surface-brand-low);
|
|
77
77
|
--cornerbadge-brand-border: var(--border-brand);
|
|
78
78
|
--cornerbadge-brand-text: var(--text-brand);
|
|
79
79
|
--cornerbadge-accent-surface: var(--surface-accent);
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
--cornerbadge-alternate-text: var(--text-alternate);
|
|
88
88
|
--cornerbadge-canvas-surface: var(--surface-canvas);
|
|
89
89
|
--cornerbadge-canvas-border: var(--border-canvas);
|
|
90
|
-
--cornerbadge-canvas-text: var(--text-
|
|
90
|
+
--cornerbadge-canvas-text: var(--text-secondary);
|
|
91
91
|
--cornerbadge-special-surface: var(--surface-special);
|
|
92
92
|
--cornerbadge-special-border: var(--border-special);
|
|
93
93
|
--cornerbadge-special-text: var(--text-special);
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
--cornerbadge-warning-surface: var(--surface-warning);
|
|
98
98
|
--cornerbadge-warning-border: var(--border-warning);
|
|
99
99
|
--cornerbadge-warning-text: var(--text-warning);
|
|
100
|
-
--cornerbadge-danger-surface: var(--surface-danger);
|
|
100
|
+
--cornerbadge-danger-surface: var(--surface-danger-low);
|
|
101
101
|
--cornerbadge-danger-border: var(--border-danger);
|
|
102
102
|
--cornerbadge-danger-text: var(--text-danger);
|
|
103
103
|
--cornerbadge-info-surface: var(--surface-info);
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { createEventDispatcher, tick } from 'svelte';
|
|
16
16
|
import type { Snippet } from 'svelte';
|
|
17
17
|
import { portal } from '../internal/portal';
|
|
18
|
+
import { tabbables, trapTab } from '../internal/focusTrap';
|
|
18
19
|
import Button from './Button.svelte';
|
|
19
20
|
import type { ButtonVariant, DialogButtonSpec } from './types';
|
|
20
21
|
|
|
@@ -64,6 +65,8 @@
|
|
|
64
65
|
children,
|
|
65
66
|
}: Props = $props();
|
|
66
67
|
|
|
68
|
+
const titleId = $props.id();
|
|
69
|
+
|
|
67
70
|
let effectiveConfirmVariant = $derived(confirm?.variant ?? asVariant(confirmVarValue, 'primary'));
|
|
68
71
|
let effectiveCancelVariant = $derived(cancel?.variant ?? asVariant(cancelVarValue, 'outline'));
|
|
69
72
|
|
|
@@ -75,6 +78,7 @@
|
|
|
75
78
|
let confirmButtonRef: HTMLButtonElement = $state()!;
|
|
76
79
|
let cancelButtonRef: HTMLButtonElement = $state()!;
|
|
77
80
|
let closeButtonRef: HTMLButtonElement = $state()!;
|
|
81
|
+
let dialogEl: HTMLDivElement | undefined = $state();
|
|
78
82
|
|
|
79
83
|
$effect(() => {
|
|
80
84
|
if (typeof document === 'undefined') return;
|
|
@@ -86,21 +90,29 @@
|
|
|
86
90
|
return () => obs.disconnect();
|
|
87
91
|
});
|
|
88
92
|
|
|
89
|
-
//
|
|
93
|
+
// The inline preview stays out of the trap, so the editor keeps its focus.
|
|
90
94
|
$effect(() => {
|
|
91
|
-
if (open
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
95
|
+
if (!open || inline) return;
|
|
96
|
+
const returnTo = document.activeElement as HTMLElement | null;
|
|
97
|
+
tick().then(() => {
|
|
98
|
+
const target =
|
|
99
|
+
(confirm && !confirm.disabled && confirmButtonRef) ||
|
|
100
|
+
(cancel && cancelButtonRef) ||
|
|
101
|
+
closeButtonRef ||
|
|
102
|
+
(dialogEl && tabbables(dialogEl)[0]) ||
|
|
103
|
+
dialogEl;
|
|
104
|
+
target?.focus();
|
|
105
|
+
});
|
|
106
|
+
return () => returnTo?.focus();
|
|
102
107
|
});
|
|
103
108
|
|
|
109
|
+
function holdTab(event: KeyboardEvent) {
|
|
110
|
+
if (event.key !== 'Tab' || !open || inline || !dialogEl) return;
|
|
111
|
+
if (trapTab(dialogEl, event)) return;
|
|
112
|
+
event.preventDefault();
|
|
113
|
+
dialogEl.focus();
|
|
114
|
+
}
|
|
115
|
+
|
|
104
116
|
function handleConfirm() {
|
|
105
117
|
if (confirm && !confirm.disabled) {
|
|
106
118
|
confirm.onClick();
|
|
@@ -116,17 +128,34 @@
|
|
|
116
128
|
open = false;
|
|
117
129
|
}
|
|
118
130
|
}
|
|
131
|
+
|
|
132
|
+
function closeOnEscape(event: KeyboardEvent) {
|
|
133
|
+
if (event.key !== 'Escape') return;
|
|
134
|
+
event.preventDefault();
|
|
135
|
+
handleCancel();
|
|
136
|
+
}
|
|
119
137
|
</script>
|
|
120
138
|
|
|
139
|
+
<svelte:document onkeydown={holdTab} />
|
|
140
|
+
|
|
121
141
|
{#if open}
|
|
122
142
|
<!-- The fixed backdrop portals to <body> to escape transformed/isolated
|
|
123
143
|
ancestors; the inline preview variant stays in flow. -->
|
|
124
144
|
<div class="dialog-backdrop" class:inline use:portal={!inline}>
|
|
125
|
-
<div
|
|
145
|
+
<div
|
|
146
|
+
bind:this={dialogEl}
|
|
147
|
+
class="dialog"
|
|
148
|
+
role="dialog"
|
|
149
|
+
aria-modal={inline ? undefined : true}
|
|
150
|
+
aria-labelledby={title ? titleId : undefined}
|
|
151
|
+
tabindex="-1"
|
|
152
|
+
style="width: {width}; max-width: {width};"
|
|
153
|
+
onkeydown={closeOnEscape}
|
|
154
|
+
>
|
|
126
155
|
<div class="dialog-content">
|
|
127
156
|
{#if title}
|
|
128
157
|
<div class="dialog-header">
|
|
129
|
-
<h3 class="dialog-title">{title}</h3>
|
|
158
|
+
<h3 class="dialog-title" id={titleId}>{title}</h3>
|
|
130
159
|
<button
|
|
131
160
|
bind:this={closeButtonRef}
|
|
132
161
|
class="dialog-close"
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
description: 'An action that an icon alone carries.',
|
|
6
6
|
whenToUse: 'an action whose meaning is plain from the glyph, such as close, edit, or delete.',
|
|
7
7
|
whenNotToUse: [
|
|
8
|
-
{ when: 'the
|
|
8
|
+
{ when: 'the trigger needs a word to be unambiguous.', use: 'button' },
|
|
9
9
|
{ when: 'the pair confirms or cancels an inline edit.', use: 'inlineeditactions' },
|
|
10
10
|
],
|
|
11
11
|
props: {
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
props: {
|
|
13
13
|
thumbAspect:
|
|
14
14
|
'the ratio the thumbnail box holds, when it should not be the image\'s own. The image still opens whole, at its own ratio.',
|
|
15
|
+
thumbSrc:
|
|
16
|
+
'a smaller file for the closed thumbnail, when the full image is far larger than the tile. The lightbox still opens `src`.',
|
|
15
17
|
},
|
|
16
18
|
} satisfies CatalogueEntry;
|
|
17
19
|
</script>
|
|
@@ -19,6 +21,7 @@
|
|
|
19
21
|
<script lang="ts">
|
|
20
22
|
import { onMount, tick, type Snippet } from 'svelte';
|
|
21
23
|
import { portal } from '../internal/portal';
|
|
24
|
+
import { trapTab } from '../internal/focusTrap';
|
|
22
25
|
|
|
23
26
|
interface GalleryImage {
|
|
24
27
|
src: string;
|
|
@@ -39,8 +42,12 @@
|
|
|
39
42
|
maxWidth?: number | string | undefined;
|
|
40
43
|
/** The thumbnail box's ratio, when it differs from the image's own. */
|
|
41
44
|
thumbAspect?: number | undefined;
|
|
45
|
+
/** A smaller file for the closed thumbnail; the modal still opens `src`. */
|
|
46
|
+
thumbSrc?: string | undefined;
|
|
42
47
|
/** Closed-thumbnail object-fit. `cover` crops the thumbnail to fill its box;
|
|
43
48
|
the expanded modal always uses `contain` so the whole image stays visible.
|
|
49
|
+
Unset, it follows the theme's `--imagelightbox-tile-object-fit`, which
|
|
50
|
+
defaults to `contain`.
|
|
44
51
|
`cover` only crops when the thumbnail box differs from the image's shape
|
|
45
52
|
(a `thumbAspect`, or a CSS-constrained container). */
|
|
46
53
|
fit?: 'contain' | 'cover';
|
|
@@ -106,7 +113,8 @@
|
|
|
106
113
|
images = undefined,
|
|
107
114
|
maxWidth = undefined,
|
|
108
115
|
thumbAspect = undefined,
|
|
109
|
-
|
|
116
|
+
thumbSrc = undefined,
|
|
117
|
+
fit = undefined,
|
|
110
118
|
shadow = 'box',
|
|
111
119
|
surface = undefined,
|
|
112
120
|
border = undefined,
|
|
@@ -602,29 +610,12 @@
|
|
|
602
610
|
if (!isGallery && scale <= MIN_SCALE) closeLightbox();
|
|
603
611
|
}
|
|
604
612
|
|
|
605
|
-
//
|
|
606
|
-
//
|
|
607
|
-
function
|
|
608
|
-
if (!modalEl) return;
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
];
|
|
612
|
-
if (f.length === 0) {
|
|
613
|
-
e.preventDefault();
|
|
614
|
-
stageEl?.focus();
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
const first = f[0];
|
|
618
|
-
const last = f[f.length - 1];
|
|
619
|
-
const active = document.activeElement as HTMLElement | null;
|
|
620
|
-
const inside = active && modalEl.contains(active);
|
|
621
|
-
if (e.shiftKey && (!inside || active === first)) {
|
|
622
|
-
e.preventDefault();
|
|
623
|
-
last.focus();
|
|
624
|
-
} else if (!e.shiftKey && (!inside || active === last)) {
|
|
625
|
-
e.preventDefault();
|
|
626
|
-
first.focus();
|
|
627
|
-
}
|
|
613
|
+
// The stage (tabindex -1) is no tab stop, so an image-only lightbox holds
|
|
614
|
+
// focus on its close button.
|
|
615
|
+
function onTab(e: KeyboardEvent) {
|
|
616
|
+
if (!modalEl || trapTab(modalEl, e)) return;
|
|
617
|
+
e.preventDefault();
|
|
618
|
+
stageEl?.focus();
|
|
628
619
|
}
|
|
629
620
|
|
|
630
621
|
onMount(() => {
|
|
@@ -633,7 +624,7 @@
|
|
|
633
624
|
if (e.key === 'Escape') {
|
|
634
625
|
closeLightbox();
|
|
635
626
|
} else if (e.key === 'Tab') {
|
|
636
|
-
|
|
627
|
+
onTab(e);
|
|
637
628
|
} else if (isGallery && e.key === 'ArrowRight') {
|
|
638
629
|
e.preventDefault();
|
|
639
630
|
next();
|
|
@@ -694,7 +685,9 @@
|
|
|
694
685
|
aria-expanded={open}
|
|
695
686
|
onclick={openLightbox}
|
|
696
687
|
>
|
|
697
|
-
|
|
688
|
+
<!-- A thumbnail's pixels understate the full image, so it never feeds the
|
|
689
|
+
measured size behind the tile aspect and the zoom caps. -->
|
|
690
|
+
<img src={thumbSrc ?? cover?.src} alt={cover?.alt} draggable="false" onload={thumbSrc ? undefined : onCoverLoad} />
|
|
698
691
|
</button>
|
|
699
692
|
{#if overlay}
|
|
700
693
|
<!-- Beside the thumb, not in it: the thumb clips its corners. Hidden while
|
|
@@ -913,6 +906,13 @@
|
|
|
913
906
|
transition: none;
|
|
914
907
|
}
|
|
915
908
|
|
|
909
|
+
@media (prefers-reduced-motion: reduce) {
|
|
910
|
+
.image-lightbox-thumb,
|
|
911
|
+
.image-lightbox-attached {
|
|
912
|
+
transition: none;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
916
|
.image-lightbox-thumb:focus-visible {
|
|
917
917
|
outline: 2px solid var(--border-brand-medium);
|
|
918
918
|
outline-offset: 2px;
|
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
|
|
166
166
|
/* Default */
|
|
167
167
|
--input-default-surface: var(--surface-neutral-lower);
|
|
168
|
-
--input-default-border: var(--border-neutral);
|
|
168
|
+
--input-default-border: var(--border-neutral-medium);
|
|
169
169
|
--input-default-text: var(--text-primary);
|
|
170
170
|
--input-default-text-font-family: var(--font-sans);
|
|
171
171
|
--input-default-text-font-size: var(--font-size-sm);
|
|
@@ -305,6 +305,13 @@
|
|
|
305
305
|
outline: var(--input-error-border-width) solid var(--input-error-border);
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
+
/* The red outline stays put on focus, so focus adds a neutral ring outside
|
|
309
|
+
it. The spread covers the outline's width, which paints over the shadow. */
|
|
310
|
+
.input-field.invalid .input-control:focus,
|
|
311
|
+
.input-field.invalid.force-focus .input-control {
|
|
312
|
+
box-shadow: 0 0 0 calc(var(--input-error-border-width) + var(--border-width-2)) var(--border-neutral-strong);
|
|
313
|
+
}
|
|
314
|
+
|
|
308
315
|
.input-icon {
|
|
309
316
|
color: var(--input-default-icon);
|
|
310
317
|
font-size: var(--input-default-icon-size);
|
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
minWidth?: string;
|
|
41
41
|
/** ARIA role on the wrapper. `listbox` for select-like menus; `menu` for command menus. */
|
|
42
42
|
role?: 'listbox' | 'menu';
|
|
43
|
+
/** The list's accessible name, such as the text of the Button that opens it. */
|
|
44
|
+
ariaLabel?: string;
|
|
43
45
|
/** Selection callback. */
|
|
44
46
|
onchange?: (value: string) => void;
|
|
45
47
|
}
|
|
@@ -51,6 +53,7 @@
|
|
|
51
53
|
forceHoverValue = null,
|
|
52
54
|
minWidth = undefined,
|
|
53
55
|
role = 'listbox',
|
|
56
|
+
ariaLabel = undefined,
|
|
54
57
|
onchange,
|
|
55
58
|
}: Props = $props();
|
|
56
59
|
|
|
@@ -67,16 +70,38 @@
|
|
|
67
70
|
onchange?.(v);
|
|
68
71
|
dispatch('change', v);
|
|
69
72
|
}
|
|
73
|
+
|
|
74
|
+
const buttons: HTMLButtonElement[] = $state([]);
|
|
75
|
+
|
|
76
|
+
// Roving tab stop: the list takes one Tab press, on the selected item.
|
|
77
|
+
let tabStop = $derived(
|
|
78
|
+
items.find((i) => i.value === value && !i.disabled)?.value ?? items.find((i) => !i.disabled)?.value,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
function moveFocus(event: KeyboardEvent, from: number) {
|
|
82
|
+
const enabled = items.flatMap((item, i) => (item.disabled ? [] : [i]));
|
|
83
|
+
const at = enabled.indexOf(from);
|
|
84
|
+
const to = {
|
|
85
|
+
ArrowDown: enabled[(at + 1) % enabled.length],
|
|
86
|
+
ArrowUp: enabled[(at - 1 + enabled.length) % enabled.length],
|
|
87
|
+
Home: enabled[0],
|
|
88
|
+
End: enabled[enabled.length - 1],
|
|
89
|
+
}[event.key];
|
|
90
|
+
if (to === undefined) return;
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
buttons[to].focus();
|
|
93
|
+
}
|
|
70
94
|
</script>
|
|
71
95
|
|
|
72
96
|
<ul
|
|
73
97
|
style:--menuselect-hover-tint-enabled={hoverTintValue}
|
|
74
98
|
class="menuselect"
|
|
75
99
|
{role}
|
|
100
|
+
aria-label={ariaLabel}
|
|
76
101
|
style:min-width={minWidth}
|
|
77
102
|
>
|
|
78
|
-
{#each items as item (item.value)}
|
|
79
|
-
<li>
|
|
103
|
+
{#each items as item, i (item.value)}
|
|
104
|
+
<li role="none">
|
|
80
105
|
<button
|
|
81
106
|
type="button"
|
|
82
107
|
class="menuselect-item"
|
|
@@ -85,7 +110,10 @@
|
|
|
85
110
|
disabled={item.disabled}
|
|
86
111
|
role={role === 'listbox' ? 'option' : 'menuitem'}
|
|
87
112
|
aria-selected={role === 'listbox' ? value === item.value : undefined}
|
|
113
|
+
tabindex={item.value === tabStop ? 0 : -1}
|
|
114
|
+
bind:this={buttons[i]}
|
|
88
115
|
onclick={() => select(item.value)}
|
|
116
|
+
onkeydown={(event) => moveFocus(event, i)}
|
|
89
117
|
>
|
|
90
118
|
{#if item.icon}<i class="menuselect-icon {item.icon}" aria-hidden="true"></i>{/if}
|
|
91
119
|
<span class="menuselect-label">{item.label}</span>
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
--notification-warning-text-line-height: var(--line-height-normal);
|
|
189
189
|
|
|
190
190
|
/* Danger */
|
|
191
|
-
--notification-danger-surface: var(--surface-danger);
|
|
191
|
+
--notification-danger-surface: var(--surface-danger-low);
|
|
192
192
|
--notification-danger-action-surface: var(--surface-neutral-lowest);
|
|
193
193
|
--notification-danger-border: var(--border-danger);
|
|
194
194
|
--notification-danger-border-width: var(--border-width-1);
|