Lesson Description

The "Prefers Reduced Motion" Lesson is part of the full, Award-Winning Marketing Websites course featured in this preview video. Here's what you'd learn in this lesson:

Matias refactors the animation to accommodate the user's motion preference. The prefers-reduced-motion media query is added, giving the user the ability to experience the page without the scroll-based animation. When prefers-reduced-motion is enabled, the content appears stacked on the page in its natural scroll position.

Preview

Transcript from the "Prefers Reduced Motion" Lesson

[00:00:00]
>> Matias Gonzalez: OK, so the next experiment is going to allow us to play with the reduced motion property. Let's go ahead and open the coding here and go into the reduced motion experiment. So we have the final state of our previous experiment with all of the image sequence movement, and what we want is that we want to detect if the user has reduced motion, and we want to display a more static version of this website.

[00:00:38]
So first, how do we even go about like testing this? There's a way to enable that in Chrome for ourselves. If we open the dev tools and hit Escape, we can go into this menu here and open rendering. And there we will see like a lot of options, and if we go into every one of these options, there's a lot of things to learn about everything here.

[00:01:10]
But for now we will just scroll until we find the emulate CSS media feature prefer reduced motion. We'll just enable the reduced motion version, and of course by default the site is going to look the same because we need to add our own custom logic, you know, to change the behavior of our websites.

[00:01:36]
So let's start by doing that. It's going to the starter. OK, let's first comment out the scroll sequence. We'll figure out how to enable or disable it later. And what we want right now is to transform all of this fixed sections into like a layout that we have on the starter where every section was one below the other.

[00:02:20]
So Tailwind has a really useful selector for this, which is motion-reduce. So what this is going to do is it's going to apply whatever style we want only if the user has reduced motion. So if the user has motion reduced, let's actually apply a relative class. And if I hover I can see this is how the media query looks in case we want to do it manually on CSS.

[00:02:56]
But for now, we'll use Tailwind to get things a little bit more faster. So I'm going to grab all of the sections that have a position fixed and just change them to relative if we have reduced motion. So go ahead and do that in here. Cool. So as I can see, right now they're not fixed anymore, they just scroll with the scroll.

[00:03:33]
We still have all of the animations triggering, so I want to wait to first like disable these animations. But these animations are being added with JavaScript, so I actually need a way to detect this media query from JavaScript. There is a way to test media queries on JavaScript.

[00:04:05]
It's called matchMedia, so we can actually go ahead and say let's just create a state called reduced motion. I'm going to start with false by default. And then let's create the useEffect. And then the way matchMedia works is that I'm going to create a query, so const query equal to window.matchMedia, sorry.

[00:04:50]
And here I can write my queries, so exactly as we would do with CSS, we can just write in here prefers-reduced-motion: reduce. And oh, they knew it's not needed since it's not a class. And then we can also subscribe for changes on this media query, so addEventListener change.

[00:05:33]
And every time this media query changes, we can just update our reduced motion. And the way I get if this query is matching or not is with query.matches. So this is going to be a boolean, and it's going to be true if we have reduced motion or false if not. We can actually write any CSS media query in here, so we can also detect things like if the user has a touch screen or the device width or whatever we want to do in here.

[00:06:11]
Let's actually for the first time that we run this useEffect also set reduced motion. Let's ignore this for now. And also let's create a controller so that we can unsubscribe from this event listener, so new AbortController. And then we'll connect the signal as we saw before in here, so signal: controller.signal.

[00:07:01]
So we will automatically unsubscribe whenever we unmount this component. So now we can use this value in JavaScript. Let's test that this is working. So let's do a console log with this value. And if I go to Chrome and go into the console, I can see that the value starts as false, and then when it detects that I have reduced motion, it goes into true.

[00:07:38]
So now we can disable all of our animations that we added with GSAP using JavaScript. So as we saw before with GSAP, we have a dependency array that we can use to react to state changes, so I would add dependencies. And this feature is going to depend on the reduced motion.

