Topic 29 of 30
Scroll-Driven Animations
Overview
Scroll-driven animations link CSS animation progress to scroll position without JavaScript. They enable scroll-triggered effects, parallax, and sticky progress bars — all with GPU-accelerated performance.
Syntax
css
/* Reading progress bar */
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.progress-bar {
animation: grow-progress linear;
animation-timeline: scroll(root); /* tied to page scroll */
transform-origin: left;
}
/* Fade in elements as they enter viewport */
@keyframes fade-up {
from { opacity: 0; translate: 0 40px; }
to { opacity: 1; translate: 0 0; }
}
.reveal {
animation: fade-up linear both;
animation-timeline: view(); /* tied to element's visibility */
animation-range: entry 0% entry 40%;
}Common Pitfalls
- Scroll-driven animations are still gaining browser support — always provide a fallback or check with @supports.
- animation-range defines WHEN during the scroll the animation runs — entry means as the element enters the viewport.
- Interview tip: Scroll-driven animations run off the main thread — they're more performant than JavaScript scroll listeners + GSAP for simple effects.
Real-World Example
A scroll-driven reading progress bar and element reveal:
example
css
/* Reading progress bar — zero JavaScript needed */
body { position: relative; }
.reading-progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(to right, #FFD700, #FFA500);
transform-origin: left center;
animation: scaleX linear;
animation-timeline: scroll(root block);
}
@keyframes scaleX {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
/* Staggered card reveal on scroll */
.card {
opacity: 0;
animation: slideUp linear both;
animation-timeline: view();
animation-range: entry 10% entry 50%;
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(60px); }
to { opacity: 1; transform: translateY(0); }
}