loading...
Cover image for Respecting “prefers reduced motion” with Javascript and React.

Respecting “prefers reduced motion” with Javascript and React.

vanaf1979 profile image VA79 Stephan Originally published at since1979.dev ・4 min read

Originally posted on my website on August 30th 2020

Prefers reduced motion

I recently read a tweet by someone asking if there were any accessibility issues with adding a parallax effect to a website. And some of the reactions pointed out that there are users that have a hard time with visual movement because they experience motion-sickness or other unwanted reactions.

I immediately had and reacted with the following thought: "I guess you could detect the prefers-reduced-motion setting, and show the parallax as a normal image when it is set to true!?"

Most modern operation systems give the user the ability to opt-in to a reduced motions setting and browser vendors (see support table below) are exposing this setting to us web developers through a handy Css media query.

caniuse support table for prefers reduced motion

But not all animation originate from a Css rule. So in this article i would like to show you how you can get access to this setting using javascript/react and adjust any animations accordingly.

Prefers reduced motion with vanilla Javascript

As far as i know there is no direct way to access the OS setting directly, but fortunately for us we can grab the value of any media queries as shown in the code example below.

In this code example we first grab a reference to the media query. We then use a if/else to check if the media query exist (is supported) and if it is set to true. If it is then the user has explicitly expressed a preference for reduced motion so we provide a non-animated experience.

Else it can either mean that the user has not set a preference, or that the media query isn't supported at all. In this last case we have to make a decision our self's. I guess the most "decent" route would be to default to no animations. (I would love to hear your insights on this so please leave a comment or react to my question on twitter.)

Lastly we add an event listener to watch for changes in the media query. A user could change his preference in the middle of his visit to our website. So in the listener callback we do the same check and either remove the animations or not.

A more practical example

Below is a more real live example involving a slider. From what I've been reading lately sliders are often considered problematic to begin with, but from the reduced motion perspective we can make some good steps forward by not using auto slide, and making the slides change instantly without a transition animation.

In this example we use the SwiperJs library as our slider. We start of by creating a default settings abject. We then add settings based on the reduced motion query setting.

If the user requested reduced motion we set the effect to fade instead of slide, then set the animation speed to 0ms making the slides swap out instantly and finally we set auto slide to false so that the users can switch between slides them self's if they want to.

If the user has not requested reduced motion we add the setting to allow for animations between slides and automatically looping through them.

Finally we also add a event listener here to watch for changes in the media query and re-initializing the slider with the correct settings.

You can check out a working example over at codesandbox.io. You can toggle the reduced motion like so:

  • MacOs: "System Preferences > Accessibility > Display > Reduce motion"
  • Windows: "Settings > Ease of Access > Display > Show animations in windows"

Prefers reduced motion with React.

If you prefer React as your weapon of choice we could extract the logic out to a custom hook! There are probably more advanced Npm packages out there so below is a simple example.

In this simple hook, we have the same logic as in the vanilla js version except that we only track the media query to set the local reducedMotion state. This hook accepts a default value and if the media query is not available the default remains, allowing you to choose what the default/fallback state should be.

Below is a example usage of our new hook.

In this example we import and initialize our useReducedMotion hook to false, meaning we are not showing any animations as the default state, and storing this value i a constant named reducedMotion.

We then set the inner text of our H1 tag to reflect the reduceMotion state. And lastly we are setting a class on a div based on the reduceMotion state.

You can check out a working example over at codesandbox.io.

Conclusion

Respecting the reduced motion setting is only the tip of the accessibility ice berg, but as you can see from the examples above it isn't hard to do so.

I would love to hear your thoughts so please comment or reach out on twitter if you have any possible suggestion for improvement.

Thanks for reading.

Discussion

pic
Editor guide
Collapse
feerzlay profile image
Denis Yakshov

Thanks for sharing this media query, I'll give it a try in my pet project.

Collapse
vanaf1979 profile image
VA79 Stephan Author

Your welcome. Let me know if and how it worked for you! 😊