Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 12.2Intermediate ~21 min · shot 2 of 7

View Progress Timelines and Ranges

In one line

A view progress timeline measures how far one element has travelled across its scroller's visible area. animation-timeline: view() makes it, and animation-range chooses the part of the journey (entry, contain, exit or cover) over which the animation plays.

0/7 · 0%

Think of it like this

A train passing a platform. For you, standing on the platform, the train has a start (the nose reaches you), a middle (it fills your view) and an end (the tail leaves). What matters is the train's own journey past you, not the clock. A view timeline measures that journey for an element crossing the visible area of a scroller.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

subject
The element a view timeline follows: the one that has animation-timeline: view() (or the element passed to new ViewTimeline).
scrollport
The visible area of a scroller: the box you look through, without the part scrolled out of sight.
range
The part of a timeline an animation uses, written as animation-range. A name (entry) or a name and a percentage (entry 50%).
inset
A distance that shrinks the scrollport at its edges, so a view animation starts later and ends sooner.

Step by step

01Scroll one card in

In "Cards that fade in as they arrive" press Next twice: each card rises and turns opaque as it enters, done when fully inside.

02Read the range table

Each row is a range. For the 40 px subject entry reaches 1.00 at offset 240, contain at 300 and exit at 340.

03Compare short and tall

For the 160 px subject contain reads 0.72 at 340 and 1.00 at 360: it plays only while the subject covers the box.

04Read the edges

The first box is at 54% before any scrolling. The plain range is 200 to 348 px and the inset one 220 to 328 px.

Try it yourself

  1. 1

    Add the two crossing ranges

    In "The same animation over each range" change the list ["entry", "contain", "exit", "cover"] to ["entry-crossing", "exit-crossing"]. Measured: for the 40 px subject they print the same rows as entry and exit. For the 160 px subject, entry-crossing reads 0.15, 0.36, 0.68, 0.89 and then 1.00 (from offset 360), slower than entry, which is done at 300.

Code & diagrams

Cards that fade in as they arrive

Each card has its own view timeline. Press Next (it scrolls 70 px) and read the Scroll tracks on the Motion Timeline.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

animations: 3
timeline: ViewTimeline · subject: 80 km range · axis: block
animation-range: entry

Try it: interactive

Drag the range

Pick a range (cover, contain, entry, exit and the two crossing ranges) and change the subject's height. Watch where the start and end fall, and the progress readout.

The same animation over each range

The script scrolls a 100 px box to seven offsets and prints the opacity (0.15 before the range, 1 after). The animation is `fade`, from opacity 0.15 to 1. It takes about two seconds.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

scroll offsets: 200 240 300 340 360 400 460
subject 40px, entry: 0.15 1.00 1.00 1.00 1.00 1.00 1.00
subject 40px, contain: 0.15 0.15 1.00 1.00 1.00 1.00 1.00
subject 40px, exit: 0.15 0.15 0.15 1.00 1.00 1.00 1.00
subject 40px, cover: 0.15 0.39 0.76 1.00 1.00 1.00 1.00
subject 160px, entry: 0.15 0.49 1.00 1.00 1.00 1.00 1.00
subject 160px, contain: 0.15 0.15 0.15 0.72 1.00 1.00 1.00
subject 160px, exit: 0.15 0.15 0.15 0.15 0.15 0.49 1.00
subject 160px, cover: 0.15 0.28 0.48 0.61 0.67 0.80 1.00
Where a journey starts, and what an inset does

Three small scrollers. The first box is already in view at load. The third uses `view(block 20px)`.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

in view at load: progress 54% before any scrolling
plain: range 200px to 348px
inset 20px: range 220px to 328px

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Use exit where you meant entry

In "Cards that fade in as they arrive" change the range to animation-range: exit 0% exit 100%;.

terminal
$ Press Next several times
── what you'll see ──
The second card stays at opacity 0 for every scroll position from 0 to 180 px (measured), including while it is fully inside the box. It would only appear while leaving.
From a real run: the card's cover progress is still below 51% at 180 px.

Myth vs fact

Myth

view() fires when an element enters the screen.

Fact

It does not fire anything. It is a clock whose time is the element's position, so the animation is at a point on its journey all the time, forwards and backwards.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    The progress is exact at every scroll position, so there is no "already past it" problem: load a page scrolled half way, or press Back and have the browser restore the scroll, and every reveal already shows its finished state. Event-based reveals have to handle that case by hand (Topic 12.5).

Remember this

  1. 1

    The problem this solves: a scroll progress timeline (Topic 12.1) measures a whole scroller, so it cannot say when one card arrives. view() gives every element its own timeline, so its animation plays exactly while that element enters, with no JavaScript (the alternative is IntersectionObserver, Topic 12.5).

  2. 2

    **view() follows one element, the subject,** against the nearest scroller's visible area (the scrollport). 0% is when the subject's first edge touches the scrollport, 100% when its last edge has left. view(block 20px) also takes an axis and insets. The first example puts view() on three cards; the console shows a ViewTimeline per card whose subject is the card.

  3. 3

    **animation-range cuts the journey.** The whole journey is cover. entry runs from the first pixel arriving until the subject is fully inside. exit runs from the first pixel leaving until it is gone. contain runs while the subject is fully inside (or, if taller than the scrollport, while it covers it). Write a name (entry) or a pair (entry 0% entry 100%; the computed style shows plain entry), and names mix: entry 0% cover 40%.

  4. 4

    Two more ranges follow the subject's own size. entry-crossing and exit-crossing differ from entry and exit only for a subject taller than the scrollport. In a 100 px scrollport (second example) a 40 px subject has entry equal to entry-crossing (200 to 240 px of scroll); a 160 px subject has entry from 200 to 300, entry-crossing from 200 to 360, and contain from 300 to 360.

  5. 5

    Insets shrink the scrollport. view(block 20px) (or view-timeline-inset) treats the scrollport as 20 px smaller at each edge, so the animation starts later and ends sooner. Measured in the third example: the plain timeline runs from 200 to 348 px of scroll, the inset one from 220 to 328. Use one for "not until the card is clear of the sticky header".

  6. 6

    An element in view at load starts part-way. In the third example the first box is 54% through its cover journey before any scrolling, so an entry animation on it is already finished and it looks normal: a hero never waits. Before the journey starts, fill-mode both holds the first keyframe (opacity 0 in the first example). Animate opacity and transform only.

Explain it without notes

01

What is the difference between entry, contain and cover?

02

A card is already on screen when the page loads. What does its entry animation show, and why is that right?

Practice

01

Write the CSS for a feature card that fades and rises over entry, but only starts 40 px after it first touches the bottom of the screen.

02

A tall photo (500 px) in a 300 px box should fade in while it covers the box, never while half of it is out. Which range, and why?

Done when you can

  • You can write animation-timeline: view() and animation-range for a reveal

  • You can say what entry, contain, exit and cover mean, and when the crossing ranges differ

  • You know what an inset does and what happens to an element in view at load

  • You choose the range so the effect ends before the reader needs the content