Ever wondered why we've spent the last decade building massive JavaScript architectures just to get a simple page fade? For years, if you wanted a smooth transition between pages, you were essentially forced into the Single Page Application (SPA) rabbit hole. You needed a router, a state manager, and a whole lot of client-side complexity just to avoid that jarring white flash during navigation.

This changes everything. With the arrival of CSS View Transitions Level 2, we can finally bring that high-end polish to Multi-Page Applications (MPAs) using nothing but a few lines of CSS. No routers, no complex lifecycle hooks—just the browser doing what it does best.

The End of the MPA 'Jank'

Right, let's talk about the core problem. Traditionally, when you click a link on an MPA, the browser clears the current page and starts fresh. It's a hard reset. The CSS View Transitions API—specifically the cross-document implementation—intercepts this process. It takes a screenshot of the old state, captures the new state, and lets you animate between them using standard CSS Keyframes.

The beauty of this is that it's declarative. You aren't telling the browser how to manage memory or DOM nodes; you're just telling it how the transition should look. As the W3C spec puts it, the API avoids the 'troublesome in-between state' by performing a visual transition in a separate layer.

Setting Up Your Design System

To get this working in a production design system, you need to opt-in on both the source and destination pages. If you miss one, the browser defaults back to a standard hard navigation. In my experience, the best way to handle this is by adding a global utility or base style rule.

@view-transition {
  navigation: auto;
}

This snippet is the 'magic button.' Once applied, the browser will automatically perform a cross-fade transition between any same-origin pages. It's a massive win for UX with almost zero effort.

Beyond the Cross-Fade: Element Morphing

A simple fade is nice, but the real power lies in element-level transitions. Imagine a card on a blog index page 'morphing' into the hero image of the article page. To achieve this, we use the view-transition-name property.

You must give the related elements the same unique name on both pages. Here's a quick look at how you'd structure that in your CSS:

/* Page A: The List View */
.card-thumbnail {
  view-transition-name: active-image;
  contain: layout;
}

/* Page B: The Article Detail */
.hero-image {
  view-transition-name: active-image;
  contain: layout;
}

Pro tip: Don't forget the contain: layout or contain: paint. The browser needs clear boundaries to capture the element correctly. Without it, you might see some very strange layout shifts during the animation.

Common Pitfalls to Avoid

I've been playing with this in several projects lately, and there are a few things that trip everyone up at first. First, remember that there is no JavaScript API for cross-document transitions. You can't call a function to trigger it; it's purely triggered by navigation.

  • Same-Origin Only: If you link to an external site or even a different subdomain, the transition won't fire.
  • Pseudo-Elements: You aren't animating the actual DOM. You're animating ::view-transition-old and ::view-transition-new. This means standard dev tools inspection for the 'live' animation can be tricky.
  • Browser Support: While Chrome and Edge have solid support (126+), and Safari has joined the party (18.2+), Firefox is still catching up. Always treat this as a progressive enhancement.

Customising the Animation

You don't have to stick with the default fade. By targeting the root pseudo-elements, you can create slide effects, zooms, or even complex multi-stage reveals. Here's how I usually set up a simple slide-out for the old page:

::view-transition-old(root) {
  animation: 300ms ease-out both slide-to-left;
}

@keyframes slide-to-left {
  to { transform: translateX(-100%); }
}
• • •

Wrapping Up

  • Use @view-transition { navigation: auto; } on both pages to enable the basic cross-fade.
  • Assign view-transition-name to matching elements across pages for high-end morphing effects.
  • Treat transitions as a progressive enhancement to ensure users on all browsers still have a functional experience.

I'm genuinely excited about this. It levels the playing field for MPAs, allowing us to keep the simplicity of server-side routing while delivering the delightful motion that users have come to expect from modern web apps. I'd encourage you to try adding it to a small project today—you'll be surprised how much it transforms the feel of the site.

If you want to go deeper and learn how to build real, production-ready CSS design systems step by step, check out my full course here: CSS Design Systems Course

I cover this and the other twenty-seven CSS shifts of 2026 in Modern CSS 2026 on Amazon, my catch-up book for people who learned CSS five years ago.

Feel free to reach out and share your transition experiments with me on Twitter or LinkedIn. I'd love to see what you're building!