[00:08:16]
And then I want to revert my changes if any dependency is updated, so true. And what I want to do here is if I have reduced motion, I'll just for now do an early return. So I would just avoid adding any animation to the timeline. So my texts are already in here, but my previous text has opacity zero, so it is here but it's hidden because it was like the initial state of it.

[00:08:53]
So we need to go down into the CSS and search for opacity zero. And I only want this class to be applied if I have motion safe. So again, if I have reduced motion, I don't want this class to be applied, so what I'm going to do is I'm going to use the motion-safe query.

[00:09:15]
And what this is going to do is it's going to test for no preference on reduced motion, and then it's going to apply opacity zero. So with these two utilities, we can actually add or remove classes based on the reduced motion settings of our user. So as I can see, if I have reduced motion, I will not apply the opacity zero, so I can see the text.

[00:09:55]
Let's make this more interesting again by adding some images, so I have a couple of images that I extracted from the sequence in this folder so that we can use those. Let's actually go ahead and import them. So I'm going to go in here and import hero image from assets 001.

[00:10:43]
And then the camera image, it's going to be 0130. And then the wheels image, it's going to be 0300. So let's also import Next Image, so import. And I'm just going to add some images to the backgrounds of our sections so that we have that in there. So I'm going to go in here and add a new Next Image component.

[00:11:29]
Let's put the source hero image. And then a className. We can do that, position absolute. Inset 0 and then z-4. Let's not forget to add an alt text, so this would be the first runners are over, for example. So if I save, I should see the image here. Cool, let's add an object-contain.

[00:12:07]
So that it actually changed how the image fits. I think cover is going to be better. Yeah, here. OK, so we already recovered our screenshot, our hero, but instead of being an animated sequence image, it's just something that's going to scroll with our page. Let's do the same for the other two sections, so it's going to be the cameras, camera image.

[00:12:53]
And for the final section, wheels image. And we can now see how the different sections we have will have like their own images, but we do have this div that's really huge because we were creating like a really long scroll, so let's go ahead and again only apply this image if we have motion safe, so motion-safe.

[00:13:25]
So again, what this is going to do on Tailwind is it's going to add the media query prefers reduced motion no preference, and only then it's going to apply our class. And since we already are measuring the reduced motion on JavaScript, what we can do is if we only display this component if we don't have reduced motion, so if we have reduced motion, you will not instance this component at all.

[00:14:15]
So we can go ahead and see what happens if we change this. So if I go into no emulation mode, I will have also the images in there. Let's actually hide these images if we are motion safe, so motion-safe. I just don't want these images, so hidden. And now if I am with all of the motion features enabled, I get the image sequence experience, and if I have preferred reduced motion, then I will get the reduced motion version.

[00:15:05]
So that way we can edit our CSS rules depending on the configuration of the users and also disable some effects based on that. OK, so one more thing related to matchMedia. GSAP has its own solution for it, so it's actually called GSAP.matchMedia. So instead of having this useEffect where we manually do a matchMedia and then subscribe to all of the changes, we can just do const matchMedia equal to GSAP.matchMedia.

[00:16:01]
So what this enables us to do is to easily create queries. So we can do matchMedia.add, and we can write our own query in here, so we can do prefers-reduced-motion no preference because we want to only add our animations if we are safe on the motions. And then we're going to have a callback function that we can use, and then in here just we will add our entire timeline.

[00:16:45]
So then we can just remove all of these dependencies. Sorry, one more thing, it's not safe, it's no preference. So if we don't specify this motion, we are going to have all of our animations, and then if we change into reduced motion, it's going to unmount all of our animations.

[00:00:00]
Again, we could use this to do things, for example, like change our animations depending on different media queries for different devices, device sizes or measure if the user has like a mouse or a touch screen.

Learn Straight from the Experts Who Shape the Modern Web

  • 250+
    In-depth Courses
  • Industry Leading Experts
  • 24
    Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now