Basic Cross-Fade with the View Transitions API
Wrap any DOM mutation in document.startViewTransition() and the browser cross-fades between the old and new state for free — no @keyframes required.
Detailed Explanation
The Default Effect: Cross-Fade
When you call document.startViewTransition(callback) and do not specify any custom animation, the browser performs a 300ms cross-fade between the captured before-and-after snapshots. This is the cheapest and most universal effect — it works on any kind of DOM change without requiring you to opt specific elements in via view-transition-name.
function update() {
document.body.classList.toggle('dark');
}
if (document.startViewTransition) {
document.startViewTransition(update);
} else {
update();
}
Tweaking the timing
To override the default 300ms, target the two pseudo-elements that the browser auto-creates:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 500ms;
animation-timing-function: ease-in-out;
}
The root argument refers to the root snapshot group that the browser creates implicitly. Every element that does not have its own view-transition-name falls into this group, and the cross-fade happens at the document level.
Why this is better than opacity transitions
A naive transition: opacity 300ms only animates one element at a time and breaks down for layout-affecting changes (adding nodes, changing widths, swapping templates). The View Transitions API captures the entire visible viewport as raster snapshots, so even reflow-heavy mutations animate smoothly without thrashing the layout engine.
Use Case
Theme toggles, content swaps, search results re-rendering, or any state change where you want a polished cross-fade with zero per-element opt-in. Especially valuable for dynamic content where you do not know in advance which nodes will change.