Respecting User Motion Preferences with prefers-reduced-motion
Don't just remove animations—replace motion with opacity to preserve context while respecting accessibility
Vestibular disorders affect millions of users, causing dizziness, nausea, or headaches when triggered by large areas of movement or parallax effects. The prefers-reduced-motion media query allows you to respect these user preferences—but "reduced" doesn't mean "none".
The "Nuclear" Option vs. The Surgical Approach
A common mistake is to globally disable all animations for users who prefer reduced motion. While effective, this can break user experience by removing context cues or, worse, breaking functionality if JavaScript relies on animationend events.
/* ❌ The Nuclear Option: Can break JS logic */
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition: none !important;
}
}Instead, replace motion with opacity. This preserves the "change of state" context without the triggering movement.
/* ✅ The Surgical Approach */
@media (prefers-reduced-motion: reduce) {
.modal {
/* Replace slide-in with fade-in */
animation-name: fadeIn;
transform: none;
}
.card {
/* Remove hover lift, keep focus ring */
transition: border-color 0.2s;
transform: none;
}
}Handling JavaScript Animations
CSS media queries are accessible in JavaScript via window.matchMedia. Use this to disable or adjust complex JS-driven animations (like Three.js scenes or scroll-linked animations).
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
function initAnimation() {
if (prefersReducedMotion.matches) {
// Render static state or simple fade
renderStaticScene();
} else {
// Initialize complex motion
initParallax();
}
}
// Listen for changes (users can toggle this at runtime!)
prefersReducedMotion.addEventListener('change', () => {
// Re-evaluate animation state
updateAnimationState();
});Testing
You don't need to change your OS settings to test this. Chrome DevTools allows you to emulate the preference:
- Open Command Menu (Cmd+Shift+P)
- Type "Show Rendering"
- Scroll to "Emulate CSS media feature prefers-reduced-motion"
- Select "prefers-reduced-motion: reduce"
Advertisement
Explore these curated resources to deepen your understanding
Official Documentation
Related Insights
Explore related edge cases and patterns
Advertisement