CSS scroll() Function
The CSS scroll() function creates an anonymous scroll progress timeline that controls a CSS animation based on how far an element or document has been scrolled.
It is primarily used with the animation-timeline property. Instead of advancing an animation according to elapsed time, scroll() connects its progress to a scroll container and a selected scrolling axis.
Syntax
animation-timeline: scroll();
animation-timeline: scroll(scroller);
animation-timeline: scroll(axis);
animation-timeline: scroll(scroller axis);
The scroll() function accepts up to two optional parameters that identify the scroll container and scrolling direction.
For example:
animation-timeline: scroll();
animation-timeline: scroll(nearest);
animation-timeline: scroll(root);
animation-timeline: scroll(self);
animation-timeline: scroll(block);
animation-timeline: scroll(inline);
animation-timeline: scroll(x);
animation-timeline: scroll(y);
animation-timeline: scroll(root block);
animation-timeline: scroll(nearest inline);
animation-timeline: scroll(self x);
The parameters may appear in either order:
animation-timeline: scroll(root block);
animation-timeline: scroll(block root);
Both declarations create a timeline based on scrolling the root element along its block axis.
When no parameters are supplied, the defaults are nearest and block.
The function returns a scroll progress timeline value, not a distance, percentage, or scroll position.
Parameters
The scroll() function supports two optional parameters.
| Parameter | Description | Default |
|---|---|---|
scroller |
Identifies the scroll container that provides the animation timeline. Accepts nearest, root, or self. |
nearest |
axis |
Identifies the scrolling direction. Accepts block, inline, x, or y. |
block |
Both parameters use predefined keywords rather than numeric values.
For example:
animation-timeline: scroll(root y);
This creates a timeline based on vertical scrolling of the document's root scroll container.
Another example:
animation-timeline: scroll(nearest inline);
This creates a timeline based on scrolling along the inline axis of the nearest ancestor scroll container.
Because both parameters are optional, the simplest valid form is scroll().
How It Works
Traditional CSS animations advance according to elapsed time. Scroll-driven animations advance according to scrolling progress.
For example, a normal animation might use:
.box {
animation: grow 2s linear;
}
This animation progresses over two seconds.
To connect the animation to scrolling, add animation-timeline:
.box {
animation: grow linear both;
animation-timeline: scroll();
}
The animation now follows the progress of the nearest ancestor scroll container along its block axis.
For example, when the selected scroll container is halfway through its scrollable range, the animation is approximately halfway through its timeline.
Scrolling backward reverses the animation's progress.
The animation can remain at a particular frame when scrolling stops because its progress is tied to the scroll position rather than a running clock.
A scroll progress timeline does not require JavaScript to update the animation on every scroll event.
The scroll container must have a usable scroll range along the selected axis. Otherwise, the timeline is inactive.
Scroller Values
The first optional parameter identifies which scroll container controls the animation.
| Value | Description |
|---|---|
nearest |
Uses the nearest ancestor scroll container of the animated element. This is the default. |
root |
Uses the document's root scroll container, typically the page viewport. |
self |
Uses the animated element itself as the scroll container. |
nearest
The nearest keyword selects the closest ancestor scroll container.
animation-timeline: scroll(nearest);
This is useful when an animation should respond to scrolling inside a particular panel, list, or other scrollable region.
The nearest scroll container is selected based on its scrolling mechanism. It does not necessarily have a usable scroll range along the requested axis.
root
The root keyword selects the document's root scroll container.
animation-timeline: scroll(root);
This is useful for page-level effects such as reading progress indicators.
Scrolling a nested panel does not advance a timeline connected specifically to the root scroller unless that scrolling also changes the root scroll position.
self
The self keyword uses the animated element as its own scroll container.
animation-timeline: scroll(self);
This is useful when a scrollable element should animate one of its own properties as its contents are scrolled.
The element must itself have a usable scroll range along the selected axis.
Axis Values
The second optional parameter specifies the scrolling axis that controls the timeline.
| Value | Description |
|---|---|
block |
Uses the block axis determined by the element's writing mode. This is the default. |
inline |
Uses the inline axis determined by the element's writing mode. |
x |
Uses the physical horizontal axis. |
y |
Uses the physical vertical axis. |
Block and Inline Axes
In a typical horizontal writing mode, the block axis is vertical and the inline axis is horizontal.
animation-timeline: scroll(block);
animation-timeline: scroll(inline);
In vertical writing modes, these directions may differ.
Physical X and Y Axes
The x and y keywords refer to physical directions regardless of writing mode.
animation-timeline: scroll(x);
animation-timeline: scroll(y);
Use x for horizontal scrolling and y for vertical scrolling.
Use block and inline when the animation should adapt to the document's writing mode.
Understanding Scroll Progress
A scroll progress timeline represents the position of a scroll container within its scrollable range.
For a typical vertical scroll container, the timeline progresses from the starting scroll position to the ending scroll position.
| Scroll Position | Timeline Progress |
|---|---|
| Beginning | 0% |
| One-quarter through | 25% |
| Halfway through | 50% |
| Three-quarters through | 75% |
| End | 100% |
For example, consider a scroll container with 600px of available scrolling distance.
When the user has scrolled 300px through that range, the timeline is approximately 50% complete.
An animation using this timeline displays the corresponding point between its keyframes.
Scrolling back toward the beginning moves the animation toward its starting keyframe.
The function does not directly report the scroll position or calculate a CSS percentage. It creates a timeline that the browser uses to control an animation.
Using animation-timeline
The animation-timeline property connects a CSS animation to a timeline.
A basic scroll-driven animation requires keyframes, an animation declaration, and a scroll progress timeline.
@keyframes fade-in {
from {
opacity: 0.2;
}
to {
opacity: 1;
}
}
.box {
animation: fade-in linear both;
animation-timeline: scroll();
}
The animation's opacity changes as the selected scroll container moves through its scroll range.
Declaration Order
Place animation-timeline after the animation shorthand.
Correct:
.box {
animation: fade-in linear both;
animation-timeline: scroll();
}
The following order can produce an unintended result:
.box {
animation-timeline: scroll();
animation: fade-in linear both;
}
The animation shorthand resets animation-timeline to its initial value, auto.
Using Named Keyframes
The scroll() function does not define the animation's visual changes. Those changes are specified using @keyframes.
@keyframes slide-across {
from {
transform: translateX(0);
}
to {
transform: translateX(100px);
}
}
.box {
animation: slide-across linear both;
animation-timeline: scroll(root block);
}
In this example, the element moves horizontally as the page is scrolled along its block axis.
The timeline determines animation progress, while the keyframes determine the properties being animated.
scroll() vs. view()
Both scroll() and view() create scroll-driven animation timelines, but they track different conditions.
| Feature | scroll() | view() |
|---|---|---|
| Timeline type | Scroll progress | View progress |
| Tracks | How far a scroll container has scrolled | How an element moves through a scrollport |
| Typical use | Reading progress bars | Entry and exit animations |
| Common syntax | scroll(root block) |
view(block) |
| Primary reference | Scroll range | Element visibility within the scrollport |
For example:
.progress-bar {
animation-timeline: scroll(root block);
}
.reveal-item {
animation-timeline: view(block);
}
The first animation follows document scrolling. The second follows the element's movement through its nearest scroll container's visible area.
Use scroll() when the animation should reflect overall scrolling progress. Use view() when the animation should respond to an element entering, moving through, or leaving the viewport or another scrollport.
Example
The following example uses scroll(nearest block) to rotate a square as its container is scrolled vertically.
HTML:
<div class="scroll-demo">
<div class="scroll-demo-sticky">
<div class="scroll-demo-box">Scroll</div>
</div>
<div class="scroll-demo-space">
<p>Scroll down inside this area.</p>
</div>
</div>
CSS:
.scroll-demo {
height: 260px;
overflow-y: auto;
border: 1px solid #ddd;
background-color: #f5f5f5;
}
.scroll-demo-sticky {
position: sticky;
top: 0;
display: flex;
justify-content: center;
padding: 30px;
}
.scroll-demo-box {
display: flex;
align-items: center;
justify-content: center;
width: 90px;
height: 90px;
color: #fff;
background-color: #3498db;
}
.scroll-demo-space {
height: 500px;
padding: 20px;
}
@keyframes scroll-rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@supports (animation-timeline: scroll()) {
.scroll-demo-box {
animation: scroll-rotate linear both;
animation-timeline: scroll(nearest block);
}
}
Interactive example: Scroll inside the box to see the square rotate in supporting browsers.
Scroll down inside this area.
The scroll container provides the timeline, and the square uses that timeline to determine its rotation.
At the beginning of the scroll range, the square is at its starting angle. As the user scrolls, it rotates toward 360 degrees.
Scrolling upward reverses the rotation.
The example includes an @supports check so the animation is applied only when the browser recognizes the scroll timeline syntax.
Document Scroll Example
The scroll(root block) function can connect an animation to the document's scrolling progress.
A common use is a reading progress indicator that expands as the user scrolls toward the bottom of the page.
HTML:
<div class="reading-progress" aria-hidden="true"></div>
CSS:
.reading-progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 5px;
background-color: #3498db;
transform: scaleX(0);
transform-origin: left center;
z-index: 1000;
}
@keyframes reading-progress-fill {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@supports (animation-timeline: scroll()) {
.reading-progress {
animation: reading-progress-fill linear both;
animation-timeline: scroll(root block);
}
}
At the beginning of the document, the indicator has zero visible width.
As the user scrolls through the page, the indicator expands horizontally. At the end of the scroll range, it reaches its full width.
The transform-origin: left center declaration keeps the indicator anchored to the left edge as it expands.
This effect does not require JavaScript scroll event listeners.
The indicator is decorative in this example. If progress information must be available to assistive technologies, provide an accessible text or progress representation as well.
Horizontal Scroll Example
The scroll() function also supports horizontal scroll-driven animations.
The following example moves a square vertically as the user scrolls a container horizontally.
HTML:
<div class="horizontal-scroll">
<div class="horizontal-track">
<div class="horizontal-box">Move</div>
</div>
</div>
CSS:
.horizontal-scroll {
width: 100%;
max-width: 400px;
overflow-x: auto;
overflow-y: hidden;
border: 1px solid #ddd;
background-color: #f5f5f5;
}
.horizontal-track {
position: relative;
width: 800px;
height: 150px;
}
.horizontal-box {
position: sticky;
left: 20px;
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 80px;
color: #fff;
background-color: #3498db;
}
@keyframes horizontal-scroll-move {
from {
transform: translateY(0);
}
to {
transform: translateY(50px);
}
}
@supports (animation-timeline: scroll()) {
.horizontal-box {
animation: horizontal-scroll-move linear both;
animation-timeline: scroll(nearest x);
}
}
Interactive example: Scroll horizontally to move the square downward in supporting browsers.
The x keyword selects the physical horizontal scroll axis.
As the container is scrolled horizontally, the square moves downward according to the keyframes.
The animation's visual movement does not have to match the direction of scrolling. The selected axis controls progress, while the keyframes control the resulting effect.
Animation Behavior
Scroll-driven animations differ from time-based animations in several important ways.
Scroll Position Controls Progress
The animation advances when the selected scroll position changes.
If scrolling stops, the animation remains at the corresponding point in its timeline.
Scrolling Backward Reverses Progress
Moving toward the start of the scroll range moves the animation toward its earlier keyframes.
This behavior makes scroll timelines useful for effects that should track user navigation in either direction.
Animation Duration
A scroll progress timeline is based on scrolling distance rather than elapsed time.
For example:
.box {
animation: fade-in 1s linear both;
animation-timeline: scroll(root block);
}
The animation does not simply finish after one second. Its progress follows the root scroll timeline.
In a scroll-driven animation, the duration participates in mapping the animation onto the timeline rather than making it advance independently with time.
Multiple Animations
Different animations can use different timelines.
.box {
animation: fade-in linear both, slide-across linear both;
animation-timeline: scroll(root block), scroll(nearest inline);
}
Each animation is associated with the timeline in the corresponding position of the comma-separated list.
Reduced Motion
Scroll-linked movement, scaling, and rotation can be uncomfortable for some users. Respect reduced-motion preferences.
@media (prefers-reduced-motion: reduce) {
.scroll-demo-box,
.horizontal-box {
animation: none;
}
}
For essential interfaces, ensure that removing an animation does not hide information or prevent interaction.
Common Uses
The scroll() function is useful for effects that should respond directly to scrolling progress.
- Reading progress indicators: Expand a bar as the user scrolls through a document.
- Scroll-controlled transforms: Rotate, translate, or scale elements according to scroll position.
- Horizontal scrolling effects: Animate content based on horizontal movement through a scroll container.
- Interactive demonstrations: Connect visual changes to scrolling within an example panel.
- Color transitions: Change background or text colors as scrolling progresses.
- Decorative backgrounds: Create scroll-linked visual effects without JavaScript scroll listeners.
- Scroll-based indicators: Show how far a scrollable panel has progressed.
- Layered effects: Coordinate multiple animations with the same scroll progress timeline.
For example, a background color can change as the document scrolls:
@keyframes scroll-color {
from {
background-color: #3498db;
}
to {
background-color: #8e44ad;
}
}
.banner {
animation: scroll-color linear both;
animation-timeline: scroll(root block);
}
The banner's background color changes according to the document's scroll progress.
Use scroll-driven animations for meaningful visual feedback rather than effects that distract from reading or navigation.
Limitations
The scroll() function has several requirements and behaviors to consider.
- The function creates an anonymous scroll progress timeline; it does not scroll an element.
- Both parameters are optional.
- The default scroller is
nearest. - The default axis is
block. - Valid scroller keywords are
nearest,root, andself. - Valid axis keywords are
block,inline,x, andy. - The scroller and axis keywords may appear in either order.
- A timeline is inactive if the selected scroll container has no usable scroll range along the chosen axis.
- The function does not define keyframes or animation properties.
- The
animation-timelinedeclaration should follow theanimationshorthand. - Scroll-driven animations require browser support for the relevant animation timeline features.
- Animations should remain usable when reduced motion is requested or the feature is unsupported.
The following is valid:
animation-timeline: scroll();
The following is also valid:
animation-timeline: scroll(root y);
The following is invalid because left is not a supported axis keyword:
animation-timeline: scroll(root left);
The following is invalid because a numeric scroll distance is not an accepted parameter:
animation-timeline: scroll(200px);
The following is invalid because it supplies two scroller keywords:
animation-timeline: scroll(root nearest);
Feature Detection
Use @supports to apply scroll-driven animation declarations only when the browser recognizes the syntax.
@supports (animation-timeline: scroll()) {
.box {
animation: fade-in linear both;
animation-timeline: scroll();
}
}
A browser that does not support the feature can continue displaying the element without the scroll-driven effect.
Feature detection checks whether the declaration is supported, but it does not guarantee that the selected scroll container has an active timeline.
Browser Support
The CSS scroll() function is classified as Baseline Newly available as of October 2026. It works across the latest major browser versions, but older browsers may not support scroll-driven animation timelines.
Because support is relatively recent, use progressive enhancement and provide a usable appearance when the function is unavailable.
For current compatibility information, visit Can I Use? and search for animation-timeline.
Specifications
The scroll() function is defined in the Scroll-driven Animations Level 1 specification.
The specification defines anonymous scroll progress timelines, scroller selection, scrolling axes, and their use with CSS animations.
Related features include animation-timeline, view(), scroll-timeline, and animation-range.
