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.
Step-by-step resolution
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.
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. picturechooses 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.
Related
- prefers-reduced-motion Architecture — the parent topic
- Detecting prefers-reduced-motion in JavaScript — reacting to preference changes
- Building an In-App Motion Preference Toggle — offering a setting inside the product