
Designing Inclusive Web Animations with prefers-reduced-motion
Parallax scrolling, zooming cards, and automatic smooth-scroll jumps can trigger motion sickness, vertigo, and migraines for users with vestibular disorders. Modern operating systems—including macOS, iOS, Windows 10, and Android—allow users to enable a system-wide "Reduce motion" preference, which browsers expose directly to CSS and JavaScript via the prefers-reduced-motion media query.
Respecting reduced motion does not mean removing every visual transition on the page. Color, border, and opacity fades are completely safe; the goal is to eliminate spatial movement (transform: translate(), scale(), rotate()), keyframe loops, and scroll-behavior: smooth when the user requests calmer motion.
Example
/* Opt-in approach: enable smooth scrolling only when the user has no motion restriction */
@media (prefers-reduced-motion: no-preference) {
html {
scroll-behavior: smooth;
}
}
/* Defensive reset: strip spatial animations when reduced motion is active */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
Input : Interactive UI card with hover elevation, slide-in entrance animation, and smooth anchor scrolling
Output : Full spatial animation for default users and instant or opacity-only transitions for users with prefers-reduced-motion: reduce.
Implementation
.feature-card {
padding: 24px;
border: 1px solid #e2e8f0;
border-radius: 8px;
background-color: #ffffff;
/* Safe default transition: color and border changes do not trigger vestibular discomfort */
transition: border-color 200ms ease, box-shadow 200ms ease, opacity 200ms ease;
}
.feature-card:hover,
.feature-card:focus-within {
border-color: #04ae92;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);
}
/* Layer spatial movement strictly inside no-preference */
@media (prefers-reduced-motion: no-preference) {
.feature-card {
transition: transform 250ms cubic-bezier(0.22, 1, 0.36, 1),
border-color 200ms ease,
box-shadow 200ms ease;
}
.feature-card:hover,
.feature-card:focus-within {
transform: translateY(-6px);
}
}
// Checking the user's motion preference in JavaScript before running requestAnimationFrame or Web Animations
const motionQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
function scrollToSection(targetSelector) {
const target = document.querySelector(targetSelector);
if (!target) return;
target.scrollIntoView({
behavior: motionQuery.matches ? "auto" : "smooth",
block: "start"
});
}
// Listen for live OS-level changes without requiring a page reload
motionQuery.addEventListener("change", () => {
if (motionQuery.matches) {
document.documentElement.classList.add("reduced-motion");
} else {
document.documentElement.classList.remove("reduced-motion");
}
});
Main Point: Treat spatial motion (transform, parallax, and scroll-behavior: smooth) as a progressive enhancement inside @media (prefers-reduced-motion: no-preference) while keeping safe opacity and color states accessible to everyone.
Credits
Photo by Lorenzo Herrera