Skip to content
intermediatePhase 39 · Accessibility

Reduced Motion

Respect prefers-reduced-motion for users with vestibular disorders.

30m
0 problems
Topic Progress0%

prefers-reduced-motion

prefers-reduced-motion

Respect user preference for reduced motion.

CSS Media Query

/* Base animation */
.animated-element {
  transition: transform 0.3s ease;
  animation: slideIn 0.5s ease;
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .animated-element {
    transition: none;
    animation: none;
  }
}

React Hook

// hooks/useReducedMotion.js
function useReducedMotion() {
  const [prefersReduced, setPrefersReduced] = useState(false);

  useEffect(() => {
    const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
    setPrefersReduced(mediaQuery.matches);

    const handler = (e) => setPrefersReduced(e.matches);
    mediaQuery.addEventListener('change', handler);
    return () => mediaQuery.removeEventListener('change', handler);
  }, []);

  return prefersReduced;
}

// Usage
function AnimatedComponent() {
  const prefersReduced = useReducedMotion();

  return (
    <div
      style={{
        transition: prefersReduced ? 'none' : 'transform 0.3s ease',
      }}
    >
      Content
    </div>
  );
}

Framer Motion Example

import { motion, useReducedMotion } from 'framer-motion';

function AnimatedBox() {
  const shouldReduceMotion = useReducedMotion();

  return (
    <motion.div
      initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration: shouldReduceMotion ? 0 : 0.3 }}
    >
      Animated content
    </motion.div>
  );
}

Implementing Reduced Motion

Implementing Reduced Motion

CSS Transitions

/* Before */
.button {
  transition: all 0.3s ease;
}

/* After */
.button {
  transition: background-color 0.3s ease, color 0.3s ease;
}

@media (prefers-reduced-motion: reduce) {
  .button {
    transition: none;
  }
}

CSS Animations

@keyframes slideIn {
  from { transform: translateY(-20px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

.animate-slide-in {
  animation: slideIn 0.5s ease;
}

@media (prefers-reduced-motion: reduce) {
  .animate-slide-in {
    animation: none;
    opacity: 1;
  }
}

JavaScript Animations

function useAnimation() {
  const prefersReduced = useReducedMotion();

  const animate = useCallback((element, keyframes, options) => {
    if (prefersReduced) return;
    
    element.animate(keyframes, {
      duration: 300,
      easing: 'ease-in-out',
      ...options,
    });
  }, [prefersReduced]);

  return animate;
}

Scroll Animations

/* Intersection Observer animations */
.fade-in {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.5s, transform 0.5s;
}

.fade-in.visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .fade-in {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

Testing

Testing Reduced Motion

Chrome DevTools

  1. Open DevTools (F12)
  2. Open Rendering panel (⋮ → More tools → Rendering)
  3. Enable "Emulate CSS media feature prefers-reduced-motion: reduce"

System Settings

macOS:

  • System Preferences → Accessibility → Display → Reduce motion

Windows:

  • Settings → Ease of Access → Display → Show animations

iOS:

  • Settings → Accessibility → Motion → Reduce Motion

Android:

  • Settings → Accessibility → Remove animations

Testing Checklist

  • Animations disabled with reduced motion
  • Essential animations still work
  • No flashing or blinking
  • Transitions instant
  • Scroll effects disabled
  • Auto-playing videos paused

Essential vs Decorative

// ✅ Essential animation (keep)
function Progress({ value }) {
  return (
    <div className="progress-bar">
      <div
        className="progress-fill"
        style={{ width: `${value}%`, transition: 'width 0.3s' }}
      />
    </div>
  );
}

// ❌ Decorative animation (remove)
function Header() {
  return (
    <div className="header animate-fade-in">
      <h1>Title</h1>
    </div>
  );
}

Practice Problems

0/3solved
Build Reduced Motion Component

Create a reusable React component implementing Reduced Motion. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Reduced Motion Testing

Write unit and integration tests for Reduced Motion using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Reduced Motion Performance

Optimize Reduced Motion for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What does prefers-reduced-motion: reduce do?

Question 1 options

2. Should all animations be removed with reduced motion?

Question 2 options

3. What is the primary purpose of Reduced Motion?

Question 3 options

4. What is a common mistake when implementing Reduced Motion?

Question 4 options

Flashcards

Question

What is prefers-reduced-motion?

Answer

A CSS media query that detects if the user has requested reduced motion.

Question

How do you implement reduced motion?

Answer

Use @media (prefers-reduced-motion: reduce) to disable animations and transitions.

Question

What animations should be kept?

Answer

Essential animations that convey information, like progress bars and loading indicators.

Question

How do you test reduced motion?

Answer

Chrome DevTools Rendering panel, system settings, or browser extensions.

Question

What is Reduced Motion?

Answer

Reduced Motion is a key concept in frontend development.

Revision Notes

Key Takeaways

  • 1.Always respect prefers-reduced-motion preference
  • 2.Disable decorative animations, keep essential ones
  • 3.Test with Chrome DevTools and system settings
  • 4.Use CSS media query for CSS animations
  • 5.Use React hook for JavaScript animations

Interview Tips

  • Explain what prefers-reduced-motion does
  • Discuss which animations to keep vs remove
  • Know how to test reduced motion

Cheat Sheet

Reduced Motion Cheat Sheet

CSS

@media (prefers-reduced-motion: reduce) {
  .animated {
    animation: none;
    transition: none;
  }
}

React

const prefersReduced = useReducedMotion();
style={{ transition: prefersReduced ? 'none' : '...' }}

Testing

  • Chrome DevTools Rendering panel
  • System Accessibility settings

Guidelines

  • Remove decorative animations
  • Keep essential animations
  • No flashing or blinking