CSS Scroll-driven Animations without JavaScript

Post on X
Copy URL
Share

Scroll-driven Animations use the amount scrolled to control an animation’s progress. They make it possible to create scrub animations that stay fully synchronized with scrolling, as well as sticky effects, using CSS alone.

CSS scroll animations generally fall into the following two types:

  • Scroll-driven animations: Progress is linked to the amount scrolled.
  • Scroll-triggered animations: A scroll position triggers a time-based animation.

This article focuses on the first type. The following demos show examples of what you can create with CSS alone, without JavaScript.

Examples of scroll-driven animations

Benefits and considerations of scroll animations

Scroll animations offer benefits such as the following:

  • Guiding attention through the content
  • Creating a more immersive website experience

Effective animation can contribute to a better user experience. However, some users are uncomfortable with motion or prone to motion sickness.

Consider respecting reduced-motion preferences for users who find scroll animations uncomfortable. The article “CSSでもアクセシビリティに配慮しよう! モーション軽減・文字サイズ変更・ダークモードの実装方法” (Japanese) explains how to implement this.

Scroll-driven animations

Scroll-driven animations use one of the following timelines, depending on the scroll range linked to the animation:

  • Scroll Progress Timeline: Represents the overall scroll progress of a scroll container. Specify it with the scroll() function.
  • View Progress Timeline: Represents the progress of a target element as it passes through the scrollport. Specify it with the view() function.

Scroll Progress Timeline

A Scroll Progress Timeline treats scrolling from the beginning to the end of a scroll container as progress from 0% to 100%.

The following example creates a scrub animation in which an element scales up vertically as the page scrolls.

Left: the demo described below; right: a variation

Demo of a scrub animation linked to page scroll progress

1. Preparation: Add the element to animate (div.animationElement), then give the body element enough height to make the page scroll.

<div class="animationElement"></div>
body {
  height: 200vh; /* Add enough height to make the page scroll */
}

2. Define the animation: Create the animation with an @keyframes rule and apply it to the element.

/* Keyframes that change the vertical scale */
@keyframes grow-progress {
  from {
    scale: 1 0;
  }
  to {
    scale: 1 1;
  }
}

3. Set the timeline: Specify the scroll() function for the animation-timeline property.

.animationElement {
  /* Styles */
  position: fixed;
  width: 100px;
  height: 100px;
  background: royalblue;
  transform-origin: bottom; /* Use the bottom edge as the transform origin */

  /* Scroll animation settings */
  animation: grow-progress linear; /* Apply the animation with linear easing */
  animation-timeline: scroll();
}

The scrub animation is now complete with just a few lines of CSS.

View Progress Timeline

A View Progress Timeline treats the range in which a target element passes through the scrollport as progress from 0% to 100%.

Next, the following example creates a scrub animation in which a mask changes as the animated element enters the viewport while the page scrolls.

Demo of a mask animation linked to page scrolling

1. Preparation: Add the element to animate (div.animationElement), with elements (div.skeleton) before and after it that provide enough height to make the page scroll.

<div class="skeleton">This area provides enough space to make the page scroll</div>
<img class="animationElement" src="./images/sapphire.webp" alt="Sapphire" width="300" height="300">
<div class="skeleton">This area provides enough space to make the page scroll</div>
.skeleton {
  height: 100vh; /* Add enough height to make the page scroll */
}

2. Define the animation: Create the animation with an @keyframes rule and apply it to the element.

This example uses a mask animation.

/* Change the mask from a circle through a rounded rectangle to a square */
@keyframes reveal-image {
  from {
    clip-path: inset(30% round 20%);
  }
  to {
    clip-path: inset(0);
  }
}

3. Set the timeline: Specify the view() function for the animation-timeline property.

4. Set the animation range: Configure the animation-range property.

The animation starts when the image is fully inside the viewport (contain 0%) and ends when the image reaches the middle of the viewport (contain 50%).

.animationElement {
  /* Scroll animation settings */
  animation: reveal-image linear both; /* Linear easing with animation-fill-mode: both */
  animation-timeline: view();
  animation-range: contain 0% contain 50%; /* Start: fully inside the viewport; end: at the middle of the viewport */
}

The animation-range property accepts many possible values, which can make the options difficult to understand. The View Progress Timeline: Ranges and Animation Progress Visualizer can help. It lets you adjust the values and see the ranges in a clear visualization.

Example with animation-range: contain 0% contain 50%;

View Progress Timeline: Ranges and Animation Progress Visualizer

Example: Combining with sticky positioning

This example uses the two timelines introduced above and combines sticky effects with parallax. It incorporates part of a story, with decorative elements and animations designed to create a sense of immersion.

Scroll-driven animation demo page

Example of a scroll-driven animation

Every animation in the example uses either a Scroll Progress Timeline (scroll()) or a View Progress Timeline (view()) to control its progress. See the source code for details.

Browser support

The animation-timeline property used for scroll-driven animations is supported in Chrome and Edge 115 or later (July 2023), Safari 26.0 or later (September 2025), and Firefox 159 or later (October 2026).

Reference: Can I use…

Conclusion

This article introduced scroll-driven animations that can be created with CSS alone. You may now find it easier to implement the animations you have in mind. As implementation becomes easier, deciding what kind of animation to create may become increasingly important.

For the other type, scroll-triggered animations, see the follow-up article “CSS scroll-triggered animations without JavaScript”.

Find ICS MEDIA articles more easily on Google

Add ICS MEDIA as a preferred source to see our articles more often in Top Stories and AI Search.

Add as a preferred source on Google
Share on social media
Your shares help us keep the site running.
Post on X
Copy URL
Share
New articleCSS scroll-triggered animations without JavaScript