Named Animation Ranges Explained

Part of Scroll Timeline Scoping & Ranges in Modern View Transitions & Scroll APIs.

The problem

A team adopts view timelines and sprinkles animation-range values copied from demos: entry, cover 0% cover 50%, contain, entry-crossing 50%. Some effects finish too early, some never finish on tall elements, and one only works on elements shorter than the viewport. Nobody can say, for a given element, at what scroll position entry 100% occurs — so every tweak is trial and error.

Each named range is defined by two edge crossings. Once those are clear, the right range for an effect can be chosen rather than guessed.

Root cause analysis: edges of the subject crossing edges of the scrollport

A view timeline tracks a subject element moving through a scrollport on one axis. For the block axis with normal scrolling, think of the subject moving up the screen: it enters at the bottom (the scrollport’s end edge) and leaves at the top (the start edge). Each range is a span of scroll positions between two events.

  • cover — from the subject’s start edge meeting the scrollport’s end edge (first pixel visible) to the subject’s end edge meeting the scrollport’s start edge (last pixel leaves). The full passage.
  • contain — while the subject is fully inside the scrollport, or, if it is taller than the scrollport, while it fully covers it. From the later of “subject fully entered” or “subject covers scrollport”, to the earlier of the corresponding exit events.
  • entry — from the first pixel entering until the subject is fully inside (or until it fills the scrollport, if taller). The subject’s own size decides how long this is.
  • exit — the mirror of entry at the start edge.
  • entry-crossing — from the subject’s start edge crossing the scrollport’s end edge until the subject’s end edge crosses that same scrollport edge. Its length is always the subject’s size, regardless of whether the subject is taller than the scrollport.
  • exit-crossing — the mirror at the start edge.

Tall subjects change entry and contain. For a subject taller than the viewport, entry ends when the subject fills the viewport, which happens before its end edge arrives — so entry is shorter than entry-crossing. And contain becomes “while it covers the viewport” rather than “while it fits”. This is the usual cause of effects that behave differently on tall sections.

Percentages are relative to the range. entry 50% is halfway through the entry range. animation-range: entry 25% cover 50% starts a quarter of the way through entry and ends halfway through cover. Mixing range names this way is allowed and often needed.

Keyframes can name ranges. Inside @keyframes, selectors such as entry 0% and exit 100% attach keyframes to positions in different ranges, so one animation can fade in during entry and out during exit without two animations. The animation’s own animation-range should then be broad enough, usually cover, to contain every keyframe position.

Ranges for a 300 px card in a 700 px viewportScroll distance in pixels. cover spans 1000 px; entry, contain and exit partition it; entry-crossing is always the card's own height.Ranges for a 300 px card in a 700 px viewportcoverwhole passage1000 pxentry / contain / exitentrycontainexit1000 pxentry-crossingcrossingrest1000 pxwhole passageentrycontainexitcrossingrest
Scroll distance in pixels. cover spans 1000 px; entry, contain and exit partition it; entry-crossing is always the card's own height.

Step-by-step resolution

Choosing a range from the effectDescribe the effect first, then pick the range that matches.Choosing a range from the effect1Reveal as it arrives: use entry, often entry 0% entry 80%.Finishes before fully in view2Parallax or progress while visible: use contain or cover.Continuous while on screen3Fade out as it leaves: use exit.Mirror of the reveal4Effects that must take exactly the element's height of scroll: use entry-crossing.Consistent regardless of viewport size5One animation for in and out: name ranges in keyframes and set animation-range: cover.Single declaration6Test with a subject taller than the viewport.
Describe the effect first, then pick the range that matches.

Production code pattern

/* Reveal on arrival. */
.card {
  animation: reveal linear both;
  animation-timeline: view();
  animation-range: entry 0% entry 80%;
}
@keyframes reveal { from { opacity: 0; translate: 0 24px; } }

/* In and out in one animation, using range names inside keyframes. */
.figure {
  animation: in-and-out linear both;
  animation-timeline: view();
  animation-range: cover;
}
@keyframes in-and-out {
  entry 0%   { opacity: 0; scale: 0.96; }
  entry 100% { opacity: 1; scale: 1; }
  exit 0%    { opacity: 1; scale: 1; }
  exit 100%  { opacity: 0; scale: 0.96; }
}

/* A progress line inside a tall section: runs while the section covers the viewport. */
.chapter__progress {
  transform-origin: left;
  animation: grow linear both;
  animation-timeline: --chapter;
  animation-range: contain;
}
.chapter { view-timeline: --chapter block; }
@keyframes grow { from { scale: 0 1; } }

@media (prefers-reduced-motion: reduce) {
  .card, .figure, .chapter__progress { animation: none; }
}

Rendering Impact: composite. Ranges only decide which scroll offsets map to 0% and 100% of the animation; the properties animated are opacity, translate and scale.

The in-and-out keyframes rely on animation-range: cover to include all four positions. With a narrower range, such as entry, the exit keyframes would fall outside the active range and never apply.

Range choice by effectStart from the effect; pick the range whose edges match it.Range choice by effectRangeLength depends onWatch out forReveal on arrivalentrySubject size, capped byviewportShort on tall subjectsFade out onleavingexitSubject size, capped byviewportMirror of entryProgress while onscreencoverSubject + viewport sizeStarts at first pixelProgress whilefully visiblecontainViewport minus subject, orreverseZero length if sizes equalExactlysubject-heighteffectentry-crossingSubject size onlyCan extend past fullvisibility
Start from the effect; pick the range whose edges match it.

Inspecting ranges in DevTools

Chromium’s DevTools can show scroll-driven animations in the Animations drawer with their timelines, and scrubbing the page scroll updates their progress. For precise checks, log progress from script: element.getAnimations()[0].effect.getComputedTiming().progress at a given scroll position tells you exactly where in the range you are. Combining that with the element’s getBoundingClientRect() and the viewport height lets you verify the edge definitions above against your own layout, which is the fastest way to settle a disagreement about which range a design needs. The broader workflow is in profiling scroll-driven animations in DevTools.

Verification checklist

Constraints and trade-offs

  • contain has zero length when subject and viewport sizes are equal, so the animation jumps.
  • entry and exit shorten for tall subjects, compressing effects.
  • Range names in keyframes need browser support for timeline range keyframe selectors.
  • Insets from view-timeline-inset shift every range’s edges together.
  • Ranges on horizontal timelines use inline edges, which reverse in right-to-left writing modes.

Frequently asked questions

What is the difference between cover and contain?

cover spans from the first pixel of the subject entering to the last pixel leaving. contain spans only while the subject is fully inside the viewport, or fully covers it if taller.

What is entry-crossing for?

It measures the subject’s start edge to end edge crossing the viewport’s end edge, so its length is always the subject’s size, unlike entry which is capped by the viewport.

Can I start in one range and end in another?

Yes. animation-range: entry 25% cover 50% starts a quarter through entry and ends halfway through cover.

Why don’t my exit keyframes apply?

The animation’s range probably does not include the exit range. Set animation-range: cover when keyframes reference both entry and exit.