How CSS view-timeline Controls Scroll Animations

The CSS view-timeline property allows developers to link CSS animations directly to the visibility and position of a specific element inside a scroll container or viewport. This article explains how view-timeline functions, how it monitors an element's entry and exit across the visible viewing area, and how related properties like animation-timeline and animation-range work together to create performant, scroll-driven visual effects without JavaScript.

Understanding the View Timeline Concept

Traditionally, tying visual changes to an element's position on screen required JavaScript event listeners (scroll events) or the IntersectionObserver API. The CSS Scroll-Driven Animations specification introduces view-timeline, which shifts this workload entirely to the browser's native rendering engine.

A view timeline is a scroll progress timeline where the progress is determined not by the total length of the page, but by the relative intersection of a target element (the "subject") with the scroll container's visible area (the "scrollport"). The timeline progresses from 0% to 100% as the subject moves from entering the viewport to completely exiting it.

The Mechanics of view-timeline

The view-timeline property is a shorthand for two distinct sub-properties:

  1. view-timeline-name: Declares an arbitrary custom identifier (prefixed with --) that represents the timeline.
  2. view-timeline-axis: Specifies the scroll direction to monitor, accepting values like block (default, typically vertical), inline (horizontal), y, or x.

When applied to an element, the browser begins tracking that element's coordinates relative to the scrollport edges:

.card {
  view-timeline: --card-timeline block;
}

Connecting Animations to the Timeline

Once defined, the timeline is connected to an animation using the animation-timeline property:

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

.card {
  view-timeline: --card-timeline block;
  animation-name: reveal;
  animation-fill-mode: both;
  animation-timeline: --card-timeline;
}

Instead of running automatically over a fixed duration (like 2s), the animation's current time is bound directly to the element's position along --card-timeline.

Controlling Visibility Phases with animation-range

By default, a view timeline spans the entire duration of an element's intersection with the scrollport—starting the moment the leading edge touches the viewport (0%) and finishing the moment the trailing edge leaves the viewport (100%).

To control precisely when the animation triggers, CSS provides the animation-range property, which divides the timeline into distinct phases:

  • cover (default): Spans from the moment the element begins to enter the scrollport until it has fully departed.
  • entry: Spans from the moment the element begins entering the scrollport until it is fully inside.
  • exit: Spans from the moment the element begins leaving the scrollport until it has fully cleared it.
  • contain: Spans from the moment the element is fully inside the scrollport until it begins to leave.

For an entrance animation that completes as soon as the element is fully visible, use the entry range:

.card {
  view-timeline: --card-timeline block;
  animation-name: reveal;
  animation-timeline: --card-timeline;
  animation-range: entry 0% entry 100%;
}

Percentage offsets can also be passed directly to these ranges (for example, entry 20% cover 80%), allowing fine-grained control over trigger points.

Performance Advantages

Because view-timeline is handled natively by the browser, animations can be offloaded to the compositor thread. This prevents frame drops and layout thrashing, ensuring smooth animations even when the main JavaScript thread is busy.