CSS3 min read
CSS Scroll-Driven Animations
Animate elements based on scroll position — no JavaScript needed
S
Shahar Amir
CSS Scroll-Driven Animations
Scroll animations used to need JavaScript. Now CSS can do it natively — and it's buttery smooth.
The Basics
Link any CSS animation to scroll progress:
css
123456789
@keyframes fade-in { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); }}
.card { animation: fade-in linear; animation-timeline: view();}The card fades in as it enters the viewport!
Two Types of Timelines
1. Scroll Timeline
Progress based on scroll container position:
css
123456789
.progress-bar { animation: grow-width linear; animation-timeline: scroll();}
@keyframes grow-width { from { width: 0%; } to { width: 100%; }}2. View Timeline
Progress based on element visibility in viewport:
css
1234
.reveal { animation: slide-in linear; animation-timeline: view();}Controlling the Range
Define when animation starts and ends:
css
1234567
.card { animation: fade-in linear; animation-timeline: view(); /* Start when element enters, end when fully visible */ animation-range: entry 0% entry 100%;}Range Keywords
css
1234567891011121314
/* When element enters viewport */animation-range: entry;
/* When element exits viewport */animation-range: exit;
/* When element is fully contained */animation-range: contain;
/* When element covers the viewport */animation-range: cover;
/* Custom range */animation-range: entry 25% cover 50%;Parallax Effect
css
123456789
.hero-image { animation: parallax linear; animation-timeline: scroll();}
@keyframes parallax { from { transform: translateY(-20%); } to { transform: translateY(20%); }}Progress Indicator
css
123456789101112131415
.reading-progress { position: fixed; top: 0; left: 0; height: 3px; background: var(--accent); transform-origin: left; animation: scale-x linear; animation-timeline: scroll();}
@keyframes scale-x { from { transform: scaleX(0); } to { transform: scaleX(1); }}Reveal on Scroll
css
12345678910111213141516
.section { animation: reveal linear both; animation-timeline: view(); animation-range: entry 10% cover 30%;}
@keyframes reveal { from { opacity: 0; transform: translateY(50px); } to { opacity: 1; transform: translateY(0); }}Named Scroll Timelines
Animate based on a specific container's scroll:
css
12345678910
.scroll-container { overflow: auto; scroll-timeline-name: --my-timeline; scroll-timeline-axis: block;}
.animated-child { animation: slide linear; animation-timeline: --my-timeline;}The JavaScript Alternative
Before, you needed this:
javascript
12345
// Old way - heavy, jankywindow.addEventListener("scroll", () => { const progress = window.scrollY / maxScroll; element.style.transform = `translateX(${progress * 100}%)`;});Now CSS handles it automatically with 60fps performance.
Browser Support
Chrome, Edge, and Firefox support it. Safari is catching up. Use feature detection:
css
123456
@supports (animation-timeline: view()) { .card { animation: fade-in linear; animation-timeline: view(); }}Why It Matters
- Performance — runs on compositor, not main thread
- No JavaScript — simpler code
- Declarative — CSS describes what, browser handles how
Scroll-driven animations are the future of scroll effects.
#animations#scroll#modern-css#performance
Stay Updated 📬
Get the latest tips and tutorials delivered to your inbox. No spam, unsubscribe anytime.