How to Use CSS scroll-timeline for Scroll Animations
This article explores the CSS scroll-timeline property,
explaining how it enables modern web developers to create scroll-driven
animations natively in CSS without relying on external JavaScript
libraries. You will learn the mechanics behind the property, its
constituent syntax, and how to bind standard keyframe animations
directly to the scroll offsets of a scrollable container to build
performant, hardware-accelerated user experiences.
What Is the
scroll-timeline Property?
The scroll-timeline property is part of the CSS
Scroll-driven Animations Module. Traditionally, CSS animations are tied
strictly to time using the animation-duration property.
With scroll-timeline, an animation's progression is
decoupled from monotonic clock time and linked directly to the scroll
position of a scroll container (the "scrollport").
As the user scrolls from the beginning of the container to the end,
the animation advances from 0% to 100%. If
scrolling stops, the animation pauses at that exact progress point; if
the user scrolls backward, the animation plays in reverse.
Syntax and Sub-Properties
The scroll-timeline property is a shorthand for two
individual properties:
scroll-timeline-name: Defines a custom identifier (prefixed with--) used to reference the timeline on animated elements.scroll-timeline-axis: Specifies the scroll direction that drives the timeline. Accepted values areblock(default),inline,y, orx.
/* Shorthand syntax */
.scroll-container {
scroll-timeline: --pageTimeline block;
}
/* Longhand equivalent */
.scroll-container {
scroll-timeline-name: --pageTimeline;
scroll-timeline-axis: block;
}How
scroll-timeline Binds to Container Scroll Offsets
To link an element's animation to a container's scroll progress, the browser establishes a link between the scroller, the defined timeline, and the animated element using the following process:
1. Define the Scroll Container
You must establish a scroll context by setting an overflow property
(such as overflow: auto or overflow-y: scroll)
on the parent or root element and assigning it a named
scroll-timeline.
.scroller {
overflow-y: auto;
scroll-timeline: --readingProgress y;
}2. Define Keyframes Independent of Time
Keyframes represent the visual transformation from the scroll start
offset (0%) to the scroll end offset
(100%).
@keyframes stretch {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}3. Attach the Timeline to the Animated Element
On the element you wish to animate, declare the
@keyframes name using animation-name and map
it to the defined timeline using animation-timeline. The
animation-duration property is ignored or omitted.
.progress-bar {
animation-name: stretch;
animation-timeline: --readingProgress;
transform-origin: left;
}Mapping Scroll Offsets to Keyframe Percentages
By default, the scroll timeline starts at the minimum scroll offset
(0px) and ends at the maximum scroll offset
(scrollHeight - clientHeight):
- Scroll Offset:
0px\(\rightarrow\) Animation frame:0% - Scroll Offset: 50% of max scroll \(\rightarrow\) Animation frame:
50% - Scroll Offset: Maximum reachable scroll \(\rightarrow\) Animation frame:
100%
For granular control, CSS also provides the
animation-range property, which lets you define custom
start and end scroll offsets rather than spanning the entire container
scroll track.
The Anonymous
scroll() Alternative
If you do not need to share a timeline between unrelated elements,
CSS allows you to define an inline, anonymous scroll timeline directly
on the animated element using the scroll() functional
notation:
.progress-bar {
animation: stretch auto linear;
animation-timeline: scroll(nearest block);
}The scroll() function automatically searches the DOM
tree for the nearest ancestor with a scrollable overflow, binding the
animation to its scroll offsets without requiring explicit naming on the
parent.
Advantages of Native Scroll Timelines
- Compositor-Thread Execution: Unlike JavaScript
scrollevent listeners orrequestAnimationFrameloops, CSS scroll-driven animations run off the main thread on the browser compositor, preventing frame drops and layout thrashing. - Declarative Code: Replaces complex position calculation scripts with concise, maintainable CSS declarations.
- Responsive by Default: When window dimensions or layout sizes shift, the browser automatically recalculates the container scroll bounds without manual re-initialization.