Der Übergang kostet
kein Framework mehr.

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:

resources/scss/app.scss
@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:

resources/scss/app.scss
/* 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.

Übersicht
  • Seitenübergänge ohne SPA
  • Die Fensterbreite als Zahl
  • Lesbare Textfarben ohne JavaScript

Die Demo braucht JavaScript.

Zwischen den beiden Ansichten liegt eine einzige Funktion, die Elemente ein- und ausblendet. Einmal wird sie an den Browser übergeben, einmal direkt aufgerufen – mehr Unterschied gibt es im JavaScript nicht. Den Weg von der kleinen Kachel zum großen Kopfbild beschreibt niemand; er ergibt sich daraus, dass beide denselben Namen tragen.

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:

resources/views/pages/blog/parts/article-card.blade.php
<img class="zw-blog-card__image"
     style="view-transition-name: bild-{{ $article['slug'] }}"
     src="{{ $article['image'] }}" alt="">
resources/views/pages/blog/article.blade.php
<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.

resources/scss/app.scss
/* 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.

resources/js/seitenuebergang.js
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:

resources/scss/app.scss
/* 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.

Fragen zum Thema – oder ein Projekt, das genau daran hängt?

Schreib mir über das Kontaktformular kurz, worum es geht. Ich schaue mir deinen Fall an und sage dir konkret, was sich verbessern lässt – unverbindlich und ohne Verkaufsgespräch.