hints / preference

Sec-CH-Prefers-Reduced-Motion

Sec-CH-Prefers-Reduced-Motion carries the user’s reduced-motion accessibility setting to the server, enabling motion-free server-rendered pages from the first byte.

requires Accept-CH Preference
Example
Sec-CH-Prefers-Reduced-Motion: reduce
JavaScript
matchMedia('(prefers-reduced-motion: reduce)')
Available since
Chrome 108 (2022)
Engines
Chrome Edge Opera Firefox Safari
References
MDN · Specification

Sec-CH-Prefers-Reduced-Motion is the request-header form of the prefers-reduced-motion media query, the accessibility setting people enable when animation causes them discomfort, from vestibular disorders to plain distaste for parallax.

Accept-CH: Sec-CH-Prefers-Reduced-Motion

Sec-CH-Prefers-Reduced-Motion: reduce         # user asked for calm
Sec-CH-Prefers-Reduced-Motion: no-preference  # default

The value is an unquoted token: no-preference or reduce.

When the header beats the media query

Pure CSS animation is already handled by @media (prefers-reduced-motion: reduce); you don’t need a server for that. The header earns its place when motion is decided server-side:

  • Choosing between a video hero and a static image in the HTML itself.
  • Skipping the autoplaying carousel markup entirely, rather than pausing it after hydration.
  • Not shipping the 80 KB animation library to someone who asked for stillness, a performance win disguised as an accessibility feature.

As with all preference hints, add Vary: Sec-CH-Prefers-Reduced-Motion when the response depends on it, and consider Critical-CH if the un-hinted first response would be jarring (details on the Accept-CH page).

In JavaScript

const calm = matchMedia('(prefers-reduced-motion: reduce)').matches;

Works everywhere; only the header is Chromium-only. This very site uses the media-query form: the capture panel’s typing animation collapses to an instant render under reduce.

Respect what it means

reduce is not “no animation ever”; it means unnecessary motion should go. A subtle opacity fade is fine. A full-viewport zoom-parallax scroll is not. The header just gets that decision to your server one round-trip earlier.