I missed the days of Flash-like smoothness. I did not miss the 10MB of overhead that came with it. For years, a smooth transition between pages in WordPress meant Barba.js or some other heavy PJAX library that would eventually break your wp_footer hooks. The View Transitions API replaces all of that.
The API is now Baseline, which means every major browser supports it. That gets you app-like fluidity out of plain CSS. The docs make it look like a one-liner, but in a multi-page application like WordPress it needs a specific setup, or the result feels like a slideshow with a delay bolted on.
Setting up the View Transitions API
The recipes need an opt-in first. WordPress serves a separate document on every page load, so the @view-transition at-rule has to be present on both the source and the destination page. On most of my projects it goes in the main theme stylesheet, or in a <style> block in the header.
@media (prefers-reduced-motion: no-preference) {
@view-transition {
navigation: auto;
}
}
The prefers-reduced-motion query is there on purpose. If someone has told their OS they do not want things flying across the screen, honor it. The navigation: auto descriptor is the part that turns this on for cross-document navigation, which is what an ordinary WordPress link does.
If your styling workflow is still a work in progress, I wrote separately about streamlining frontend CSS workflows.
Recipe 1: pixelate dissolve
A cross-fade with one extra step. Rather than only shifting opacity, the outgoing content blurs out while the incoming page sharpens into focus. It reads as expensive without pulling attention off the content.
html:active-view-transition-type(pixelate-dissolve)::view-transition-old(root) {
animation: bbioon_pixelate_out 1.4s ease forwards;
}
html:active-view-transition-type(pixelate-dissolve)::view-transition-new(root) {
animation: bbioon_pixelate_in 1.4s ease forwards;
}
@keyframes bbioon_pixelate_out {
0% { filter: blur(0px); opacity: 1; }
100% { filter: blur(40px); opacity: 0; }
}
@keyframes bbioon_pixelate_in {
0% { filter: blur(40px); opacity: 0; }
100% { filter: blur(0px); opacity: 1; }
}
Recipe 2: the wipe up
clip-path does a lot of the work with the View Transitions API. Here the new page reveals itself from the bottom up and pushes the old one out of frame.
@keyframes bbioon_wipe_in {
from { clip-path: inset(100% 0 0 0); }
to { clip-path: inset(0 0 0 0); }
}
html:active-view-transition-type(wipe-up)::view-transition-new(root) {
animation: bbioon_wipe_in 1.2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
Recipe 3: the 3D flip
I once burned three days getting a card-flip transition to work for a client with GSAP. A two-sided flip along the Y-axis now takes a few lines of CSS. It is an aggressive effect, so keep it for something like a product detail page rather than site-wide navigation.
@keyframes bbioon_flip_out {
100% { transform: rotateY(-90deg) translateX(-100vw); opacity: 0; }
}
@keyframes bbioon_flip_in {
0% { transform: rotateY(90deg) translateX(100vw); }
100% { transform: rotateY(0deg) translateX(0vw); }
}
Recipe 4: circular reveal
This pattern turns up everywhere: the new page expands out from a center point. The View Transition API documentation on MDN covers the mechanics underneath it.
@keyframes bbioon_circle_in {
from { clip-path: circle(0% at 50% 50%); }
to { clip-path: circle(150% at 50% 50%); }
}
Implementation gotchas
The bottleneck is rarely the CSS. It is the race condition between your animations and WordPress rendering the next document. On a slow server the transition can finish before the DOM is ready, so fix backend performance before you start tuning keyframes. I have written about syncing frontend and backend workflows for exactly this kind of jank.
If View Transitions API work is eating your dev hours, I can take it over. I have been working with WordPress since the 4.x days.
Where to start
The native API takes the JavaScript library out of the equation, and what is left is smoother and easier to make accessible. Start with a plain cross-fade, get your selector types debugging cleanly, then move on to the clip-path wipes. Most users will not be able to name what changed, but they notice.