Tuesday, January 21, 2025
HomeWeb developmentScroll And View Progress Timelines — Smashing Journal

Scroll And View Progress Timelines — Smashing Journal


You may 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 possibly can nonetheless supply a seamless expertise throughout all browsers with a polyfill. Simply remember that including a polyfill entails a JavaScript library, so that you gained’t get the identical efficiency enhance.

There are many precious 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 by means of the newest revealed model by the W3C and discover the 2 sorts 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.

Observe: All demos on this article solely work in Chrome 116 or later on the time of writing.

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 have now two sorts of scroll-driven animations, we have now two sorts of scroll-timeline animations: nameless timelines and named timelines.

Nameless scroll-timeline

Let’s begin with a basic instance: making a scroll progress bar on the high of a weblog submit to trace your studying progress.

See the Pen [Scroll Progress Timeline example – before animation-timeline scroll() [forked]](https://codepen.io/smashingmag/pen/RNbRqoj) by Mariana Beldi.

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 shade, an outlined width and peak, and it’s fastened 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 for those who’re accustomed to CSS animations!

Right here’s the related a part of the types:

#progress {
  /* ... */
  animation: progressBar 1s linear;
}


@keyframes progressBar {
  from { rework: 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 period — the scrolling habits 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 straight 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 example – animation-timeline scroll() [forked]](https://codepen.io/smashingmag/pen/ByBzGpO) by Mariana Beldi.

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 may be set as nearest (the default), root, or self.
  • <axis> refers back to the scroll axis, which may 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 might obtain the identical outcome with:

animation-timeline: scroll(nearest block);

Right here, the nearest scroll container is the foundation scroll of the HTML component. So, we might additionally write it this manner 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 wish to animate alongside that axis, we might use the inline or x values (relying on whether or not we wish the scrolling path to at all times 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 mixtures, and this instrument by Bramus helps you to do precisely that. Spend a couple of minutes earlier than we leap 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 lets you resolve when the animation begins or ends whereas scrolling by means of 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 identical as:

animation-range: 0% 100%;

You may declare any CSS size models and even calculations. For instance, let’s say I’ve a footer that’s 500px tall. It’s full of 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 begin 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 example – animation-timeline, animation-range [forked]](https://codepen.io/smashingmag/pen/azoZQym) by Mariana Beldi.

See the Pen Scroll Progress Timeline instance – animation-timeline, animation-range [forked] by Mariana Beldi.

Similar to that, we’ve lined the important thing properties of scroll-timeline animations. Able to take it a step additional?

Named scroll-timeline

Let’s say I wish to use the scroll place of a unique scroll container for a similar animation. The scroll-timeline-name property lets you 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 wish to use. This container will then management the animation’s progress because the person scrolls by means of it.

Subsequent, we have to outline the scroll axis for this new container through the use of the scroll-timeline-axis, which tells the animation which axis will set off the movement. Right here’s the way it seems within the code:

.my-class { 
  /* That is my new scroll-container */
  scroll-timeline-name: --my-custom-name;
  scroll-timeline-axis: inline;
}

In case you omit the axis, then the default block worth can be used. Nevertheless, you may also use the shorthand scroll-timeline property to mix each the title and axis in a single declaration:

.my-class { 
  /* Shorthand for scroll-container with axis */
  scroll-timeline: --my-custom-name inline;
}

I feel it’s simpler to know 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]](https://codepen.io/smashingmag/pen/pvzbQrM) by Mariana Beldi.

See the Pen Named Scroll Progress Timeline [forked] by Mariana Beldi.

Now we have two animations working:

  1. A progress bar grows wider when scrolling in an inline path.
  2. The container’s background shade modifications the additional you scroll.

The HTML construction seems like the next:

<div class="gallery">
  <div class="gallery-scroll-container">
    <div class="gallery-progress" position="progressbar" aria-label="progress"></div>
    <img src="https://smashingmagazine.com/2024/12/introduction-css-scroll-driven-animations/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 modifications its background shade as you scroll. Usually, we might simply use animation-timeline: scroll(self inline) to attain this. Nevertheless, we additionally need the gallery-progress component to make use of the identical scroll for its animation.

The gallery-progress component is the primary inside gallery-scroll-container, and we’ll lose it when scrolling until it’s completely positioned. However after we do that, the component now not occupies house within the regular doc move, and that impacts how the component behaves with its dad or mum and siblings. We have to specify which scroll container we wish it to hearken to.

That’s the place naming the scroll container is 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 title to the progress bar that’s utilizing a unique 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 shade) to comply with the identical scroll habits, regardless that they’re separate components and animations.

The timeline-scope Property

What occurs if we wish to animate one thing based mostly on the scroll place of a sibling or perhaps a greater 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 component’s subtree. The worth of timeline-scope have to 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 Driven Animations – timeline-scope [forked]](https://codepen.io/smashingmag/pen/jENrQGo) by Mariana Beldi.

See the Pen Scroll Pushed Animations – timeline-scope [forked] by Mariana Beldi.

We will 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 block (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 by means of 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, nonetheless, the animation nonetheless gained’t work as a result of the scroll-container will not be straight associated to the photographs. To make this work, we have to prolong the scroll-timeline-name so it turns into reachable. That is performed by including the timeline-scope to the dad or mum component (or the next ancestor) shared by each components:

.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 lined find out how to use timeline-scope, we’re prepared to maneuver on to the subsequent sort 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 sort 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 sort of sort of scroll-driven animation that we’re . It tracks a component because it enters or exits the scrollport (the seen space of the scrollable content material). This habits is kind of just like how an IntersectionObserver works in JavaScript however may be performed totally in CSS.

Now we have nameless and named view progress timelines, simply as we have now 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 whenever you scroll all the way down to a sure level on the web page:

See the Pen [View Timeline Animation – view() [forked]](https://codepen.io/smashingmag/pen/KwPMrQO) by Mariana Beldi.

See the Pen View Timeline Animation – view() [forked] by Mariana Beldi.

Let’s say we wish 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 would possibly write that very same animation in basic 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 kind 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!

Now we have a view() perform that makes this a view progress animation with a single line of CSS:

img {
  /* ... */
  animation: fadeIn;
  animation-timeline: view();
}

And see how we now not must declare an animation-duration like we did in basic CSS. The animation is now not tied by time however by house. The animation is triggered because the picture turns into seen within the scrollport.

View Timeline Parameters

Similar 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 component’s visibility inside the scrollport. It defines the margin between the sides of the scrollport and the component being tracked. The default worth is auto, however it could actually additionally take size percentages in addition to begin and finish values.
  • <axis>
    That is just like the scroll-timeline’s axis parameter. It defines which axis (horizontal or vertical) the animation is tied to. The default is block, which implies it tracks the vertical motion. It’s also possible to 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:

  1. The animation begins when the picture is 20% seen within the scrollport.
  2. The animation is triggered by vertical scrolling (block axis).

Parallax Impact

With the view() perform, it’s additionally straightforward to create parallax results by merely adjusting the animation properties. For instance, you possibly can have a component transfer or scale because it enters the scrollport with none JavaScript:

img {
  animation: parallaxMove 1s;
  animation-timeline: view();
}

@keyframes parallaxMove {
  to { rework: translateY(-50px); }
}

This makes it extremely easy to create dynamic and fascinating scroll animations with just some traces of CSS.

See the Pen [Parallax effect with CSS Scroll driven animations – view() [forked]](https://codepen.io/smashingmag/pen/mybEQLK) by Mariana Beldi.

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 component’s visibility within the viewport.

Whereas the default worth is regular, in view timelines, it interprets to monitoring the total visibility of the component from the second it begins coming into the scrollport till it totally 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:

  1. cowl
    Tracks the total visibility of the component, from when it begins coming into the scrollport to when it fully leaves it.
  2. include
    Tracks when the component is totally seen contained in the scrollport, from the second it’s totally contained till it now not is.
  3. entry
    Tracks the component from the purpose it begins coming into the scrollport till it’s totally inside.
  4. exit
    Tracks the component from the purpose it begins, leaving the scrollport till it’s totally exterior.
  5. entry-crossing
    Tracks the component because it crosses the beginning fringe of the scrollport, from begin to full crossing.
  6. exit-crossing
    Tracks the component because it crosses the top fringe of the scrollport, from begin to full crossing.

You may combine completely different timeline-range-names to regulate the beginning and finish factors of the animation vary. For instance, you may make the animation begin when the component enters the scrollport and finish when it exits:

animation-range: entry exit;

It’s also possible to mix these values with percentages to outline extra {custom} habits, similar to beginning the animation midway by means of the component’s entry and ending it midway by means of its exit:

animation-range: entry 50% exit 50%;

Exploring all these values and mixtures is finest performed interactively. Instruments like Bramus’ view-timeline vary visualizer make it simpler to know.

Goal Vary Inside @keyframes

One of many highly effective options of timeline-range-names is their capacity for use inside @keyframes:

See the Pen [target range inside @keyframes – view-timeline, timeline-range-name [forked]](https://codepen.io/smashingmag/pen/zxOBMaK) by Mariana Beldi.

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:

  1. slideIn
    When the component enters the scrollport, it scales up and turns into seen.
  2. slideOut
    When the component leaves, it scales down and fades out.
@keyframes slideIn {
  from {
    rework: scale(.8) translateY(100px); 
    opacity: 0;
  }
  to { 
    rework: scale(1) translateY(0); 
    opacity: 1;
  }
}

@keyframes slideOut {
  from {
    rework: scale(1) translateY(0); 
    opacity: 1;    
  }
  to { 
    rework: 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 component enters the scrollport */
  entry 0% {
    rework: scale(.8) translateY(100px); 
    opacity: 0;
  }
  entry 100% { 
    rework: scale(1) translateY(0); 
    opacity: 1;
  }
  /* Animation for when the component exits the scrollport */
  exit 0% {
    rework: scale(1) translateY(0); 
    opacity: 1;    
  }
  exit 100% { 
    rework: scale(.8) translateY(-100px); 
    opacity: 0;
  }
}
  • entry 0%
    Defines the state of the component initially of its entry into the scrollport (scaled down and clear).
  • entry 100%
    Defines the state when the component has totally entered the scrollport (totally seen and scaled up).
  • exit 0%
    Begins monitoring the component because it begins to depart the scrollport (seen and scaled up).
  • exit 100%
    Defines the state when the component has totally left the scrollport (scaled down and clear).

This strategy permits us to animate the component’s habits 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 components can actually develop the probabilities for scroll-driven animations. On this case, we’re linking the scroll-driven animation of photographs with the animations of unrelated paragraphs within the DOM construction through the use of 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 components enter the scrollport. I took the earlier demo and added an animation to the paragraphs so you possibly can see how the opacity of the textual content is animated when scrolling the photographs on the left:

See the Pen [View-timeline, timeline-scope [forked]](https://codepen.io/smashingmag/pen/KwPMrBP) by Mariana Beldi.

See the Pen View-timeline, timeline-scope [forked] by Mariana Beldi.

This one seems a bit verbose, however I discovered it arduous to give you 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(1) { view-timeline: --one; }
.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} title, similar 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 photographs. The corresponding paragraph ought to animate when the photographs enter the scrollport:

.vertical-text p:nth-of-type(1) { animation-timeline: --one; }
.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 photographs and paragraphs aren’t straight 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) may 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 photographs and the paragraphs can take part in the identical scroll-timeline logic, regardless of being in separate components of the DOM tree.

Last Notes

We’ve lined the overwhelming majority of what’s at present outlined within the CSS Scroll-Pushed Animations Module Leve 1 specification at present in December 2024. However I wish to spotlight a couple of key takeaways that helped me higher perceive these new guidelines that you could be not get straight from the spec:

  • Scroll container necessities
    It might appear apparent, however a scroll container is important for scroll-driven animations to work. Points usually come up when components 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 intrude with the meant habits of scroll-driven animations. The connection between components and their dad or mum components will get difficult when place: absolute is utilized.
  • Monitoring a component’s preliminary state
    The browser evaluates the component’s state earlier than any transformations (like translate) are utilized. This impacts when animations, notably view timelines, start. Your animation would possibly 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 beneficial resolution is to modify to overflow: clip. Bramus has an ideal article about this and a video from Kevin Powell additionally means that we could now not want overflow: hidden.
  • Efficiency
    For the most effective outcomes, follow animating GPU-friendly properties like transforms, opacity, and a few filters. These skip the heavy lifting of recalculating format and repainting. However, animating issues like width, peak, 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 peak — can be animatable on the compositor, making the efficiency even higher.
  • Use will-change properly
    Leverage this property to advertise components 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, at all times place the animation-timeline after it.
  • Progressive enhancement and accessibility
    Mix media queries for decreased movement preferences with the @helps rule to make sure animations solely apply when the person has no movement restrictions, and the browser helps them.

For instance:

@media display screen and (prefers-reduce-motion: no-preference) {
  @helps ((animation-timeline: scroll()) and (animation-range: 0% 100%)) { 
    .my-class {
      animation: moveCard linear each;    
      animation-timeline: view(); 
    }
  } 
}

My most important wrestle whereas attempting to construct the demos was extra about CSS itself than the scroll animations. Generally, constructing the format and producing the scroll was harder than making use of the scroll animation. Additionally, some issues that confused me initially because the spec retains evolving, and a few of these aren’t there anymore (keep in mind, it has been beneath improvement for greater than 5 years now!):

  • x and y axes
    These was once referred to as the “horizontal” and “vertical” axes, and whereas Firefox should still help the outdated terminology, it has been up to date.
  • Previous @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 had been initially referred to as scroll-linked animations. In case you come throughout this older time period in articles, double-check whether or not the content material has been up to date to mirror the newest spec, notably with options like timeline-scope.

Assets

Smashing Editorial
(gg, yk)
RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments