Skip to content

The publication for web craftspeople Sunday, 25 October 2026

Design & UX

CSS Scroll-Driven Animations: scroll-timeline and view-timeline Without JavaScript

Scroll-driven animations, powered by animation-timeline, scroll-timeline and view-timeline, now run natively across all three major engines: Firefox and Safari joined Chrome in 2026. They sync a CSS animation to scroll position without a single scroll event listener in JavaScript.

Scroll-driven animations rest on three CSS properties: animation-timeline, scroll-timeline and view-timeline. Supported in Chrome and Edge since 2023, they finished their cross-browser journey in 2026 with Firefox (version 159) and Safari (version 26.0) joining in, for roughly 87.8% global browser coverage. It’s now possible to sync an animation to scroll position without listening to a single scroll event in JavaScript.

Two timelines, one principle

A scroll-driven timeline replaces time with a 0% to 100% progress value. Two variants exist. The scroll progress timeline (scroll-timeline) measures scroll progress inside a container, from top to bottom. The view progress timeline (view-timeline) instead measures a specific element’s progress through the visible viewport: 0% as it enters, 100% as it exits.

Comparing scroll-timeline and view-timeline

scroll-timeline 0% at the top of the scroll container 100% at the bottom

view-timeline 0% as the element enters the viewport 100% as it exits


Putting scroll-timeline and view-timeline to work

A reading progress bar

The most common case is an article’s reading progress bar, filling up as the page scrolls.

<div class="progress-track">
  <div class="progress-bar"></div>
</div>
:root {
  timeline-scope: --page-scroll;
}

html {
  scroll-timeline: --page-scroll block;
}

.progress-bar {
  animation: fill linear;
  animation-timeline: --page-scroll;
}

@keyframes fill {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

No event listener, no manual percentage math: the rendering engine interpolates the animated properties directly against scroll progress.

Revealing a card as it enters the viewport

For scroll-triggered reveal effects (fade, slide-in), view-timeline targets the element to animate directly, rather than the container that scrolls.

.card {
  view-timeline-name: --card-reveal;
  view-timeline-axis: block;

  animation: reveal linear;
  animation-timeline: --card-reveal;
  animation-range: entry 10% cover 40%;
}

@keyframes reveal {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: translateY(0); }
}

animation-range bounds the range over which the animation plays: here it starts once the card is 10% through its entry into the viewport and finishes at 40% of its full crossing, avoiding an effect that fires too early or wraps up too late.

A scroll-driven timeline listens for no event: progress is computed directly in the rendering engine’s style pipeline, without ever touching the JavaScript main thread.

Compatibility, fallback and debugging

Real cross-browser support, finally, in 2026

BrowserMinimum versionArrival
Chrome115July 2023
Edge115July 2023
Safari26.02026
Firefox1592026

Firefox and Safari support only landed this year: until then, the three properties stayed confined to strictly progressive-enhancement effects, falling back to no animation at all on unsupported browsers.

Debugging a timeline in developer tools

Chrome and Edge surface active scroll-driven timelines directly in the Animations panel of their developer tools: each timeline shows its current progress, viewable by scrolling the page while the panel stays open. It’s the fastest way to confirm an animation-range starts and ends at the right spot, rather than guessing values through trial and error in the stylesheet. Firefox exposes comparable inspection since support landed in version 159.

Fallback for unsupported browsers

For audiences that still carry a share of older browsers, a feature query avoids shipping content that’s frozen by a styling mistake rather than by the absence of animation:

@supports (animation-timeline: works) {
  .card {
    opacity: 0;
  }
}

Without this check, an element whose initial state depends on the animation (here, zero opacity before reveal) would stay invisible on a browser that never plays the animation.

Accessibility, carousels and performance

Respecting the reduced-motion preference

A scroll-tied reveal or parallax animation needs to stay silent for visitors who have reduced motion enabled at the system level. Turning it off outright in that case, rather than slowing it down, avoids any overlap effect where content gets stuck mid-animation.
@media (prefers-reduced-motion: reduce) {
  .card {
    animation: none;
    opacity: 1;
    transform: none;
  }
}

The two mechanisms combine well: a container with scroll-snap-type forces a clean stop on each slide, while a view-timeline set on each carousel item drives its scale or opacity as it crosses the central zone.

.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
}

.carousel > * {
  scroll-snap-align: center;
  view-timeline-name: --slide;
  view-timeline-axis: inline;

  animation: shrink-at-edges linear;
  animation-timeline: --slide;
  animation-range: cover 0% cover 100%;
}

@keyframes shrink-at-edges {
  0%, 100% { transform: scale(0.85); opacity: 0.5; }
  50%      { transform: scale(1);    opacity: 1;   }
}

The result: the centered slide grows slightly and its neighbors shrink, with no script ever recalculating scroll position frame by frame.

Impact on performance

These animations run in the rendering engine’s style pipeline rather than in a requestAnimationFrame loop driven by the main thread. In practice, fast scrolling no longer competes with the page’s own JavaScript for the same CPU resources, cutting the risk of stutter on entry-level devices. The trade-off: only properties that can be composited (transform, opacity in particular) stay smooth without hitches; animating properties that trigger layout (such as width or top) remains costly, scroll-driven timeline or not.

Production use cases

Beyond the progress bar, card reveals and the carousel above, the same mechanism works for shrinking a page header as the user scrolls, or for a light parallax image effect. In every case the animation stays declarative and no longer depends on a dedicated scroll JavaScript library, unlike the approaches still needed for full page transitions as covered in our article on View Transitions.

This mechanism usefully rounds out the techniques already available without JavaScript, from CSS anchor positioning for menus and tooltips to container queries for adaptive components: layout and interaction keep migrating from JavaScript toward declarative mechanisms natively handled by the rendering engine.

Key takeaways

With Firefox 159 and Safari 26 now on board, scroll-driven animations cover the vast majority of the browser market and can move from an experimental curiosity to a production tool. A fallback @supports query and respecting prefers-reduced-motion remain the two precautions that keep a visual enhancement from turning into an accessibility regression.

I replaced a JavaScript parallax library with these three properties on a client project this year, for a clear smoothness gain on mobile and one fewer dependency line in the bundle. My only gripe: the documentation stays scattered between MDN and Chrome’s own blog posts, and each animation-range still takes trial and error to get right. — Simon Janvier

Further reading: the CSS Scroll-driven Animations reference on MDN.

Read next