# How to Make Animations Accessible with prefers-reduced-motion: CSS and React Implementation Patterns

> Make animations accessible with prefers-reduced-motion CSS. Learn React patterns to disable jarring motion for a better user experience. Ensure UI feedback is smooth and inclusive.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: how-to-guide
- Published: 2026-08-03

---

**Wrap all animations in `@media (prefers-reduced-motion: reduce)` media queries to replace motion-heavy transforms with subtle opacity or color changes, ensuring users who disable motion in system settings receive accessible, non-jarring UI feedback.**

Animation accessibility requires respecting the user's system-level motion preferences through the `prefers-reduced-motion` media query. According to the animation standards in the `emilkowalski/skills` repository, every animated component must check this setting before applying transforms or keyframe animations. The repository defines canonical patterns that demonstrate how to make animations accessible with `prefers-reduced-motion` while preserving essential visual feedback.

## The Foundation: CSS Patterns for Reduced Motion

The core implementation resides in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) at lines 162-166, which mandates wrapping animation definitions to honor user preferences. When `prefers-reduced-motion: reduce` is active, **eliminate movement-based transforms** (translate, scale, rotate) and **retain only opacity or color transitions** to indicate state changes.

```css
/* Normal animation with spatial movement */
.element {
  animation: slideIn 300ms ease-out forwards;
}

/* Reduced-motion fallback – keep only opacity/color */
@media (prefers-reduced-motion: reduce) {
  .element {
    animation: fadeIn 200ms ease;
  }
}

```

The `slideIn` keyframes might move the element across the screen, while `fadeIn` simply adjusts opacity. This pattern ensures users with vestibular disorders or motion sensitivity receive subtle feedback without experiencing nausea-inducing movement.

## Gating Hover Effects for Pointer Precision

Beyond reduced motion, the repository emphasizes gating hover animations to prevent accidental triggers on touch devices. As specified in [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md) at lines 37-38, you must nest hover rules inside `@media (hover: hover) and (pointer: fine)` to ensure animations only fire on devices with true hover capability.

```css
/* Hover only activates on mice/stylus, not touchscreens */
@media (hover: hover) and (pointer: fine) {
  .element:hover { 
    transform: scale(1.05); 
  }
}

```

This prevents mobile devices from firing "hover" events during scroll or touch interactions, eliminating unwanted motion for users navigating with coarse pointers.

## React Implementation with useReducedMotion

For JavaScript-driven animations, mirror the CSS media query detection using the `useReducedMotion()` hook. This approach allows you to swap full sliding transitions for simple fades when the user prefers reduced motion.

```tsx
import { useReducedMotion } from '@framer-motion/hooks';

function Drawer({ isOpen }) {
  const reduce = useReducedMotion();      // true if user prefers reduced motion
  const translateX = reduce ? 0 : '-100%';

  return (
    <motion.div
      animate={{ x: isOpen ? 0 : translateX }}
      transition={{ duration: reduce ? 0.2 : 0.4, ease: "easeOut" }}
    >
      {/* …content… */}
    </motion.div>
  );
}

```

The hook returns `true` when the system preference is set, allowing you to adjust spring physics, durations, or disable movement entirely while maintaining the component's functional state transitions.

## Real-World Component Example

The prototype documentation in [`skills/prototype/PICKER.md`](https://github.com/emilkowalski/skills/blob/main/skills/prototype/PICKER.md) at line 70 demonstrates production-ready implementation of these patterns. The file shows how to maintain component usability while respecting motion preferences.

```css
@media (prefers-reduced-motion: reduce) {
  .picker { 
    animation: fade 0.2s ease; 
  }   /* keep opacity, drop translate */
}

```

This example illustrates the **essential feedback principle**: even when disabling spatial movement, the animation persists as a fade to indicate the picker activated, preventing users from missing critical state changes.

## Testing and Auditing Your Implementation

To ensure compliance, reference the audit checklist in [`skills/improve-animations/AUDIT.md`](https://github.com/emilkowalski/skills/blob/main/skills/improve-animations/AUDIT.md). This file flags missing `prefers-reduced-motion` handling during code reviews and mandates that every animated component include the media query fallback.

Key audit criteria include:
- **Interruptibility**: Animations must be stoppable for rapidly-triggered UI (toggles, toasts)
- **Property constraints**: Avoid animating `width`, `height`, or `margin` which force re-layout
- **Motion alternatives**: Verify opacity or color substitutes exist for all transform animations

## Summary

- Wrap animations in `@media (prefers-reduced-motion: reduce)` blocks to detect system preferences
- Replace transform-based movements with **opacity** or **color** transitions for reduced motion users
- Gate hover effects behind `@media (hover: hover) and (pointer: fine)` to prevent touch device accidents
- Use `useReducedMotion()` hooks in React or JavaScript frameworks for dynamic animation values
- Audit implementations using the checklist in [`skills/improve-animations/AUDIT.md`](https://github.com/emilkowalski/skills/blob/main/skills/improve-animations/AUDIT.md) to ensure compliance

## Frequently Asked Questions

### What CSS properties should I animate when prefers-reduced-motion is enabled?

Focus on **opacity** and **color** transitions rather than spatial movement. According to [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md), when the user prefers reduced motion, maintain essential feedback through subtle fades or color shifts while eliminating `translate`, `scale`, or `rotate` transforms that could trigger vestibular disorders.

### How do I handle hover animations on touch devices?

Nest hover rules inside `@media (hover: hover) and (pointer: fine)` to prevent unwanted motion on touchscreens. As specified in [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md) at lines 37-38, this ensures hover effects only activate on devices with true hover capability, preventing accidental triggers during mobile scrolling.

### Can I use JavaScript spring libraries with prefers-reduced-motion?

Yes, but you must check the motion preference before calculating animation values. Use the `useReducedMotion()` hook from libraries like Framer Motion to detect the setting, then adjust spring stiffness, duration, or disable movement entirely. Replace sliding transitions with opacity fades as shown in the `emilkowalski/skills` React examples.

### Where should I document the reduced-motion implementation in my project?

Follow the pattern in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) by creating a central animation standards document that mandates `prefers-reduced-motion` handling for every component. Include specific code blocks showing the media query wrap and reference this documentation in your component-specific files, as demonstrated in [`skills/prototype/PICKER.md`](https://github.com/emilkowalski/skills/blob/main/skills/prototype/PICKER.md) at line 70.