/** * usePrefersReducedMotion Hook * * Tracks the `prefers-reduced-motion` media query so animations can be * suppressed for users who request reduced motion (a11y). */ 'use client'; import { useEffect, useState } from 'react'; const QUERY = '(prefers-reduced-motion: reduce)'; /** * Returns `true` when the user has requested reduced motion. * * SSR-safe: returns `false` on the server and during the first client * render, then syncs with the actual media query after mount. */ export function usePrefersReducedMotion(): boolean { const [prefersReducedMotion, setPrefersReducedMotion] = useState(false); useEffect(() => { if (typeof window === 'undefined' || !window.matchMedia) { return; } const mediaQuery = window.matchMedia(QUERY); setPrefersReducedMotion(mediaQuery.matches); const handleChange = (event: MediaQueryListEvent) => { setPrefersReducedMotion(event.matches); }; // Safari < 14 only supports the deprecated addListener API. if (typeof mediaQuery.addEventListener === 'function') { mediaQuery.addEventListener('change', handleChange); return () => mediaQuery.removeEventListener('change', handleChange); } mediaQuery.addListener(handleChange); return () => mediaQuery.removeListener(handleChange); }, []); return prefersReducedMotion; }