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.