Scroll-Driven & Reduced Motion

Animate elements as you scroll, and learn how to respect user motion preferences.

CSS6 min readConcept 32 of 43

What are Scroll-driven Animations?

Historically, tying an animation to a user's scroll position required heavy JavaScript event listeners that often caused performance lag (jank).

Modern CSS introduced animation-timeline, allowing you to link an @keyframes animation directly to the scroll progress of the page or a container. As you scroll down, the animation scrubs forward; as you scroll up, it scrubs backward.

Why does Reduced Motion matter?

While scroll-linked parallax and zooming effects look incredibly cool, they can induce nausea, dizziness, or headaches in users with vestibular disorders.

Operating systems have a 'Reduce Motion' accessibility setting. As a developer, it is your absolute responsibility to read this setting using the @media (prefers-reduced-motion: reduce) query and disable heavy movement.

How do you use them?

Instead of an animation-duration, you assign a timeline. animation-timeline: scroll() links the animation to the nearest scrolling parent.

Alternatively, animation-timeline: view() tracks when the specific element enters and exits the viewport.

To handle accessibility, wrap your animations in @media (prefers-reduced-motion: no-preference) so they only play for users who haven't explicitly opted out of motion.

Try it

Scroll the panel below to see a native CSS scroll-driven animation in action, and observe how the media query safely neutralizes it.

// 1. Scroll Progress Bar
.progress-bar {
animation-name: grow-x;
animation-timeline: scroll(nearest);
/* Drives animation 0% to 100% */
/* over the entire scroll height */
}
// 2. Viewport Reveal
.reveal-card {
animation-name: pop-in;
animation-timeline: view();
/* Start animating at 10% entry */
/* Finish when 40% into view */
animation-range: entry 10% cover 40%;
}
Scroll Down ↓
Item 1
Item 2
Item 3
// 3. The Accessibility Escape Hatch
@media (prefers-reduced-motion: reduce) {
.reveal-card {
/* Instantly jump to final state */
animation: none;
opacity: 1;
transform: none;
}
}

Never trap users with vestibular disorders in a dizzying scroll loop. Always provide a CSS fallback that cancels physical travel and zooming. Notice how turning on Reduce Motion in your OS settings instantly stops the animations in the panels above.

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1What is the primary benefit of native CSS scroll-driven animations over JavaScript scroll listeners?
  2. 2Which value tracks an element's journey specifically as it crosses the viewport?
  3. 3How should you handle @media (prefers-reduced-motion: reduce)?

Remember this

  • Use animation-timeline: scroll() for progress bars or global scroll tracking.
  • Use animation-timeline: view() for on-scroll reveal effects.
  • Always check @media (prefers-reduced-motion: reduce).
  • Replace sweeping movement with subtle opacity fades for users who request reduced motion.

Done with this concept?

Mark it complete to track your progress. No login needed.