Der Moment zwischen zwei Seiten ist der einzige, in dem eine Website wie eine Website aussieht und nicht wie ein Programm. Genau dafür haben Projekte jahrelang ihre gesamte Navigation umgebaut – für eine Viertelsekunde.
Ein Effekt und sein Preis
Ein Klick auf einen Link, kurz wird es weiß, die neue Seite steht da. Header und Navigation sind auf beiden Seiten dieselben, werden aber neu gezeichnet. Der Besucher sieht einen Bruch an einer Stelle, an der inhaltlich keiner ist, und genau dieser Bruch ist der Unterschied zwischen einer Website und der App auf seinem Telefon.
Die übliche Antwort darauf war, dem Browser die Navigation ganz wegzunehmen und
die Seite als Single Page Application zu bauen – als SPA also, als eine einzige
HTML-Seite, die ihre Inhalte danach nur noch austauscht. Das heißt: ein Router im
Frontend, Inhalte per fetch nachladen, den Zustand im Speicher halten,
den Übergang selbst animieren. Damit handelt man sich alles ein, was ein
serverseitig geliefertes Dokument von Haus aus richtig macht – History,
Scrollposition, Zurück-Button, Fehlerseiten, den ersten Aufbau ohne JavaScript. Man
baut es nach, meistens schlechter, und wartet es danach für immer.
Das ist ein hoher Preis für einen visuellen Effekt, und er ist seit einer Weile nicht mehr nötig. Der Browser kann den Übergang selbst.
Zwei APIs, ein Name
„View Transitions“ meint zwei verwandte, aber getrennt zu betrachtende Dinge. Wer sie durcheinanderbringt, wundert sich später über den Browser-Support:
- Innerhalb eines Dokuments: document.startViewTransition() nimmt eine Funktion entgegen, die den DOM verändert – Filter, Sortierung, ein Tab-Wechsel. Der Browser macht vorher und nachher einen Snapshot und animiert dazwischen. Braucht JavaScript.
- Zwischen zwei Dokumenten: @view-transition { navigation: auto } im CSS. Ein echter Seitenwechsel, zwei getrennte HTML-Dokumente, kein einziges Byte JavaScript.
Für eine klassisch gerenderte Seite ist der zweite Fall der interessante, und genau er ist der schwächer unterstützte. Der erste kommt trotzdem vor, denn er ist das, was die Demo weiter unten zeigt – und was du für Filter und Sortierungen ohnehin brauchst.
Drei Zeilen für die ganze Seite
Der Einstieg ist ungewöhnlich kurz. Diese Regel muss im CSS beider Dokumente stehen, also der Seite, die verlassen wird, und der Seite, die kommt:
@view-transition {
navigation: auto;
}
In einem Projekt mit einem gebündelten Stylesheet ist das wörtlich eine Stelle, und ab da gilt es für jede Route. Der Browser blendet dann beim Navigieren die alte Seite in die neue über – ein Crossfade von einer knappen Viertelsekunde.
Damit das überhaupt greift, müssen ein paar Bedingungen erfüllt sein: Beide Seiten
liegen auf derselben Origin, dazwischen darf keine Umleitung auf eine fremde Origin
stehen, und die Navigation muss vom Typ push, replace
oder traverse sein. Ein Neuladen der Seite zählt also nicht, ein Klick
auf einen Link und der Zurück-Button schon.
Angepasst wird der Übergang über einen Baum aus Pseudo-Elementen, den der Browser
für die Dauer der Animation über allem anderen aufspannt. Ganz oben liegt
::view-transition, darunter je eine Gruppe pro aufgenommenem Element,
und in jeder Gruppe liegen der alte und der neue Snapshot übereinander. Das
Wurzelelement bekommt dabei automatisch den Namen root:
/* Der Voreinstellung fehlt nichts außer Charakter: Sie blendet
die ganze Seite über. Zwei eigene Keyframes reichen, um daraus
eine Richtung zu machen. */
::view-transition-old(root) {
animation: 180ms both zw-seite-raus;
}
::view-transition-new(root) {
animation: 260ms 60ms both zw-seite-rein;
}
@keyframes zw-seite-raus {
to { opacity: 0; translate: 0 -1.2rem; }
}
@keyframes zw-seite-rein {
from { opacity: 0; translate: 0 1.2rem; }
}
Ein Element weiterverfolgen
Eine übergeblendete Seite ist nett, aber nicht der Punkt. Interessant wird es,
wenn ein Element auf beiden Seiten vorkommt. Bekommt er dort denselben
view-transition-name, nimmt der Browser ihn aus dem Bild heraus und
animiert Position, Größe und Seitenverhältnis von der alten an die neue Stelle. Das
Kachelbild in der Übersicht wird dann zum Kopfbild im Artikel, sichtbar auf dem Weg
dorthin.
Die Demo darunter macht genau diesen Wechsel – allerdings innerhalb einer Seite, damit du ihn auch in einem Browser siehst, der den echten Seitenwechsel noch nicht animiert. Der zweite Button macht dieselbe Umschaltung ohne Übergang.
- Seitenübergänge ohne SPA
- Die Fensterbreite als Zahl
- Lesbare Textfarben ohne JavaScript
Dasselbe Bild und dieselbe Überschrift wie in der Übersicht – nur an einem anderen Platz und in einer anderen Größe. Den Weg dazwischen hat niemand programmiert.
Die Demo braucht JavaScript.
Ein Name darf im selben Dokument allerdings nur einmal vergeben sein. Genau daran
scheitert der erste Versuch auf einer Übersichtsseite: Zwölf Kacheln, alle mit
view-transition-name: bild, und der Browser bricht den gesamten
Übergang ab – mit einer Warnung in der Konsole, sonst kommentarlos.
Auf einer serverseitig gerenderten Seite ist die Lösung einfacher als in einer Single Page Application, weil die eindeutige Kennung schon dasteht. Der Slug des Artikels macht aus einem Namen zwölf:
<img class="zw-blog-card__image"
style="view-transition-name: bild-{{ $article['slug'] }}"
src="{{ $article['image'] }}" alt="">
<img class="zw-article__hero"
style="view-transition-name: bild-{{ $article['slug'] }}"
src="{{ $article['image'] }}" alt="">
In der Übersicht ist damit jeder Name einmalig, auf der Artikelseite gibt es nur einen einzigen, und weil beide Seiten denselben Slug einsetzen, finden sie zueinander. Der Server weiß, welcher Artikel gemeint ist – er muss es dem Browser nur hinschreiben.
Vorwärts sieht anders aus als zurück
Derselbe Übergang für „in den Artikel hinein“ und „zurück zur Übersicht“ fühlt sich nach kurzer Zeit falsch an. Dafür gibt es Typen: Jede Seite deklariert, in welchem Zustand sie sich befindet, und das Stylesheet wählt danach die Animation aus.
/* Auf der Artikelseite */
@view-transition {
navigation: auto;
types: detail;
}
/* Im gemeinsamen Stylesheet: gilt nur, solange ein Übergang
dieses Typs läuft. */
:root:active-view-transition-type(detail)::view-transition-old(root) {
animation-name: zw-nach-links;
}
Das reicht, solange die Richtung an der Zielseite hängt. Soll sie davon abhängen,
woher jemand kommt, braucht es doch etwas JavaScript. Zwei Events
gehören zum selben Paket: pageswap feuert auf der alten Seite, kurz
bevor sie verschwindet, und pagereveal auf der neuen, bevor ihr erstes
Bild steht.
window.addEventListener('pagereveal', (event) => {
// Ohne aktiven Übergang gibt es nichts zu entscheiden – das ist
// zugleich die Prüfung auf Browser, die keinen anbieten.
if (!event.viewTransition) return;
const herkunft = navigation.activation.from?.url ?? '';
event.viewTransition.types.add(
herkunft.includes('/blog/') ? 'zurueck' : 'vorwaerts'
);
});
Die Abfrage auf event.viewTransition ganz oben ist wichtiger, als sie
aussieht: Sie ist gleichzeitig die Feature Detection. Wo der Browser keinen Übergang
startet, kommt der Rest der Funktion nie zur Ausführung, und die Seite navigiert
normal.
Was in der Praxis schiefgeht
Der Einstieg ist drei Zeilen lang, die Liste der Überraschungen ist länger. Eine davon ist keine Kleinigkeit, sondern ein Argument gegen den sorglosen Einbau.
Deshalb gehört vor den Übergang die Arbeit an der Zeit bis zum Hauptinhalt und nicht umgekehrt. Der Rest ist überschaubar:
- Doppelte Namen brechen den kompletten Übergang ab, nicht nur das betroffene Element. Besonders leicht passiert das mit Komponenten, die auf einer Seite zweimal vorkommen – dieselbe Kachel im Inhalt und noch einmal in einer Empfehlungsliste darunter.
- Aufgenommen wird der sichtbare Bereich, nicht das Dokument. Wer weit unten auf einer langen Seite klickt und oben auf der nächsten landet, sieht einen Sprung, den keine Animation glättet.
- Ein Header mit position: fixed wandert im Crossfade des root-Snapshots mit, obwohl er auf beiden Seiten an derselben Stelle steht. Ein eigener view-transition-name auf dem Header nimmt ihn aus der Bewegung heraus und lässt ihn stehen.
- Ein benanntes Element darf nicht umbrochen werden. Steht es in einem mehrspaltigen Layout und verteilt sich über zwei Spalten, verwirft der Browser den Namen.
Und schließlich die Bewegung selbst. Ein reiner Crossfade ist unkritisch, aber
sobald etwas über den Bildschirm fährt oder skaliert, gehört die Abfrage dazu.
@view-transition darf dafür in einer Media Query stehen, was die
Sache angenehm kurz macht:
/* Voreinstellung ist kein Übergang; er wird nur dort
eingeschaltet, wo niemand widersprochen hat. */
@view-transition {
navigation: none;
}
@media (prefers-reduced-motion: no-preference) {
@view-transition {
navigation: auto;
}
}
Wo die Browser stehen
Die beiden Hälften der API stehen unterschiedlich da, und das ist der wichtigste Satz dieses Artikels. Innerhalb eines Dokuments ist die Sache erledigt:
- document.startViewTransition() gibt es in Chrome und Edge seit Version 111 vom März 2023, in Safari seit Version 18 vom September 2024 und in Firefox seit Version 144 vom 14. Oktober 2025. Seit diesem Tag ist die Funktion Baseline.
- Auch der Selektor :active-view-transition ist inzwischen überall angekommen – Firefox 147 hat ihn am 13. Januar 2026 nachgezogen, Chrome und Edge hatten ihn seit 125, Safari seit 18.2.
Beim Wechsel zwischen zwei Dokumenten fehlt dagegen ein Browser, und zwar ausgerechnet der, der die andere Hälfte längst hat:
- Chrome und Edge liefern @view-transition seit Version 126 vom Juni 2024 aus.
- Safari seit Version 18.2 vom Dezember 2024, auf dem iPhone in derselben Version.
- Firefox unterstützt den dokumentübergreifenden Fall bislang nicht. Die Regel wird dort schlicht ignoriert, die Navigation läuft wie vorher.
Damit ist dieser Teil ausdrücklich nicht Baseline, und das wird er erst,
wenn Firefox nachzieht. Für die Entscheidung im Projekt ist das aber weniger
dramatisch, als es klingt: Der Ausfallmodus ist der Zustand von vorher. Eine
unbekannte At-Regel überspringt der Browser beim Parsen, ein unbekannter
view-transition-name fällt als ungültige Deklaration weg, und was
bleibt, ist ein gewöhnlicher Seitenwechsel. Es gibt hier kein kaputtes Zwischending,
das man abfangen müsste.
Fazit
Ein animierter Seitenwechsel war jahrelang kein Effekt, sondern eine Architekturentscheidung. Wer ihn wollte, kaufte einen Router, ein Framework und die Pflicht, die History des Browsers nachzubauen. Dieser Zusammenhang ist aufgelöst. Der Effekt kostet jetzt drei Zeilen in einer Datei und ein Attribut an dem Bild, das mitwandern soll.
Der eigentliche Unterschied liegt aber im Scheitern. Eine Anwendung, deren JavaScript nicht lädt, zeigt eine leere Seite. Ein Übergang, den der Browser nicht kennt, zeigt die Seite. Das ist die Art von Technik, die man einbauen kann, ohne einen Plan B dafür zu haben – und der Grund, warum ich für eine Inhaltsseite weiterhin keinen Router brauche.
Quellen
Syntax, Bedingungen für den dokumentübergreifenden Fall und die Pseudo-Elemente nach der MDN-Referenz zu @view-transition und der Übersicht zur View Transition API. Die Versionsangaben und der Baseline-Stand stammen aus Web Platform Status, abgerufen im September 2026.