feat(MotionPreference): Create common logic to store information about prefers-reduced-motion
This commit is contained in:
37
src/shared/lib/accessibility/motion.svelte.ts
Normal file
37
src/shared/lib/accessibility/motion.svelte.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Check if we are in a browser environment
|
||||||
|
const isBrowser = typeof window !== 'undefined';
|
||||||
|
|
||||||
|
class MotionPreference {
|
||||||
|
// Reactive state
|
||||||
|
#reduced = $state(false);
|
||||||
|
#mediaQuery: MediaQueryList = new MediaQueryList();
|
||||||
|
|
||||||
|
private handleChange = (e: MediaQueryListEvent) => {
|
||||||
|
this.#reduced = e.matches;
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
if (isBrowser) {
|
||||||
|
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
|
||||||
|
|
||||||
|
// Set initial value immediately
|
||||||
|
this.#reduced = mediaQuery.matches;
|
||||||
|
|
||||||
|
mediaQuery.addEventListener('change', this.handleChange);
|
||||||
|
|
||||||
|
this.#mediaQuery = mediaQuery;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getter allows us to use 'motion.reduced' reactively in components
|
||||||
|
get reduced() {
|
||||||
|
return this.#reduced;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.#mediaQuery.removeEventListener('change', this.handleChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export a single instance to be used everywhere
|
||||||
|
export const motion = new MotionPreference();
|
||||||
@@ -13,3 +13,5 @@ export {
|
|||||||
type Virtualizer,
|
type Virtualizer,
|
||||||
type VirtualizerOptions,
|
type VirtualizerOptions,
|
||||||
} from './helpers';
|
} from './helpers';
|
||||||
|
|
||||||
|
export { motion } from './accessibility/motion.svelte';
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Filter } from '$shared/lib';
|
import type { Filter } from '$shared/lib';
|
||||||
|
import { motion } from '$shared/lib';
|
||||||
import { Badge } from '$shared/shadcn/ui/badge';
|
import { Badge } from '$shared/shadcn/ui/badge';
|
||||||
import { buttonVariants } from '$shared/shadcn/ui/button';
|
import { buttonVariants } from '$shared/shadcn/ui/button';
|
||||||
import { Checkbox } from '$shared/shadcn/ui/checkbox';
|
import { Checkbox } from '$shared/shadcn/ui/checkbox';
|
||||||
@@ -37,29 +38,11 @@ const { displayedLabel, filter }: PropertyFilterProps = $props();
|
|||||||
|
|
||||||
// Toggle state - defaults to open for better discoverability
|
// Toggle state - defaults to open for better discoverability
|
||||||
let isOpen = $state(true);
|
let isOpen = $state(true);
|
||||||
// Accessibility preference to disable animations
|
|
||||||
let prefersReducedMotion = $state(false);
|
|
||||||
|
|
||||||
// Check reduced motion preference on mount (window access required)
|
|
||||||
// Event listener allows responding to system preference changes
|
|
||||||
onMount(() => {
|
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
|
|
||||||
prefersReducedMotion = mediaQuery.matches;
|
|
||||||
|
|
||||||
const handleChange = (e: MediaQueryListEvent) => {
|
|
||||||
prefersReducedMotion = e.matches;
|
|
||||||
};
|
|
||||||
|
|
||||||
mediaQuery.addEventListener('change', handleChange);
|
|
||||||
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Animation config respects user preferences - zero duration if reduced motion enabled
|
// Animation config respects user preferences - zero duration if reduced motion enabled
|
||||||
// Local modifier prevents animation on initial render, only animates user interactions
|
// Local modifier prevents animation on initial render, only animates user interactions
|
||||||
const slideConfig = $derived({
|
const slideConfig = $derived({
|
||||||
duration: prefersReducedMotion ? 0 : 250,
|
duration: motion.reduced ? 0 : 250,
|
||||||
easing: cubicOut,
|
easing: cubicOut,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user