Skip to content

Commit 6c93d77

Browse files
committed
fix(toggle): commits immediately with reduced motion
When `prefers-reduced-motion` is active, the tracker's transition duration becomes 0ms. This can prevent the `transitionend` event from firing, leaving the toggler in a pending state and preventing it from committing the new selection or navigating. Committing immediately in this scenario ensures the toggler functions correctly for users with reduced motion preferences.
1 parent 7d60547 commit 6c93d77

1 file changed

Lines changed: 29 additions & 10 deletions

File tree

projects/client/src/lib/components/toggles/Toggler.svelte

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,12 @@
2727
let trackerElement: HTMLDivElement;
2828
let pending = $state<{ index: number; href?: string } | null>(null);
2929
30-
const handleChange = (index: number, href?: string) => {
31-
pending = { index, href };
32-
trackerIndex.set(index);
33-
trackerElement?.classList.add("moving");
34-
};
30+
const prefersReducedMotion = () =>
31+
globalThis.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches ??
32+
false;
3533
36-
const handleTransitionEnd = (event: TransitionEvent) => {
37-
if (event.propertyName === "opacity" || !pending) return;
38-
39-
(event.currentTarget as HTMLDivElement).classList.remove("moving");
34+
const commit = () => {
35+
if (!pending) return;
4036
4137
const { index, href } = pending;
4238
pending = null;
@@ -49,6 +45,27 @@
4945
}
5046
};
5147
48+
const handleChange = (index: number, href?: string) => {
49+
pending = { index, href };
50+
trackerIndex.set(index);
51+
52+
// With reduced motion the tracker transition is 0ms, so transitionend
53+
// never fires - commit immediately instead of waiting for it.
54+
if (prefersReducedMotion()) {
55+
commit();
56+
return;
57+
}
58+
59+
trackerElement?.classList.add("moving");
60+
};
61+
62+
const handleTransitionEnd = (event: TransitionEvent) => {
63+
if (event.propertyName === "opacity" || !pending) return;
64+
65+
(event.currentTarget as HTMLDivElement).classList.remove("moving");
66+
commit();
67+
};
68+
5269
const getTriggerProps = (option: ToggleOption<T>, index: number) => {
5370
if (option.href) {
5471
return {
@@ -131,7 +148,9 @@
131148
var(--toggler-animation-duration) + var(--toggle-animation-delay)
132149
);
133150
134-
inset-inline-start: calc(var(--tracker-inline-start) + var(--tracker-offset));
151+
inset-inline-start: calc(
152+
var(--tracker-inline-start) + var(--tracker-offset)
153+
);
135154
margin-inline-start: var(--ni-4);
136155
137156
position: absolute;

0 commit comments

Comments
 (0)