December 5, 2024
4
min read

Accessibility tip: Detecting reduced motion in one line of JavaScript

Chris Heilmann

Animation can be a great tool to make apps seem more performant, be more engaging or even just more interesting. There are, however, quite a few groups of people who can not deal with things animating, as it distracts them or can even cause nausea. This is why operating systems allow you to enable "reduced motion" which turns of all unnecessary animation in the OS. Our own apps should also abide by this setting in order to be accessible, as it is even a WCAG Success Criterion.

Detecting prefers-reduced-motion

For browser based apps, we have a media feature that gets triggered when a user has reduced motion turned on called prefers-reduced-motion. This one can be used in CSS, but also as a media attribute on link elements.

In JavaScript, we can use matchmedia(), which means that checking if animations are wanted or not is a one liner (here on 3 to be more readable):

let prefersReducedMotion = window.matchMedia( 
  '(prefers-reduced-motion: reduce)' 
).matches;

On devices with reduced motion turned on, prefersReducedMotion is true, otherwise it is false.

We can use this to simply not show animations when it is false. Take for example a button that picks one item from an array of categories. When reduced motion is not turned requested, we show a random amount of options before we get to the last one. When it is requested, we just show one category every time the user activates the button. You can try this in the reduced no checkbox codepen:

Screen recording showing the functionality

The code is pretty straight forward:

<button>Pick Category</button>
<output></output>
// test for reduced motion
let prefersReducedMotion = window.matchMedia(
    '(prefers-reduced-motion: reduce)'
).matches;
// define categories
let categories  = [
    'Accessibility','AI/ML','Command Line tools','Code'
    // … more …
];
let counter = 0; // repeat counter
// dom elements
const button = document.querySelector('button');
const out = document.querySelector('output');
// recursive function to call counter times 
const shuffle = counter => {
  // show a random category
  out.innerText = categories[
    Math.floor(Math.random() * categories.length)
  ];
// if animation is wanted 
if (!prefersReducedMotion) {
  // turn off button
  button.disabled = true;
  // reduce counter and call shuffle again after 100ms
  counter = counter - 1;
  if (counter > 0){
    setTimeout(() => { shuffle(counter); }, 100);
   // if counter is zero, enable button
   } else {
    button.disabled = false;
   }
}
};
// When the button is clicked, define counter as 
// a random number between 0 and 30 and call shuffle
document.querySelector('button').addEventListener('click', e => {
  counter = Math.random() * 30 | 0;
  shuffle(counter);
});

Allowing for manual override

This is a simple example, but whilst people have requested reduced motion, they may still want to have animations from time to time. To enable that, we can offer a checkbox so they can turn it on and off. You can see this in action in the Animation Test codepen:

screen recording showing an opt in to animate despite setting reduced motion

The main difference is that we add a checkbox to the HTML:

<input id="animated" type="checkbox">
<label for="animated">Animated</label>
<p>
  <button>Pick Category</button>
  <output></output>
</p>

And in the JavaScript, we set this to the state of media feature:

let prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches
document.querySelector('input').checked = !prefersReducedMotion

We then need to override the prefersReducedMotion value when the user chooses to have animations. This is a simple event handler on the checkbox:

document.querySelector('input').addEventListener(
  'change', e => {
    prefersReducedMotion = !prefersReducedMotion;    
   }
 );


That way, we offer the best of both states. We turn off animations by default when the user has reduced motion enabled, and we allow them to override this.

If you use a Chromium browser, you can even simulate reduced motion without having to change it in the operating system.

Accessibility tip: Detecting reduced motion in one line of JavaScript

December 5, 2024
4
min read

Continue reading

We are busy writing more posts on this topic right now. Sign up for our newsletter to not miss them.

Subscribe to DevDigest

Get a weekly, curated and easy to digest email with everything that matters in the developer world.

From developers. For developers.