Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 11.2Intermediate ~18 min · shot 2 of 7

Naming Elements and Morphing

In one line

Give an element a view-transition-name and the browser snapshots it separately and moves it from its old box to its new one. Names must be unique at the moment of capture, or the whole transition is skipped.

0/7 · 0%

Think of it like this

Two train lines each have a carriage painted 7. The conductor pairs old carriage 7 with new carriage 7 and slides one into the other's place. If two carriages are both numbered 7 he cannot tell which is which, and the shuffle is called off.

Words you'll meet

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

view-transition-name
A CSS property that makes an element its own snapshot group, paired with the same name in the other state.
group
The pseudo-element ::view-transition-group(name) that moves from the old box to the new box.
view-transition-class
A shared styling label, not unique, so one rule can style many groups.
aspect ratio
Width divided by height. Equal ratios scale into each other without squashing.

Step by step

01Without a name, everything cross-fades

Pause, press Swap (no name) and step. Only the root tracks exist (group, old and new): the old card fades out at the left while the new blue card fades in at its final spot. Two cards are visible at once.

02With a name, one card travels

Press Swap (named). group(volt), old(volt) and new(volt) tracks join the root ones, 600 ms because the CSS says so. One card moves and grows.

03Break the rule: two names the same

The second example shares one name between three cards. ready rejects with InvalidStateError and nothing animates. A unique name per card makes all three promises fulfill.

04Then change the speed

Set ::view-transition-group(volt) to animation-duration: 1500ms: the volt tracks are 1500 ms and the root tracks stay 250 ms (measured). Changing ease-in-out to linear changes group, old and new together: the pictures inherit from the group.

Try it yourself

  1. 1

    Change the morph's duration and easing

    In the first example set ::view-transition-group(volt) to 1500 ms and read the tracks: volt is 1500 ms, root stays 250 ms. Then use linear and read group, old and new.

  2. 2

    Name only the new state

    Change #card.named { view-transition-name: volt; } to #card.big { view-transition-name: volt; } and press Swap (named): see the breakIt below.

Code & diagrams

One card, named and not named

Pause, press one button, then Step. The named version has volt tracks (600 ms, from the CSS) next to the root tracks; the unnamed one has only the root ones.

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

view-transition-name: volt
view-transition-name: none
Two elements, one name (and the fix)

The first flip fails with InvalidStateError but still reorders the cards. The second gives each card a unique name and animates.

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

update ran
same name on 3 cards | updateCallbackDone fulfilled | ready rejected InvalidStateError: Transition was aborted because of invalid state. Snapshot capture failed | finished fulfilled
update ran
one name per card | updateCallbackDone fulfilled | ready fulfilled | finished fulfilled
names: card-A, card-B, card-C

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

Name an element in one state only

Change #card.named { view-transition-name: volt; } to #card.big { view-transition-name: volt; }.

terminal
$ Press Swap (named) and step
── what you'll see ──
No error and no `group(volt)` track: only `new(volt)` (600 ms) plus the `root` tracks (measured: six animations on four pseudo-elements). The new card fades in, the old one fades out with the page, nothing travels.

Myth vs fact

Myth

A view-transition-name is an id: it must be unique on the whole site.

Fact

It must be unique only among elements being rendered at the moment of capture. Both pages can use volt; it need not match any id.

Pro corner

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

  • ▸

    A name set from script stays until you clear it: reset it when finished settles if the element must not join later transitions (Topic 11.4 does this for the back/forward cache).

Remember this

  1. 1

    The problem this solves: the default transition cross-fades the whole page, so a card that moved fades out in one place while an identical card fades in at another, and people lose track of it. A named card is tracked from its old box to its new one.

  2. 2

    Naming. view-transition-name: volt makes an element its own group, cut out of the page snapshot. If the old and new states both have an element with that name, the browser animates the group from the old box to the new box while the old picture fades out and the new fades in inside it. Measured for the first example below (a 60 × 40 card that moves 120 px right and 22 px down into a 150 × 64 box): the group's transform goes from matrix(1, 0, 0, 1, 6, 41) to matrix(1, 0, 0, 1, 126, 63) (the start offset depends on what sits above the card, but the travel of 120 and 22 px is the card's own), and width/height from 60px/40px to 150px/64px. The pictures are bitmaps scaled to the group, so text goes soft mid-morph; the same aspect ratio (the Volt drawing has one viewBox on both pages) hardly distorts.

  3. 3

    A name must be unique when the snapshot is taken. Two elements with one name make ready reject with InvalidStateError; the update still runs and finished fulfills, so the change happens with no animation. Give list items a name each (el.style.viewTransitionName = "card-" + id).

  4. 4

    Names in one state only. A name only in the old or only in the new state is no error: that group just fades out or in. A display: none element's name is ignored, and when it appears only its new picture exists (measured). A named element entirely off screen still gets a group and an animation (measured: x from -492 to -292), so name only what is visible.

  5. 5

    **view-transition-class** styles many groups with one rule: view-transition-class: chip on each chip, then ::view-transition-group(.chip) { animation-duration: 800ms }; names stay unique. view-transition-name: match-element gives each element an automatic unique name (the groups are all called match-element in the timeline). In our Chromium 153 runs the class rule applied to them (three groups of 800 ms, old pictures of 700 ms), but an earlier run left the groups at 250 ms, so name items explicitly when the timing matters. view-transition-name: auto is not supported there (CSS.supports says so).

Explain it without notes

01

Why does a cross-fade of a moved card confuse people, and what changes with a name?

02

A transition animates nothing and ready rejects with InvalidStateError. What is the likely cause, and what is view-transition-class for?

Practice

01

A grid of 12 cards reorders when sorted. Name each card from data-id and give every group a 400 ms ease.

02

Make a home-page image morph into the detail page's image without ever naming two elements hero on one page.

Done when you can

  • You can make one element morph between two states

  • You can recognise and fix the duplicate-name failure

  • You can use a class to style many groups at once