Scroll-linked animations without JavaScript | Trying out CSS Scroll-driven Animations|Yamada Frontend Lab
A progress bar that advances according to the scroll position.
Elements that change as they enter the screen.
For such UI, implementations that monitor scroll amount or intersections using JavaScript have been standard.
With CSS Scroll-driven Animations, you can replace the time axis of CSS animations themselves with scrolling.
Conclusion
.progress { transform-origin: left; }
@supports (animation-timeline: scroll(root)) {
.progress {
animation: grow 1ms linear both;
animation-timeline: scroll(root);
}
}
@keyframes grow {
from { scale: 0 1; }
to { scale: 1 1; }
}
With this, you can link animation progress to scroll amount.
However, at this moment (as of October 2026), animation-timeline is outside of Baseline. It has limited availability.
In other words, now is the stage for using it in sites where you can be aggressive or for effects that can be fallen back, rather than for full-scale adoption.
Two ways of thinking
scroll progress
Uses the progress from the start to the end of the specified scroll container. This applies not only to the entire page but also to horizontal scroll areas of cards, etc.
animation-timeline: scroll();
scroll() without arguments uses the nearest ancestor scroll container and the block axis. The first progress bar example explicitly specifies the root with scroll(root). If there is no scrollable range on the intended axis, the timeline will not advance, so check nested overflows and writing modes as well.
view progress
Uses the progress of the target element passing through the display area (scrollport) of the nearest ancestor scroll container. It is not a mechanism that only detects whether an element has entered once.
animation-timeline: view();
In the default range, the position where the element begins to enter the display area is 0%, and the position where it finishes exiting the opposite side is 100%. It is not “100% when the whole thing is visible.” If you scroll back, the progress also returns.
You should distinguish which progress to use between page reading progress and decorations that match the passage of elements.
Why is it interesting?
It reduces the responsibility of reading the scroll position, calculating values, and reflecting them in the style using JavaScript every time.
Another strength is that it can be combined with the CSS animation mechanism.
However, do not assume that “CSS is always faster” without careful consideration; verify with the actual animation target properties and on the target devices.
Fallback
First, create a state where the content can be read even without the effects.
.card {
opacity: 1;
}
@supports (animation-timeline: view()) {
.card {
animation: reveal 1ms linear both;
animation-timeline: view();
}
}@keyframes reveal {
from { opacity: 0; transform: translateY(1rem); }
to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
.progress, .card { animation: none; }
.card { opacity: 1; transform: none; }
}
Since the animation shorthand resets the timeline, write animation-timeline after it. Although the duration is specified here, the progress is determined by the scroll range, not the elapsed time.
The view() example above is a basic form that binds keyframes to the default passage range. If you only set opacity to 1 in ‘to’, it will not be fully visible until the element has finished exiting. If you want to complete the display while it is entering, advance the completion position using animation-range or similar, and verify the correspondence of that declaration with the actual display separately. It is easier to handle by trying it out with decorative images first, rather than making the readability of the main text dependent on the completion of the effect.
@supports checks whether the written declarations are accepted. It does not guarantee support for all related features or the actual behavior on the target site.
Display normally if unsupported.
Works only in supported environments.
This format is easy to introduce.
When a JavaScript fallback is necessary
If the scroll position is required for functional logic rather than just “effects,” IntersectionObserver or the Web Animations API are also candidates.
Do not make relying solely on CSS the goal itself.
reduced motion
Since scroll-linked animations tend to have significant visual movement, prefers-reduced-motion is important.
Easy to adopt
Caution
-
UI with high public accessibility
-
Animation is essential for understanding information
-
High ratio of older devices
-
Broad WebView support
Summary
Scroll-driven Animations are quite interesting.
However, they are currently outside the Baseline.
First, it is easiest to try them out on a small scale in places where content and operation are functional even without the effects. Determine the scope of adoption by checking target browsers, settings to reduce motion, and readability at narrow widths or when zoomed in.
References
MDN — CSS scroll-driven animations
https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Scroll-driven_animations
MDN — animation-timeline
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/animation-timeline
MDN — Scroll-driven animation timelines
https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Scroll-driven_animations/Timelines
Chrome for Developers — Animate elements on scroll
https://developer.chrome.com/docs/css-ui/scroll-driven-animations
CSSWG — Scroll-driven Animations Level 1
https://drafts.csswg.org/scroll-animations-1/