Ein Klick auf einen Link ist der härteste Moment einer Website. Die alte Seite verschwindet, kurz steht etwas Weißes da, die neue Seite ist plötzlich vollständig vorhanden. Niemand hat je gesagt, dass das so sein muss: Es ist nur die Art, wie Browser seit jeher navigieren. Die View Transitions API kehrt diesen Ablauf um. Der Browser macht ein Bild vom alten Zustand, ein Bild vom neuen, legt beide übereinander und blendet sie ineinander. Dieser Beitrag zeigt, wie weit das inzwischen ohne eine einzige Zeile JavaScript trägt, wie ein Element vom alten in den neuen Zustand hinüberwandert und an welchen vier Stellen die Sache trotzdem noch Arbeit macht.
- Der Browser schnappt sich zwei Bilder, eines vom alten und eines vom neuen Zustand, und legt sie als eigene Ebene über die Seite. Alles, was du danach machst, ist eine ganz normale CSS-Animation auf Pseudo-Elementen.
- Für einen Seitenwechsel zwischen zwei echten HTML-Dateien reicht ein Block mit navigation: auto, und zwar in beiden Seiten. Same-Origin ist Bedingung, JavaScript ist keine.
- view-transition-name hebt ein Element aus der Überblendung heraus: Es bekommt eine eigene Gruppe und wandert von seiner alten an seine neue Stelle. Der Name muss pro Seite genau einmal vorkommen, sonst fällt der ganze Übergang aus.
- Die Cross-Document-Variante fehlt in Firefox noch, und ein Ladevorgang über vier Sekunden bricht sie ab. Beides ist kein Fehler im Code, sondern der Grund, warum der Übergang eine Zugabe bleiben muss und nie das Gerüst.
Warum fühlt sich ein Seitenwechsel härter an als ein Klick?
Innerhalb einer Seite ist Bewegung selbstverständlich geworden. Ein Panel fährt auf, eine Liste sortiert sich um, ein Bild wächst beim Zeigen. Sobald aber ein Link angeklickt wird, hört das auf. Die alte Seite wird verworfen, der Bildschirm ist für einen Moment leer, die neue Seite steht fertig da. Zwischen den beiden Zuständen liegt nichts, was erklärt, wie man vom einen zum anderen gekommen ist.
Der Grund dafür ist technischer Natur und leuchtet sofort ein, sobald man ihn einmal ausspricht: Es gibt keinen Moment, in dem beide Seiten gleichzeitig existieren. Die alte ist weg, bevor die neue anfängt. Eine Animation braucht aber genau das, nämlich zwei Zustände nebeneinander. Deshalb haben alle bisherigen Lösungen dasselbe getan: Sie haben die echte Navigation abgefangen, den Inhalt selbst nachgeladen und beide Zustände künstlich im selben Dokument gehalten. Das ist der Grund, warum Seitenübergänge so lange an eine Single-Page-Anwendung gebunden waren.
Die View Transitions API dreht das um. Statt beide Seiten gleichzeitig am Leben zu halten, hält der Browser zwei Bilder fest: eines vom alten Zustand, kurz bevor er verschwindet, eines vom neuen, kurz bevor er erscheint. Diese beiden Bilder legt er als eigene Ebene über alles und animiert sie gegeneinander. Die Seite darunter ist zu diesem Zeitpunkt längst die neue. Was man sieht, ist ein Nachbild.
Ein Seitenübergang animiert keine Seiten. Er animiert zwei Bilder von Seiten.
Was macht der Browser dabei genau?
Der Ablauf hat vier Schritte, und er ist in beiden Varianten derselbe. Der Browser hält den alten Zustand als Bild fest. Er baut den neuen Zustand auf, hält auch davon ein Bild fest. Er legt beide Bilder in eine eigene Ebene über dem Dokument. Und er animiert sie, standardmäßig als Überblendung über eine Viertelsekunde.
Diese Ebene ist kein geheimer Mechanismus, sondern ein kleiner Baum aus Pseudo-Elementen, den du mit ganz normalem CSS ansprichst. Ganz oben steht ::view-transition, darunter je eine ::view-transition-group für jeden benannten Teil, darin ein ::view-transition-image-pair und darin schließlich ::view-transition-old und ::view-transition-new. Ohne weitere Angaben gibt es genau eine Gruppe, und die heißt root: der ganze sichtbare Bereich als ein Bild.
Weil das Pseudo-Elemente sind, gilt dort alles, was du über CSS-Animationen weißt. animation-name, animation-duration, animation-timing-function, eigene Keyframes. Es gibt keine neue Sprache für Bewegung und keine eigene Zeitachse. Es gibt nur zwei zusätzliche Bilder, an die du herankommst.
- initial
- opacity 0, y 12
- animate
- opacity 1, y 0
- duration
- 0.5 s
- ease
- cubic-bezier(.22, 1, .36, 1)
- stagger
- 80 ms
Zwölf Pixel Weg und eine halbe Sekunde. Mehr braucht ein Hero nicht.
Das alte Bild geht, das neue kommt. Beide liegen für die Dauer des Übergangs in derselben Ebene über der Seite.
::view-transition-old(root) { animation: 220ms cubic-bezier(0.4, 0, 1, 1) both fade-out;} ::view-transition-new(root) { animation: 320ms cubic-bezier(0.16, 1, 0.3, 1) both rise-in;} @keyframes rise-in { from { opacity: 0; translate: 0 1.5rem; }}
Wie viel CSS braucht ein echter Seitenwechsel?
Weniger, als man erwartet. Für den Wechsel zwischen zwei echten HTML-Dateien steht in beiden Seiten ein Block mit einer einzigen Angabe darin, und damit ist die Sache eingerichtet. Kein Skript, kein Router, kein abgefangener Klick. Der Browser erkennt die Navigation selbst und legt den Übergang dazwischen.
Zwei Bedingungen gehören dazu. Beide Seiten müssen denselben Ursprung haben, also gleiches Protokoll, gleicher Hostname, gleicher Port. Und beide müssen die Regel enthalten: Die alte Seite erlaubt damit, dass ihr Bild festgehalten wird, die neue, dass sie das Gegenstück dazu liefert. Fehlt eine der beiden Seiten, passiert nichts, und zwar wortlos.
Welche Navigation zählt, entscheidet der Wert. Bei auto sind es die Wege, die aus der Seite selbst kommen: ein Klick auf einen Link, ein abgeschicktes Formular, die Zurück- und Vorwärts-Taste. Nicht dazu gehört alles, was aus der Oberfläche des Browsers stammt, also die Adresszeile, ein Lesezeichen und das Neuladen. Das ist eine sinnvolle Trennung: Ein Übergang soll eine Bewegung innerhalb einer Sitzung zeigen, nicht das Betreten der Seite von außen.
@view-transition { navigation: auto; } /* optional: die Voreinstellung überschreiben */::view-transition-group(root) { animation-duration: 280ms;}
Zwei Seiten, dieselbe Regel. Vergisst eine von beiden sie, gibt es keinen Übergang.
Wie wandert ein einzelnes Element mit?
Bis hierher ist der ganze Bildschirm ein einziges Bild. Interessant wird es, sobald ein Teil davon eine eigene Rolle bekommt. Genau das macht view-transition-name: Das Element wird aus der großen Überblendung herausgenommen und bekommt eine eigene Gruppe in der Ebene darüber.
Findet der Browser denselben Namen in beiden Zuständen, behandelt er die beiden Vorkommen als dasselbe Ding. Er vergleicht Lage und Größe und animiert die Gruppe von der alten Position an die neue, während die beiden Bilder darin ineinander übergehen. Das ist der Effekt, den man von Bildergalerien kennt: Ein kleines Vorschaubild in der Übersicht wird zum großen Bild in der Detailansicht, und der Weg dazwischen ist sichtbar. Dafür sind es zwei Deklarationen, eine pro Seite.
Eine Regel dabei ist streng: Der Name muss innerhalb eines Dokuments eindeutig sein. Taucht er zweimal auf, etwa weil alle zwölf Karten einer Übersicht dieselbe Klasse tragen, kann der Browser die Paare nicht bilden und überspringt den gesamten Übergang. Nicht nur diese eine Gruppe, den ganzen. In einer Liste gehört der Name deshalb an das eine Element, um das es geht, nicht an die Klasse. Wo die Namen aus Daten kommen, setzt man sie über eine Custom Property oder liest sie mit der typisierten Fassung von attr aus einem Attribut. Gemeinsames Aussehen für viele Gruppen beschreibt dagegen view-transition-class, ohne dass dafür ein Name doppelt vergeben werden müsste.
/* Übersicht: nur die angeklickte Karte bekommt den Namen */ .karte[data-aktiv] .bild { view-transition-name: beitragsbild; } /* Detailseite: dasselbe Bild, dieselbe Bezeichnung */ .beitrag > img { view-transition-name: beitragsbild; } /* verzerrte Bilder geraderücken */::view-transition-old(beitragsbild),::view-transition-new(beitragsbild) { object-fit: cover; height: 100%;}
- Den Namen an eine Klasse hängen, die in der Übersicht zwölfmal vorkommt. Der Browser findet zwölf Elemente mit demselben Namen und lässt den ganzen Übergang aus.
- Ein Element benennen, das auf der Zielseite gar nicht vorkommt. Dann gibt es nur eine Hälfte des Paares, und statt einer Wanderung sieht man ein Verschwinden.
- Die Größe des Bildes über eine Klasse am Element regeln. Die Pseudo-Elemente erben das nicht, und bei unterschiedlichen Seitenverhältnissen wird das Bild im Übergang gestaucht.
- Jeden sichtbaren Teil der Seite benennen, damit möglichst viel mitwandert. Es entstehen Dutzende Gruppen, die alle gleichzeitig laufen, und das Ergebnis wirkt unruhig statt zusammenhängend.
- Den Namen kurz vor der Navigation genau an das eine Element setzen, das angeklickt wurde, etwa über ein Attribut am Vorfahren.
- Drei bis vier Teile benennen, die auf beiden Seiten vorhanden sind: das Bild, die Überschrift, der Kopfbereich. Alles andere überblendet mit.
- object-fit auf den Pseudo-Elementen selbst setzen, damit das Seitenverhältnis über den ganzen Weg stimmt.
- Namen aus den Daten bilden, wenn mehrere Elemente in Frage kommen, und sie über eine Custom Property oder attr in das CSS geben.
Drei Kurven decken den Alltag ab.
Eine Seite mit drei Kurven wirkt geordnet, eine mit zwölf wirkt zufällig. Diese drei reichen fast immer.
Woher weiß der Übergang, in welche Richtung es geht?
Ein Übergang, der immer gleich aussieht, verliert schnell seinen Sinn. Ein Schritt in die Tiefe und ein Schritt zurück sollten sich unterscheiden, sonst weiß man nach dem dritten Klick nicht mehr, wo man ist. Dafür gibt es Typen: Namen, die du an den Übergang hängst und die du im CSS wieder abfragst.
Feste Typen stehen direkt in der Regel, neben der Navigation. Interessanter sind die beweglichen. Beim Seitenwechsel feuern zwei Ereignisse, pageswap auf der alten Seite kurz vor dem letzten Bild und pagereveal auf der neuen, bevor sie zum ersten Mal erscheint. Beide geben dir das Übergangsobjekt in die Hand, und dort kannst du einen Typ ergänzen, etwa aus einem Vergleich der beiden Adressen: Woher man kommt, steht in navigation.activation, wohin es geht, in location. Geht es eine Ebene tiefer oder eine zurück? Abgefragt wird das danach mit der Pseudo-Klasse :active-view-transition-type.
Zur Richtung kommt die Dauer, und die ist der Teil, an dem die meisten Übergänge scheitern. Ein Seitenwechsel darf sich nicht wie ein Wartezimmer anfühlen. Die Voreinstellung des Browsers liegt bei einer Viertelsekunde, und das ist eine gute Orientierung: zwischen 200 und 350 Millisekunden für eine Überblendung, eher am oberen Ende für ein Element, das eine sichtbare Strecke zurücklegt. Alles über einer halben Sekunde steht der Person im Weg, die eigentlich nur auf die nächste Seite wollte.
Kurz wirkt souverän, lang wirkt wie Ladezeit. Für einen Seitenwechsel liegt der brauchbare Bereich zwischen 200 und 350 Millisekunden.
window.addEventListener("pagereveal", (e) => { if (!e.viewTransition) return; const von = navigation.activation.from?.url; const tiefe = (u) => new URL(u).pathname.split("/").length; const tiefer = !von || tiefe(location.href) > tiefe(von); e.viewTransition.types.add(tiefer ? "vorwaerts" : "zurueck");}); html:active-view-transition-type(zurueck) { &::view-transition-old(root) { animation-name: nach-rechts; } }
Was übernimmt die API nicht?
Der Gewinn ist groß, und er hat klare Kanten. Die API beschreibt einen Moment zwischen zwei Zuständen, nicht das Verhalten einer Anwendung. Vier Punkte bleiben Arbeit, und drei davon sind keine Sache des Codes, sondern der Erwartung.
- Cross-Document fehlt in Firefox. Die Variante innerhalb eines Dokuments ist überall vorhanden, die zwischen zwei Dokumenten liefern Chromium und Safari aus. Das ist kein Grund zu warten: Wo die Regel nicht verstanden wird, navigiert der Browser wie immer. Es ist aber ein Grund, den Übergang nie zum Träger einer Information zu machen.
- Vier Sekunden sind die Obergrenze. Braucht die neue Seite länger bis zum ersten Bild, bricht der Browser ab und navigiert hart. Das macht den Übergang zu einer ehrlichen Anzeige für die eigene Ladezeit: Wer ihn auf dem Telefon selten sieht, hat kein Animationsproblem, sondern ein Geschwindigkeitsproblem.
- Die Bilder sind Bilder. Was der Browser festhält, ist eine Momentaufnahme, keine lebende Kopie. Unterscheiden sich die Seitenverhältnisse zwischen altem und neuem Zustand, wird gestaucht, und zwar so lange, bis du object-fit auf den Pseudo-Elementen selbst setzt. Am Element hilft es nicht.
- Ein Router muss Bescheid sagen. Sobald eine Anwendung die Navigation selbst übernimmt, gibt es keinen Seitenwechsel mehr, den der Browser erkennen könnte. Dort umschließt document.startViewTransition den Moment, in dem der neue Zustand gesetzt wird. Viele Router bringen dafür längst einen Schalter mit, und darunter steckt genau dieser eine Aufruf.
Was die API dagegen von selbst richtig macht, wird leicht übersehen: Sie hält die Bewegung außerhalb des Hauptstrangs. Die Überblendung läuft im Compositor, während die neue Seite bereits fertig ist. Eine nachgebaute Lösung hat an genau dieser Stelle immer gerungen, weil sie animieren musste, während parallel Inhalte geladen und eingehängt wurden.
Der Übergang ist eine Zugabe. Sobald eine Seite ohne ihn unverständlich wird, ist etwas anderes schiefgelaufen.
Wo lohnt sich ein Übergang, wo stört er?
Nicht jeder Seitenwechsel wird durch eine Bewegung besser. Der Nutzen hängt daran, ob zwischen den beiden Zuständen überhaupt eine Beziehung besteht, die man zeigen kann. Vier Fälle lassen sich klar unterscheiden.
- Lohnt sich sofort. Von der Übersicht in die Detailansicht. Das angeklickte Bild wächst an seine neue Stelle, und damit ist ohne ein Wort erklärt, worauf man gerade geklickt hat. Dasselbe gilt für Produktlisten, Galerien und Beitragsübersichten.
- Lohnt sich in klein. Zwischen Seiten derselben Ebene, etwa im Hauptmenü. Dort reicht die Voreinstellung mit einer eigenen Kurve und einer leichten Verschiebung. Wandernde Elemente braucht es nicht, weil es zwischen den Seiten nichts zu verfolgen gibt.
- Bleibt ohne. Formularstrecken, Kassenvorgänge, alles, was man mehrmals hintereinander durchläuft. Eine Bewegung, die man zum zehnten Mal sieht, ist keine Erklärung mehr, sondern eine Verzögerung.
- Braucht vorher etwas anderes. Seiten, die auf dem Telefon über eine Sekunde bis zum ersten Bild brauchen. Dort greift die Zeitgrenze regelmäßig, und der Übergang erscheint willkürlich. Erst die Ladezeit, dann die Bewegung.
Ein brauchbarer Prüfstein ist die Frage, ob sich in beiden Zuständen dasselbe Ding zeigen lässt. Gibt es ein Bild, eine Überschrift, eine Fläche, die vorher und nachher vorhanden ist, dann trägt ein Übergang etwas bei, weil er eine Verbindung sichtbar macht. Gibt es sie nicht, bleibt eine Überblendung, und eine Überblendung ist selten falsch, aber auch selten wichtig.
Zeige eine Verbindung, die es wirklich gibt. Alles andere ist Dekoration mit Wartezeit.




