Scroll-Linked Animations specification is a future and experimental addition to CSS. Using @scroll-timeline at rule and animation-timeline This configuration allows you to control the time position of regular CSS animations by scrolling.

In this post, we’ll look at some practical cases where scrolling-related animations are convenient to replace the typical JavaScript approach.

πŸ‘¨β€πŸ”¬ The CSS features described in this message are still experimental and have not been finalized at all. No browser supports writing at the time of writing, unless Chromium is β‰₯ 89 and #experimental-web-platform-features ticket in use.

CSS Scroll-Linked Animations, a quick start

With CSS Scroll-Linked Animations, you can run a CSS animation by scrolling: when you scroll up or down inside a scroll container, the linked CSS animation advances or rewinds. The best part is that this is all going on main thread, from the composer.

You need three basic information to implement a scrolling animation:

  1. CSS animation
  2. scrolling schedule
  3. a link between the two

CSS animation

This is a standard CSS animation, as we already know:

@keyframes adjust-progressbar {
  from {
    transform: scaleX(0);
  to {
    transform: scaleX(1);

As usual, attach it to the element using animation property:

#progressbar {
  animation: 1s linear forwards adjust-progressbar;

Scroll the timeline

In the scrolling timeline, we can map the scrolling distance to the animation progress. In CSS, we describe this with CSS @scroll-timeline at rule.

@scroll-timeline scroll-in-document-timeline {
  source: auto;
  orientation: vertical;
  scroll-offsets: 0%, 100%;

This rule consists of graphs, including:

  1. source describes a scrollable element whose scrolling triggers activation and controls the progress of the timeline. By default, this is the entire document.
  2. orientation specifies the scroll direction that should start the animation. By default, this is vertical.
  3. scroll-offsets the feature is a set of key points that describe the area where the animation should be active. It can be absolute values ​​(e.g., percentages and lengths) or element-based.

An earlier version of the specification required you to set a as well time-range cameraman. This graph has been removed and will be automatically enabled animation-duration linked animation. You may still see traces of it in the demos, but you can skip it safely.

Connects us @scroll-timeline With CSS animation, we use something new animation-timeline CSS property and ask it to reference the timeline name.

#progressbar {
  animation: 1s linear forwards adjust-progressbar;
  animation-timeline: scroll-in-document-timeline; /* πŸ‘ˆ THIS! */

With it adjust-progressbar the animation does not work automatically when the page loads, but it only progresses as we scroll down the page.

You will get a more in-depth introduction @scroll-timeline Look Part 1 and Part 2 my series on the future of scroll-connected animations.

first message examines each graph / feature in more detail and explains them with an example that fits them before discussing many other interesting demos.

another message digs even deeper, looking at element-based transitions that allow us to run an animation when an element appears and disappear from the scroll window as we scroll.

Practical cases

In addition to the above progress, there are a few use cases or scenarios where scrolling-related animations can typically replace a solution implemented using JavaScript.

  1. parallax title
  2. image reveal / hide
  3. typing animation
  4. carousel indicators
  5. scrollspy

A typical use of scroll-switched animations is the parallax effect, where several parts of the page have different scrolling speeds. There is a way to create such effects using only CSS, but it requires bending the mind transform hack including translate-z() and scale().

Inspired Firewatch Header– which uses said transform hack – I created this version that uses the CSS scrolling timeline:

Compared to the original presentation:

  • Entries were kept, except that extra .parallax__cover it is no longer needed.
  • <body> was given a min-height create scroll mode.
  • Positioning .parallax element and its .parallax_layer child elements were changed.
  • transform/perspective-hack was replaced with a scrolling timeline.

Each different layer uses the same scrolling timeline: scroll over 100vh.

@scroll-timeline scroll-for-100vh {
  time-range: 1s;
  scroll-offsets: 0, 100vh;

.parallax__layer {
  animation: 1s parallax linear;
  animation-timeline: scroll-for-100vh;

There is a distance between the layers that they move as we scroll down:

  • The front layer should remain in place, e.g., move 0vh.
  • The last layer should move the fastest, e.g. 100vh.
  • All intermediate layers are interpolated.
@keyframes parallax {
  to {
    transform: translateY(var(--offset));

.parallax__layer__0 {
  --offset: 100vh;

.parallax__layer__1 {
  --offset: 83vh;

.parallax__layer__2 {
  --offset: 67vh;

.parallax__layer__3 {
  --offset: 50vh;

.parallax__layer__4 {
  --offset: 34vh;

.parallax__layer__5 {
  --offset: 17vh;

.parallax__layer__6 {
  --offset: 0vh;

When the front layers move a greater distance, they appear to move faster than the lower layers, achieving a parallax effect.

Image reveal / hide

Another great way to use scrollable animations is to reveal the image: the image slides into view as is.

By default, the opacity of the image is 0 and it is covered with an a clip-path:

#revealing-image {
  opacity: 0;
  clip-path: inset(45% 20% 45% 20%);

In the final mode, we want the image to be fully visible, so we sent the final frame of the animation to reflect it:

@keyframes reveal {
  to {
    clip-path: inset(0% 0% 0% 0%);
    opacity: 1;

Using element-based transitions as our scrolling time shifts, we can get it so that the image only starts to appear when the image itself slides into view.

@scroll-timeline revealing-image-timeline {
    selector(#revealing-image) end 0.5,
    selector(#revealing-image) end 1

#revealing-image {
  animation: reveal 1s linear forwards;
  animation-timeline: revealing-image-timeline;

😡 Can’t you keep up with these element-based transfers? This visualization / tool has got you covered.

Writing an animation

Because CSS browsing schedules can be linked to any existing CSS animation, you can basically take a demo of any CSS animation and change it. Take, for example, this writing animation:

Adding a scrolling timeline and animation-timeline feature, it can be set as a type for scrolling:

Note that to create some scrolling property <body>also received height 300vh.

Using different animations, the code above can be easily adjusted to create a zoom scrolling effect:

I see these two working great in article intros.

One of the components of the carousel (aka slider) is an indicator that reveals how many slides it contains and which slide is currently active. This is typically done with bullets.

Again, this is something we can achieve by using the CSS scrolling timeline, as shown in this presentation by Fabrizio Calderan:

The active state bullet is injected .slider nav::before and it has a series of animations that move it over other bullets

/* Styling of the dots */
.slider nav::before, .slider a {
  inline-size: 1rem;
  aspect-ratio: 1;
  border-radius: 50%;
  background: #9bc;

/* Positioning of the active dot */
.slider nav::before {
  content: "";
  position: absolute;
  z-index: 1;
  display: block;
  cursor: not-allowed;
  transform: translateX(0);
  animation: dot 1s steps(1, end) 0s forwards;

/* Position over time of the active dot */
@keyframes dot {
    { transform: translateX(0); }
    { transform: translateX(calc((100% + var(--gap)) * 1)); }
    { transform: translateX(calc((100% + var(--gap)) * 2)); } 
    { transform: translateX(calc((100% + var(--gap)) * 3)); }

By attaching a @scroll-timeline for the slider, a point indicating the active state can move as you scroll:

@scroll-timeline slide {
  source: selector(#s);
  orientation: inline; 

.slider nav::before {
  /* etc. */
  animation-timeline: slide;

The dot does not move until the slide has clicked into place a steps() function in animation. When you delete it, it becomes clearer how the point moves as you scroll

Feels This feels like the last missing song Christian Shaefer’s only CSS carousel.


Already in the beginning of 2020 I created a sticky table of contents with a scrolling active state. The last part in creating the demo was to use IntersectionObserver to set the table of contents (TOC) active states as you scroll up / down the document.

Unlike the presentation of carousel indicators from above, we cannot get there simply by moving one point around because the texts of the technical content are regulated. In order to approach this situation, we need to attach two animations to each element of content production:

  1. The first animation is to activate the license object visually when the right part appears at the bottom of the document.
  2. Another animation is to visually disable the overlay feature when the right part slides out of sight at the top of the document.
.section-nav li > a {
    1s activate-on-enter linear forwards,
    1s deactivate-on-leave linear forwards;

Since we have two animations, we also need to create two scrolling schedules, and this for each piece of content. Take it #introduction for example:

@scroll-timeline section-introduction-enter {
  source: auto;
    selector(#introduction) end 0,
    selector(#introduction) end 1;

@scroll-timeline section-introduction-leave {
  source: auto;
    selector(#introduction) start 1,
    selector(#introduction) start 0;

When both schedules are linked to both animations, everything works as expected:

.section-nav li > a[href"#introduction"] {


I hope I have convinced you of the possibilities offered by the CSS Scroll-linked Animations specification. Unfortunately, it is currently only supported in Chromium-based browsers, hidden behind the flag.

Because of this potential, I personally hope that – when the specification is satisfied with a particular syntax – other browser vendors will follow suit. If you also want the scrolled Linked Animations to land in other browsers, you can star / follow the relevant browser issues.

By actively emphasizing things, we as developers can show our interest in these features to browser vendors.


Please enter your comment!
Please enter your name here