View Transitions: The Smooth Parts
Now that cross-document view transitions are gradually making their way into modern browsers, now seems like the perfect time to explore them, if you haven’t already. They are, in fact, surprisingly straightforward to implement. And just like we’ve seen with modern images, view transitions can be slapped onto existing projects as a progressive enhancement.
That my website is now called a “multi-page app” (MPA) is still a hilarious thought 😂, but hey – I’ll take it if I get a smoother experience with basically just one line of CS…
View Transitions: The Smooth Parts
Now that cross-document view transitions are gradually making their way into modern browsers, now seems like the perfect time to explore them, if you haven’t already. They are, in fact, surprisingly straightforward to implement. And just like we’ve seen with modern images, view transitions can be slapped onto existing projects as a progressive enhancement.
That my website is now called a “multi-page app” (MPA) is still a hilarious thought 😂, but hey – I’ll take it if I get a smoother experience with basically just one line of CSS:
@view-transition {
navigation: auto;
}
You add this @view-transition
declaration to all pages of your site that should be transitioned – in my case I added it to the main.css
– and every browser that supports cross-document view-transitions, will now fade between the (same-origin) pages. It just works.
And: You can try it on this website already. 🥳 To see view transitions in action, click on “Home” or ”Articles” in the main navigation, for example. But please come back here after that!
The way cross-document view transitions work is that the browser opens the page you are navigating to in the background, takes a snapshot, and then cross-fades between your current page snapshot, aka the old state, and the new one.
Here are Bramus and Kevin explaining view transitions in more detail: https://www.youtube.com/watch?v=quvE1uu1f_I
Respecting prefers-reduced-motion
Something that can make the whole experience more accessible is to respect the prefers-reduced-motion
media query, so that people with motion sensitivity will not see the animation:
@view-transition {
navigation: auto;
}
@media (prefers-reduced-motion) {
@view-transition {
navigation: none;
}
}
But: you should always consider whether this is actually necessary – which depends on the type of animation. It’s certain kinds of motion, or degrees of motion that can cause problems, not simply the presence of any animation. Simple fades between pages, for example, are generally seen as quite safe and less likely to trigger vestibular disorders. If you are using grow, scroll, or slide animations, however, better use prefers-reduced-motion
to reduce or remove those motion-heavy transitions.
A Few View Transition CSS Tricks
Now, as Kevin mentions in the conversation with Bramus, once you start looking under the hood, things can become a bit more complicated. This post won’t go into all the details – we don’t have time for that – but I’ll highlight a few of the things that are really straightforward to implement and can further improve the experience with a few quick moves.
Changing the Transition Type
The first one is how you can change the types of transitions. At the most basic level, this can be done upfront by adding one additional line of CSS. Here’s how you can use a slide animation:
@view-transition {
navigation: auto;
types: slide, forwards;
}
Fine-tuning Animations
If you want a bit more control, though, you will have to dig a bit deeper and use one (or more) of the many pseudo elements, that are available once you activate view transitions for a page. As I mentioned, view transitions work by taking snapshots of the old and new states – of the root document and all named transition that have a view-transition-name. With those snapshots, the API then constructs an actual pseudo-element tree:
::view-transition
└─ ::view-transition-group(root)
└─ ::view-transition-image-pair(root)
├─ ::view-transition-old(root)
└─ ::view-transition-new(root)
The ::view-transition
sits in an overlay over everything else on the page. The ::view-transition-group
contains the image pair being used for the transition.
The cool thing is: all the animations are now done using CSS animations. So you can use regular CSS to adjust the duration or the timing function, for instance:
::view-transition-group(root) {
animation-duration: 600ms;
animation-timing-function: cubic-bezier(0.3, 0, 0.8, 1);
}
Or, you can even define @keyframe
animations and set the animations for the old and new state to completely individual animations.
::view-transition-old(root) {
animation-name: dash-out;
}
::view-transition-new(root) {
animation-name: dash-in;
}
@keyframes dash-out {
to {
translate: 100% 0;
opacity: 1;
}
}
@keyframes dash-in {
from {
translate: -100% 0;
opacity: 1;
}
}
This is still a very basic example. But the ability to use standard CSS keyframe animations gives you plenty of flexibility to craft unique, tailored transitions for your specific use case.
There’s also a lot more you can do to fine-tune animations, for example by using the JavaScript features of the View Transitions API and the pagereveal and pageswap events. That’s a topic for another post, though. For now, I’ll probably stick with a more basic implementation on this site.
Progressive enhancement FTW!
❦
This is post 20 of Blogtober 2025.
~
0 Webmentions
¯_(ツ)_/¯
ⓘ Webmentions are a way to notify other websites when you link to them, and to receive notifications when others link to you. Learn more about Webmentions.