Skip to content

Commit 59751c5

Browse files
authored
feat: disable background animations for users preferring reduced motion (#359)
1 parent baae710 commit 59751c5

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

app/routes/__root.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary'
2121
import { twMerge } from 'tailwind-merge'
2222
import { getThemeCookie, useThemeStore } from '~/components/ThemeToggle'
2323
import { useMounted } from '~/hooks/useMounted'
24+
import { usePrefersReducedMotion } from '~/utils/usePrefersReducedMotion'
2425

2526
export const Route = createRootRouteWithContext<{
2627
queryClient: QueryClient
@@ -173,8 +174,13 @@ function RootDocument({ children }: { children: React.ReactNode }) {
173174
const themeClass = themeCookie === 'dark' ? 'dark' : ''
174175

175176
const canvasRef = React.useRef<HTMLCanvasElement>(null)
177+
const prefersReducedMotion = usePrefersReducedMotion()
176178

177179
React.useEffect(() => {
180+
if (prefersReducedMotion !== false) {
181+
return
182+
}
183+
178184
const canvas = canvasRef.current
179185

180186
const morphDuration = 4000
@@ -317,7 +323,7 @@ function RootDocument({ children }: { children: React.ReactNode }) {
317323
window.removeEventListener('resize', start)
318324
}
319325
}
320-
}, [])
326+
}, [prefersReducedMotion])
321327

322328
const isHomePage = useRouterState({
323329
select: (s) => s.location.pathname === '/',
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import * as React from 'react'
2+
3+
/**
4+
* Hook that returns the user's preference for reduced motion.
5+
* @returns null if the value is not yet determined or the user's preference for reduced motion.
6+
*/
7+
export const usePrefersReducedMotion = () => {
8+
const [prefersReducedMotion, setPrefersReducedMotion] = React.useState<
9+
boolean | null
10+
>(null)
11+
12+
React.useEffect(() => {
13+
const mediaQueryList = window.matchMedia('(prefers-reduced-motion: reduce)')
14+
setPrefersReducedMotion(mediaQueryList.matches)
15+
16+
const listener = (event: MediaQueryListEvent) => {
17+
setPrefersReducedMotion(event.matches)
18+
}
19+
20+
mediaQueryList.addEventListener('change', listener)
21+
return () => {
22+
mediaQueryList.removeEventListener('change', listener)
23+
}
24+
}, [])
25+
26+
return prefersReducedMotion
27+
}

0 commit comments

Comments
 (0)