Spaces:
Running
Running
File size: 1,921 Bytes
990cf29 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
/**
* useReducedMotion Hook
*
* Accessibility hook that detects user's motion preference.
* When enabled, particle effects should be disabled or minimized.
*
* VISUAL ACCESSIBILITY CONTEXT:
* Some users experience motion sickness, vestibular disorders, or simply
* prefer reduced animation. The "prefers-reduced-motion" media query
* indicates this preference.
*
* When this hook returns true:
* - Particle effects should NOT be rendered
* - OR particles should appear statically (no animation)
* - This respects user autonomy and WCAG guidelines
*/
import { useState, useEffect } from 'react';
/**
* Detects if the user prefers reduced motion
*
* @returns true if user prefers reduced motion, false otherwise
*
* USAGE:
* ```tsx
* const prefersReducedMotion = useReducedMotion();
*
* if (prefersReducedMotion) {
* // Skip particle animation entirely
* return null;
* }
* ```
*/
export function useReducedMotion(): boolean {
// Server-side rendering safety: default to false
const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
useEffect(() => {
// Check if matchMedia is available (browser environment)
if (typeof window === 'undefined' || !window.matchMedia) {
return;
}
// Create media query matcher
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
// Set initial value
setPrefersReducedMotion(mediaQuery.matches);
// Listen for changes (user might toggle system preference)
const handleChange = (event: MediaQueryListEvent) => {
setPrefersReducedMotion(event.matches);
};
// Modern browsers use addEventListener
mediaQuery.addEventListener('change', handleChange);
// Cleanup listener on unmount
return () => {
mediaQuery.removeEventListener('change', handleChange);
};
}, []);
return prefersReducedMotion;
}
export default useReducedMotion;
|