CSS Scroll-Driven Animations (now baseline in Chrome 115+ and Edge 115+, with Firefox support landing) let you link an animation’s progress directly to scroll position — either the page scroll (scroll() timeline) or an element’s visibility in the viewport (view() timeline) — entirely in CSS, with no JavaScript needed.
Problem: Scroll-triggered animations in WordPress themes — progress bars, sticky headers, parallax effects — require JavaScript scroll event listeners, IntersectionObserver, and manual timeline calculations that are brittle and performance-intensive.
Solution: Use CSS Scroll-Driven Animations — attach an animation to a scroll timeline with animation-timeline: scroll() for page-level progress or animation-timeline: view() for element visibility. Combine with animation-range to start and end the animation at specific scroll positions relative to the element entering or leaving the viewport.
The examples below show a reading-progress bar driven by page scroll, a fade-in animation triggered as elements enter the viewport, and a parallax effect using animation-timeline: scroll() with named scroll timelines.
/* ── 1. Reading-progress bar (scroll timeline) ────────────────────────── */
@keyframes grow-bar {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
.reading-progress {
position: fixed;
inset-block-start: 0;
inset-inline: 0;
height: 4px;
background: #0073aa;
transform-origin: left;
animation: grow-bar linear;
animation-timeline: scroll(root block); /* root = , block axis */
}
/* ── 2. Fade-in as element enters viewport (view timeline) ──────────────── */
@keyframes fade-up {
from { opacity: 0; translate: 0 2rem; }
to { opacity: 1; translate: 0 0; }
}
.reveal {
animation: fade-up 0.5s ease-out both;
animation-timeline: view();
/* start when element is 20% into view, finish when 60% is visible */
animation-range: entry 20% entry 60%;
}
/* ── 3. Named scroll timeline for parallax ──────────────────────────────── */
.scroll-container {
overflow-y: scroll;
scroll-timeline: --page-scroll block; /* name the timeline */
}
@keyframes parallax-shift {
from { transform: translateY(0); }
to { transform: translateY(-80px); }
}
.hero-background {
animation: parallax-shift linear both;
animation-timeline: --page-scroll;
animation-range: 0% 50%; /* animate over first half of scroll */
}
/* ── 4. Staggered entry animations using view() ─────────────────────────── */
.card-grid > .card {
animation: fade-up 0.4s ease-out both;
animation-timeline: view();
animation-range: entry 0% entry 50%;
}
.card-grid > .card:nth-child(2) { animation-delay: 0.1s; }
.card-grid > .card:nth-child(3) { animation-delay: 0.2s; }
.card-grid > .card:nth-child(4) { animation-delay: 0.3s; }
NOTE: Wrap scroll-driven animations in a @supports (animation-timeline: scroll()) block and provide a non-animated fallback — Firefox shipped support in version 132 (Dec 2024) but users on older versions will see unanimated content if you do not guard against it.