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

  1. Basic Trigger Demo

    Cards that fade and slide in when they scroll into the viewport. Uses animation-trigger: view() with a @keyframes animation — no JavaScript, no IntersectionObserver. Scroll down to see them activate one by one.

  2. Scroll Position Demo

    Elements animated based on the scroll container's position using animation-trigger with named timeline ranges. Shows progress bars, counters, and reveal effects tied to exact scroll percentages.

  3. Trigger state machine

    Interactive comparison of once, repeat, state and alternate trigger types: scroll a panel and watch the state badge and event log react differently per variant.

  4. Staggered Gallery

    A photo grid using animation-trigger: view() on every card plus animation-delay per 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

references