Reduced Motion for Video and Animated Images

Part of prefers-reduced-motion Architecture in Accessible Motion Architecture.

The problem

A product page has a reduced-motion stylesheet that removes every CSS animation and transition. With the preference enabled, the page is calm — except for the autoplaying hero video, the animated GIF in the feature comparison, the looping WebP in the testimonial section and the Lottie animation in the pricing table, all of which keep moving.

prefers-reduced-motion is a CSS media query, and CSS cannot stop media from playing. These need different handling, and each format needs its own.

Root cause analysis: the media query is not a playback control

CSS can hide, not pause. A reduced-motion rule can set display: none on a video, which stops it being rendered but is a poor experience and does not stop audio in every case. There is no CSS property that pauses media.

Animated images have no API. A GIF, animated WebP or animated AVIF plays whenever it is rendered. Script cannot pause it. The only reliable approach is not to load the animated file: serve a static image and let the user opt in.

<picture> can choose by media query. A source with a media attribute lets the browser pick a still image when prefers-reduced-motion: reduce matches, and the animated version otherwise. The decision happens at load time, before any animated bytes are fetched — which also saves bandwidth.

Video is controllable from script. The autoplay attribute is declarative, so the safe pattern is to omit it and start playback from script only when the preference allows, or to pause immediately on load when it does not.

Library-driven animations need their own check. Lottie-style players, canvas loops and WebGL scenes must read the preference and render a static frame instead.

The preference can change while the page is open. Listening for changes on the media query list keeps behaviour correct without a reload, the pattern in detecting prefers-reduced-motion in JavaScript.

Media types and how to respect the preferenceOnly video and scripted animation can be controlled after loading.Media types and how to respect the preferenceControllable after load?ApproachCSS animation /transitionYesMedia query in CSSAnimated GIF /WebP / AVIFNopicture with a still sourcevideo autoplayYesDo not autoplay; provide controlsLottie / canvas/ WebGLYesCheck the preference, render one frameAnimated SVG (inimg)NoMedia query inside the SVG file
Only video and scripted animation can be controlled after loading.

Step-by-step resolution

Making media respect the preferenceDecide at load for images; control playback for video and scripts.Making media respect the preference1Replace animated img elements with picture elements offering a still source.No animated bytes when reduced2Remove the autoplay attribute from decorative video.Nothing starts by itself3Start playback from script only when motion is allowed.Preference respected4Add visible controls so anyone can play or pause.User control for everyone5Make library players check the preference and render a static frame.Scripted motion covered6Listen for preference changes and apply them live.
Decide at load for images; control playback for video and scripts.

Production code pattern

<!-- Animated image: the browser never downloads the animated file when reduced motion is preferred. -->
<picture>
  <source srcset="demo-still.avif" media="(prefers-reduced-motion: reduce)" type="image/avif">
  <source srcset="demo-animated.avif" type="image/avif">
  <img src="demo-still.png" alt="The editor reformatting a document" width="640" height="360">
</picture>

<!-- Decorative video: no autoplay attribute; controls always available. -->
<video id="hero-video" class="hero__video" poster="hero-poster.avif" muted loop playsinline
       controls preload="metadata" width="1280" height="720">
  <source src="hero.webm" type="video/webm">
</video>
const reduce = matchMedia('(prefers-reduced-motion: reduce)');
const video = document.getElementById('hero-video');

function applyMotionPreference() {
  if (reduce.matches) {
    video.pause();                              // never autoplay decorative video
    lottiePlayer?.goToAndStop(lottiePlayer.totalFrames * 0.5, true);   // a representative frame
  } else {
    video.play().catch(() => {});               // may be blocked by the browser; that is fine
    lottiePlayer?.play();
  }
}

reduce.addEventListener('change', applyMotionPreference);
applyMotionPreference();
/* The poster remains visible until the user plays the video. */
.hero__video { background: center / cover no-repeat url("hero-poster.avif"); }

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

Rendering Impact: avoiding autoplay removes continuous video decoding and its compositor layer from pages where the preference is set, which is a large saving on low-end devices as well as an accessibility improvement — the cost analysed in video and canvas layer memory costs.

Choosing a mid-sequence frame for the static Lottie state is deliberate: the first frame of many animations is empty or incomplete, so it communicates nothing. Pick the frame that best represents the finished state.

Moving elements remaining with reduced motion enabledProduct page audit before and after handling media.Moving elements remaining with reduced motion enabledCSS-only reduced-motion stylesheet4 elementsPlus picture element for animated images2 elementsPlus video and player handling0 elements
Product page audit before and after handling media.

Controls are for everyone

Reduced motion is a preference, not a permanent ban, and users who set it may still want to watch a demo. Every piece of motion that has been stilled should be startable: a visible play button on the video, a “play animation” control next to a stilled Lottie, and, for an animated image swapped to a still, a control that swaps it back.

The same controls serve users who have not set the preference but find a particular animation distracting, which is what providing pause controls for autoplay motion requires for content that moves for more than five seconds. Building them once covers both cases.

Verification checklist

Constraints and trade-offs

  • Serving both still and animated assets increases build and storage requirements.
  • A still frame may not convey what an animated demo does; consider adding a text description.
  • Some browsers block scripted play() without user interaction, which is acceptable for decorative video.
  • picture chooses at load time, so changing the preference afterwards does not swap the image without a reload or script.
  • Muted autoplay is still motion, even without sound.

Frequently asked questions

Can CSS stop a GIF from animating?

No. CSS cannot control media playback. Serve a static image with a picture element when the preference is set, so the animated file is never loaded.

Should videos autoplay if reduced motion is not preferred?

Decorative video is better started by the user in either case, but if it autoplays, it must be muted, must respect the preference, and must have visible controls.

How do I handle Lottie or canvas animations?

Check the preference in script, render a representative static frame instead of playing, and offer a control to start it.

Does the preference update without a reload?

For CSS and for script listening to the media query, yes. Images chosen by a picture element’s media attribute are selected at load time.