Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 12.3Intermediate ~19 min · shot 3 of 7

Named Timelines and timeline-scope

In one line

A timeline can have a name, so any element can use it. scroll-timeline-name and view-timeline-name create the name, animation-timeline: --name uses it, and timeline-scope makes a name visible outside the subtree where it was made. A misspelled name raises no error.

0/7 · 0%

Think of it like this

A clock on a town square. People in the square read it, but a shop two streets away cannot see it. The town can put a repeater clock on the shop's wall, which shows the same time. A named timeline is the square's clock, and timeline-scope is the repeater: it makes the same clock readable where it could not be seen.

Words you'll meet

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

named timeline
A scroll or view timeline given a name like --page, so that animation-timeline: --page can use it.
timeline-scope
A property on an ancestor that makes a timeline name usable by all of its descendants, not only by the element that made the name.
dashed ident
A name that starts with two dashes (--page). Timeline names and custom properties use them.
descendant
An element inside another element, at any depth. A sibling is not a descendant.

Step by step

01Three elements, one name

In "One name, three places" the first bar has a ScrollTimeline. The typo bar and the outside bar have no timeline and are full.

02Scope it

The script adds body.scoped (timeline-scope: --page): the outside bar now has a ScrollTimeline. Toggle timeline-scope takes it away again.

03Two with the same name

In "Same name twice", nested timelines named --x give #inner. Two siblings named --y give no timeline.

04Debug with getAnimations

In the console, el.getAnimations()[0].timeline is null when a name did not resolve.

Try it yourself

  1. 1

    Toggle the scope

    In "One name, three places" scroll the box to 100 px: the third bar reads scaleX 0.26 (measured). Now press Toggle timeline-scope. This press removes the scope: the third bar loses its timeline, jumps to full. Press again: it has a ScrollTimeline once more, but in headless Chromium its animation comes back paused at scaleX 0 and ignores the scroll until you reload (measured at 100 and 150 px). Decide the scope in the stylesheet, not at run time.

Code & diagrams

One name, three places

Scroll the 90 px box and watch the first and (after scoping) third bars. The button toggles `timeline-scope` on body.

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

inside the scroller: ScrollTimeline
typo --pgae: no timeline, bar is full: true
outside, no timeline-scope: no timeline, bar is full: true
outside, with timeline-scope on body: ScrollTimeline, source: s
Same name twice

Edit the CSS: rename `#inner`'s timeline to `--z` and the bar follows `#outer`.

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

nested, both named --x: the bar follows #inner
two scrollers named --y in one scope: no timeline

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

Misspell the name in one place

In "One name, three places" change the .bar rule's animation-timeline: --page; to animation-timeline: --pge;.

terminal
$ Read the console and look at the bar
── what you'll see ──
No error and no warning. The first console line reads `inside the scroller: no timeline`, and the sticky bar is drawn full (scaleX 1, measured) and never moves.
From a real run.

Myth vs fact

Myth

A named timeline is global: any element on the page can use it.

Fact

It is visible only below the element that made it. Use timeline-scope on a common ancestor to widen it.

Remember this

  1. 1

    The problem this solves: scroll() and view() make an anonymous timeline for the element that uses them. Often something else should follow a scroller: a header bar driven by a long article, a caption driven by a card. That element is outside the scroller, so it needs a way to point at someone else's timeline.

  2. 2

    Make a name. On the scroller: scroll-timeline: --page block (name, then axis; or scroll-timeline-name and -axis). On a subject: view-timeline: --volt block (or view-timeline-name, -axis, -inset). A name starts with two dashes, like a custom property, and animation-timeline: --page uses it. It is the same timeline scroll() or view() makes; only the name is new.

  3. 3

    A name is visible only below its element. The element that defines a name, and all its descendants, can use it. Siblings and parents cannot: a sticky bar inside the scroller works, a meter after it does not (first example), and the browser raises no error.

  4. 4

    **timeline-scope lifts the name.** Put timeline-scope: --page on a common ancestor (here body): every descendant can then use --page, including the meter outside the scroller. In the first example a button toggles it on body: with it the meter follows the scroller, without it the meter has no timeline.

  5. 5

    A wrong name does nothing, quietly. A misspelled name (--pgae) matches no timeline, with no console error. Measured: the animation reports finished, and with fill-mode both the last keyframe stays, so the bar is drawn full. A bar full from the first frame is the usual sign, and el.getAnimations()[0].timeline is null.

  6. 6

    Two timelines with one name. Nested, the nearest ancestor wins: in the second example the bar follows #inner, and after renaming the inner one, #outer. Two siblings that share a name in one timeline-scope are ambiguous, and measured here the animation gets no timeline. Use unique names.

Explain it without notes

01

Why does a meter placed after a scroller not see the scroller's named timeline, and how do you fix it?

02

A bar is full from the first frame and the console is silent. What do you check?

Practice

01

A header progress bar must follow the scroll of the <main> element (not the page). Write the three CSS rules.

02

A list of cards each has its own view timeline named --card. Why will a single animation-timeline: --card on a sticky counter not follow the one in view, and what is a safer design?

Done when you can

  • You can name a scroll or view timeline and use the name in animation-timeline

  • You can say who can see a name, and use timeline-scope to widen it

  • You know a misspelled name is silent and leaves the animation at its last keyframe

  • You can debug a timeline with getAnimations()[0].timeline