CSS view-timeline-name: Broadcast Scroll Progress
The CSS view-timeline-name property is a core feature of
the CSS Scroll-driven Animations specification that establishes a named
view progress timeline linked to a specific element's position within a
scroll container. By assigning a custom identifier (a dashed-ident) to
an element, developers can track its intersection with the scrollport
and broadcast that progress to other elements across the document. This
allows sibling, parent, or completely separate elements to trigger and
synchronize their own CSS animations based on the scroll position of the
tracked element.
What is view-timeline-name?
The view-timeline-name property converts a DOM element
(known as the subject) into a progress tracker. Unlike a
standard scroll timeline that tracks the overall scrollbar progress of a
container from 0% to 100%, a view timeline tracks the relative progress
of a specific subject entering, crossing, and exiting its nearest
scrollable ancestor.
The property accepts a custom dashed identifier:
.subject-element {
view-timeline-name: --subject-timeline;
view-timeline-axis: block; /* or inline */
}You can also use the shorthand property
view-timeline:
.subject-element {
view-timeline: --subject-timeline block;
}How Progress is Broadcasted to Other Elements
By default, a named timeline is scoped to the element that defines it
and its descendants. However, CSS provides the
timeline-scope property to elevate the visibility of a
timeline up the DOM tree, enabling arbitrary elements to read the
subject's scroll state.
To broadcast scroll progress from a subject to an external target element:
- Define the Timeline on the Subject: Use
view-timeline-nameon the element whose visibility in the viewport drives the animation. - Elevate Scope on a Common Ancestor: Apply
timeline-scopeto a parent container that encapsulates both the subject and the target element that will consume the timeline. - Consume the Timeline on the Target: Set
animation-timelineon the target element using the declared dashed-ident.
Code Example: Driving a Target Element from a Subject
Consider a scenario where a reading progress bar or a floating indicator updates its position based on when a specific section of an article enters the viewport.
<div class="page-container">
<div class="progress-indicator"></div>
<main>
<section class="content-block">Regular content</section>
<section class="target-section">Trigger Section</section>
<section class="content-block">More content</section>
</main>
</div>/* Step 1: Elevate timeline scope on the shared ancestor */
.page-container {
timeline-scope: --section-timeline;
}
/* Step 2: Establish the named view timeline on the subject */
.target-section {
view-timeline-name: --section-timeline;
view-timeline-axis: block;
}
/* Step 3: Attach the timeline to the independent element */
.progress-indicator {
position: fixed;
top: 0;
left: 0;
height: 8px;
width: 100%;
background-color: #0070f3;
transform-origin: left;
animation-name: scale-bar;
animation-timing-function: linear;
animation-timeline: --section-timeline;
animation-range: entry 0% exit 100%;
}
@keyframes scale-bar {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}Controlling the Animation Range
When an element consumes a broadcasted view timeline, its animation
progress is mapped by default to the entire journey of the subject
passing through the viewport (cover 0% to
cover 100%).
You can fine-tune when the receiving animation starts and stops using
the animation-range property:
entry 0% entry 100%: Animates only while the subject is entering the scrollport.exit 0% exit 100%: Animates only while the subject is exiting the scrollport.contain 0% contain 100%: Animates while the subject is fully contained inside the scrollport.
Through view-timeline-name and
timeline-scope, CSS allows decoupled, performant,
hardware-accelerated animations across disparate interface elements
without the need for JavaScript scroll listeners or
IntersectionObserver.