Scroll-triggered animations link the playback of a CSS animation to the user's scroll position. Instead of running on a timer, the animation scrubs forward or backward as the user scrolls up or down a container, offering a powerful way to create engaging and interactive web experiences. This modern CSS feature uses the animation-timeline
property to bind an animation to the scroll progress of an element.
Example 1: Fade-in on Scroll
HTML
<div class="scroll-container">
<div class="animated-element fade-in"></div>
</div>
CSS
/* Assigns a name to the scroll timeline */
.scroll-container {
scroll-timeline-name: --fade-in-timeline;
scroll-timeline-axis: block; /* Vertical scrolling */
height: 200vh; /* Ensures there is scrollable area */
}
/* Links the animation to the scroll timeline */
.animated-element.fade-in {
animation: fade-in-animation linear;
animation-timeline: --fade-in-timeline;
}
/* Defines the keyframes for the fade-in effect */
@keyframes fade-in-animation {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
Explanation This example demonstrates a simple fade-in effect. As the user scrolls down the .scroll-container
, the .animated-element
gradually becomes visible, with its opacity directly tied to the scroll progress.
Example 2: Progress Bar
HTML
<div id="progress-bar"></div>
<div class="content"></div>
CSS
/* Creates a scroll timeline based on the entire document */
body {
scroll-timeline-name: --page-scroll;
}
/* Styles the progress bar and links it to the page's scroll timeline */
#progress-bar {
position: fixed;
top: 0;
left: 0;
height: 5px;
background-color: blue;
animation: progress linear;
animation-timeline: --page-scroll;
}
/* Defines the keyframes for the progress bar's width */
@keyframes progress {
from { width: 0%; }
to { width: 100%; }
}
.content {
height: 2000px; /* Gives the page scrollable content */
}
Explanation This code creates a reading progress bar at the top of the page. The width of the #progress-bar
expands from 0% to 100% as the user scrolls from the top to the bottom of the document.
Example 3: Rotate on Scroll
HTML
<div class="scroll-container">
<img src="gear.svg" class="animated-element rotate">
</div>
CSS
.scroll-container {
scroll-timeline-name: --rotate-timeline;
height: 300vh;
}
/* Ties the rotation animation to the scroll timeline */
.animated-element.rotate {
width: 100px;
height: 100px;
animation: rotate-animation linear;
animation-timeline: --rotate-timeline;
}
/* Defines the keyframes for a full 360-degree rotation */
@keyframes rotate-animation {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
Explanation In this example, an image will complete a full 360-degree rotation as the user scrolls through the .scroll-container
. The rotation progress is directly synchronized with the vertical scroll position.
Example 4: Scaling Up an Element
HTML
<div class="view-container">
<div class="scale-up-box"></div>
</div>
CSS
/* Defines an animation timeline based on the element's visibility */
.scale-up-box {
animation: scale-up-animation linear forwards; /* 'forwards' keeps the final state */
animation-timeline: view();
animation-range: entry 0% cover 50%; /* Animation runs as the box enters the viewport */
}
/* Keyframes to scale the element from small to large */
@keyframes scale-up-animation {
from {
transform: scale(0.5);
opacity: 0.5;
}
to {
transform: scale(1);
opacity: 1;
}
}
.view-container{
height: 200vh;
padding-top: 100vh; /* Pushes the box down to demonstrate entry */
}
Explanation Here, the .scale-up-box
animates as it enters the viewport. The view()
function creates a timeline based on the element's visibility, and animation-range
specifies that the animation occurs as the element enters and covers the first 50% of the viewport.
Example 5: Horizontal Movement
HTML
<div class="scroll-container">
<div class="animated-element move-horizontal"></div>
</div>
CSS
.scroll-container {
scroll-timeline-name: --move-timeline;
height: 250vh;
}
/* Links the horizontal movement animation to the scroll timeline */
.animated-element.move-horizontal {
animation: move-animation linear;
animation-timeline: --move-timeline;
}
/* Keyframes for moving the element from left to right */
@keyframes move-animation {
from { transform: translateX(-100px); }
to { transform: translateX(100px); }
}
Explanation This demonstrates moving an element horizontally based on vertical scroll progress. As you scroll down, the .move-horizontal
element slides from left to right across the screen.
Example 6: Chaining Multiple Animations
HTML
<div class="scroll-container">
<div class="animated-element multi-step"></div>
</div>
CSS
.scroll-container {
scroll-timeline-name: --multi-step-timeline;
height: 400vh;
}
.multi-step {
animation: multi-step-animation linear;
animation-timeline: --multi-step-timeline;
}
/* Keyframes with multiple steps for a complex animation sequence */
@keyframes multi-step-animation {
0% { opacity: 0; transform: translateY(50px); }
50% { opacity: 1; transform: translateY(0); background-color: blue; }
100% { opacity: 0; transform: translateY(-50px); background-color: red; }
}
Explanation This example shows a multi-step animation where an element fades in, changes color, and then fades out as the user scrolls. The keyframe percentages correspond to the scroll completion within the container.
Example 7: Parallax Effect
HTML
<div class="parallax-container">
<div class="background-layer"></div>
<div class="foreground-text">Scroll Down</div>
</div>
CSS
.parallax-container {
height: 150vh;
position: relative;
overflow: hidden;
scroll-timeline-name: --parallax-timeline;
}
/* The background moves slower, creating a parallax effect */
.background-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background-image: url('https://via.placeholder.com/800x600');
background-size: cover;
animation: parallax-scroll linear;
animation-timeline: --parallax-timeline;
}
/* Keyframes to move the background vertically at a different rate */
@keyframes parallax-scroll {
to {
transform: translateY(50vh);
}
}
Explanation A classic parallax effect is achieved by animating the background's position based on scroll. As the user scrolls, the background layer moves at a slower rate than the scroll speed, creating a sense of depth.