diff --git a/src/components/AnimateItems.tsx b/src/components/AnimateItems.tsx index 5fe89d4a..9179a697 100644 --- a/src/components/AnimateItems.tsx +++ b/src/components/AnimateItems.tsx @@ -3,6 +3,7 @@ import { ReactNode, useRef } from 'react'; import { Variant, motion } from 'framer-motion'; import { useAppState } from '@/state'; +import usePrefersReducedMotion from '@/utility/usePrefersReducedMotion'; export type AnimationType = 'none' | 'scale' | 'left' | 'right' | 'bottom'; @@ -41,11 +42,14 @@ function AnimateItems({ nextPhotoAnimation, clearNextPhotoAnimation, } = useAppState(); + + const prefersReducedMotion = usePrefersReducedMotion(); const hasLoadedInitial = useRef(hasLoaded); const nextPhotoAnimationInitial = useRef(nextPhotoAnimation); const shouldAnimate = type !== 'none' && + !prefersReducedMotion && !(animateOnFirstLoadOnly && hasLoadedInitial.current); const shouldStagger = !(staggerOnFirstLoadOnly && hasLoadedInitial.current); diff --git a/src/utility/usePrefersReducedMotion.ts b/src/utility/usePrefersReducedMotion.ts new file mode 100644 index 00000000..7f8e70cf --- /dev/null +++ b/src/utility/usePrefersReducedMotion.ts @@ -0,0 +1,25 @@ +import { useEffect, useState } from 'react'; + +const MEDIA_QUERY_SELECTOR = '(prefers-reduced-motion: reduce)'; +const MEDIA_QUERY_EVENT = 'change'; + +const usePrefersReducedMotion = () => { + const [prefersReducedMotion, setPrefersReducedMotion] = useState( + window.matchMedia(MEDIA_QUERY_SELECTOR).matches + ); + + useEffect(() => { + const mediaQuery = window.matchMedia(MEDIA_QUERY_SELECTOR); + + const listener = () => { + setPrefersReducedMotion(mediaQuery.matches); + }; + + mediaQuery.addEventListener(MEDIA_QUERY_EVENT, listener); + return () => mediaQuery.removeEventListener(MEDIA_QUERY_EVENT, listener); + }, []); + + return prefersReducedMotion; +}; + +export default usePrefersReducedMotion;