You possibly can safely use scroll-driven animations in Chrome as of December 2024. Firefox helps them, too, although you’ll must allow a flag. Safari? Not but, however don’t fear — you may nonetheless supply a seamless expertise throughout all browsers with a polyfill. Simply take into account that including a polyfill entails a JavaScript library, so that you received’t get the identical efficiency increase.
There are many worthwhile sources to dive into scroll-driven animations, which I’ll be linking all through the article. My place to begin was Bramus’ video tutorial, which pairs properly with Geoff’s in-depth notes Graham that construct on the tutorial.
On this article, we’ll stroll via the newest revealed model by the W3C and discover the 2 varieties of scroll-driven timelines — scroll progress timelines and view progress timelines. By the top, I hope that you’re accustomed to each timelines, not solely with the ability to inform them aside but in addition feeling assured sufficient to make use of them in your work.
Notice: All demos on this article solely work in Chrome 116 or later on the time of writing.
Scroll Progress Timelines
The scroll progress timeline hyperlinks an animation’s timeline to the scroll place of a scroll container alongside a particular axis. So, the animation is tied on to scrolling. As you scroll ahead, so does the animation. You’ll see me confer with them as scroll-timeline animations along with calling them scroll progress timelines.
Simply as we’ve got two varieties of scroll-driven animations, we’ve got two varieties of scroll-timeline animations: nameless timelines and named timelines.
Nameless scroll-timeline
Let’s begin with a traditional instance: making a scroll progress bar on the high of a weblog submit to trace your studying progress.
See the Pen Scroll Progress Timeline instance – earlier than animation-timeline scroll() [forked] by Mariana Beldi.
On this instance, there’s a <div> with the ID “progress.” On the finish of the CSS file, you’ll see it has a background colour, an outlined width and top, and it’s mounted on the high of the web page. There’s additionally an animation that scales it from 0 to 1 alongside the x-axis — fairly customary in case you’re accustomed to CSS animations!
Right here’s the related a part of the kinds:
#progress {
/* … */
animation: progressBar 1s linear;
}
@keyframes progressBar {
from { remodel: scaleX(0); }
}
The progressBar animation runs as soon as and lasts one second with a linear timing perform. Linking this animation scrolling is only a single line in CSS:
animation-timeline: scroll();
No must specify seconds for the length — the scrolling conduct itself will dictate the timing. And that’s it! You’ve simply created your first scroll-driven animation! Discover how the animation’s path is instantly tied to the scrolling path — scroll down, and the progress indicator grows wider; scroll up, and it turns into narrower.
See the Pen Scroll Progress Timeline instance – animation-timeline scroll() [forked] by Mariana Beldi.
scroll-timeline Property Parameters
In a scroll-timeline animation, the scroll() perform is used contained in the animation-timeline property. It solely takes two parameters: <scroller> and <axis>.
<scroller> refers back to the scroll container, which will be set as nearest (the default), root, or self.
<axis> refers back to the scroll axis, which will be block (the default), inline, x, or y.
Within the studying progress instance above, we didn’t declare any of those as a result of we used the defaults. However we may obtain the identical consequence with:
animation-timeline: scroll(nearest block);
Right here, the closest scroll container is the foundation scroll of the HTML ingredient. So, we may additionally write it this fashion as an alternative:
animation-timeline: scroll(root block);
The block axis confirms that the scroll strikes high to backside in a left-to-right writing mode. If the web page has a large horizontal scroll, and we need to animate alongside that axis, we may use the inline or x values (relying on whether or not we wish the scrolling path to all the time be left-to-right or adapt based mostly on the writing mode).
We’ll dive into self and inline in additional examples later, however the easiest way to be taught is to mess around with all of the combos, and this instrument by Bramus helps you to do precisely that. Spend a couple of minutes earlier than we soar into the subsequent property related to scroll timelines.
The animation-range Property
The animation-range for scroll-timeline defines which a part of the scrollable content material controls the beginning and finish of an animation’s progress based mostly on the scroll place. It means that you can determine when the animation begins or ends whereas scrolling via the container.
By default, the animation-range is about to regular, which is shorthand for the next:
animation-range-start: regular;
animation-range-end: regular;
This interprets to 0% (begin) and 100% (finish) in a scroll-timeline animation:
animation-range: regular regular;
…which is similar as:
animation-range: 0% 100%;
You possibly can declare any CSS size models and even calculations. For instance, let’s say I’ve a footer that’s 500px tall. It’s crammed with banners, advertisements, and associated posts. I don’t need the scroll progress bar to incorporate any of that as a part of the studying progress. What I would like is for the animation to start out on the high and finish 500px earlier than the underside. Right here we go:
animation-range: 0% calc(100% – 500px);
See the Pen Scroll Progress Timeline instance – animation-timeline, animation-range [forked] by Mariana Beldi.
Identical to that, we’ve coated the important thing properties of scroll-timeline animations. Able to take it a step additional?
Named scroll-timeline
Let’s say I need to use the scroll place of a special scroll container for a similar animation. The scroll-timeline-name property means that you can specify which scroll container the scroll animation needs to be linked to. You give it a reputation (a dashed-ident, e.g., –my-scroll-timeline) that maps to the scroll container you need to use. This container will then management the animation’s progress because the consumer scrolls via it.
Subsequent, we have to outline the scroll axis for this new container by utilizing the scroll-timeline-axis, which tells the animation which axis will set off the movement. Right here’s the way it appears to be like within the code:
.my-class {
/* That is my new scroll-container */
scroll-timeline-name: –my-custom-name;
scroll-timeline-axis: inline;
}
Should you omit the axis, then the default block worth can be used. Nevertheless, you can too use the shorthand scroll-timeline property to mix each the identify and axis in a single declaration:
.my-class {
/* Shorthand for scroll-container with axis */
scroll-timeline: –my-custom-name inline;
}
I believe it’s simpler to grasp all this with a sensible instance. Right here’s the identical progress indicator we’ve been working with, however with inline scrolling (i.e., alongside the x-axis):
See the Pen Named Scroll Progress Timeline [forked] by Mariana Beldi.
We’ve got two animations working:
A progress bar grows wider when scrolling in an inline path.
The container’s background colour adjustments the additional you scroll.
The HTML construction appears to be like like the next:
<div class=”gallery-scroll-container”>
<div class=”gallery-progress” position=”progressbar” aria-label=”progress”></div>
<img src=”image1.svg” alt=”Alt textual content” draggable=”false” width=”500″>
<img src=”image2.svg” alt=”Alt textual content” draggable=”false” width=”500″>
<img src=”image3.svg” alt=”Alt textual content” draggable=”false” width=”500″>
</div>
</div>
On this case, the gallery-scroll-container has horizontal scrolling and adjustments its background colour as you scroll. Usually, we may simply use animation-timeline: scroll(self inline) to attain this. Nevertheless, we additionally need the gallery-progress ingredient to make use of the identical scroll for its animation.
The gallery-progress ingredient is the primary inside gallery-scroll-container, and we’ll lose it when scrolling except it’s completely positioned. However once we do that, the ingredient now not occupies area within the regular doc move, and that impacts how the ingredient behaves with its father or mother and siblings. We have to specify which scroll container we wish it to hearken to.
That’s the place naming the scroll container turns out to be useful. By giving gallery-scroll-container a scroll-timeline-name and scroll-timeline-axis, we will guarantee each animations sync to the identical scroll:
.gallery-scroll-container {
/* … */
animation: bg steps(1);
scroll-timeline: –scroller inline;
}
And is utilizing that scrolling to outline its personal animation-timeline:
.gallery-scroll-container {
/* … */
animation: bg steps(1);
scroll-timeline: –scroller inline;
animation-timeline: –scroller;
}
Now we will scale this identify to the progress bar that’s utilizing a special animation however listening to the identical scroll:
.gallery-progress {
/* … */
animation: progressBar linear;
animation-timeline: –scroller;
}
This enables each animations (the rising progress bar and altering background colour) to observe the identical scroll conduct, regardless that they’re separate parts and animations.
The timeline-scope Property
What occurs if we need to animate one thing based mostly on the scroll place of a sibling or perhaps a increased ancestor? That is the place the timeline-scope property comes into play. It permits us to increase the scope of a scroll-timeline past the present ingredient’s subtree. The worth of timeline-scope should be a {custom} identifier, which once more is a dashed-ident.
Let’s illustrate this with a brand new instance. This time, scrolling in a single container runs an animation inside one other container:
See the Pen Scroll Pushed Animations – timeline-scope [forked] by Mariana Beldi.
We are able to play the animation on the picture when scrolling the textual content container as a result of they’re siblings within the HTML construction:
<div class=”main-container”>
<div class=”sardinas-container”>
<img …>
</div>
<div class=”scroll-container”>
<p>Lengthy textual content…</p>
</div>
</div>
Right here, solely the .scroll-container has scrollable content material, so let’s begin by naming this:
.scroll-container {
/* … */
overflow-y: scroll;
scroll-timeline: –containerText;
}
Discover that I haven’t specified the scroll axis, because it defaults to dam (vertical scrolling), and that’s the worth I would like.
Let’s transfer on to the picture contained in the sardinas-container. We wish this picture to animate as we scroll via the scroll-container. I’ve added a scroll-timeline-name to its animation-timeline property:
.sardinas-container img {
/* … */
animation: moveUp steps(6) each;
animation-timeline: –containerText;
}
At this level, nevertheless, the animation nonetheless received’t work as a result of the scroll-container shouldn’t be instantly associated to the pictures. To make this work, we have to prolong the scroll-timeline-name so it turns into reachable. That is carried out by including the timeline-scope to the father or mother ingredient (or a better ancestor) shared by each parts:
.main-container {
/* … */
timeline-scope: –containerText;
}
With this setup, the scroll of the scroll-container will now management the animation of the picture contained in the sardinas-container!
Now that we’ve coated how you can use timeline-scope, we’re prepared to maneuver on to the subsequent kind of scroll-driven animations, the place the identical properties will apply however with slight variations in how they behave.
View Progress Timelines
We simply checked out scroll progress animations. That’s the primary kind of scroll-driven animation of the 2. Subsequent, we’re turning our consideration to view progress animations. There’s numerous similarities between the 2! However they’re completely different sufficient to warrant their very own part for us to discover how they work. You’ll see me refer to those as view-timeline animations along with calling them view progress animations, as they revolve round a view() perform.
The view progress timeline is the second kind of kind of scroll-driven animation that we’re taking a look at. It tracks a component because it enters or exits the scrollport (the seen space of the scrollable content material). This conduct is kind of much like how an IntersectionObserver works in JavaScript however will be carried out solely in CSS.
We’ve got nameless and named view progress timelines, simply as we’ve got nameless and named scroll progress animations. Let’s unpack these.
Nameless View Timeline
Right here’s a easy instance to assist us see the essential thought of nameless view timelines. Discover how the picture fades into view if you scroll all the way down to a sure level on the web page:
See the Pen View Timeline Animation – view() [forked] by Mariana Beldi.
Let’s say we need to animate a picture that fades in because it seems within the scrollport. The picture’s opacity will go from 0 to 1. That is the way you may write that very same animation in traditional CSS utilizing @keyframes:
img {
/* … */
animation: fadeIn 1s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
That’s nice, however we wish the picture to fadeIn when it’s in view. In any other case, the animation is form of like a tree that falls in a forest with nobody there to witness it… did the animation ever occur? We’ll by no means know!
We’ve got a view() perform that makes this a view progress animation with a single line of CSS:
img {
/* … */
animation: fadeIn;
animation-timeline: view();
}
And spot how we now not must declare an animation-duration like we did in traditional CSS. The animation is now not tied by time however by area. The animation is triggered because the picture turns into seen within the scrollport.
View Timeline Parameters
Identical to the scroll-timeline property, the view-timeline property accepts parameters that enable for extra customization:
animation-timeline: view( );
<inset>
Controls when the animation begins and ends relative to the ingredient’s visibility inside the scrollport. It defines the margin between the perimeters of the scrollport and the ingredient being tracked. The default worth is auto, however it could additionally take size percentages in addition to begin and finish values.
<axis>
That is much like the scroll-timeline’s axis parameter. It defines which axis (horizontal or vertical) the animation is tied to. The default is block, which suggests it tracks the vertical motion. You may as well use inline to trace horizontal motion or easy x or y.
Right here’s an instance that makes use of each inset and axis to customise when and the way the animation begins:
img {
animation-timeline: view(20% block);
}
On this case:
The animation begins when the picture is 20% seen within the scrollport.
The animation is triggered by vertical scrolling (block axis).
Parallax Impact
With the view() perform, it’s additionally simple to create parallax results by merely adjusting the animation properties. For instance, you may have a component transfer or scale because it enters the scrollport with none JavaScript:
img {
animation: parallaxMove 1s;
animation-timeline: view();
}
@keyframes parallaxMove {
to { remodel: translateY(-50px); }
}
This makes it extremely easy to create dynamic and fascinating scroll animations with only a few traces of CSS.
See the Pen Parallax impact with CSS Scroll pushed animations – view() [forked] by Mariana Beldi.
The animation-range Property
Utilizing the CSS animation-range property with view timelines defines how a lot of a component’s visibility inside the scrollport controls the beginning and finish factors of the animation’s progress. This can be utilized to fine-tune when the animation begins and ends based mostly on the ingredient’s visibility within the viewport.
Whereas the default worth is regular, in view timelines, it interprets to monitoring the complete visibility of the ingredient from the second it begins getting into the scrollport till it absolutely leaves. That is represented by the next:
animation-range: regular regular;
/* Equal to */
animation-range: cowl 0% cowl 100%;
Or, extra merely:
animation-range: cowl;
There are six potential values or timeline-range-names:
cowl
Tracks the complete visibility of the ingredient, from when it begins getting into the scrollport to when it fully leaves it.
comprise
Tracks when the ingredient is absolutely seen contained in the scrollport, from the second it’s absolutely contained till it now not is.
entry
Tracks the ingredient from the purpose it begins getting into the scrollport till it’s absolutely inside.
exit
Tracks the ingredient from the purpose it begins, leaving the scrollport till it’s absolutely exterior.
entry-crossing
Tracks the ingredient because it crosses the beginning fringe of the scrollport, from begin to full crossing.
exit-crossing
Tracks the ingredient because it crosses the top fringe of the scrollport, from begin to full crossing.
You possibly can combine completely different timeline-range-names to regulate the beginning and finish factors of the animation vary. For instance, you can make the animation begin when the ingredient enters the scrollport and finish when it exits:
animation-range: entry exit;
You may as well mix these values with percentages to outline extra {custom} conduct, akin to beginning the animation midway via the ingredient’s entry and ending it midway via its exit:
animation-range: entry 50% exit 50%;
Exploring all these values and combos is finest carried out interactively. Instruments like Bramus’ view-timeline vary visualizer make it simpler to grasp.
Goal Vary Inside @keyframes
One of many highly effective options of timeline-range-names is their capability for use inside @keyframes:
See the Pen goal vary inside @keyframes – view-timeline, timeline-range-name [forked] by Mariana Beldi.
Two completely different animations are occurring in that demo:
slideIn
When the ingredient enters the scrollport, it scales up and turns into seen.
slideOut
When the ingredient leaves, it scales down and fades out.
@keyframes slideIn {
from {
remodel: scale(.8) translateY(100px);
opacity: 0;
}
to {
remodel: scale(1) translateY(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
remodel: scale(1) translateY(0);
opacity: 1;
}
to {
remodel: scale(.8) translateY(-100px);
opacity: 0
}
}
The brand new factor is that now we will merge these two animations utilizing the entry and exit timeline-range-names, simplifying it into one animation that handles each circumstances:
@keyframes slideInOut {
/* Animation for when the ingredient enters the scrollport */
entry 0% {
remodel: scale(.8) translateY(100px);
opacity: 0;
}
entry 100% {
remodel: scale(1) translateY(0);
opacity: 1;
}
/* Animation for when the ingredient exits the scrollport */
exit 0% {
remodel: scale(1) translateY(0);
opacity: 1;
}
exit 100% {
remodel: scale(.8) translateY(-100px);
opacity: 0;
}
}
entry 0%
Defines the state of the ingredient at first of its entry into the scrollport (scaled down and clear).
entry 100%
Defines the state when the ingredient has absolutely entered the scrollport (absolutely seen and scaled up).
exit 0%
Begins monitoring the ingredient because it begins to depart the scrollport (seen and scaled up).
exit 100%
Defines the state when the ingredient has absolutely left the scrollport (scaled down and clear).
This method permits us to animate the ingredient’s conduct easily because it each enters and leaves the scrollport, all inside a single @keyframes block.
Named view-timeline And timeline-scope
The idea of utilizing view-timeline with named timelines and linking them throughout completely different parts can really broaden the chances for scroll-driven animations. On this case, we’re linking the scroll-driven animation of photos with the animations of unrelated paragraphs within the DOM construction by utilizing a named view-timeline and timeline-scope.
The view-timeline property works equally to the scroll-timeline property. It’s the shorthand for declaring the view-timeline-name and view-timeline-axis properties in a single line. Nevertheless, the distinction from scroll-timeline is that we will hyperlink the animation of a component when the linked parts enter the scrollport. I took the earlier demo and added an animation to the paragraphs so you may see how the opacity of the textual content is animated when scrolling the pictures on the left:
See the Pen View-timeline, timeline-scope [forked] by Mariana Beldi.
This one appears to be like a bit verbose, however I discovered it arduous to provide you with a greater instance to point out the ability of it. Every picture within the vertical scroll container is assigned a named view-timeline with a singular identifier:
.vertical-scroll-container img:nth-of-type(2) { view-timeline: –two; }
.vertical-scroll-container img:nth-of-type(3) { view-timeline: –three; }
.vertical-scroll-container img:nth-of-type(4) { view-timeline: –four; }
This makes the scroll timeline of every picture have its personal {custom} identify, akin to –one for the primary picture, –two for the second, and so forth.
Subsequent, we join the animations of the paragraphs to the named timelines of the pictures. The corresponding paragraph ought to animate when the pictures enter the scrollport:
.vertical-text p:nth-of-type(2) { animation-timeline: –two; }
.vertical-text p:nth-of-type(3) { animation-timeline: –three; }
.vertical-text p:nth-of-type(4) { animation-timeline: –four; }
Nevertheless, because the photos and paragraphs usually are not instantly associated within the DOM, we have to declare a timeline-scope on their frequent ancestor. This ensures that the named timelines (–one, –two, and so forth) will be referenced and shared between the weather:
.porto {
/* … */
timeline-scope: –one, –two, –three, –four;
}
By declaring the timeline-scope with all of the named timelines (–one, —two, –three, –four), each the pictures and the paragraphs can take part in the identical scroll-timeline logic, regardless of being in separate elements of the DOM tree.
Closing Notes
We’ve coated the overwhelming majority of what’s presently outlined within the CSS Scroll-Pushed Animations Module Leve 1 specification at this time in December 2024. However I need to spotlight just a few key takeaways that helped me higher perceive these new guidelines that you could be not get instantly from the spec:
Scroll container necessities
It could appear apparent, however a scroll container is important for scroll-driven animations to work. Points usually come up when parts like textual content or containers are resized or when animations are examined on bigger screens, inflicting the scrollable space to vanish.
Impression of place: absolute
Utilizing absolute positioning can generally intervene with the supposed conduct of scroll-driven animations. The connection between parts and their father or mother parts will get difficult when place: absolute is utilized.
Monitoring a component’s preliminary state
The browser evaluates the ingredient’s state earlier than any transformations (like translate) are utilized. This impacts when animations, significantly view timelines, start. Your animation may set off earlier or later than anticipated because of the preliminary state.
Keep away from hiding overflow
Utilizing overflow: hidden can disrupt the scroll-seeking mechanism in scroll-driven animations. The advisable resolution is to change to overflow: clip. Bramus has an amazing article about this and a video from Kevin Powell additionally means that we might now not want overflow: hidden.
Efficiency
For the very best outcomes, persist with animating GPU-friendly properties like transforms, opacity, and a few filters. These skip the heavy lifting of recalculating format and repainting. Then again, animating issues like width, top, or box-shadow can sluggish issues down since they require re-rendering. Bramus talked about that quickly, extra properties — like background-color, clip-path, width, and top — can be animatable on the compositor, making the efficiency even higher.
Use will-change correctly
Leverage this property to advertise parts to the GPU, however use it sparingly. Overusing will-change can result in extreme reminiscence utilization because the browser reserves sources even when the animations don’t often change.
The order issues
In case you are utilizing the animation shorthand, all the time place the animation-timeline after it.
Progressive enhancement and accessibility
Mix media queries for diminished movement preferences with the @helps rule to make sure animations solely apply when the consumer has no movement restrictions, and the browser helps them.
For instance:
@helps ((animation-timeline: scroll()) and (animation-range: 0% 100%)) {
.my-class {
animation: moveCard linear each;
animation-timeline: view();
}
}
}
My predominant battle whereas making an attempt to construct the demos was extra about CSS itself than the scroll animations. Typically, constructing the format and producing the scroll was tougher than making use of the scroll animation. Additionally, some issues that confused me at first because the spec retains evolving, and a few of these usually are not there anymore (keep in mind, it has been below growth for greater than 5 years now!):
x and y axes
These was referred to as the “horizontal” and “vertical” axes, and whereas Firefox should still help the previous terminology, it has been up to date.
Outdated @scroll-timeline syntax
Prior to now, @scroll-timeline was used to declare scroll timelines, however this has modified in the latest model of the spec.
Scroll-driven vs. scroll-linked animations
Scroll-pushed animations have been initially referred to as scroll-linked animations. Should you come throughout this older time period in articles, double-check whether or not the content material has been up to date to replicate the most recent spec, significantly with options like timeline-scope.
Assets
All demos from this text will be discovered on this assortment, and I would embrace extra as I experiment additional.
A set of demos from CodePen that I discover attention-grabbing (ship me yours, and I’ll embrace it!)
This GitHub repo is the place you may report points or be part of discussions about scroll-driven animations.
Demos, instruments, movies, and (even) extra info from Bramus
Google Chrome video tutorial
Subscribe to MarketingSolution.
Receive web development discounts & web design tutorials.
Now! Lets GROW Together!