CSS view-timeline Property
The view-timeline property is a shorthand that defines a named scroll progress timeline based on an element's visibility within its scroll container.
| none | Default value indicating the element does not define a named view progress timeline. |
| <custom-ident> | A developer-defined name prefixed with two dashes used to identify the timeline. |
| block | The scroll progress is measured along the block axis of the scroll container. |
| inline | The scroll progress is measured along the inline axis of the scroll container. |
| y | The scroll progress is measured along the vertical axis. |
| x | The scroll progress is measured along the horizontal axis. |
Code Examples
A basic example showing an element that fades and scales as it enters the viewport during a scroll.
<style>
.spacer { height: 100vh; }
.reveal-box {
width: 200px; height: 200px;
background-color: #3498db;
margin: 50px auto;
view-timeline: --box-reveal block;
animation: fade-and-scale linear both;
animation-timeline: --box-reveal;
}
@keyframes fade-and-scale {
from { opacity: 0; transform: scale(0.5); }
to { opacity: 1; transform: scale(1); }
}
</style>
<div class="spacer"></div>
<div class="reveal-box"></div>
<div class="spacer"></div>An advanced implementation using a sticky indicator that grows as the user scrolls, featuring a JavaScript support check.
<style>
#scroll-container {
height: 400px; overflow-y: scroll;
background-color: #eeeeee;
border: 2px solid #333333;
}
.content-block {
height: 800px; padding: 20px;
}
.indicator {
height: 10px; background-color: #e74c3c;
width: 0%; position: sticky; top: 0;
view-timeline: --scroll-path block;
animation: progress-grow linear both;
animation-timeline: --scroll-path;
}
@keyframes progress-grow {
from { width: 0%; }
to { width: 100%; }
}
</style>
<div id="scroll-container">
<div class="indicator" id="myIndicator"></div>
<div class="content-block">
<h2>Scroll down to see the progress bar grow</h2>
</div>
</div>
<script>
const indicator = document.getElementById("myIndicator");
// Check if view-timeline is supported, otherwise fallback
if (!CSS.supports("view-timeline", "--test block")) {
console.log("View Timeline not supported. Using JS fallback logic.");
indicator.style.backgroundColor = "#cccccc";
}
</script>Pro Tip
While you can use the view() function for quick one-off animations, using view-timeline is much better for complex scenes where multiple different elements need to react to the scroll progress of one specific target element.
Deep Dive
Think of view-timeline like a stopwatch that only ticks when an element is inside the viewer's window. Standard animations use time as a ruler, but view-timeline uses the element's position relative to the scrollport. It tracks an element's journey as it enters, crosses, and exits the visible area. You assign a custom name to this journey using a double-dash prefix, like --my-timeline. Once named, other properties like animation-timeline hook into this specific progress. It transforms the scroll position into a percentage from 0% (entry) to 100% (exit), giving you precise control over scroll-triggered visual effects without writing heavy JavaScript scroll listeners. This property combines view-timeline-name and view-timeline-axis into a single declaration.
Best Practices
Use clear, semantic names for your timelines to keep your CSS maintainable in large projects. Always define the axis explicitly if you are working with horizontal layouts to avoid the default block behavior. Pair this property with view-timeline-inset when you need the animation to start or end at a specific margin inside the scrollport rather than the exact edges.
Common Pitfalls
The most common mistake is forgetting the double-dash prefix for the timeline name. Also, if the parent container is not scrollable or the element is not positioned within a scrolling context, the timeline will have no progress and your animation will stay at the first frame. Note that display: none will destroy the timeline context entirely.
Accessibility
Scroll-linked animations can cause motion sickness for some users. Always wrap your scroll animation styles inside a @media (prefers-reduced-motion: no-preference) media query. This ensures that you only serve the motion-heavy experience to users who haven't expressed a preference for reduced movement.
Dev Data Table: view-timeline property
| default | none auto |
| animatable | no |
| inherited | no |
| experimental | yes |
| year_intro | 2022 |
| year_standard | 0 |
| js_syntax_1 | element.style.viewTimeline = "--my-timeline block"; |
| js_syntax_2 | element.style.setProperty("view-timeline", "--my-timeline block"); |
| js_note | When manipulating this property in JavaScript, ensure you include the required double-dash prefix for the timeline name string. |
| browsers | { "Chrome": 115, "Edge": 115, "Firefox": 114, "Safari": 18, "Opera": 101, "Chrome Android": 115, "Safari on iOS": 18, "Samsung Internet": 23, "Opera Mobile": 77 } |