CSS prefers-reduced-motion: Making Animations Accessible
AI generated
CSS · Accessibility · WCAG · Animations
CSS prefers-reduced-motion
Accessible and WCAG-compliant animations

For people with vestibular disorders, migraines, or epilepsy, excessive animation on websites can trigger real physical discomfort. The CSS media query prefers-reduced-motion gives developers a way to respect that system-wide preference, and WCAG 2.2 makes it a requirement. This article explains the background, the correct implementation strategies, and the most common mistakes.

10 min read prefers-reduced-motion · WCAG 2.2 · CSS media query · animation All modern browsers

1. Why animation is an accessibility issue

Web accessibility is often reduced to screen reader compatibility, color contrast, and keyboard operability. In many projects animation is treated as a purely aesthetic element that cannot cause accessibility problems. That is a serious misconception. For people with vestibular disorders, migraines, epilepsy, or other neurological conditions, excessive motion, parallax effects, and fast transitions on websites can trigger dizziness, nausea, headaches, or even seizures.

The CSS media query prefers-reduced-motion is the technical answer to this problem. It reads the user's system setting: on macOS, Windows, iOS, Android, and every modern operating system, users can indicate in the accessibility settings that they prefer reduced motion. With prefers-reduced-motion, CSS can respond to that preference and adjust or disable animations accordingly. Support is complete across all modern browsers, so there is no excuse for omitting this media query in any project that uses animation.

The importance of prefers-reduced-motion goes beyond individual accessibility. In an environment where websites are increasingly shaped by complex animation and scroll effects, this media query is a fundamental safety feature for affected users. Estimates suggest that around 35% of all people experience a vestibular disorder at some point in their lives, so the group affected is considerably larger than most people assume.

2. Vestibular disorders and motion sickness on the web

The vestibular system is the part of the inner ear that controls balance and spatial orientation. When the visual impressions on a screen do not match the signals from the vestibular system, as happens with parallax scrolling, large-scale animation, or rapidly changing content, it can overload the brain and produce symptoms such as dizziness, nausea, and disorientation. This effect is known as cybersickness or web motion sickness.

Particularly problematic are large-scale movements that were not triggered by the user's own physical motion: background videos that shift position while scrolling, hero sections with parallax effects, page-wide transition animations during navigation, and elements that float in or out on scroll. All of these patterns cause discomfort for affected users. The prefers-reduced-motion media query is the only reliable way to protect this group of users without giving up animation for everyone else.


/* Base: animations enabled for all users */
.hero-background {
  animation: parallax-drift 8s ease-in-out infinite alternate;
}

