What does the CSS animation-range property control on a scroll-driven animation, and what do named ranges like entry and cover mean?
answer
- there is no duration to shorten
- where, not when
- cover is the whole passage
- entry finishes as it lands
- pair the range with a fill mode
basics
~20 sanimation-range picks which slice of a progress timeline the animation occupies. On a view timeline you express the endpoints with named ranges — cover, contain, entry, exit — plus optional percentage offsets, instead of using a time-based duration.
solid answer
~40 sOn a progress timeline there is no duration to shorten, so `animation-range` is how you say "run only over this part of the scroll". It is the shorthand for `animation-range-start` and `animation-range-end`, and each endpoint is a length, a percentage, or a *named range* with an optional offset. For a view timeline the names describe the subject's trip past the scrollport: `cover` is the whole passage from first appearing to fully gone, `entry` is just the arrival, `exit` just the departure, and `contain` is the stretch where the subject is fully inside the scrollport (or fully covering it, when it is the larger of the two). So `animation-range: entry 0% entry 100%` finishes a reveal the moment the element is fully on screen, instead of dragging it out until the element leaves.
code
css · 12 lines@keyframes fade-up {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: none; }
}
.card {
animation: fade-up linear both;
animation-timeline: view();
/* Done by the time the card is fully on screen. */
animation-range-start: entry 0%;
animation-range-end: entry 100%;
}go deeper
Know that animation-range decides which part of the scroll an animation happens over, and that entry is the range you use for a fade-in as an element arrives.
Explain that a progress timeline has no duration, so the range shorthand's two endpoints replace delay and duration, and define cover, entry, exit, and contain in terms of the subject and the scrollport.
Diagnose from symptoms: an effect that never completes points at the default cover extent, an element that reverts points at fill-mode, and an oversized subject points at contain versus the crossing variants.
Be ready to argue for ranges as an authoring contract — a small vocabulary of named ranges keeps motion consistent across a component library, where ad-hoc pixel offsets drift as layout changes.
## Why the property exists On a clock-driven animation you scope the effect with `animation-duration` and `animation-delay`. Neither is meaningful against scroll progress — there are no seconds to allocate. `animation-range` is the replacement: it declares which portion of the timeline the animation's keyframes are stretched across. Outside that portion the animation is in its before- or after-phase, which is why `animation-fill-mode` matters so much here. It is a shorthand for two longhands: - `animation-range-start` — where 0% of the keyframes sits - `animation-range-end` — where 100% of the keyframes sits Both default to `normal`, which means the full timeline. ## Endpoint syntax Each endpoint can be: - **`normal`** — the timeline's own start or end. - **A `<length-percentage>`** — an offset into the timeline, such as `20%` or `400px`. This is the form you use with a scroll progress timeline, which has no named ranges of its own. - **A named range, optionally with an offset** — `entry`, `entry 50%`, `cover 25%`. Named ranges belong to view progress timelines. When you write the shorthand with two values, the first is the start and the second is the end: `animation-range: entry 0% cover 50%`. ## What the names mean All of the named ranges describe the relationship between the **subject** box and the **scrollport** as the subject travels through it. - **`cover`** — the entire passage: from the instant the subject's box first begins to intersect the scrollport, to the instant it has completely left. This is the default extent of a view timeline. - **`entry`** — the arrival phase only: from first touching the scrollport to being fully inside it. - **`exit`** — the departure phase only: from beginning to leave to being completely gone. - **`contain`** — the middle stretch where the subject is fully contained within the scrollport. When the subject is *taller* than the scrollport, containment is impossible, and the range instead covers the period where the subject fully covers the scrollport. - **`entry-crossing`** and **`exit-crossing`** — variants defined by the subject's box crossing the scrollport's end and start edges. For subjects smaller than the scrollport they coincide with `entry` and `exit`; for larger subjects they differ, which is precisely when you reach for them. Adding a percentage picks a point inside that named range: `entry 50%` is the moment the subject is half-way through arriving, not half-way through the whole passage. ## The practical recipe The most common real-world use is a reveal that should be finished by the time the element is properly on screen: ```css .card { animation: fade-up linear both; animation-timeline: view(); animation-range: entry 0% entry 100%; } ``` Without the range, the default `cover` extent means the fade only completes as the card is *leaving* the viewport — an effect that feels permanently unfinished. This is one of the most reported "my scroll animation looks broken" symptoms, and the fix is always the range, never the duration. A parallax-style effect wants the opposite: the whole `cover` range, so the element keeps drifting for its entire passage. ## Fill mode is part of the answer Because `animation-range` deliberately leaves timeline outside the animation, what happens there is determined by `animation-fill-mode`. With the default `none`, the element renders its base styles before the range starts and again after it ends — so a fade-in reverts to transparent after the element passes. `both` holds the first keyframe before and the last keyframe after, which is what a reveal wants. Treat `animation-range` and a deliberate fill mode as a pair. ## Ranges on scroll timelines Named ranges are a view-timeline concept: they are defined by a subject crossing a scrollport, and a scroll progress timeline has no subject. On a `scroll()` timeline you scope with lengths or percentages of the scrollable range instead — `animation-range: 10% 60%` runs the animation over the middle stretch of the page and leaves the first tenth and last forty percent alone. Writing `entry` against a `scroll()` timeline does not give you the effect you are imagining. ## What to say in an interview The crisp framing is: on a time-based animation you choose *when* and *how long*; on a progress-based animation you choose *where*, and `animation-range` is the only tool that does it. Then name `cover` as the default extent and `entry` as the one you reach for nine times out of ten.
- Why does a view()-driven fade-in with no animation-range feel like it never finishes?Because the default extent is the `cover` range — the subject's entire passage from first appearing to fully gone. Full opacity is therefore only reached as the element leaves the far edge, so it looks permanently half-faded while you are reading it. Narrowing the range to `entry 0%` through `entry 100%` completes the reveal as the element lands.
- How is animation-range different from animation-delay?`animation-delay` postpones a clock-driven animation by a length of time and does nothing useful on a progress timeline, since there is no clock to wait on. `animation-range` positions the keyframes spatially within the timeline, so its start value plays the role a delay would and its end value plays the role a duration would.
- When would you use entry-crossing rather than entry?When the subject is taller than the scrollport. `entry` ends when the subject is fully inside the scrollport, which can never happen for an oversized element, whereas `entry-crossing` is defined by the box crossing the scrollport's end edge and stays meaningful. Full-viewport sections and tall hero images are the usual cases.
saying these in an interview costs you the question
- Uses animation-duration to shorten a scroll-driven animation
- Thinks entry 50% means half-way through the whole scroll
- Applies named ranges like entry to a scroll() timeline
- Forgets fill-mode and sees the element revert outside the range
- Assumes cover and contain are the same for oversized elements