CSS animiert jetzt zwischen Seiten: View Transitions ohne JavaScript
Ein Klick auf einen Link bedeutet in einer klassischen Website einen harten Schnitt: Die alte Seite verschwindet, die neue erscheint. Single-Page-Apps haben sich diesen weichen Übergang lange als Alleinstellungsmerkmal reserviert — um den Preis von Routing-Logik, Hydration und einer ordentlichen Portion JavaScript. Seit Cross-Document View Transitions in den Browsern angekommen sind, fällt dieser Preis weg. Eine @view-transition-Regel reicht, und ein normaler <a href> fühlt sich an wie ein App-Übergang.
Die Opt-in-Regel
Der Einstieg besteht aus genau einer At-Rule. Sie muss auf jeder Seite stehen, die am Übergang teilnehmen soll:
@view-transition {
navigation: auto;
}
Mehr braucht es für den Standardfall nicht. Sobald die Regel aktiv ist, blendet der Browser beim Navigieren die alte Seite per Crossfade in die neue über. Kein JavaScript, kein Router, keine Bibliothek.
Zwei Bedingungen sind dabei wichtig. Erstens funktioniert der Übergang nur innerhalb derselben Origin — zwischen zwei verschiedenen Domains greift er nicht. Zweitens müssen beide Seiten dieselbe CSS-Grundlage teilen. In der Praxis heißt das: ein gemeinsames Stylesheet für alle HTML-Seiten. Genau deshalb ist das Feature ein Geschenk für statische Sites und serverseitig gerenderte Multi-Page-Anwendungen, die ohnehin ein zentrales CSS ausliefern.
Den Standard-Crossfade steuern
Der Default-Übergang ist ein Anfang, kein Ziel. Um ihn anzufassen, muss man die Pseudo-Elemente kennen, die der Browser während der Transition erzeugt. Sie bilden eine kleine Hierarchie:
| Pseudo-Element | Bedeutung |
|---|---|
::view-transition-group(root) | Der gemeinsame Wrapper um den gesamten Übergang |
::view-transition-old(root) | Der Snapshot der alten Seite |
::view-transition-new(root) | Der Snapshot der neuen Seite |
Der Parameter root adressiert die ganze Seite. Wer nur die Dauer des Default-Blends verlangsamen will, setzt sie auf der Group:
::view-transition-group(root) {
animation-duration: 1s;
}
Der Browser verwendet weiterhin seinen Standard-Crossfade, nimmt sich dafür aber eine Sekunde Zeit. Die Group ist der richtige Ort für alles, was den gesamten Übergang betrifft — Dauer, Timing-Funktion, Verzögerung.
Eigene Keyframes statt Crossfade
Für einen echten Slide reicht der Default nicht. Hier kommen klassische @keyframes ins Spiel, je eine für die abgehende und die ankommende Seite:
@keyframes slide-out {
to { translate: -100vw 0; }
}
@keyframes slide-in {
from { translate: 100vw 0; }
}
Die slide-out-Animation schiebt die alte Seite nach links aus dem Bild. Die slide-in-Animation lässt die neue Seite von rechts hereinfahren. Zugewiesen werden sie über die passenden Pseudo-Elemente:
::view-transition-old(root) {
animation-name: slide-out;
}
::view-transition-new(root) {
animation-name: slide-in;
}
Die Arbeitsteilung ist dabei sauber: Auf der Group steht alles, was beide Snapshots gemeinsam betrifft — Dauer und Timing. Auf den einzelnen Pseudo-Elementen steht nur der jeweilige animation-name. Das Ergebnis fühlt sich an wie eine Single-Page-App, obwohl im Hintergrund zwei getrennte HTML-Dokumente geladen werden.
Teile der Seite stehen lassen
Ein durchgehender Slide hat einen Haken: Auch die Navigationsleiste fährt mit aus dem Bild, obwohl sie auf beiden Seiten identisch existiert. Das wirkt falsch. Die Nav-Bar sollte stehen bleiben, nur der Inhalt soll sich bewegen.
Die Lösung liegt im Parameter der Pseudo-Elemente. Statt root vergibt man einen eigenen Namen und heftet ihn per view-transition-name an genau das Element, das animiert werden soll:
main {
view-transition-name: page-content;
}
Anschließend animiert man page-content statt root in allen drei Pseudo-Elementen:
::view-transition-group(page-content) {
animation-duration: 0.4s;
}
::view-transition-old(page-content) {
animation-name: slide-out;
}
::view-transition-new(page-content) {
animation-name: slide-in;
}
Jetzt gleitet nur noch das <main>-Element, die Nav-Bar bleibt stehen. Wer mag, kann jedem Bereich einen eigenen view-transition-name geben und unterschiedlich animieren. In den meisten Fällen sieht aber gerade die zurückhaltende Variante am besten aus.
Geteilte Bilder zwischen zwei Seiten
Der eindrucksvollste Trick ist zugleich der einfachste. Ein Bild, das auf beiden Seiten vorkommt — etwa ein Vorschaubild in einer Card, das auf der Detailseite zum Hero-Bild wird — lässt sich morphen. Der Browser vergleicht Position und Größe vor und nach der Navigation und animiert visuell zwischen beiden Zuständen.
Dafür bekommen beide Bilder denselben view-transition-name. Weil beide Seiten dasselbe Stylesheet nutzen, genügt ein gemeinsamer Selektor:
.card-image,
.hero-image {
view-transition-name: article-image;
}
Mehr ist nicht nötig. Der Browser erkennt das gemeinsame Element, legt eine eigene Transition-Group dafür an und expandiert das Card-Bild beim Klick fließend in das Header-Bild der Artikelseite. Wenn parallel noch Slide-Animationen laufen, lohnt es sich, sie für diesen Effekt vorübergehend zu deaktivieren — zu viele gleichzeitige Bewegungen wirken unruhig.
Wenn Safari beim Bild-Morph ruckelt
Genau dieser Bild-Morph hat eine Eigenheit, die in der Praxis auffällt: In Safari ruckelt er sichtbar, während er in Chrome glatt läuft. Das ist bekanntes Verhalten, kein Verdrahtungsfehler. Der Grund liegt in der Mechanik des Morphs. Beim Klick aufs Card-Bild skaliert der Browser ein rasterisiertes Snapshot von der kleinen Kartengröße auf die größere Hero-Größe und blendet dabei zusätzlich über. Chrome erledigt dieses Skalieren GPU-beschleunigt. Safaris View-Transition-Engine rechnet mehr auf dem Main-Thread und gerät dabei ins Stocken — besonders, wenn alte und neue Darstellung unterschiedliche Anzeigegrößen oder Seitenverhältnisse haben. Der reine Crossfade zwischen zwei Seiten ohne Bild ist in Safari dagegen meist unauffällig. Es ist spezifisch der Bild-Morph, der ruckelt.
Zwei Stellschrauben reduzieren den Jank spürbar, ohne Chrome zu verschlechtern. Die erste vereinheitlicht die Geometrie der Snapshots. Mit voller Größe und object-fit: cover auf beiden Pseudo-Elementen animiert Safari nur noch reine Geometrie statt einer verzerrenden Aspect-Ratio-Überblendung:
::view-transition-old(article-image),
::view-transition-new(article-image) {
height: 100%;
width: 100%;
object-fit: cover;
}
Die zweite verkürzt die Dauer des Morphs. Eine kürzere Animation lässt weniger Zeit, in der das Ruckeln sichtbar wird:
::view-transition-group(article-image) {
animation-duration: 0.3s;
}
Wer es härter braucht, kann den Bild-Morph gezielt nur in Safari abschalten und dort beim sauberen Crossfade bleiben, der nicht ruckelt. Das bedeutet aber Browser-Sniffing und ist eher der letzte Schritt als der erste. Die beiden CSS-Tweaks sind die ehrlichere Lösung: Sie kosten nichts in Chrome und mildern das Problem in Safari, ohne eine Browser-Weiche einzuziehen.
Drei Dinge, die in der Praxis beißen
Bevor das Feature in ein echtes Projekt wandert, lohnt ein Blick auf die Fallstricke.
Namen müssen eindeutig sein. Ein view-transition-name darf auf der aktuellen Seite nur ein einziges Mal vorkommen. Im Demo mit einer einzelnen Card geht das gut. Sobald eine Übersicht mehrere Cards zeigt, braucht jedes Vorschaubild seinen eigenen, eindeutigen Namen — sonst weiß der Browser nicht, welches Element er morphen soll.
Reduced Motion respektieren. Nicht jeder will animierte Übergänge. Wer auf seinem Gerät reduzierte Bewegung aktiviert hat, sollte sie auch bekommen. Das gesamte Transition-Setup gehört deshalb in eine Media-Query:
@media (prefers-reduced-motion: no-preference) {
@view-transition {
navigation: auto;
}
/* ... alle weiteren Transition-Regeln ... */
}
Nur wenn der Nutzer keine Reduktion verlangt hat, greifen die Animationen.
Browser-Support einplanen. Cross-Document View Transitions laufen in Chrome und Edge ab Version 126 sowie in Safari ab 18.2. Firefox hat das Feature zum jetzigen Zeitpunkt noch hinter einem Flag. Während der Entwicklung kann es zudem mit manchen Live-Servern oder im Chrome-Dev-Modus zu Glitches kommen — ein Wechsel des Test-Browsers hilft hier oft.
Die gute Nachricht: All das ist als Progressive Enhancement angelegt. Unterstützt ein Browser die Transition nicht, navigiert er einfach ganz normal weiter. Der harte Schnitt ist das Fallback, kein Fehler. Genau das macht @view-transition zu einem Feature, das man heute schon ausliefern kann — ohne auf den letzten Browser zu warten.
Sources: Cross-Document View Transitions Are Finally Cross-Browser (2026), View Transitions (cross-document) — Can I use, Cross-Document View Transitions: The Gotchas Nobody Mentions — CSS-Tricks