Scroll-Driven Animations in Pure CSS

A
Author
14 May, 2026 1 min read 777 views

Scroll effects used to mean scroll event listeners or IntersectionObserver. Scroll-driven animations connect a normal CSS animation to the scroll position, and they run off the main thread, so they stay smooth.

A reading progress bar

.progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 4px;
  background: var(--brand);
  transform-origin: left;
  animation: grow linear;
  animation-timeline: scroll(root);   /* progress = how far the page has scrolled */
}

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

Reveal elements as they enter the viewport

.reveal {
  animation: fade-up linear both;
  animation-timeline: view();             /* progress = element's journey through the viewport */
  animation-range: entry 0% cover 30%;     /* animate from entering until 30% visible */
}

@keyframes fade-up {
  from { opacity: 0; transform: translateY(40px); }
  to   { opacity: 1; transform: none; }
}

Key concepts

  • scroll() - a timeline based on a scroll container's position.
  • view() - a timeline based on an element's visibility inside its scroll container.
  • animation-range - which part of the timeline the animation covers.
  • Use linear easing; the scroll itself provides the pacing.

Progressive enhancement

@supports (animation-timeline: view()) {
  .reveal { animation: fade-up linear both; animation-timeline: view(); }
}

@media (prefers-reduced-motion: reduce) {
  .reveal { animation: none; }
}

Browser support is good in Chromium-based browsers and growing elsewhere, so make sure content is fully visible without the animation.

Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.