Briefly
One of the uses of the @media directive to check user settings for animation playback.
Most modern operating systems allow users to adjust animation settings. The media query prefers
allows you to determine whether animation is disabled or reduced in the system and apply CSS styles that take this into account.
With prefers
, you can slow down or completely disable animations.
How to Write It
The prefers
has two values:
no
— default animation settings.- preference reduce
— animation is disabled.
In the example, we add smooth scrolling only for users who have not disabled animations at the system level.
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; }}
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
Why?
A user may disable animation for various reasons, for example:
- not everyone perceives animation the same way. What may seem smooth and pleasant to some can irritate or distract others (flashing ads, complex parallax effects, autoplaying videos);
- medical aspects: some people may have vestibular disorders that can cause dizziness, nausea, or seizures even with simple animations;
- websites with a lot of animation can quickly drain the battery of mobile devices or use more traffic. For example, autoplaying videos will require more data than displaying a static image.
Examples of Use
Disabling or Slowing Down Animation
To disable an element's animation or change its speed if the user has explicitly set a preference to reduce it, you can write the following in CSS:
.button { /* Fun shaking button animation */ animation: shake 300ms linear infinite both;}/* Completely disables animation */@media (prefers-reduced-motion: reduce) { .button { animation: none; }}/* Slows down the animation by 2 times */@media (prefers-reduced-motion: reduce) { .button { animation: shake 600ms linear infinite both; }}
.button { /* Fun shaking button animation */ animation: shake 300ms linear infinite both; } /* Completely disables animation */ @media (prefers-reduced-motion: reduce) { .button { animation: none; } } /* Slows down the animation by 2 times */ @media (prefers-reduced-motion: reduce) { .button { animation: shake 600ms linear infinite both; } }
Conversely, we can play the animation only if the user has no preferences for showing animation:
@media (prefers-reduced-motion: no-preference) { .button { animation: shake 300ms linear infinite both; }}
@media (prefers-reduced-motion: no-preference) { .button { animation: shake 300ms linear infinite both; } }
The second method has two advantages:
- less code;
- older browsers that do not support
prefers
will simply ignore this rule and display only the original element without animation.- reduced - motion
Smooth Scrolling
html { scroll-behavior: smooth;}
html { scroll-behavior: smooth; }
If you set scroll
on <html>
, when the user clicks on an anchor link, the page will smoothly scroll to the desired position.
Unfortunately, there is currently no control over the page scroll speed in CSS. If the page is long, the scrolling might be very fast, which can be unpleasant for people sensitive to sudden animations.
You can wrap scroll
in a media query to prevent smooth scrolling and simply open the page in the desired place if the user has changed animation settings:
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; }}
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
What About JavaScript?
If you need to know animation preferences using JavaScript, you can do so with match
. Here’s how this scrolling behavior setting looks in JavaScript:
const prefersReducedMotion = window.matchMedia( '(prefers-reduced-motion: reduce)')a.addEventListener('click', () => { const behavior = prefersReducedMotion.matches ? 'auto' : 'smooth' window.scrollTo({ x: 0, y: 0, behavior })})
const prefersReducedMotion = window.matchMedia( '(prefers-reduced-motion: reduce)') a.addEventListener('click', () => { const behavior = prefersReducedMotion.matches ? 'auto' : 'smooth' window.scrollTo({ x: 0, y: 0, behavior }) })
Optimizing Style and Library Loading
If you have a lot of CSS related to animations, you can separate the playback styles into a different file and not load it for users who have opted out of animations:
<link rel="stylesheet" href="animations.css" media="(prefers-reduced-motion: no-preference)">
<link rel="stylesheet" href="animations.css" media="(prefers-reduced-motion: no-preference)" >
Similarly, you can prevent loading heavy animation libraries. In the example below, if the user prefers to reduce the amount of animation, the function will make a return
and its execution will be halted. As a result, GreenSock (GSAP) will not be imported.
const prefersReducedMotion = window.matchMedia( '(prefers-reduced-motion: reduce)')const loadGSAPAndInitAnimations = () => { if (prefersReducedMotion.matches) return import('gsap').then((object) => { const gsap = object.default // Initialize the animation using GSAP here })};loadGSAPAndInitAnimations()
const prefersReducedMotion = window.matchMedia( '(prefers-reduced-motion: reduce)') const loadGSAPAndInitAnimations = () => { if (prefersReducedMotion.matches) return import('gsap').then((object) => { const gsap = object.default // Initialize the animation using GSAP here }) }; loadGSAPAndInitAnimations()