Scroll-triggered Animations
Scroll-triggered animations reveal content as users scroll down the page. CSS now supports scroll-driven animations natively, and the Intersection Observer API provides a lightweight way to trigger CSS animations on scroll.
Scroll Animation Techniques
- Intersection Observer + CSS classes — JS detects when elements enter viewport, CSS handles the animation
- animation-timeline: scroll() — Native CSS scroll-driven animations (newest, growing support)
- Scroll progress — Track scroll position to drive animation progress (e.g., reading progress bar)
- Fade-up on scroll — Elements start invisible and fade up when they enter the viewport
- Staggered reveals — Multiple elements animate in sequence with animation-delay
- Performance — Only animate transform and opacity. Never animate layout properties on scroll
- Respect motion preferences — Check prefers-reduced-motion and disable scroll animations
Scroll Animation Code
/* CSS animation classes */
.reveal {
opacity: 0;
transform: translateY(30px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.reveal.visible {
opacity: 1;
transform: translateY(0);
}
/* Staggered delay */
.reveal:nth-child(2) { transition-delay: 0.1s; }
.reveal:nth-child(3) { transition-delay: 0.2s; }
.reveal:nth-child(4) { transition-delay: 0.3s; }
/* JavaScript: Intersection Observer */
/*
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
*/
/* Native CSS scroll animation (modern browsers) */
@keyframes scrollFadeIn {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: translateY(0); }
}
.scroll-reveal {
animation: scrollFadeIn linear;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}Tip
The CSS-only scroll animation (animation-timeline: view()) is now supported in all modern browsers. For the broadest compatibility, use the Intersection Observer + CSS class pattern — it works in all browsers, requires no polyfills, and is less than 10 lines of JavaScript.
Transitions for state changes, @keyframes for complex multi-step animations
Common Mistake
Triggering scroll animations with a scroll event listener. This fires hundreds of times per second and causes jank. Intersection Observer is purpose-built for this: it runs off the main thread, fires only when elements enter/exit the viewport, and consumes virtually zero CPU when elements aren't moving.
Practice Task
Add scroll-reveal to a page: (1) Give all section headings and cards the class 'reveal' with opacity: 0 + translateY(30px), (2) Add the Intersection Observer script that adds 'visible' when threshold: 0.15, (3) .reveal.visible should transition to opacity: 1 + translateY(0), (4) Wrap in @media (prefers-reduced-motion: no-preference) to respect accessibility.
Quick Quiz
Key Takeaways
- Scroll-triggered animations reveal content as users scroll down the page.
- Intersection Observer + CSS classes — JS detects when elements enter viewport, CSS handles the animation
- animation-timeline: scroll() — Native CSS scroll-driven animations (newest, growing support)
- Scroll progress — Track scroll position to drive animation progress (e.g., reading progress bar)