Submind YouTube summaries
Thumbnail for Nobody told me CSS could do this

Nobody told me CSS could do this

Watch on YouTube

Video summary

The video demonstrates how to create scroll-triggered animations using only CSS, without relying on JavaScript. The core technique involves setting up each portfolio item or card with two layers: an outer container and an inner article. By defining a custom property named `in-view` on the outer layer and toggling its value between false and true via keyframe animations tied to the scroll position, developers can control when elements enter the viewport. This setup allows for dynamic changes based on visibility, such as applying a red outline or adjusting opacity, simply by querying whether the `in-view` property is set to true using container queries. A significant challenge addressed in the tutorial is preventing animations from firing repeatedly as users scroll up and down, which can be visually annoying. The solution involves moving the animation logic outside the container query and utilizing the `animation-play-state` property to pause or resume animations based on whether the element is in view. Additionally, the video explains how to handle layout issues where elements might appear to slide up into empty space; this is resolved by adjusting the animation range within the scroll-driven timeline. The presenter also introduces a method for adding staggered delays to animations across grid layouts, calculating specific delays based on column counts and gaps to ensure items appear sequentially rather than all at once. To ensure compatibility and accessibility, the tutorial emphasizes the importance of browser support and progressive enhancement. Since style queries have broader support than animation timelines, the code wraps the advanced CSS effects in an `@supports` block that checks specifically for the `animation-timeline: view` feature. This ensures that users on browsers lacking this capability still see static content without errors. Furthermore, the video advocates for including a `prefers-reduced-motion` media query to respect user preferences, disabling animations for those who have set their system settings to reduce motion, thereby providing a smooth experience for everyone while maintaining performance and usability standards.
Read the full video transcript
Hello, my friend and friend. Creating scroll triggered animations like I have on this page is something that seems like you shouldn't be able to do it with CSS yet. They are working on adding that. Uh, but right now I've created these scroll triggered animations that only trigger once. You can scroll away, come back. I think that's the way scroll triggered animation should generally work. And I've done all of this with only CSS. I didn't have to use any JavaScript or anything else. And it isn't that complicated to be able to do it. So, let's dive in and see how. And we can start right here with the setup of it, which is kind of important. Where every portfolio or every item I want to be coming in does need two layers to it. So, in this case I have my portfolio item, and then inside of there I have my article. And then in the CSS on the outer one, so whatever you call it, you want to give that a custom property called in view, and you can set that to false. And we obviously need a way to be able to change the false to true for each one of those. So, we can set up a keyframes animation like we can see right here, where my portfolio items in view are going from false to true, which might make you wonder how we're actually going to make that transition from one to the other. And quite simply we can use a animation to do it since we're using keyframes, where I list the name out. I'm going to put both right there, but also importantly I'm using a animation timeline of view. Now, you might be concerned about the browser support for this, and we will be addressing that, but it's actually not too bad right now. But, I will be talking about browser support for some of the more modern features I am using for this later on in the video. Now, with a setup like this, all we're doing is switching the value of a custom property and nothing else. So, we need to be able to do something when that changes. So, to be able to do that, on the cards themselves, we can then use a container style query. So, here you can see I'm looking for when my style from in view is true, and I can change something. In this case I'm doing an outline just cuz it's very visual. So, if I come in and I scroll down on my page, when they come into view, we can see that the color or not the color, but the outline is switching over to red on each one of them. Once again, you might be wondering about browser support, and again, we'll be talking about that a little bit later on. But, yeah, you can see it's working, but there's a few issues for it. And I think the primary issue is that the entire thing has to be in view, and right now it's not the end of the world, but if we're dealing with an opacity, you'd have this big empty area at the bottom of your page before the card would actually slide up. And we can solve this by going back over to our animations when we're setting those up and adding an animation range, which is something we can do with scroll-driven animations, where now we've changed the entry point, so you can see that yet. And we can see that it's going to turn off and once again turn on as we're going down. And this does present another problem. Right now, these are turning on and off, and as I said at the beginning, this is something that I don't think is a really good thing. Scroll-triggered animations can be super annoying if they're constantly firing as we're scrolling up and down the page, uh like we have right now, where the borders are turning on and off, but it's even worse as different things are moving around. So, the solution here is once again to use an animation, so we can come in with some keyframes, and in those keyframes, what we're actually going to do is move the outline from being something that's on the card to something that is inside the keyframes instead. Then we simply move the animation up into the card, and now we actually have basically the same thing that we had before. You can see there's a slight difference cuz it's actually animating uh the border in a little bit, so it grows in thickness as we're going there, but we're exactly where we left off. But, this opens the door to the fix of the problem that we couldn't view before, which is actually to move the animation outside of the container style query that we have right now, and rely instead on an animation play state of paused or running. So, let's come all the way to the top here, and I'm going to refresh that page. So, those first ones are red. As I scroll down, that gets it. We scroll down, those get it. But, then now as I'm scrolling up, they're not changing back. And the reason that they're not changing back is it runs, it sets it to this 100% and then it gets stuck there because if it's out of view, then it's getting paused and it's paused at this 100% state right here. So, we're now able to run the animation in one direction and have it stick there permanently. But, of course, we don't want red outline-y border things happening. Instead, it would be much better if this was happening with our opacity, some transitions, maybe you'd even want to put a scale on there and I think that's looking pretty good. But, there's something that's missing. I think it looks decent already and this is actually a relatively easy setup. But, things like this always look better if there's a slight stagger to them when they're happening. It just looks a little bit more dynamic, a little bit more interesting. And we can do that. It does get a little bit more complicated, but it's not that bad. So, yeah, we're leaving off where we just were. Again, we have it working now where they're all at the same time. What we want to do next is this depends actually if you have an auto grid setup. I like having these types of things set up using grid template columns with the auto fit or an auto fill. And to be able to do this, we're going to need two things. There's the min call size and the gap. These don't have to be custom properties, but it's just these are two numbers that are going to be quite important. And the second thing that we do need here is our container. You don't have to name the container, but you will have to have a container of inline size on the parent to be able to pull off what we're going to do here. What this opens up the door to is something that looks a little bit strange. I know you're looking at this, right? But, essentially, what we're able to do because we're using the auto fit and we know the widths of our minimum widths of our columns, we're able to select all the items in given columns at any of the different sizes that are there. So, here I'm selecting my second one and I can apply some different styles. So, I can set a delay on those. And then over here, I'm selecting any item in my first column, any item in my second column, or any item in my third column. And we're doing this by getting the minimum column size multiplied by two cuz we have two columns and we have to have the gap in there. Sadly, we can't put custom properties here, which is why I said they don't have to be custom properties on that previous slide. So they are a little bit magic numbery. It's sort of one of the limitations of how we have to deal with this. But here I'm doing three plus two rem times two because we have two gaps with our three columns. And if you'd like more information on this, I have covered this in a previous video in more depth including another use case that I really like for this type of thing. So if you'd like to check it out, it is linked in the description. But for our purposes here, what we're going to do is use it to set some delays and I find the easiest way to do that is to just use a custom property. I'm saying stagger. Maybe a delay would have been a better name here. But we're going to stagger our animation. And so when it's two columns, the second one gets a stagger of one or delay of one, whatever we want to call it. And then over here, I'm setting it for the one, the two and the three. I am setting it for the first column here because I need to overwrite anything that would have got set on that media query. I could also use ranges of media queries instead to prevent having to do this, but I find this a little bit easier. And then with this in place, what we want to do is then apply those so we can go back to our cards where we were setting up the animation in the first place where they were fading in. And all we have to say is that there's going to be an animation delay on them. Right now I'm saying that it's going to be the stagger times one second. So if our stagger value is zero, there's no delay. If it's one or two or three, we're then multiplying it by the duration that I have here. Now I wouldn't normally use one second. That would be quite slow. So you just do this multiplier of whatever you want the delay to be in between each one of those cards. And with all of that in place, we now have a stagger effect working right here as it comes down through all of those. And if we switch over to a larger viewport size, once again, they're locked into place, but we can refresh the page and we can see those ones come in with our stagger, those ones and then the final row right there. Now I did say I was going to mention browser support. So the browser support for both of the features we're using here, which are our scroll driven animations as well as the style queries is actually quite good. Style queries have been around a little bit longer, but we of course want to make sure that people that come on to a page where these aren't supported don't get zero opacity cards, which is what they would have if we set things up the way we have right now. But, all we need to do is wrap everything in an @supports looking for our scroll-driven animation to say all of this animation stuff is only being applied when the browser is supporting our animation timeline of view, and that means the default styles are going to be there for everybody else, and everything will work completely fine. They won't transition or animate in, so it just makes for a very nice progressive enhancement. Now, importantly, I am checking for an animation timeline of view here. I'm not even worrying about style queries because style queries have been supported in all the browser engines longer than animation timeline of view has. So, just by checking for this, we know we're safe across the board. And one last thing, if we're taking the time to set this up, we might as well also use a prefers-reduced-motion media query here. So, if a user hasn't set their preferences to reduced, then everything will work fine. This is if they have no preference, but if they have taken the time to set it to reduced, then again, none of this will kick in, and they'll just have the cards there visible for them the entire time. So, I really hope you enjoyed this video. If you did, you might really enjoy this one that is right here for your viewing pleasure where I look at one of my favorite new CSS tricks. And with that, I want to say a very big thank you to my enabler of awesome, Johnny, as well as all my other patrons and channel members for their monthly support. And of course, until next time, don't forget to make your corner of the internet just a little bit more awesome.