Some people get dizzy or nauseous from movement on screen, and turn on
Reduce motion in their operating system. Browsers expose that choice as
the prefers-reduced-motion media query. Every animated component listens to
it.
Wrap movement in a media query, or use Tailwind's motion-safe and
motion-reduce variants:
<div className="motion-safe:animate-marquee motion-reduce:animate-none" />@media (prefers-reduced-motion: reduce) {
.orbit {
animation: none;
}
}Set reducedMotion="user" once near the root. Motion then skips transform
and layout animations for people who asked for less motion, and keeps opacity
and colour changes:
"use client"
import { MotionConfig } from "motion/react"
export function Providers({ children }: { children: React.ReactNode }) {
return <MotionConfig reducedMotion="user">{children}</MotionConfig>
}For effects you drive yourself, read the preference with the hook:
import { useReducedMotion } from "motion/react"
const reduce = useReducedMotion()
const variants = reduce
? { hidden: { opacity: 0 }, show: { opacity: 1 } }
: { hidden: { opacity: 0, y: 16 }, show: { opacity: 1, y: 0 } }For canvas effects, check the media query before starting the loop, draw one static frame when it matches, and listen for changes:
const query = window.matchMedia("(prefers-reduced-motion: reduce)")
if (query.matches) drawStillFrame()
else start()
query.addEventListener("change", () => (query.matches ? stop() : start()))Also pause the loop when the element is off screen with an
IntersectionObserver; it saves battery for everyone.
prefers-reduced-motion.page.emulateMedia({ reducedMotion: "reduce" }).