@motion-proto/live-tokens 0.97.0 → 0.99.0
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 +339 -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 +144 -19
- package/bin/lib/findings.mjs +20 -4
- package/bin/rules/a11y.mjs +53 -0
- package/dist-plugin/{chunk-A4TBP4MM.js → chunk-SLICH4IH.js} +2 -2
- package/dist-plugin/index.cjs +204 -3
- package/dist-plugin/index.js +306 -105
- package/dist-plugin/setColors/index.js +1 -1
- package/package.json +47 -2
- package/src/demo/LabeledSelect.svelte +1 -1
- package/src/editor/bootstrap.ts +5 -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 +40 -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 -3
- package/src/editor/component-editor/scaffolding/editorContext.ts +7 -0
- package/src/editor/core/a11y/a11yReport.ts +164 -0
- package/src/editor/core/a11y/a11yReportTypes.ts +77 -0
- package/src/editor/core/a11y/contrastFailures.ts +22 -0
- package/src/editor/core/a11y/contrastFixes.ts +41 -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 +82 -0
- package/src/editor/core/palettes/resolveColor.ts +77 -0
- package/src/editor/core/store/editorStore.ts +4 -0
- package/src/editor/core/store/editorViewStore.ts +2 -2
- package/src/editor/core/store/unsavedEdits.ts +10 -0
- package/src/editor/core/themes/liveStateStream.ts +7 -1
- package/src/editor/core/themes/parsers/gradient.ts +5 -2
- package/src/editor/docs/chapters.ts +1 -0
- package/src/editor/docs/content/accessibility.md +411 -0
- package/src/editor/docs/content/editing-tokens.md +32 -1
- package/src/editor/docs/content.generated.ts +2 -1
- package/src/editor/pages/ComponentEditorPage.svelte +11 -1
- package/src/editor/pages/EditorShell.svelte +98 -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/styles/ui-editor.css +2 -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 +9 -0
- package/src/editor/ui/UITokenSelector.svelte +26 -2
- package/src/editor/ui/a11y/A11yByEye.svelte +264 -0
- package/src/editor/ui/a11y/A11yComponentIssues.svelte +121 -0
- package/src/editor/ui/a11y/A11yFindingRow.svelte +119 -0
- package/src/editor/ui/a11y/A11yIssueRow.svelte +152 -0
- package/src/editor/ui/a11y/A11yOverview.svelte +246 -0
- package/src/editor/ui/a11y/A11yPanel.svelte +153 -0
- package/src/editor/ui/a11y/A11yPropertyMark.svelte +84 -0
- package/src/editor/ui/a11y/A11yRailMark.svelte +41 -0
- package/src/editor/ui/a11y/AccessibilityTab.svelte +321 -0
- package/src/editor/ui/a11y/a11yView.ts +257 -0
- package/src/editor/ui/contrastReadout.ts +45 -0
- package/src/live-tokens/data/colors-and-type/halloween.json +1 -1
- package/src/live-tokens/data/themes/autumn.json +35 -35
- package/src/live-tokens/data/themes/halloween.json +36 -36
- package/src/live-tokens/data/themes/midnight-study.json +35 -35
- package/src/live-tokens/data/themes/ocean.json +35 -35
- package/src/live-tokens/data/themes/royal-velvet.json +35 -35
- package/src/live-tokens/data/themes/sketchy.json +35 -35
- package/src/live-tokens/data/themes/spring-meadow.json +35 -35
- package/src/live-tokens/data/themes/sunset.json +35 -35
- package/src/system/components/Badge.svelte +3 -3
- package/src/system/components/Button.svelte +12 -6
- 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 +18 -25
- package/src/system/components/InlineEditActions.svelte +1 -1
- package/src/system/components/Input.svelte +9 -2
- package/src/system/components/MenuSelect.svelte +31 -3
- package/src/system/components/Notification.svelte +1 -1
- package/src/system/components/ProgressBar.svelte +7 -1
- package/src/system/components/RadioButton.svelte +36 -3
- package/src/system/components/SegmentedControl.svelte +36 -2
- package/src/system/components/SlidePager.svelte +3 -3
- package/src/system/components/Slider.svelte +19 -15
- package/src/system/components/TabBar.svelte +33 -3
- package/src/system/components/Toggle.svelte +3 -3
- 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-NB3NZRBM.js → chunk-7CM5D7PF.js} +5 -1
- package/src/testing-js/chunk-7CM5D7PF.js.map +1 -0
- package/src/testing-js/{chunk-X6KHEVIG.js → chunk-7QXUJRSO.js} +97 -29
- package/src/testing-js/chunk-7QXUJRSO.js.map +1 -0
- package/src/testing-js/chunk-GZKUYLDW.js +193 -0
- package/src/testing-js/chunk-GZKUYLDW.js.map +1 -0
- package/src/testing-js/chunk-RTZQLW3F.js +649 -0
- package/src/testing-js/chunk-RTZQLW3F.js.map +1 -0
- package/src/testing-js/{chunk-SHBYZUPD.js → chunk-RWOT2XDB.js} +212 -15
- package/src/testing-js/chunk-RWOT2XDB.js.map +1 -0
- package/src/testing-js/{chunk-GKO27NGN.js → chunk-THWGMSRH.js} +2 -2
- 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 +313 -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 +11 -11
- package/src/testing-js/index.js.map +1 -1
- package/src/testing-js/page-compliance.contract.js +132 -13
- package/src/testing-js/page-compliance.contract.js.map +1 -1
- package/src/testing-js/vitest.js +3 -3
- package/template/_gitignore +3 -0
- package/template/package.json +9 -0
- 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-THWGMSRH.js.map} +0 -0
|
@@ -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: {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
<script lang="ts">
|
|
22
22
|
import { onMount, tick, type Snippet } from 'svelte';
|
|
23
23
|
import { portal } from '../internal/portal';
|
|
24
|
+
import { trapTab } from '../internal/focusTrap';
|
|
24
25
|
|
|
25
26
|
interface GalleryImage {
|
|
26
27
|
src: string;
|
|
@@ -45,6 +46,8 @@
|
|
|
45
46
|
thumbSrc?: string | undefined;
|
|
46
47
|
/** Closed-thumbnail object-fit. `cover` crops the thumbnail to fill its box;
|
|
47
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`.
|
|
48
51
|
`cover` only crops when the thumbnail box differs from the image's shape
|
|
49
52
|
(a `thumbAspect`, or a CSS-constrained container). */
|
|
50
53
|
fit?: 'contain' | 'cover';
|
|
@@ -111,7 +114,7 @@
|
|
|
111
114
|
maxWidth = undefined,
|
|
112
115
|
thumbAspect = undefined,
|
|
113
116
|
thumbSrc = undefined,
|
|
114
|
-
fit =
|
|
117
|
+
fit = undefined,
|
|
115
118
|
shadow = 'box',
|
|
116
119
|
surface = undefined,
|
|
117
120
|
border = undefined,
|
|
@@ -607,29 +610,12 @@
|
|
|
607
610
|
if (!isGallery && scale <= MIN_SCALE) closeLightbox();
|
|
608
611
|
}
|
|
609
612
|
|
|
610
|
-
//
|
|
611
|
-
//
|
|
612
|
-
function
|
|
613
|
-
if (!modalEl) return;
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
];
|
|
617
|
-
if (f.length === 0) {
|
|
618
|
-
e.preventDefault();
|
|
619
|
-
stageEl?.focus();
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
const first = f[0];
|
|
623
|
-
const last = f[f.length - 1];
|
|
624
|
-
const active = document.activeElement as HTMLElement | null;
|
|
625
|
-
const inside = active && modalEl.contains(active);
|
|
626
|
-
if (e.shiftKey && (!inside || active === first)) {
|
|
627
|
-
e.preventDefault();
|
|
628
|
-
last.focus();
|
|
629
|
-
} else if (!e.shiftKey && (!inside || active === last)) {
|
|
630
|
-
e.preventDefault();
|
|
631
|
-
first.focus();
|
|
632
|
-
}
|
|
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();
|
|
633
619
|
}
|
|
634
620
|
|
|
635
621
|
onMount(() => {
|
|
@@ -638,7 +624,7 @@
|
|
|
638
624
|
if (e.key === 'Escape') {
|
|
639
625
|
closeLightbox();
|
|
640
626
|
} else if (e.key === 'Tab') {
|
|
641
|
-
|
|
627
|
+
onTab(e);
|
|
642
628
|
} else if (isGallery && e.key === 'ArrowRight') {
|
|
643
629
|
e.preventDefault();
|
|
644
630
|
next();
|
|
@@ -920,6 +906,13 @@
|
|
|
920
906
|
transition: none;
|
|
921
907
|
}
|
|
922
908
|
|
|
909
|
+
@media (prefers-reduced-motion: reduce) {
|
|
910
|
+
.image-lightbox-thumb,
|
|
911
|
+
.image-lightbox-attached {
|
|
912
|
+
transition: none;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
923
916
|
.image-lightbox-thumb:focus-visible {
|
|
924
917
|
outline: 2px solid var(--border-brand-medium);
|
|
925
918
|
outline-offset: 2px;
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
|
|
93
93
|
/* Cancel (hover) */
|
|
94
94
|
--inlineeditactions-cancel-hover-surface: var(--surface-danger-high);
|
|
95
|
-
--inlineeditactions-cancel-hover-text: var(--text-
|
|
95
|
+
--inlineeditactions-cancel-hover-text: var(--text-primary);
|
|
96
96
|
--inlineeditactions-cancel-hover-border: var(--border-danger);
|
|
97
97
|
--inlineeditactions-cancel-hover-border-width: var(--border-width-0);
|
|
98
98
|
--inlineeditactions-cancel-hover-radius: var(--radius-md);
|
|
@@ -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);
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
|
|
178
178
|
/* Focused — applied as a CSS outline so the layout doesn't shift on focus */
|
|
179
179
|
--input-focused-surface: var(--surface-neutral-lower);
|
|
180
|
-
--input-focused-border: var(--border-brand);
|
|
180
|
+
--input-focused-border: var(--border-brand-strong);
|
|
181
181
|
--input-focused-border-width: var(--border-width-2);
|
|
182
182
|
--input-focused-text: var(--text-primary);
|
|
183
183
|
--input-focused-text-font-family: var(--font-sans);
|
|
@@ -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>
|
|
@@ -130,7 +158,7 @@
|
|
|
130
158
|
--menuselect-default-icon-size: var(--icon-size-sm);
|
|
131
159
|
|
|
132
160
|
/* Hover item */
|
|
133
|
-
--menuselect-hover-surface: var(--surface-neutral-
|
|
161
|
+
--menuselect-hover-surface: var(--surface-neutral-high);
|
|
134
162
|
--menuselect-hover-text: var(--text-primary);
|
|
135
163
|
--menuselect-hover-text-font-family: var(--font-sans);
|
|
136
164
|
--menuselect-hover-text-font-size: var(--font-size-sm);
|
|
@@ -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);
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
--progressbar-track-border-width: var(--border-width-1);
|
|
66
66
|
--progressbar-track-height: var(--space-16);
|
|
67
67
|
--progressbar-radius: var(--radius-full);
|
|
68
|
-
--progressbar-fill: var(--
|
|
68
|
+
--progressbar-fill: var(--border-special-medium);
|
|
69
69
|
--progressbar-label-gap: var(--space-6);
|
|
70
70
|
--progressbar-label: var(--text-secondary);
|
|
71
71
|
--progressbar-label-font-family: var(--font-sans);
|
|
@@ -135,4 +135,10 @@
|
|
|
135
135
|
border-radius: var(--progressbar-radius);
|
|
136
136
|
transition: width 0.4s ease;
|
|
137
137
|
}
|
|
138
|
+
|
|
139
|
+
@media (prefers-reduced-motion: reduce) {
|
|
140
|
+
.progress-fill {
|
|
141
|
+
transition: none;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
138
144
|
</style>
|
|
@@ -38,13 +38,46 @@
|
|
|
38
38
|
onclick?.();
|
|
39
39
|
dispatch('click');
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
let button: HTMLButtonElement;
|
|
43
|
+
|
|
44
|
+
// Each option renders alone, so the group is read from the DOM: the nearest
|
|
45
|
+
// radiogroup, else the parent the options share.
|
|
46
|
+
function groupOf(radio: HTMLButtonElement): HTMLButtonElement[] {
|
|
47
|
+
const scope = radio.closest('[role="radiogroup"]') ?? radio.parentElement;
|
|
48
|
+
return scope ? [...scope.querySelectorAll<HTMLButtonElement>('.radio-button')] : [radio];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Roving tab stop: the group takes one Tab press, on the checked option.
|
|
52
|
+
$effect(() => {
|
|
53
|
+
void selected;
|
|
54
|
+
const group = groupOf(button);
|
|
55
|
+
const stop = group.find((radio) => radio.getAttribute('aria-checked') === 'true') ?? group[0];
|
|
56
|
+
for (const radio of group) radio.tabIndex = radio === stop ? 0 : -1;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const ARROW_STEPS: Record<string, number> = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 };
|
|
60
|
+
|
|
61
|
+
function moveSelection(event: KeyboardEvent) {
|
|
62
|
+
const step = ARROW_STEPS[event.key];
|
|
63
|
+
if (step === undefined) return;
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
const group = groupOf(button);
|
|
66
|
+
const next = group[(group.indexOf(button) + step + group.length) % group.length];
|
|
67
|
+
next.focus();
|
|
68
|
+
next.click();
|
|
69
|
+
}
|
|
41
70
|
</script>
|
|
42
71
|
|
|
43
72
|
<button
|
|
44
73
|
class="radio-button {className}"
|
|
45
74
|
class:selected
|
|
75
|
+
role="radio"
|
|
76
|
+
aria-checked={selected}
|
|
46
77
|
style={color ? `--radiobutton-color: ${color};` : ''}
|
|
78
|
+
bind:this={button}
|
|
47
79
|
onclick={fireClick}
|
|
80
|
+
onkeydown={moveSelection}
|
|
48
81
|
>
|
|
49
82
|
<span class="radio-dot"><span class="radio-dot-fill"></span></span>
|
|
50
83
|
<span class="radio-label">{label}</span>
|
|
@@ -53,7 +86,7 @@
|
|
|
53
86
|
<style lang="scss">
|
|
54
87
|
:global(:root) {
|
|
55
88
|
/* Default */
|
|
56
|
-
--radiobutton-default-dot-border-color: var(--border-neutral);
|
|
89
|
+
--radiobutton-default-dot-border-color: var(--border-neutral-medium);
|
|
57
90
|
--radiobutton-default-dot-border-width: var(--border-width-2);
|
|
58
91
|
--radiobutton-default-dot-fill: var(--text-secondary);
|
|
59
92
|
--radiobutton-default-dot-size: var(--dot-size-0);
|
|
@@ -64,7 +97,7 @@
|
|
|
64
97
|
--radiobutton-default-label-line-height: var(--line-height-none);
|
|
65
98
|
|
|
66
99
|
/* Hover */
|
|
67
|
-
--radiobutton-hover-dot-border-color: var(--border-neutral);
|
|
100
|
+
--radiobutton-hover-dot-border-color: var(--border-neutral-medium);
|
|
68
101
|
--radiobutton-hover-dot-border-width: var(--border-width-2);
|
|
69
102
|
--radiobutton-hover-dot-fill: var(--text-secondary);
|
|
70
103
|
--radiobutton-hover-dot-size: var(--dot-size-50);
|
|
@@ -75,7 +108,7 @@
|
|
|
75
108
|
--radiobutton-hover-label-line-height: var(--line-height-none);
|
|
76
109
|
|
|
77
110
|
/* Selected */
|
|
78
|
-
--radiobutton-selected-dot-border-color: var(--border-neutral);
|
|
111
|
+
--radiobutton-selected-dot-border-color: var(--border-neutral-medium);
|
|
79
112
|
--radiobutton-selected-dot-border-width: var(--border-width-2);
|
|
80
113
|
--radiobutton-selected-dot-fill: var(--text-secondary);
|
|
81
114
|
--radiobutton-selected-dot-size: var(--dot-size-50);
|
|
@@ -59,6 +59,29 @@
|
|
|
59
59
|
onchange?.(v);
|
|
60
60
|
dispatch('change', v);
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
const buttons: HTMLButtonElement[] = $state([]);
|
|
64
|
+
|
|
65
|
+
// Roving tab stop: the group takes one Tab press, on the checked segment.
|
|
66
|
+
let tabStop = $derived(
|
|
67
|
+
segments.find((s) => s.value === value && !s.disabled)?.value ?? segments.find((s) => !s.disabled)?.value,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const ARROW_STEPS: Record<string, number> = { ArrowRight: 1, ArrowDown: 1, ArrowLeft: -1, ArrowUp: -1 };
|
|
71
|
+
|
|
72
|
+
function moveSelection(event: KeyboardEvent, from: number) {
|
|
73
|
+
const step = ARROW_STEPS[event.key];
|
|
74
|
+
if (step === undefined) return;
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
const count = segments.length;
|
|
77
|
+
for (let n = 1; n < count; n++) {
|
|
78
|
+
const i = (((from + step * n) % count) + count) % count;
|
|
79
|
+
if (segments[i].disabled) continue;
|
|
80
|
+
buttons[i].focus();
|
|
81
|
+
select(segments[i].value);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
62
85
|
</script>
|
|
63
86
|
|
|
64
87
|
<div
|
|
@@ -77,7 +100,10 @@
|
|
|
77
100
|
disabled={disabled || seg.disabled}
|
|
78
101
|
role="radio"
|
|
79
102
|
aria-checked={value === seg.value}
|
|
103
|
+
tabindex={seg.value === tabStop ? 0 : -1}
|
|
104
|
+
bind:this={buttons[i]}
|
|
80
105
|
onclick={() => select(seg.value)}
|
|
106
|
+
onkeydown={(event) => moveSelection(event, i)}
|
|
81
107
|
>
|
|
82
108
|
{#if seg.icon}<i class={seg.icon}></i>{/if}
|
|
83
109
|
<span>{seg.label}</span>
|
|
@@ -96,7 +122,7 @@
|
|
|
96
122
|
|
|
97
123
|
/* Bar (outer wrapper) */
|
|
98
124
|
--segmentedcontrol-bar-surface: color-mix(in srgb, var(--surface-neutral-lowest) 75%, transparent);
|
|
99
|
-
--segmentedcontrol-bar-border: var(--border-neutral);
|
|
125
|
+
--segmentedcontrol-bar-border: var(--border-neutral-medium);
|
|
100
126
|
--segmentedcontrol-bar-border-width: var(--border-width-1);
|
|
101
127
|
--segmentedcontrol-bar-radius: var(--radius-lg);
|
|
102
128
|
--segmentedcontrol-bar-padding: var(--space-4);
|
|
@@ -138,7 +164,7 @@
|
|
|
138
164
|
--segmentedcontrol-selected-text-font-weight: var(--font-weight-normal);
|
|
139
165
|
--segmentedcontrol-selected-text-line-height: var(--line-height-normal);
|
|
140
166
|
--segmentedcontrol-selected-icon: var(--text-secondary);
|
|
141
|
-
--segmentedcontrol-selected-border: var(--border-brand);
|
|
167
|
+
--segmentedcontrol-selected-border: var(--border-brand-medium);
|
|
142
168
|
--segmentedcontrol-selected-border-width: var(--border-width-1);
|
|
143
169
|
--segmentedcontrol-selected-radius: var(--radius-md);
|
|
144
170
|
|
|
@@ -245,6 +271,7 @@
|
|
|
245
271
|
@include themed-padding(--segmentedcontrol-option-padding, $h: 2);
|
|
246
272
|
background: transparent;
|
|
247
273
|
border: 0;
|
|
274
|
+
outline: none;
|
|
248
275
|
border-radius: var(--segmentedcontrol-selected-radius);
|
|
249
276
|
color: var(--_text-color);
|
|
250
277
|
font-family: var(--_text-family);
|
|
@@ -308,6 +335,13 @@
|
|
|
308
335
|
outline-offset: calc(var(--segmentedcontrol-selected-border-width) * -1);
|
|
309
336
|
}
|
|
310
337
|
|
|
338
|
+
/* A ring outside the box, in a neutral that reads apart from the selected
|
|
339
|
+
option's brand outline. It is a shadow because the selected option
|
|
340
|
+
already owns the outline. */
|
|
341
|
+
.segment:focus-visible {
|
|
342
|
+
box-shadow: 0 0 0 var(--border-width-2) var(--border-neutral-strong);
|
|
343
|
+
}
|
|
344
|
+
|
|
311
345
|
.segment:disabled {
|
|
312
346
|
--_text-color: var(--segmentedcontrol-disabled-text);
|
|
313
347
|
--_text-family: var(--segmentedcontrol-disabled-text-font-family);
|
|
@@ -298,7 +298,7 @@
|
|
|
298
298
|
|
|
299
299
|
--slidepager-control-hover-surface: var(--surface-brand-high);
|
|
300
300
|
--slidepager-control-hover-border: var(--border-brand);
|
|
301
|
-
--slidepager-control-hover-icon: var(--text-
|
|
301
|
+
--slidepager-control-hover-icon: var(--text-primary);
|
|
302
302
|
|
|
303
303
|
--slidepager-control-disabled-surface: var(--surface-neutral-lower);
|
|
304
304
|
--slidepager-control-disabled-border: var(--border-neutral-subtle);
|
|
@@ -357,7 +357,7 @@
|
|
|
357
357
|
|
|
358
358
|
.slidepager-stage:focus-visible {
|
|
359
359
|
outline: none;
|
|
360
|
-
box-shadow: var(--
|
|
360
|
+
box-shadow: 0 0 0 var(--border-width-2) var(--border-neutral-strong);
|
|
361
361
|
}
|
|
362
362
|
|
|
363
363
|
.slidepager-stage img {
|
|
@@ -438,7 +438,7 @@
|
|
|
438
438
|
|
|
439
439
|
.slidepager-control:focus-visible {
|
|
440
440
|
outline: none;
|
|
441
|
-
box-shadow: var(--
|
|
441
|
+
box-shadow: 0 0 0 var(--border-width-2) var(--border-neutral-strong);
|
|
442
442
|
}
|
|
443
443
|
|
|
444
444
|
.slidepager-counter {
|
|
@@ -131,18 +131,18 @@
|
|
|
131
131
|
to one moves the other until the designer unlinks it. */
|
|
132
132
|
/* Single: one thumb. */
|
|
133
133
|
--slider-single-track-surface: var(--surface-neutral-lowest);
|
|
134
|
-
--slider-single-track-border: var(--border-neutral-
|
|
134
|
+
--slider-single-track-border: var(--border-neutral-medium);
|
|
135
135
|
--slider-single-track-border-width: var(--border-width-1);
|
|
136
136
|
--slider-single-track-height: var(--space-8);
|
|
137
137
|
--slider-single-track-radius: var(--radius-full);
|
|
138
|
-
--slider-single-fill: var(--
|
|
139
|
-
--slider-single-thumb-surface: var(--
|
|
140
|
-
--slider-single-thumb-border: var(--border-brand);
|
|
138
|
+
--slider-single-fill: var(--border-brand-medium);
|
|
139
|
+
--slider-single-thumb-surface: var(--text-primary);
|
|
140
|
+
--slider-single-thumb-border: var(--border-brand-medium);
|
|
141
141
|
--slider-single-thumb-border-width: var(--border-width-2);
|
|
142
142
|
--slider-single-thumb-size: var(--font-size-lg);
|
|
143
143
|
--slider-single-thumb-radius: var(--radius-full);
|
|
144
144
|
--slider-single-thumb-shadow: var(--shadow-sm);
|
|
145
|
-
--slider-single-hover-thumb-surface: var(--
|
|
145
|
+
--slider-single-hover-thumb-surface: var(--text-primary);
|
|
146
146
|
--slider-single-hover-thumb-border: var(--border-brand-strong);
|
|
147
147
|
--slider-single-disabled-track-surface: var(--surface-neutral-lower);
|
|
148
148
|
--slider-single-disabled-fill: var(--surface-neutral);
|
|
@@ -151,18 +151,18 @@
|
|
|
151
151
|
|
|
152
152
|
/* Range: two thumbs bounding a fill. */
|
|
153
153
|
--slider-range-track-surface: var(--surface-neutral-lowest);
|
|
154
|
-
--slider-range-track-border: var(--border-neutral-
|
|
154
|
+
--slider-range-track-border: var(--border-neutral-medium);
|
|
155
155
|
--slider-range-track-border-width: var(--border-width-1);
|
|
156
156
|
--slider-range-track-height: var(--space-8);
|
|
157
157
|
--slider-range-track-radius: var(--radius-full);
|
|
158
|
-
--slider-range-fill: var(--
|
|
159
|
-
--slider-range-thumb-surface: var(--
|
|
160
|
-
--slider-range-thumb-border: var(--border-brand);
|
|
158
|
+
--slider-range-fill: var(--border-brand-medium);
|
|
159
|
+
--slider-range-thumb-surface: var(--text-primary);
|
|
160
|
+
--slider-range-thumb-border: var(--border-brand-medium);
|
|
161
161
|
--slider-range-thumb-border-width: var(--border-width-2);
|
|
162
162
|
--slider-range-thumb-size: var(--font-size-lg);
|
|
163
163
|
--slider-range-thumb-radius: var(--radius-full);
|
|
164
164
|
--slider-range-thumb-shadow: var(--shadow-sm);
|
|
165
|
-
--slider-range-hover-thumb-surface: var(--
|
|
165
|
+
--slider-range-hover-thumb-surface: var(--text-primary);
|
|
166
166
|
--slider-range-hover-thumb-border: var(--border-brand-strong);
|
|
167
167
|
--slider-range-disabled-track-surface: var(--surface-neutral-lower);
|
|
168
168
|
--slider-range-disabled-fill: var(--surface-neutral);
|
|
@@ -224,6 +224,10 @@
|
|
|
224
224
|
margin-block: calc((var(--_thumb-size) - var(--_track-height)) / 2);
|
|
225
225
|
border-style: solid;
|
|
226
226
|
transition: background var(--duration-150), border-color var(--duration-150);
|
|
227
|
+
/* WCAG 2.2 SC 2.5.8: the native thumb takes the pointer over 24px or
|
|
228
|
+
more. A cap smaller than that sits up to half the difference off the
|
|
229
|
+
native thumb's centre at either end of the travel. */
|
|
230
|
+
--_hit-size: max(var(--_thumb-size), var(--space-24));
|
|
227
231
|
--_origin: calc(var(--_thumb-size) / 2 - var(--_track-border-width));
|
|
228
232
|
--_travel: calc(100% + var(--_track-border-width) * 2 - var(--_thumb-size));
|
|
229
233
|
}
|
|
@@ -287,7 +291,7 @@
|
|
|
287
291
|
top: 50%;
|
|
288
292
|
left: calc(-1 * var(--_track-border-width));
|
|
289
293
|
width: calc(100% + var(--_track-border-width) * 2);
|
|
290
|
-
height: var(--
|
|
294
|
+
height: var(--_hit-size);
|
|
291
295
|
margin: 0;
|
|
292
296
|
padding: 0;
|
|
293
297
|
transform: translateY(-50%);
|
|
@@ -299,14 +303,14 @@
|
|
|
299
303
|
.thumb:disabled { cursor: not-allowed; }
|
|
300
304
|
.thumb::-webkit-slider-thumb {
|
|
301
305
|
appearance: none;
|
|
302
|
-
width: var(--
|
|
303
|
-
height: var(--
|
|
306
|
+
width: var(--_hit-size);
|
|
307
|
+
height: var(--_hit-size);
|
|
304
308
|
opacity: 0;
|
|
305
309
|
pointer-events: auto;
|
|
306
310
|
}
|
|
307
311
|
.thumb::-moz-range-thumb {
|
|
308
|
-
width: var(--
|
|
309
|
-
height: var(--
|
|
312
|
+
width: var(--_hit-size);
|
|
313
|
+
height: var(--_hit-size);
|
|
310
314
|
border: 0;
|
|
311
315
|
opacity: 0;
|
|
312
316
|
pointer-events: auto;
|