animation-timeline: scroll() / view()
Scroll-Driven Animations
Instead of an animation's progress being driven by time, animation-timeline lets it be driven by scroll position: how far a container has scrolled, or how far an element has travelled through the scrollport. It replaces a whole category of scroll-listener-plus-IntersectionObserver JavaScript with three lines of CSS.
Scroll inside the box. Every animation below is driven by scroll position, no JS.
Saturn
Its rings are made of ice and rock, some pieces the size of a house.
Halley’s Comet
Returns to the inner solar system roughly every 76 years.
The Moon
Drifting away from Earth at about 3.8cm a year.
The ISS
Orbits Earth every 90 minutes at roughly 28,000km/h.
Betelgeuse
A red supergiant big enough to swallow Jupiter’s orbit.
Black holes
Warp spacetime so much that light can’t escape.
The CSS
.progress-bar {
transform-origin: left;
animation: grow-progress linear;
animation-timeline: scroll(nearest);
animation-duration: auto;
}
.card {
animation: reveal linear both;
animation-timeline: view(block 20%);
animation-range: entry 0% cover 30%;
}How it works
animation-timeline: scroll(nearest) maps 0%–100% of an animation to the scroll offset of the nearest scrollable ancestor. That drives the progress bar and the spinning globe in this demo, both of which just keep tracking wherever the box has been scrolled to.
animation-timeline: view() instead maps progress to how far an element has travelled through its scroll container’s viewport, refined by animation-range (here, entry 0% cover 30%, so each card finishes fading in shortly after it starts appearing). Both replace scroll listeners entirely; the compositor drives the animation, so it stays smooth even on a janky main thread.
Chrome / Edge 115+. Behind a flag in Firefox; not yet in Safari.