CSS Scroll Animations and View Timelines: A Practical Guide
CSS scroll animations let an element’s animation progress follow the user’s scroll position instead of time. This is useful for progress indicators, reveal effects, parallax-like motion, and other interactions that feel tied to the page itself.
Quick answer: Scroll animations use scroll-timeline, view-timeline, and animation-range to connect a CSS animation to scrolling. A scroll timeline tracks a scroll container, while a view timeline tracks an element as it enters and leaves the viewport.
Difficulty: Intermediate
You’ll understand this better if you know: basic CSS animations, how overflow scrolling works, and how elements are sized and positioned in the layout.
1. What Is CSS Scroll Animations and View Timelines?
CSS scroll animations are animations whose progress is driven by scrolling instead of elapsed time. Instead of saying “run this animation for 3 seconds,” you say “run this animation as the user scrolls this container” or “as this element moves through the viewport.”
- scroll-timeline connects an animation to a scroll container.
- view-timeline connects an animation to an element’s visibility within a scrollport.
- animation-range controls which part of the scroll or view progress maps to the animation.
- These features are part of CSS Scroll-Driven Animations.
In practice, that means you can build effects that start, stop, and progress naturally as the user scrolls without writing scroll event code.
2. Why CSS Scroll Animations Matter
Scroll-driven motion can make interfaces feel clearer and more responsive because the animation matches what the user is already doing. For example, a progress bar can show how far through an article the reader has moved, or cards can reveal themselves as they enter the viewport.
These features matter because they move a common interaction pattern from script into CSS. That can reduce complexity, improve maintainability, and make the effect easier to reason about in stylesheets.
They are especially useful when you want:
- animations tied to reading progress
- visual feedback for sections coming into view
- less dependency on custom scroll handling
- more declarative, reusable motion rules
3. Basic Syntax or Core Idea
At the core, you define a timeline and then tell an animation to use it. The animation no longer uses the default time-based timeline.
Basic scroll timeline example
This example links an animation to the page scroll progress. As the page scrolls, the element’s opacity changes.
.scroller {
height: 100vh;
overflow-y: auto;
scroll-timeline-name: --page-scroll;
scroll-timeline-axis: block;
}
.headline {
animation-name: fade-in;
animation-duration: 1s;
animation-timeline: --page-scroll;
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}Here, the scroll container defines the timeline, and the animated element listens to that timeline. The animation still uses keyframes, but its progress is controlled by scrolling.
Basic view timeline example
A view timeline follows an element as it moves through the viewport. This is useful for reveal effects that begin when the element becomes visible.
.card {
view-timeline-name: --card-view;
view-timeline-axis: block;
animation-name: slide-up;
animation-timeline: --card-view;
}
@keyframes slide-up {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
}This pattern is often used for cards, sections, and illustrations that should animate as they come into view.
4. Step-by-Step Examples
Example 1: Reading progress bar
A common scroll-timeline use case is a progress bar that fills as the page scrolls. The bar’s width follows the scroll position of the article container.
.article {
height: 100vh;
overflow-y: auto;
scroll-timeline-name: --article-scroll;
scroll-timeline-axis: block;
}
.progress {
animation: fill-progress 1s linear both;
animation-timeline: --article-scroll;
}
@keyframes fill-progress {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}This works well because a transform-based fill is smooth and easy to animate. The timeline makes the bar reflect scroll progress instead of elapsed time.
Example 2: Reveal cards as they enter the viewport
View timelines are ideal for entrance effects. Each card can animate when it enters view, without needing a script to detect visibility.
.feature-card {
view-timeline-name: --card;
view-timeline-axis: block;
animation-name: reveal;
animation-timeline: --card;
animation-range: entry 0% entry 100%;
}
@keyframes reveal {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}The range starts the animation when the element begins entering and finishes it when the entry phase completes. That gives you a natural reveal tied to visibility.
Example 3: Parallax-like motion on a banner
You can use scroll progress to move a decorative element at a slower rate than the rest of the page. This creates a parallax-like effect without scripting.
.banner {
scroll-timeline-name: --banner-scroll;
scroll-timeline-axis: block;
}
.banner__shape {
animation-name: float-shape;
animation-timeline: --banner-scroll;
animation-range: cover 0% cover 100%;
}
@keyframes float-shape {
from {
transform: translateY(0);
}
to {
transform: translateY(-60px);
}
}This kind of motion works best for subtle decorative content. Strong motion can become distracting if it fights the content the user is trying to read.
Example 4: Section marker that activates near view center
Sometimes you want a nav indicator or highlight to change when a section passes through a specific part of the viewport.
.chapter {
view-timeline-name: --chapter;
view-timeline-axis: block;
}
.chapter-marker {
animation-name: activate-marker;
animation-timeline: --chapter;
animation-range: contain 25% contain 75%;
}
@keyframes activate-marker {
from {
background-color: transparent;
}
to {
background-color: rebeccapurple;
}
}This shows how animation range can target a middle portion of the element’s view progress rather than only the beginning or the end.
5. Practical Use Cases
- Article reading progress bars that mirror scroll position.
- Image and card reveal effects as content enters the viewport.
- Sticky section indicators that respond to the user’s reading position.
- Decorative motion for headers, hero sections, and banners.
- Animated outlines or highlights for active content areas.
These use cases are strongest when the motion is informational or subtle. They are less appropriate for critical UI states that must remain obvious without animation.
6. Common Mistakes
Mistake 1: Defining the timeline on the wrong element
Scroll timelines are created on the scroll container, not on an unrelated child element. If the element never scrolls, the animation will not have meaningful progress.
Problem: The animation listens to a timeline name that is never created on the actual scrolling container, so the effect appears not to work.
.content {
animation-timeline: --page-scroll;
}
.headline {
animation-name: fade-in;
animation-duration: 1s;
}Fix: Put the scroll timeline on the element that actually scrolls, then reference that timeline from the animated element.
.content {
height: 100vh;
overflow-y: auto;
scroll-timeline-name: --page-scroll;
scroll-timeline-axis: block;
}
.headline {
animation-name: fade-in;
animation-duration: 1s;
animation-timeline: --page-scroll;
}The corrected version works because the timeline is attached to the element that provides scroll progress.
Mistake 2: Using time-based expectations with a scroll-driven animation
A scroll-linked animation does not complete in a fixed number of seconds. Its progress depends on how far and how fast the user scrolls.
Problem: Expecting a scroll-linked animation to finish after a precise duration causes confusion, especially when the animation seems to pause whenever scrolling stops.
.card {
animation-name: reveal;
animation-duration: 2s;
animation-timeline: --card-view;
}Fix: Keep the duration as part of the animation syntax, but understand that the timeline determines progress. Use the duration as a scaling reference, not as a real clock.
.card {
animation: reveal 1s both;
animation-timeline: --card-view;
animation-range: entry 0% entry 100%;
}The corrected version works because the animation is explicitly tied to view progress, not wall-clock time.
Mistake 3: Forgetting that overflow and size determine scrollability
Scroll timelines need actual scrolling context. If the element cannot scroll, the timeline has no useful range.
Problem: The container has no fixed height or no overflow, so there is nothing to scroll and the animation appears frozen.
.panel {
scroll-timeline-name: --panel-scroll;
}
.indicator {
animation-timeline: --panel-scroll;
}Fix: Give the container a scrollable area by setting a size and overflow.
.panel {
height: 400px;
overflow-y: auto;
scroll-timeline-name: --panel-scroll;
scroll-timeline-axis: block;
}
.indicator {
animation-timeline: --panel-scroll;
}The corrected version works because the timeline now has a real scroll range to measure.
7. Best Practices
Practice 1: Use scroll timelines for state-like progress, not constant motion
Scroll animations work best when they represent progress, reveal, or position. They are less effective for constant looping effects that should run independently of scrolling.
.reading-progress {
animation-name: fill-progress;
animation-timeline: --article-scroll;
}This approach keeps the animation meaningful because the visual change reflects user position.
Practice 2: Prefer subtle transforms and opacity changes
Performance and readability are usually better when you animate transform and opacity rather than layout properties. Those properties are easier for browsers to handle smoothly.
.reveal {
animation-name: reveal;
}
@keyframes reveal {
from {
opacity: 0;
transform: translateY(16px);
}
}These properties are usually enough to create polished scroll-driven motion without causing layout shifts.
Practice 3: Keep animation ranges intentional
The right animation-range makes the motion feel precise. If the effect starts too early or ends too late, tune the range instead of forcing the keyframes to compensate.
.card {
animation-timeline: --card-view;
animation-range: entry 20% cover 60%;
}Thoughtful ranges make the effect line up with what the user is actually seeing.
8. Limitations and Edge Cases
- Browser support is still evolving, so some users may not see the effect if their browser does not support scroll-driven animations.
- Scroll timelines depend on actual scrollable overflow; if there is no scrolling, there is no progress.
- View timelines are sensitive to element size and viewport size, so the same effect may feel different on small and large screens.
- Strong motion can feel distracting, especially when the user is trying to read text.
- Effects tied to nested scroll containers can be harder to reason about than page-level scrolling.
If an animation appears to do nothing, first check that the browser supports scroll-driven animations, then confirm that the container actually scrolls and that the timeline name matches exactly.
9. Practical Mini Project
Here is a small article layout with a reading progress bar and reveal cards. It combines a scroll timeline for the progress indicator with view timelines for the cards.
.reader {
height: 100vh;
overflow-y: auto;
scroll-timeline-name: --reader-scroll;
scroll-timeline-axis: block;
}
.progress {
position: sticky;
top: 0;
height: 4px;
background: #ddd;
}
.progress__bar {
height: 100%;
background: #7c3aed;
transform-origin: left;
animation: fill-progress 1s linear both;
animation-timeline: --reader-scroll;
}
.reveal-card {
view-timeline-name: --card-view;
view-timeline-axis: block;
animation: reveal 1s both;
animation-timeline: --card-view;
animation-range: entry 0% cover 40%;
}
@keyframes fill-progress {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
@keyframes reveal {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}This mini project shows the main pattern: one scroll timeline for page progress and one view timeline for element entry. It is a good starting point for article layouts, documentation pages, and editorial interfaces.
10. Key Points
- Scroll animations in CSS connect animation progress to scrolling instead of time.
- scroll-timeline tracks a scrolling container.
- view-timeline tracks an element’s visibility in the viewport.
- animation-range lets you fine-tune when the animation starts and ends.
- The best results usually come from subtle, informative motion.
11. Practice Exercise
- Create a scrollable article container with a sticky header.
- Add a progress bar that grows as the reader scrolls.
- Add three content cards that fade and move upward when they enter view.
- Adjust the animation range so each card finishes its reveal before it fully reaches the center of the screen.
Expected output: A reading interface where the top bar fills with scroll progress and each card animates smoothly as it becomes visible.
Hint: Use one timeline name for the article container and a separate view timeline name for each card.
.reader {
height: 100vh;
overflow-y: auto;
scroll-timeline-name: --reader-scroll;
scroll-timeline-axis: block;
}
.progress__bar {
animation: fill-progress 1s linear both;
animation-timeline: --reader-scroll;
}
.card {
view-timeline-name: --card-view;
view-timeline-axis: block;
animation: reveal 1s both;
animation-timeline: --card-view;
animation-range: entry 0% cover 40%;
}12. Final Summary
CSS scroll animations let you tie motion to the user’s scrolling position, which makes interfaces feel more connected to the content being viewed. Use scroll-timeline when you want the animation to follow a scroll container, and use view-timeline when you want the animation to respond to an element entering or leaving the viewport.
For most projects, the best scroll-driven effects are subtle and purposeful. Reading bars, reveal animations, and section indicators are all strong candidates because the motion helps communicate position and progress rather than distracting from the page.
If you want to go further, the next step is to experiment with different animation-range values and compare page-level scroll timelines with view-based timelines in a simple article layout.