</> HTML5Advent
ENFRESDEITPT

// css · Web Platform Advent #9

The View Transitions API: smooth page and state transitions

Animate between two DOM states with the View Transitions API. startViewTransition, view-transition-name, same-document and cross-document transitions explained.

A glossy Google Chrome browser logo on a metallic ring

The View Transitions API animates the change between two states of a page - a list turning into a detail view, a theme toggling, an item being filtered out - with almost no animation code. The browser snapshots the old state, snapshots the new one, and cross-fades (or transforms) between them for you.

Same-document transitions

For state changes inside a single page - a single-page app updating the DOM - wrap the DOM update in document.startViewTransition():

function showDetail(item) {
  if (!document.startViewTransition) {
    updateDOM(item); // no support: just update, no animation
    return;
  }
  document.startViewTransition(() => updateDOM(item));
}

The callback does your normal DOM work. The browser captures a "before" snapshot just before it runs and an "after" snapshot once it finishes, then animates between them. The default is a smooth cross-fade - already a nice upgrade for free.

Customising the animation with CSS

During a transition the browser builds a tree of pseudo-elements you can style: ::view-transition at the root, then ::view-transition-group(), then ::view-transition-image-pair(), which holds ::view-transition-old() and ::view-transition-new(). That is 5 nested levels, and knowing the nesting is what tells you where a given declaration will actually land. The simplest hook is the root transition group:

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.3s;
}

You target the outgoing snapshot with ::view-transition-old() and the incoming one with ::view-transition-new(), then apply any CSS animation you like. The 0.3s above is an override: the user-agent stylesheet sets animation-duration: 0.25s on ::view-transition-group(*), and the old and new snapshots inherit it through the cascade, so a transition you never style still lasts 250 ms.

A hand holding a phone showing an app profile screen in front of two desktop monitors
View transitions smooth the jump between screens - the kind of fluid navigation users expect from native apps, now achievable on the web.

Animating a specific element across states

To make one element appear to move and morph between the two states - a thumbnail growing into a full image - give it a shared view-transition-name in both states:

.hero-image {
  view-transition-name: hero;
}

The property's initial value is none, so nothing is tracked as a shared element until you opt an element in by naming it. The name must be unique on the page at any moment. When the browser finds the same name before and after, it tweens that element's position and size directly instead of cross-fading it, producing a continuous "shared element" effect.

Cross-document transitions (MPA)

Multi-page sites can transition between full page navigations too, with no JavaScript. Opt in from CSS in both pages:

@view-transition {
  navigation: auto;
}

With that single rule, same-origin navigations animate automatically. Matching view-transition-name values on both pages let an element - say a post's hero image - carry over from the list page to the article page.

Quick reference

GoalAPI
SPA state changedocument.startViewTransition(cb)
MPA navigation@view-transition { navigation: auto }
Style the fade::view-transition-old/new(root)
Shared elementview-transition-name (unique)

Support and progressive enhancement

Same-document transitions ship in Chromium browsers and Safari; cross-document support is rolling out and Firefox is implementing both. Because you guard with if (!document.startViewTransition) and the cross-document version is a pure CSS opt-in, the API degrades gracefully: unsupported browsers simply update the page with no animation. That makes it safe to add today as a progressive enhancement.

Reach for view transitions when a jump between states feels abrupt. A small amount of motion gives users a sense of continuity - where things came from and where they went - without you hand-writing a single keyframe in most cases.

A transition hides the seam between two pages; it does not make the second one arrive any sooner. If the wait itself is the problem, that is what the Speculation Rules API addresses - by fetching or building the next page before the click, with the costs that implies.