View Transitions

A view transition animates between two whole DOM states. Wrap the update in document.startViewTransition(callback): the browser snapshots the page, runs the callback (rendering is paused meanwhile, so fetch data first), and overlays a pseudo-element tree over the updated DOM: ::view-transition > ::view-transition-group(name) > ::view-transition-image-pair(name) > ::view-transition-old(name) and ::view-transition-new(name). By default the page is one root group that cross-fades; an element with view-transition-name: hero gets its own group, whose box animates from old to new. Names must be unique; match-element generates them, and view-transition-class styles many groups at once.

A card morphing toward the dashed banner box, paused 250 ms into a 1 s transitionHTMLLive
<style>
  .stage { margin: 12px; width: 440px; height: 200px; outline: 2px dashed #b0bec5; }
  .card { view-transition-name: hero; width: 120px; height: 80px;
          border-radius: 12px; background: #6a1b9a; }
  .open .card { width: 440px; height: 200px; background: #ef6c00; }
  ::view-transition-group(hero) { animation-duration: 1s; }
  ::view-transition-old(hero), ::view-transition-new(hero) { height: 100%; }
</style>
<div class="stage"><div class="card"></div></div>
<script>
  onload = () => setTimeout(() => {
    const vt = document.startViewTransition(() => document.body.classList.add('open'));
    vt.ready.then(() => document.getAnimations().forEach(a => { a.pause(); a.currentTime = 250; }));
  }, 100);
</script>
Browser output of Listing 4.65
Browser output of 65

The group has grown part of the way while the purple old snapshot cross-fades into the orange new one. Snapshots are images that keep their aspect ratio, so height: 100% makes both fill the group; text in a snapshot stretches too, so give text its own name.

Cross-document transitions need no script: both same-origin pages opt in with @view-transition { navigation: auto; }, and elements sharing a view-transition-name across them morph. The pageswap and pagereveal events let script assign names for the link actually followed.

Same-document transitions are Baseline since October 2025 (Chrome 111 1 , Safari 18 10 , Firefox 144 555 ); cross-document ones ship in Chromium 126 4,389 and Safari 18.2, and Firefox simply navigates. Feature-detect with if (!document.startViewTransition) update(); and keep transitions short.