Animation can make interfaces feel responsive and help people follow changes. For some people, though, certain kinds of motion cause dizziness, nausea, headaches or migraines, and the effects can last for hours. Motion can also distract people with attention-related disabilities and make reading impossible.
Who is affected
The vestibular system in the inner ear helps control balance and the sense of movement. People with vestibular disorders, and some people with migraine, concussion or other conditions, can react strongly when what they see moves but their body does not.
The motion most likely to cause problems is:
- large movement across the screen, such as full-page slides and sweeping transitions;
- parallax, where layers scroll at different speeds;
- zooming and spinning effects;
- scroll-triggered animation that moves content as the user scrolls;
- constant background motion, such as looping videos and animated backgrounds.
Small, short changes such as a colour fade or a button that briefly changes shade are rarely a problem.
The WCAG criteria
| Success criterion | Level | In short |
|---|---|---|
| 2.2.2 Pause, Stop, Hide | A | Moving, blinking or scrolling content that starts automatically and lasts more than 5 seconds can be paused, stopped or hidden |
| 2.3.1 Three Flashes or Below Threshold | A | Nothing flashes more than three times a second, unless below the flash thresholds |
| 2.3.3 Animation from Interactions | AAA | Motion triggered by interaction can be disabled, unless it is essential |
2.3.3 is AAA, but it is easy to meet with one media query, and the harm it prevents is real. Treat it as part of your normal practice.
prefers-reduced-motion
Operating systems let users ask for less motion: Reduce motion on macOS and iOS, Animation effects off on Windows, and Remove animations on Android. Browsers expose this setting through the prefers-reduced-motion media feature, with the values reduce and no-preference.
The safer default: opt in to motion
Write your base styles without motion, then add animation only when the user has not asked for reduced motion.
.card {
opacity: 1;
}
@media (prefers-reduced-motion: no-preference) {
.card {
animation: slide-up 400ms ease-out both;
}
@keyframes slide-up {
from {
opacity: 0;
transform: translateY(2rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
}
If the browser does not support the media query, users get the static version, which is always safe.
Reduce, do not always remove
Reduced motion does not have to mean no feedback. Replace large movement with gentle, non-moving changes such as a fade.
.panel {
transition: transform 300ms ease, opacity 300ms ease;
}
@media (prefers-reduced-motion: reduce) {
.panel {
/* keep the fade, drop the movement */
transition: opacity 200ms ease;
}
}
Transitions that help users understand a change are still useful; the movement is what causes harm.
A global safety net
Some sites add a broad rule to catch animations from third-party code. Use it as a fallback, not as your main approach, because it removes useful feedback as well.
@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;
}
}
The very short duration, rather than none, keeps animationend and transitionend events firing, so scripts that wait for them still work.
Smooth scrolling
Smooth scrolling across a long page is large movement. Apply it only when motion is welcome:
@media (prefers-reduced-motion: no-preference) {
html {
scroll-behavior: smooth;
}
}
Reduced motion in JavaScript
Animations started from JavaScript need the same check. matchMedia reads the preference, and its change event fires if the user changes the setting while the page is open.
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
function scrollToSection(section) {
section.scrollIntoView({
behavior: reduceMotion.matches ? 'auto' : 'smooth',
block: 'start',
});
}
reduceMotion.addEventListener('change', () => {
document.documentElement.classList.toggle('reduce-motion', reduceMotion.matches);
});
document.documentElement.classList.toggle('reduce-motion', reduceMotion.matches);
Animation libraries and the Web Animations API do not check the preference for you. The same applies to view transitions: wrap the CSS for view transition animations in the media query.
Give users control over moving content
Under 2.2.2 Pause, Stop, Hide, anything that moves automatically for more than 5 seconds and appears alongside other content needs a control. This includes carousels, tickers, animated illustrations and background videos.
<section class="carousel" aria-roledescription="carousel" aria-label="Featured events">
<button type="button" class="carousel-pause" aria-pressed="false">Pause slideshow</button>
<div class="slides">
<!-- slides -->
</div>
</section>
<script>
const pause = document.querySelector('.carousel-pause');
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)');
let timer = null;
function nextSlide() {
// advance to the next slide here
}
function start() {
if (timer === null) timer = setInterval(nextSlide, 6000);
}
function stop() {
clearInterval(timer);
timer = null;
}
pause.addEventListener('click', () => {
const paused = pause.getAttribute('aria-pressed') === 'true';
pause.setAttribute('aria-pressed', String(!paused));
if (paused) {
start();
} else {
stop();
}
});
if (reduce.matches) {
pause.setAttribute('aria-pressed', 'true');
} else {
start();
}
</script>
- The pause control is the first element in the carousel, so keyboard and screen reader users reach it before the moving content.
- If the user prefers reduced motion, the slideshow never starts.
- A fuller version also stops rotation while any element inside has focus or the pointer is over it, and announces slide changes only when the user changes slides, not when they rotate automatically.
Better still, avoid auto-rotating carousels altogether. Many users find them hard to read and control, whatever their abilities.
Your own site setting
Not everyone knows about the operating system setting, and some people want less motion only on some sites. A site-level "Reduce motion" toggle that adds a class to the html element, and stores the choice, gives extra control. Style from both the class and the media query.
Flashing
Never create content that flashes more than three times in one second. Check animated GIFs, video edits, loading effects and strobe-like transitions. Large, saturated red flashes are the most dangerous. Tools such as the Photosensitive Epilepsy Analysis Tool (PEAT) can check video.
Summary
- Large movement, parallax, zooming and scroll-linked animation can make people with vestibular disorders unwell.
- Moving content that starts automatically and lasts more than 5 seconds needs a pause, stop or hide control (2.2.2).
- Nothing may flash more than three times a second (2.3.1).
- Respect
prefers-reduced-motion, ideally by adding motion only inside(prefers-reduced-motion: no-preference); this meets 2.3.3. - Replace movement with fades rather than removing all feedback.
- Check the preference in JavaScript with
matchMedia, including for smooth scrolling and view transitions.