v146 · CSS · Animations
Scroll Triggered Animations
Chrome 146 adds scroll-position-based triggering of CSS animations via the animation-trigger property and view-timeline. When an element enters a defined scroll range, its animation plays; when it exits, the animation pauses or resets. No JavaScript IntersectionObserver required for the most common "animate on scroll into view" pattern.
concepts
-
Basic Trigger Demo
Cards that fade and slide in when they scroll into the viewport. Uses
animation-trigger: view()with a@keyframesanimation — no JavaScript, noIntersectionObserver. Scroll down to see them activate one by one. -
Scroll Position Demo
Elements animated based on the scroll container's position using
animation-triggerwith named timeline ranges. Shows progress bars, counters, and reveal effects tied to exact scroll percentages. -
Trigger state machine
Interactive comparison of
once,repeat,stateandalternatetrigger types: scroll a panel and watch the state badge and event log react differently per variant. -
Staggered Gallery
A photo grid using
animation-trigger: view()on every card plusanimation-delayper column to create a natural stagger as you scroll. No JavaScript — compare with the always-visible version alongside.
why it shipped
Animating elements as they enter the viewport is one of the most common patterns on the web — product cards that slide in, statistics that count up, sections that fade into view. Before Chrome 146, every implementation required JavaScript: an IntersectionObserver to detect visibility, a class toggle to start the CSS animation, and careful threshold tuning. Chrome 146 exposes animation-trigger, a declarative CSS mechanism built on top of scroll timelines. The browser handles the intersection detection internally — you define when to trigger and what to play; the browser fires the animation at the right moment, even accounting for scroll-jank and back-forward navigation.
the CSS
/* Define the animation */
@keyframes fade-up {
from { opacity: 0; translate: 0 2rem; }
to { opacity: 1; translate: 0 0; }
}
/* Trigger it when the element enters the viewport */
.card {
animation: fade-up 0.5s ease forwards;
animation-trigger: view(); /* fires when element enters scrollport */
}
/* Control entry range — only trigger when fully visible */
.card {
animation-trigger: view(block 20% 80%);
}
/* Named scroll timeline on a container */
.scroller {
scroll-timeline: --my-scroll block;
}
.progress-bar {
animation: grow-width linear forwards;
animation-trigger: --my-scroll;
}
comparison with scroll-driven animations
| Feature | Scroll-Driven (Chrome 115) | Scroll Triggered (Chrome 146) |
|---|---|---|
| Animation progress | Tied 1:1 to scroll position | Runs freely after trigger fires |
| Scrubbing | Yes — reverse on scroll back | Optional — one-shot or repeat |
| Use case | Parallax, progress bars | Entrance effects, reveal animations |
| JavaScript needed | No | No |