Skip to main content
Syllabus
On this page

Motion Accessibility and Performance: prefers-reduced-motion, will-change and Smooth 60fps Animations

Badar KhalilUpdated September 30, 2026 6 min read

Why motion accessibility matters

Motion accessibility means making sure animation helps users instead of harming them. Large movement, parallax, zooming, spinning and constant looping can trigger dizziness, nausea, migraines or distraction for people with vestibular disorders, epilepsy, ADHD or cognitive differences. Operating systems provide a setting such as Reduce Motion, and the CSS media feature prefers-reduced-motion lets your site respond to it.

Focus keyword: prefers-reduced-motion. Related trending keywords: reduced motion CSS, accessible animations, WCAG animation guidelines, CSS animation performance, will-change, compositor-only animations, Interaction to Next Paint (INP), Core Web Vitals animation, 60fps animation.

The prefers-reduced-motion media query

  • @media (prefers-reduced-motion: reduce): the user asked the system to minimize non-essential motion.
  • @media (prefers-reduced-motion: no-preference): no request has been made.

Reduce does not always mean remove. Replace large movement (sliding, zooming, spinning, parallax, bouncing) with gentle alternatives such as a short opacity fade or an instant state change. Keep essential motion that explains a change, but make it smaller and calmer.

Two implementation strategies

  • Opt-out (reduce first): write animations normally, then inside @media (prefers-reduced-motion: reduce) disable or simplify them. Quick to add to an existing project.
  • Opt-in (no-preference first): write motion only inside @media (prefers-reduced-motion: no-preference). This is safer, because users with the preference (and browsers that do not report it) get the calm version by default.

A common global safety net sets animation and transition durations to near zero and stops scroll-behavior smoothing. It is a good last resort, but design intentional reduced versions for important components.

Reading the preference in JavaScript

Use window.matchMedia('(prefers-reduced-motion: reduce)'). Listen for changes with addEventListener('change', ...) because users can switch the setting while the page is open. Apply it to JavaScript animation libraries, autoplaying carousels, canvas and WebGL effects, video backgrounds and View Transitions as well.

WCAG rules you should know

  • 2.2.2 Pause, Stop, Hide (Level A): any moving, blinking or auto-updating content that starts automatically, lasts more than 5 seconds and appears alongside other content needs a mechanism to pause, stop or hide it.
  • 2.3.1 Three Flashes or Below Threshold (Level A): nothing should flash more than three times per second.
  • 2.3.3 Animation from Interactions (Level AAA): motion triggered by interaction can be disabled unless essential.

Also avoid motion that blocks reading, and never hide critical information behind an animation that only runs once.

CSS animation performance basics

Browsers render in stages: style, layout, paint and composite. The cheapest animations only touch the composite stage, which can run on the GPU and even off the main thread.

  • Cheap (composite only): transform, opacity. In many browsers filter and backdrop-filter are also composited but can be costly on large areas.
  • Medium (paint): color, background-color, box-shadow, border-radius.
  • Expensive (layout): width, height, margin, padding, top, left, font-size. These can force the page to recalculate positions for many elements.

Replace left with translateX, width with scaleX (when appropriate), and fading with opacity.

will-change, contain and content-visibility

  • will-change: transform hints that an element is about to animate so the browser can promote it to its own layer. Use it sparingly, add it shortly before an animation and remove it afterward. Too many layers waste memory and can slow the page.
  • contain: layout paint (or contain: content) limits how far the browser must recalculate when something changes.
  • content-visibility: auto skips rendering of off-screen sections, which frees time for animations that matter.

JavaScript animation tips

  • Use requestAnimationFrame for visual updates, never setInterval.
  • Prefer the Web Animations API (element.animate()) or CSS over manual style changes.
  • Avoid reading layout values (like offsetHeight) and writing styles in the same loop, which causes layout thrashing.
  • Keep main-thread work short so user input stays responsive. Long tasks hurt the Core Web Vitals metric INP.
  • Do not use animation on LCP elements in a way that delays their first visible paint.

How to measure

  • Chrome DevTools, Performance panel: record, look for long frames, layout and paint cost.
  • Rendering tab: enable Paint flashing, Layout Shift Regions and FPS meter.
  • Animations panel: slow down and inspect timing.
  • Test on a mid-range phone, not only your laptop. Also test with OS Reduce Motion turned on.

Checklist

  • Motion has a purpose (feedback, orientation, hierarchy).
  • Large or looping motion respects prefers-reduced-motion.
  • Autoplaying motion longer than 5 seconds can be paused.
  • Only transform and opacity are animated for frequent or large effects.
  • No flashing content above the WCAG threshold.
  • will-change is used rarely and removed after use.
  • Keyboard and screen reader users get the same information without the animation.

Quick summary

Great motion design is fast, purposeful and optional. Use prefers-reduced-motion to adapt to user needs, follow WCAG rules, and keep your animations on the compositor for smooth performance.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself CSS
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Exercise: Make an animation accessible and fastHTML

The .slider currently animates the left property forever. Rewrite the keyframes to use transform instead of left, and add a prefers-reduced-motion: reduce rule that stops the animation completely. Bonus: add will-change: transform only while the animation runs.

Try it Yourself HTML
Output

Press Run to execute.

Show expected output
The square slides back and forth using transform: translateX(260px), and does not animate when the OS Reduce Motion setting is enabled.

This is a self-check — compare your result with the expected output above.

Was this page helpful?