> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# usePrefersReducedMotion

> Detects the user's reduced motion preference for accessible animations.

The `usePrefersReducedMotion` hook reads the operating system's `prefers-reduced-motion` setting so you can tone down or remove motion for users who need it.

## Usage

```tsx theme={null}
import { usePrefersReducedMotion } from "@servicetitan/anvil2";

function AnimatedComponent() {
  const { prefersReducedMotion } = usePrefersReducedMotion();

  return (
    <div
      style={{
        transition: prefersReducedMotion ? "none" : "all 0.3s ease",
      }}
    >
      Content with accessible animations
    </div>
  );
}
```

## Return Value

| Property               | Type      | Description                                                                               |
| ---------------------- | --------- | ----------------------------------------------------------------------------------------- |
| `prefersReducedMotion` | `boolean` | `true` when the user prefers reduced motion (`(prefers-reduced-motion: reduce)` matches). |

## Examples

### Disable transitions

```tsx theme={null}
import { usePrefersReducedMotion } from "@servicetitan/anvil2";

function AnimatedComponent() {
  const { prefersReducedMotion } = usePrefersReducedMotion();

  return (
    <div
      style={{
        transition: prefersReducedMotion ? "none" : "all 0.3s ease",
      }}
    >
      Content with accessible animations
    </div>
  );
}
```

### Hover motion with CSS

Reduce transform motion when the user prefers reduced motion:

```tsx theme={null}
import { usePrefersReducedMotion } from "@servicetitan/anvil2";
import { type ReactNode, useState } from "react";

function HoverCard({ children }: { children: ReactNode }) {
  const { prefersReducedMotion } = usePrefersReducedMotion();
  const [hovered, setHovered] = useState(false);
  const scale = hovered && !prefersReducedMotion ? 1.05 : 1;

  return (
    <div
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
      style={{
        transform: `scale(${scale})`,
        transition: prefersReducedMotion ? "none" : "transform 0.2s ease",
      }}
    >
      {children}
    </div>
  );
}
```

### Static alternative to motion-heavy UI

When one part of the UI relies on continuous motion (marquee, auto-rotating carousel, and so on), render a static equivalent when `prefersReducedMotion` is `true`—for example a plain list or paginated controls. For libraries such as Framer Motion, install them in your application and thread `prefersReducedMotion` into their animation props the same way you would for CSS `transition`.

## Technical details

### Detection

The implementation uses:

```tsx theme={null}
window.matchMedia("(prefers-reduced-motion: reduce)");
```

and listens for the `change` event on that `MediaQueryList` so the returned boolean updates when the user changes the system setting.

### SSR

When `window` is not available (including during SSR), the hook keeps `prefersReducedMotion` as `false` until the effect runs in the browser, then it updates to the real preference.

## Accessibility guidelines

1. Respect the preference: do not override reduced motion with forced animations.
2. Preserve meaning: if motion carried information, expose that information without motion.
3. Test with the OS setting enabled and confirm layouts and focus order stay usable.
4. Reduced motion still allows short, subtle transitions; avoid large parallax or long loops.

### How to test

* **macOS**: System Settings → Accessibility → Display → Reduce motion
* **Windows**: Settings → Accessibility → Visual effects → Animation effects
* **iOS**: Settings → Accessibility → Motion → Reduce Motion
* **Android**: Settings → Accessibility → Remove animations

## Browser support

`prefers-reduced-motion` is supported in current browsers. Where it is unsupported, the media query does not match and the hook reports `prefersReducedMotion: false`.
