Elijah Manor: [0:00] Here we have a small web application with lots of animation. Many operating systems have a reduced-motion feature in their accessibility settings. As of now, our web app does not respect the setting, but we'll update our app to do so.
[0:23] Let's go to the very bottom. Thankfully, there's a media query called prefers-reduced-motion that we can detect. It could have values of no-preference and reduce. We'll detect when a user prefers a reduced experience and adjust the CSS accordingly.
[0:39] Let's go find the CSS selector that controls the rotation animation of the logo. OK, that's the App-logo. Let's add a new section for App-logo and turn off the animation. Once we save, you'll see on the right that the rotation of the logo has stopped. It's now respecting the reduce motion operating system setting.
[1:05] Now let's go find a selector that controls the stroke animation for the orbits. It's Logo-orbit. We'll go down and group our previous selector with Logo-orbit so that they both could have animation of none. We'll save our file. It'll update the right. You'll see that the stroke animation now stops as well.
[1:27] The only part of our SVG that's animating are the electrons. Those are not animated with CSS but with Smile. The animation motion element defines how the electrons move along the motion path.
[1:40] Let's grab the Logo-electron class of the circle elements, go back into our CSS file, and add a new rule set that hides the elements with display: none. Although that does remove the animation for the electrons, we should probably pause the animation instead of totally remove those elements.
[2:20] First, let's create some new state via the useState React hook and call it shouldReduceMotion with a setShouldReduceMotion setter. We'll default the setting defaults and set it later once we know what the operating systems value is. Next, we'll create a useEffect React hook. We'll have it only run after our component first renders.
[2:45] Here, we'll create a new mediaQuery with matchMedia, passing the same criteria that we used over in our CSS file previously.
[2:54] Let's go ahead and set the current state of the query to setShouldReduceMotion. The mediaQuery's match property will be true or false, based on the mediaQuery's string.
[3:06] Now let's add code to listen to changes to the prefers-reduced-motion media query. We'll define a handle media function that will accept changes and update our setShouldReduceMotion setting with e.matches. Then we'll add an EventListener to our media query and listen for any changes that occur, passing our handleMedia function.
[3:29] Finally, we'll return a function to clean up after ourselves, and remove the EventListener from the mediaQuery for the change event.
[3:38] Now that should-reduce-motion is all set up, we should be able to use it to toggle the SVG animation. Let's create a new logoRef with the React.useRef hook, and come down and add our ref to the ReactLogo SVG.
[3:55] Let's create a new useEffect hook that will toggle the SVG's animation. If shouldReduceMotion is true, then it will grab the logoRef's current property, which is the SVG DOM element, and call pauseAnimations(), so that the electrons will stop moving.
[4:12] Otherwise, we'll call the unpauseAnimations() function of the SVG to resume any pause animations. We'll tell useEffect to run this effect any time that the value of shouldReduceMotion changes. Once we save, you could see on the right that our electrons indeed did stop animating.
[4:32] This is one of the rare cases where we may want to use the useLayoutEffect hook since we're working outside of React, but it should work either way.
[5:08] Since we already have defined shouldReduceMotion, we could leverage that state and update the animate prop to either be undefined or true, based on its value. Then we'll copy that logic and update the other animate prop as well. Let's save our code and see what happens.
[5:28] If reduce motion is turned off, everything animates. Once reduce motion is enabled, not only does the SVG stop animating, but the Framer Motion bar animations stop as well. Instead of animating, the bars immediately snap to their new values.
[5:45] Before we finish, let's utilize a custom hook that Framer Motion provides for us called useReduceMotion. This essentially does all the stuff that we were mainly writing earlier. We could delete much of our code and replace it with shouldReduceMotion = useReducedMotion.
[6:04] Now we should be able to test our web app again with the same results. There should be no animations when enabled, and the animations should resume if we flip the setting off. And they do.