CSS animation-range Property

The animation-range property sets the start and end points of an animation's attachment within its timeline, specifically for scroll-driven animations.

selector { animation-range: [ <range-start> ] [ <range-end> ]; }
normal Represents the default range of the timeline as defined by the animation-timeline.
<length-percentage> Specifies a start or end offset from the beginning of the timeline using units or percentages.
entry The range where the element is entering the scroll-port from the bottom or side.
exit The range where the element is exiting the scroll-port at the top or opposite side.
cover The full range of the element's visibility from the moment it enters until it completely leaves.
contain The range where the element is fully contained within the scroll-port.
entry-crossing The range while the element is crossing the starting edge of the scroll-port.
exit-crossing The range while the element is crossing the ending edge of the scroll-port.

Code Examples

A basic example showing an element that fades and scales in only during the 'entry' phase of its visibility in the scroll container.

<style>
.scroll-box {
  height: 300px;
  overflow-y: scroll;
}
.item {
  height: 100px;
  background: #333333;
  margin: 400px 0;
  animation: fade-in linear forwards;
  view-timeline-name: --item-visible;
  animation-timeline: --item-visible;
  animation-range: entry 0% entry 100%;
}
@keyframes fade-in {
  from { opacity: 0; transform: scale(0.5); }
  to { opacity: 1; transform: scale(1); }
}
</style>
<div class="scroll-box">
  <div class="item"></div>
</div>

An advanced example using a global scroll timeline to fill a progress bar, with a specific percentage range and JavaScript support detection.

<style>
#progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 10px;
  background: #ff0000;
  transform-origin: 0% 50%;
  animation: fill-up linear forwards;
  animation-timeline: scroll();
  /* Animation starts at 10% scroll and ends at 90% scroll */
  animation-range: 10% 90%;
}
@keyframes fill-up {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
</style>
<div id="progress-bar"></div>
<div style="height: 2000px;">Scroll down to see the bar fill up between 10% and 90% of the page height.</div>
<script>
// Example of dynamically checking support and updating range
const bar = document.getElementById("progress-bar");
if (CSS.supports("animation-range", "10% 90%")) {
  console.log("Scroll animations supported");
} else {
  bar.style.display = "none";
}
</script>

Pro Tip

You can combine named ranges with percentages for surgical precision. For example, "entry 20% exit 80%" tells the browser to start the animation 20% into the entry phase and finish it 80% through the exit phase. This creates a smooth buffer so the animation doesn't feel abrupt the moment the element touches the viewport edge.

Deep Dive

Think of animation-range like setting the "In" and "Out" points on a video editor's timeline. In traditional CSS, animations run based on time. With scroll-driven animations, the "timeline" is the scroll distance. This property allows you to define exactly when that animation sequence should kick in and when it should wrap up relative to an element's position in the viewport. It is a shorthand for animation-range-start and animation-range-end. By default, an animation mapped to a view-timeline will run the entire time the element is crossing the scrollport. By adjusting the range, you can make the animation finish early or start late, providing much tighter control over the user experience.

Best Practices

Stick to the animation-range shorthand to keep your stylesheets lean. Use named constants like "entry" and "exit" instead of hard-coded pixel values to ensure the effect works across different screen sizes. Since this feature is still relatively new and experimental in some browsers, always wrap your scroll-driven logic inside an @supports rule to provide a sensible fallback for users on older browsers.

Common Pitfalls

A common mistake is trying to use animation-range without first defining an animation-timeline; the property will do absolutely nothing without a timeline to reference. Also, remember that if you provide only one value, it sets both the start and end to that same point, which usually results in the animation not playing at all or jumping to the end instantly.

Accessibility

Scroll-linked animations can be disorienting or cause motion sickness for certain users. Always wrap your animation code in a media query for "prefers-reduced-motion: no-preference" to respect user system settings. Ensure that any information revealed via scroll-animation is also accessible if the animation fails to run.

Dev Data Table: animation-range property

default normal normal
animatable no
inherited no
experimental yes
year_intro 2023
year_standard 0
js_syntax_1 element.style.animationRange = "entry 10% exit 90%";
js_syntax_2 element.style.setProperty("animation-range", "cover 0% cover 100%");
js_note Ensure you check for browser support before manipulating scroll-timeline properties in JS to prevent script crashes in older environments.
browsers { "Chrome": 115, "Edge": 115, "Firefox": 0, "Safari": 0, "Opera": 101, "Chrome Android": 115, "Safari on iOS": 0, "Samsung Internet": 23, "Opera Mobile": 77 }
results render here...