.slide-in {
  animation: slide-from-left 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes parallax-drift {
  from { transform: translateY(0) scale(1.05); }
  to   { transform: translateY(-24px) scale(1.05); }
}

@keyframes slide-from-left {
  from { opacity: 0; transform: translateX(-40px); }
  to   { opacity: 1; transform: translateX(0); }
}

/* Reduced motion: disable animations that cause vestibular issues */
@media (prefers-reduced-motion: reduce) {
  /* Large-area motion: remove completely */
  .hero-background {
    animation: none;
  }

  /* Entry animations: keep opacity fade, remove movement */
  .slide-in {
    animation: fade-in 0.3s ease both;
  }

  /* Global fallback: shorten all durations drastically */
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

3. The prefers-reduced-motion media query

The prefers-reduced-motion media query has two possible values: no-preference (the default, meaning the user has not set anything special) and reduce (the user has indicated in their system settings that they prefer less motion). In practice you write a media query block for reduce and define how animations should be adjusted inside it. There are two basic approaches: either treat prefers-reduced-motion: reduce as an opt out, enabling animation by default and disabling it inside the media query, or treat prefers-reduced-motion: no-preference as an opt in, explicitly enabling animation only when the user has not requested a reduction.

Accessibility experts favor the opt-in approach with no-preference because it ensures animation is disabled by default in unfamiliar environments. For existing CSS codebases the opt-out approach is more practical, since it requires less restructuring. Either way, prefers-reduced-motion should be used in every project that uses animation, transitions, or scroll effects. It is not an optional feature, it is a basic accessibility obligation.

4. WCAG 2.2 and animation requirements

The Web Content Accessibility Guidelines (WCAG) 2.2 contain several criteria directly related to animation. The most important is Success Criterion 2.3.3 (Animation from Interactions, Level AAA): motion animation triggered by interaction must be able to be disabled, unless the animation is essential to the function or the information it conveys. At Level AA, Criterion 2.3.1 applies, which forbids content that flashes more than three times per second, a rule aimed directly at preventing seizures.

Even though Criterion 2.3.3 sits at Level AAA and is therefore not mandatory for most projects, implementing it is strongly recommended. Many countries have passed accessibility laws that mandate WCAG AA as a minimum standard, and it is only a matter of time before AAA requirements also become binding for public-facing websites. On top of that, the technical implementation of prefers-reduced-motion is minimal, so the effort is small relative to the impact.


/*
 * Opt-in approach: animations only when the user has NOT
 * requested reduced motion, the safest accessibility pattern
 */

/* Base state: no animation */
.scroll-reveal {
  opacity: 0;
  transform: translateY(20px);
}

/* Add animation only for users who prefer motion */
@media (prefers-reduced-motion: no-preference) {
  .scroll-reveal {
    transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
  }

  /* Parallax: only for users who can handle it */
  .parallax-hero {
    animation: subtle-parallax 12s ease-in-out infinite alternate;
  }

  /* Staggered entry: fine for no-preference users */
  .card-grid .card:nth-child(1) { transition-delay: 0.05s; }
  .card-grid .card:nth-child(2) { transition-delay: 0.10s; }
  .card-grid .card:nth-child(3) { transition-delay: 0.15s; }
}

/* Visible state added by Intersection Observer */
.scroll-reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@keyframes subtle-parallax {
  from { background-position: 50% 0%; }
  to   { background-position: 50% 8%; }
}

5. Reduction strategies: turning off vs. alternatives

Not every animation is equally problematic. When applying prefers-reduced-motion, a differentiated strategy works better than switching off every animation across the board. Large-scale motion animations, parallax effects, and content that flies quickly across the screen should be removed entirely. Subtle opacity fades that signal a state change, for example a notification appearing or disappearing, can be kept as long as no position change is involved. Short transitions on small elements, such as a button's hover feedback, can be kept with a drastically shortened duration.

A common misconception is that prefers-reduced-motion means the user wants to see no motion at all. That is not correct. The preference means excessive or non-essential motion should be reduced. In practice that means: opacity fades stay, transform animations over large distances go, crossfades replace slides for page changes, no continuous background animation, and short feedback animations on interactions can remain with a reduced duration.

6. Implementation patterns for CSS animations

A practical pattern for accessible CSS animation is to use CSS custom properties as animation tokens. You define a variable such as --animation-duration, set it to 0.01ms inside the prefers-reduced-motion media query, and use that variable in every animation and transition definition. A single central override then automatically affects every animation across the whole system, without having to rework each individual rule.

For complex component libraries it is worth introducing a dedicated prefers-reduced-motion layer in the CSS architecture. All animation-related rules are moved into a dedicated layer that can be controlled as a whole through one media query. This is especially relevant for Tailwind CSS v4.0, which supports layer-based architecture, and for Hyva themes, where Alpine.js transitions should also respect the prefers-reduced-motion preference.


/* CSS custom property tokens for motion, central control */
:root {
  --duration-fast:   150ms;
  --duration-base:   300ms;
  --duration-slow:   600ms;
  --duration-xslow: 1200ms;
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --ease-out:    cubic-bezier(0.22, 1, 0.36, 1);
}

/* Single override point for reduced motion */
@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-fast:   0.01ms;
    --duration-base:   0.01ms;
    --duration-slow:   0.01ms;
    --duration-xslow:  0.01ms;
    /* Keep spring ease, it doesn't matter if duration is near 0 */
  }
}

/* All components use the tokens, automatically motion-safe */
.button {
  transition: background-color var(--duration-fast) ease,
              box-shadow var(--duration-fast) ease,
              transform var(--duration-fast) var(--ease-spring);
}

.modal {
  transition: opacity var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
}

.page-transition {
  transition: opacity var(--duration-slow) ease;
  /* Note: no transform transition, avoids large-area motion */
}

7. Querying prefers-reduced-motion in JavaScript

Not every animation is implemented in CSS. JavaScript libraries, canvas animations, SVG animations, and effects built on the Web Animations API also need to respond to prefers-reduced-motion. You access it through the JavaScript API window.matchMedia('(prefers-reduced-motion: reduce)'). The returned MediaQueryList object has a matches property and can react to changes in the system setting with addEventListener('change', ...), without a page reload.

For Alpine.js-based animation in Hyva themes, it is worth exposing a global magic property that makes the prefers-reduced-motion state reactive. Alternatively, you can wrap Alpine.js transitions using x-transition in a condition based on $store.motionReduced. Web Animations API calls should generally check the matches property before starting an animation and, when it is true, either skip the animation entirely or set the duration to nearly zero.

8. prefers-reduced-motion in Tailwind CSS

Since version 2, Tailwind CSS has offered the modifier prefix motion-reduce:, which applies classes only in a prefers-reduced-motion: reduce context. Its counterpart, motion-safe:, applies classes only when the user has not requested a reduction. These modifiers can be combined with any Tailwind animation class. The recommended practice in Tailwind is to qualify animation classes with motion-safe: by default and, where needed, define alternative presentations with motion-reduce:.

