Accessible Animations (prefers-reduced-motion)
Some users have motion sensitivities, vestibular disorders, or seizure conditions. CSS animations can cause discomfort or health issues for them. The prefers-reduced-motion media query lets you respect their preferences and build accessible animations.
Accessible Animation Rules
- prefers-reduced-motion: reduce — User's OS is set to reduce motion (System Settings → Accessibility)
- @media (prefers-reduced-motion: reduce) — Write CSS inside this query to disable or simplify animations
- Don't remove all animation — Replace with simpler alternatives: crossfade instead of slide, opacity instead of bounce
- Essential motion is OK — Loading spinners and progress indicators should still animate (they provide information)
- Test with your OS — Enable 'Reduce motion' in Windows accessibility settings and test your site
- Global disable — One rule can disable all transitions: * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
- Focus on transform and opacity — These are the safest properties. Avoid full-screen transitions and parallax for motion-sensitive users
Accessible Animation Code
/* Respect user's motion preference */
@media (prefers-reduced-motion: reduce) {
/* Option 1: Disable all animations */
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* Option 2: Provide simpler alternatives */
.card {
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-8px);
box-shadow: 0 12px 24px rgba(0,0,0,0.15);
}
@media (prefers-reduced-motion: reduce) {
.card:hover {
transform: none;
/* Keep the shadow (non-motion visual feedback) */
}
}
/* Scroll reveal: fade only, no movement */
.reveal {
opacity: 0;
transform: translateY(30px);
transition: all 0.6s ease;
}
@media (prefers-reduced-motion: reduce) {
.reveal {
opacity: 0;
transform: none; /* Remove movement */
transition: opacity 0.3s; /* Fade only, faster */
}
}Tip
Use the no-preference variant for scroll animations: @media (prefers-reduced-motion: no-preference) { .reveal { animation: fadeUp 0.6s ease forwards; } }. This way the animation only runs when the user has NOT requested reduced motion — safer than the reduce variant because it fails to the static state by default.
Good accessibility practices overlap with SEO — helping users helps search engines too
Common Mistake
Disabling ALL animations for reduced-motion users, including functional ones. Loading spinners, progress bars, and video play icons need to animate — they communicate state, not just decoration. Only disable decorative animations (parallax, entrance reveals, hover bounces). Keep informational animations.
Practice Task
Audit your CSS for motion accessibility: (1) Identify all @keyframes animations — label each as 'decorative' or 'functional', (2) Wrap all decorative animations in @media (prefers-reduced-motion: no-preference), (3) Test by enabling 'Reduce Motion' in your OS accessibility settings and verify the page is still usable, (4) Verify loading spinners and progress bars still animate.
Quick Quiz
Key Takeaways
- Some users have motion sensitivities, vestibular disorders, or seizure conditions.
- prefers-reduced-motion: reduce — User's OS is set to reduce motion (System Settings → Accessibility)
- @media (prefers-reduced-motion: reduce) — Write CSS inside this query to disable or simplify animations
- Don't remove all animation — Replace with simpler alternatives: crossfade instead of slide, opacity instead of bounce