Accessibility
How to design accessible animations with Framer Motion.
Animations can have serious usability implications, even inducing motion sickness in some people.
All modern operating systems provide a setting called "Reduced Motion", where people can indicate they prefer less physical motion, either because of personal preference or because they can suffer from motion sickness.
There are already some excellent guides about why and how we should design accessible animations, like those at A List Apart and Smashing Magazine. The main takeaways are that for users with "Reduced Motion" enabled, we should keep educational transitions but be aware of motion sickness. So that means replacing transform animations on large elements with opacity transitions, disabling auto-playing videos, and disabling parallax animations.
Framer Motion provides APIs that make it simple to respect these people's preferences. In this guide, we'll learn how to use the reducedMotion
option and useReducedMotion
hook to make our animations accessible.
#reducedMotion
The reducedMotion option
can be set on MotionConfig
to define how you want to adhere to the Reduced Motion setting.
By setting reducedMotion
it to "user"
, all motion
components will automatically disable transform and layout animations, while preserving the animation of other values like opacity
and backgroundColor
.
import { MotionConfig } from "framer-motion"
export function App({ children }) { return ( <MotionConfig reducedMotion="user"> {children} </MotionConfig> )}
In Framer, you can set this by going to Site Settings > Accessibility
and checking the "Disable transform and layout animations if user prefers reduced motion" checkbox.
Additionally, you can allow a user to override Reduced Motion for just your site by setting reducedMotion to "always"
or "never"
.
#useReducedMotion
While reducedMotion
is a great blanket tool for ensuring accessible animations across your whole site, more bespoke solutions can be created with the useReducedMotion
hook.
This hook returns true
/false
depending on whether your visitor has Reduced Motion enabled.
import { useReducedMotion } from "framer-motion"
// In your componentconst shouldReduceMotion = useReducedMotion()
We can use this boolean to fix some of the common accessibility problems identified at the start of this guide.
#Replace transitions with opacity
When Reduced Motion is enabled on iOS, the operating system still animates between states to help users transition between each context. But instead of the default scale and x/y animations, it fades content in and out.
We can achieve this in Framer Motion by passing different values to animate
based on whether useReducedMotion
returns true
or not.
function Sidebar({ isOpen }) { const shouldReduceMotion = useReducedMotion() let animate
if (isOpen) { animate = shouldReduceMotion ? { opacity: 1 } : { x: 0 } } else { animate = shouldReduceMotion ? { opacity: 0 } : { x: "-100%" } }
return <motion.div animate={animate} />}
#Disable auto-playing videos
useReducedMotion
isn’t only compatible with the Framer Motion. It returns a simple boolean, so you can use it for any purpose, like disabling the autoplay of a background video
element:
function BackgroundVideo() { const shouldReduceMotion = useReducedMotion()
return <video autoplay={!shouldReduceMotion} />}
#Disable parallax animations
Parallax animations can be very unpleasant for people pre-disposed to motion sickness.
To build parallax, we usually get scrollY
from useViewportScroll
, and create a new MotionValue
via passing that to useTransform
which will update's a motion
component's y
position as the scroll value changes.
To disable this for reduced motion devices, we can conditionally pass this MotionValue
to the animating element.
function Parallax() { const shouldReduceMotion = useReducedMotion() const { scrollY } = useViewportScroll()
const y = useTransform(scrollY, [0, 1], [0, -0.2], { clamp: false, })
return ( <motion.div style={{ y: shouldReduceMotion ? 0 : y }} /> )}
#Conclusion
We've learned to respect people's Reduced Motion setting with Framer Motion. The reducedMotion
option makes it simple to implement across a whole site, while useReducedMotion
can help us create bespoke accessibility strategies with any React API.
In the future, we'd like to set reducedMotion="user"
by default, so let us know via our Twitter if you think we can improve way it affects animation accessibility.