Scroll-driven animations (animation-timeline: scroll() / view())
Chrome 115, Safari 26.0; Firefox behind `layout.css.scroll-driven-animations.enabled` in stable, enabled in Firefox 158 Nightly (per caniuse). Interop 2026 focus area; expected Baseline late 2026.
Chrome 115
Edge 115
Safari 26
Firefox flag (158 nightly)
Drives a keyframe animation by scroll position instead of time: reading progress bars, reveal-on-scroll, parallax and shrinking headers, all off the main thread and with no scroll listeners.
Scroll to reveal these.
One
Two
Three
CSS
.fx-039-bar {
position: sticky;
top: 0;
z-index: 1;
height: 4px;
background: #2563eb;
width: 100%;
transform-origin: 0 50%;
animation: fx-039-grow linear both;
animation-timeline: scroll(root)
}
@keyframes fx-039-grow {
from {
scale: 0 1
}
to {
scale: 1 1
}
}
.fx-039 {
font-family: system-ui;
display: grid;
gap: 24px;
padding: 40vh 0
}
.fx-039-item {
padding: 24px;
background: #f3f4f6;
border-radius: 12px;
animation: fx-039-reveal linear both;
animation-timeline: view();
animation-range: entry 0% entry 60%
}
@keyframes fx-039-reveal {
from {
opacity: 0;
translate: 0 30px
}
}
@media (prefers-reduced-motion: reduce) {
.fx-039, .fx-039 * { animation: none; transition: none; }
}
HTML
<section class="fx-039">
<div class="fx-039-bar"></div>
<p>Scroll to reveal these.</p>
<div class="fx-039-item">One</div><div class="fx-039-item">Two</div><div class="fx-039-item">Three</div>
</section>Fallback: Unsupported browsers play nothing and show the final state (`both` fill + no timeline = static); wrap the hidden `from` state in `@supports (animation-timeline: scroll())` if the initial state would otherwise flash.
Prompt
Implement scroll effects with scroll-driven animations: `animation-timeline: scroll(root)` for progress indicators, `animation-timeline: view()` plus `animation-range: entry 0% entry 60%` for reveal-on-scroll, always with `animation-fill-mode: both` and a `linear` easing. Wrap the rules in `@supports (animation-timeline: scroll())` so Firefox users (stable still flagged as of 2026-09) see the content statically. No IntersectionObserver, no scroll listeners.