In Tailwind CSS v4.0 with its CSS-first configuration, you can also use prefers-reduced-motion directly as a media query in the CSS configuration and define your own utilities that are automatically disabled. Since Hyva themes use Tailwind CSS v4.0, a CSS-variable-based solution is a good fit, one that can be used both in Tailwind classes and in plain CSS.

9. Animation types compared for accessibility

Not every animation is equally problematic for users with vestibular disorders. The overview below classifies common animation types by their risk level for affected users and shows the appropriate response under prefers-reduced-motion.

Animation type Risk level Action under reduce Example
Parallax scrolling High Remove entirely animation: none
Page transition (slide) High Replace with fade transform → opacity
Element fade-in Low Keep, shorten duration duration: 0.01ms
Flashing elements Very high Remove entirely animation: none
Button hover feedback Low Keep duration: 0.01ms

Classifying animation by risk level helps prioritize where to apply prefers-reduced-motion adjustments. Not every animation needs identical treatment. The goal is not to strip out all visual dynamism, but to protect affected users from animation that can cause real physical discomfort.

Mironsoft

Accessible CSS, WCAG compliance, and an inclusive frontend

Need accessible animations for your project?

We audit your existing CSS animations for WCAG compliance, implement prefers-reduced-motion correctly, and make sure your project includes every group of users.

Accessibility audit

Check animations against WCAG 2.2 and prefers-reduced-motion

Refactoring

Introduce a CSS token system for centralized motion control

Hyva / Tailwind

Make motion-reduce: modifiers and Alpine.js transitions accessible

10. Summary

The CSS media query prefers-reduced-motion is not an optional extra, it is a fundamental accessibility requirement for any project that uses animation. For people with vestibular disorders, migraines, or epilepsy, excessive web animation is not just unpleasant, it can trigger real physical discomfort. WCAG 2.2 requires that animation triggered by interaction can be disabled. The technical implementation is minimal: a single central media query using CSS custom properties as tokens is enough to adjust the entire animation system with one rule.

A differentiated strategy, removing large-scale motion, keeping small fades, and replacing slides with fades, matters because it preserves accessibility while keeping the visual quality intact for users who are not affected. Querying prefers-reduced-motion in JavaScript through window.matchMedia lets you respect the same user preference for JS-based animation. In Tailwind CSS, the motion-reduce: and motion-safe: modifiers are the recommended path. No project with meaningful animation should ignore this media query.

prefers-reduced-motion: the essentials at a glance

Media query

@media (prefers-reduced-motion: reduce), reads the system setting. Full browser support.

WCAG 2.2

Criterion 2.3.3 (AAA): make interaction animation disableable. Criterion 2.3.1 (AA): no flashing >3x/s.

CSS token approach

--duration-base: 300ms becomes 0.01ms under reduce. Every component uses the token, giving central control.

JavaScript

window.matchMedia('(prefers-reduced-motion: reduce)').matches, respect it for JS animation too.

11. FAQ: CSS prefers-reduced-motion

1What is prefers-reduced-motion?
A CSS media query that reads the user's system setting. Under reduce, the user has enabled less motion in the operating system's accessibility settings.
2Who needs prefers-reduced-motion?
People with vestibular disorders, migraines, epilepsy, or neurological conditions. Estimated to affect around 35% of all people at some point in their lives.
3Remove all animation?
No. Keep opacity fades. Remove transform movements over large distances. Short feedback transitions can stay with a reduced duration.
4WCAG criteria for animation?
2.3.1 (AA): no flashing >3x/s. 2.3.3 (AAA): interaction animation must be disableable. Both relate to epilepsy and vestibular disorders.
5Tailwind CSS and prefers-reduced-motion?
motion-reduce: and motion-safe: modifiers. Example: motion-safe:animate-bounce motion-reduce:animate-none.
6JavaScript query?
window.matchMedia('(prefers-reduced-motion: reduce)').matches, returns true. Use addEventListener to react to changes.
7reduce vs. no-preference?
no-preference: no special setting made. reduce: the user has enabled reduction. Opt-in approach: enable animation only under no-preference.
8CSS token approach?
Custom properties as tokens: --duration-base: 300ms. Under reduce: 0.01ms. Every component uses the tokens, so one override applies system-wide.
9Which animations to remove completely?
Parallax scrolling, page slides, large-scale transform animations, background videos with motion, flashing elements. The strongest triggers for people with vestibular disorders.
10What are vestibular disorders?
The inner ear system for balance and spatial orientation. When there is a mismatch between visual and physical motion: dizziness, nausea, disorientation, known as cybersickness.