Konsistente Übergänge zwischen Dokumenten

Das Problem

Bei dokumentübergreifenden Ansichtsübergängen werden Elemente zwischen zwei Seiten während einer Navigation innerhalb desselben Ursprungs animiert. Der Browser erstellt einen Snapshot der alten Seite, navigiert und animiert dann vom Snapshot zur neuen Seite. Wenn die neue Seite noch nicht alle wichtigen Ressourcen geladen hat (z. B. Stylesheets, Layoutskripts oder wichtige DOM-Elemente), wird der Übergang in einem unvollständigen oder unformatierten Zustand animiert. Dies führt zu visuellen Fehlern, z. B. dass Elemente an die falschen Positionen verschoben werden, Inhalte während der Animation neu angeordnet werden oder Fallback-Schriftarten nach Abschluss des Übergangs zu Webschriftarten wechseln.

Die Lösung

Verwenden Sie blocking="render" für wichtige <link>- und <script>-Elemente im <head> der neuen Seite und <link rel="expect">, um das Rendering zu blockieren, bis bestimmte DOM-Elemente geparst wurden. So wird sichergestellt, dass der Browser die Animation für den Ansichtsübergang erst startet, wenn der visuelle Zustand der neuen Seite stabil ist. Der Browser parst den HTML-Code im Hintergrund weiter – nur das Rendern wird verzögert.

Implementierungsstrategie

  1. OBLIGATORISCH: Aktivieren Sie dokumentübergreifende Ansichtsübergänge mit der CSS-At-Regel @view-transition auf beiden Seiten.
  2. OBLIGATORISCH: Achten Sie darauf, dass sich wichtige Stylesheets im <head> befinden. Stylesheets im <head>-Element blockieren standardmäßig das Rendering. Dynamisch eingefügte Stylesheets erfordern explizite blocking="render".
  3. DO use blocking="render" on <script> elements that must execute before the transition animates (e.g., scripts that apply a theme or affect the layout).
  4. DO use <link rel="expect" href="#element-id" blocking="render"> to block rendering until above-the-fold content has been parsed. Das gilt für alle Übergangstypen: ganzseitige Überblendungen (um Animationen auf einer leeren Seite zu vermeiden), Morph-Animationen (um sicherzustellen, dass benannte Elemente im DOM vorhanden sind) und skriptabhängige Layouts (um sicherzustellen, dass gestaltete Inhalte geparst werden).
  5. Blockieren Sie NICHT das Rendering von nicht kritischen Inhalten. Blockieren Sie nur Ressourcen und Elemente, die sich auf den anfänglichen Darstellungsbereich auswirken. Wenn zu viele Inhalte blockiert werden, verzögert sich der Übergang und die wahrgenommene Leistung wird beeinträchtigt.

Implementierungsleitfaden

Schritt 1: Dokumentübergreifende Ansichtsübergänge aktivieren

OBLIGATORISCH: Sowohl die Quell- als auch die Landingpage müssen die @view-transition-At-Regel enthalten. Ohne diese Angabe erfolgt kein dokumentübergreifender Übergang.

/*
  MANDATORY: Include this rule in every page that participates
  in cross-document view transitions.
  `navigation: auto` enables transitions for standard navigations
  (link clicks, form submissions, back/forward).
*/
@view-transition {
  navigation: auto;
}

/* MANDATORY Copy-Paste Safety: Disable cross-document view transitions for users requesting reduced motion */
@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

Schritt 2: Rendering blockieren, bis kritische Skripts ausgeführt werden

Wenn ein nicht blockierendes Script im <head> vor der Animation des Übergangs ausgeführt werden muss (z.B. um eine Designklasse anzuwenden oder das Layout zu beeinflussen), markieren Sie es mit blocking="render". Andernfalls werden async-, defer- oder type="module"-Skripts möglicherweise ausgeführt, nachdem die Umstellung bereits begonnen hat.

<head>
  <!--
    DO: Mark layout-critical scripts with blocking="render".
  -->
  <script type=module blocking="render">
    // Example: apply a stored theme before the page renders,
    // so the transition snapshot reflects the correct theme.
    document.documentElement.dataset.theme =
      localStorage.getItem('theme') || 'light';
  </script>
</head>

Schritt 3: Rendering blockieren, bis wichtige DOM-Elemente geparst wurden

Stylesheets und blocking="render"-Skripts im <head> garantieren nur, dass der <head> vollständig verarbeitet wurde. Sie warten nicht, bis <body>-Inhalte geparst wurden. Ohne zusätzliche Blockierung kann der Browser den Snapshot der neuen Seite erstellen, bevor Elemente ohne Scrollen im DOM vorhanden sind. Das führt zu einem Übergang, der zu einer leeren oder teilweise gerenderten Seite animiert wird.

<link rel="expect"> löst dieses Problem, indem das Rendering blockiert wird, bis ein bestimmtes Element (identifiziert durch seine id) geparst wurde. Der Wert href muss eine Fragmentkennung sein (z.B. #hero), die mit dem Attribut id des Zielelements übereinstimmt. Sobald das schließende Tag dieses Elements geparst wurde, wird die Renderblockierung aufgehoben. Beachten Sie, dass <link rel="expect"> nur garantiert, dass das Ziel-DOM-Element im Dokument vorhanden ist. Es wird nicht darauf gewartet, dass externe Unterressourcen (z. B. <img>-Pixel) heruntergeladen oder decodiert werden.

Verwenden Sie <link rel="expect"> in den folgenden Szenarien:

Anwendungsfall 1: Ganzseitige Überblendung

Auch wenn keine einzelnen Elemente einen view-transition-name haben, wird die gesamte Seite mit dem Standardübergang root überblendet. Wenn der Snapshot der neuen Seite erstellt wird, bevor der Content ohne Scrollen geparst wurde, wird die Überblendung von der alten Seite zu einer leeren oder unvollständigen Seite animiert. Blockieren Sie das Rendering bei einem Element, das das Ende des sichtbaren Above-the-Fold-Inhalts markiert.

<head>
  <link rel="stylesheet" href="/css/styles.css">

  <!--
    DO: Block rendering until the main content area is parsed,
    even for a simple cross-fade. Without this, the browser may
    snapshot the page before visible content exists in the DOM,
    causing the cross-fade to reveal a blank or partial page.
  -->
  <link rel="expect" href="#main-content" blocking="render">
</head>
<body>
  <header>...</header>
  <main id="main-content">
    <h1>Page Title</h1>
    <p>Above-the-fold content the user should see immediately.</p>
  </main>
  <!-- Content below the fold does NOT need to be blocked on -->
  <section>...</section>
</body>

Anwendungsfall 2: Morph-Animationen zwischen bestimmten Elementen

Wenn Elemente auf beiden Seiten denselben view-transition-name haben, werden sie vom Browser während der Navigation fließend umgewandelt. Wenn das Zielelement beim Start des Übergangs noch nicht geparst wurde, kann der Browser es nicht finden. Der Morph wird dann in separate Ausgangs- und Eingangs-Animationen aufgeteilt. Das Rendering wird blockiert, bis das Element mit dem view-transition-name geparst wurde.

Da <link rel="expect"> das Rendering nur so lange pausiert, bis das Ziel-DOM-Element geparst wurde, und nicht auf das Herunterladen oder Decodieren von Bilddaten wartet, kann ein nicht im Cache gespeichertes <img> im Snapshot der neuen Seite als leeres Rechteck erfasst werden. Um leere Snapshots oder Layoutverschiebungen während der Bildtransformation zu vermeiden, sollten Sie wichtige Übergangsbilder vorab laden oder decodieren (z. B. mit <link rel="preload" as="image"> oder durch Vorab-Rendering der Zielseite) und explizite Größen-Fallbacks angeben (width- und height-Attribute oder CSS-aspect-ratio sowie eine Platzhalter-Hintergrundfarbe).

<head>
  <link rel="stylesheet" href="/css/styles.css">

  <!--
    DO: Preload critical transition images so their pixels are
    available to decode before the new-page snapshot is captured.
    <link rel="expect"> only waits for the DOM element to be parsed,
    NOT for image pixels to download or decode.
  -->
  <link rel="preload" as="image" href="/img/product.webp" fetchpriority="high">

  <!--
    DO: Block rendering until the element participating in the
    morph animation has been parsed. Without this, the browser
    may start the transition before #hero exists, causing the
    morph to degrade to a fade-out/fade-in.
  -->
  <link rel="expect" href="#hero" blocking="render">

  <!--
    When multiple blocking="render" resources are present,
    rendering is blocked until ALL of them are satisfied.
    Here, the browser waits for both the script to execute
    AND the #hero element to be parsed — whichever comes last.
  -->
  <script async blocking="render" src="/js/transition-setup.js"></script>
</head>
<body>
  <header>...</header>
  <section id="hero">
    <h1 style="view-transition-name: page-title">Product Name</h1>
    <!--
      DO: Provide explicit dimensions (width/height or aspect-ratio)
      as a sizing fallback so the snapshot captures the correct layout
      box even if image decoding is still in progress.
    -->
    <img
      style="view-transition-name: hero-image"
      src="/img/product.webp"
      width="800"
      height="600"
      alt="Product"
    >
  </section>
</body>

Schritt 4: Medienabfragen für responsives Render-Blocking verwenden

Bei unterschiedlichen Darstellungsbereichen können unterschiedliche Mengen an Inhalten „above the fold“ angezeigt werden. Verwende das Attribut media für <link rel="expect">, um das Rendering nur für die Inhalte zu blockieren, die bei einer bestimmten Darstellungsbereichsbreite sichtbar sind.

<head>
  <!--
    DO: Use media queries to conditionally block rendering.
    On wide screens, both the hero and the sidebar are visible,
    so block until both are parsed. On narrow screens, only the
    hero is visible initially.
  -->
  <link
    rel="expect"
    href="#hero"
    blocking="render"
    media="screen and (width <= 768px)"
  >
  <link
    rel="expect"
    href="#sidebar"
    blocking="render"
    media="screen and (width > 768px)"
  >
</head>

Schritt 5: „pagereveal“ für kontextabhängige Übergänge verwenden (optional)

Das Ereignis pagereveal ist für die grundlegende Strategie zum Blockieren des Renderns nicht erforderlich. Er ist nur erforderlich, wenn view-transition-name-Werte dynamisch zugewiesen werden müssen, je nachdem, von wo aus der Nutzer navigiert ist, z. B. wenn ein bestimmtes Listenelement in eine Überschrift auf der Detailseite umgewandelt wird.

Wenn view-transition-name-Werte statisch in CSS zugewiesen werden oder wenn Sie nur die standardmäßige ganzseitige Überblendung verwenden, überspringen Sie diesen Schritt vollständig.

<head>
  <!--
    MANDATORY: The pagereveal listener must be registered before
    the page renders. Inline small setup scripts with blocking="render"
    to avoid an extra network round-trip on cold loads, or use an
    external async script with blocking="render" so the listener is
    registered early without blocking HTML parsing.
    If the listener is registered too late (e.g., in a deferred
    script), the event may have already fired.
  -->
  <script async blocking="render" src="/js/transition-setup.js"></script>
</head>
// transition-setup.js
window.addEventListener('pagereveal', async (event) => {
  if (!event.viewTransition) return;

  const from = navigation.activation?.from;
  if (!from) return;

  const fromUrl = new URL(from.url);

  // DO: Assign view-transition-name based on navigation context.
  // This enables a morph animation from the product card on the
  // list page to the heading on the detail page.
  if (fromUrl.pathname === '/products/') {
    const heading = document.querySelector('main h1');
    if (!heading) return;

    heading.style.viewTransitionName = 'product-title';

    try {
      await event.viewTransition.ready;
    } catch (error) {
      // Skipped view transitions reject `ready` with InvalidStateError.
      if (error.name !== 'InvalidStateError') {
        throw error;
      }
    } finally {
      // MANDATORY: Remove the temporary name after the transition
      // finishes. Stale names interfere with subsequent navigations
      // and prevent the page from entering the bfcache.
      await event.viewTransition.finished;
      heading.style.viewTransitionName = '';
    }
  }
});

Best Practices

  • Weisen Sie view-transition-name nach Möglichkeit immer über CSS zu. RICHTIG Reservieren Sie die JavaScript-Zuweisung (über pagereveal) für Fälle, in denen der Name vom Navigationskontext abhängt.
  • EMPFOHLEN: Halten Sie renderblockierende Skripts klein und schnell. Der Browser hat ein integriertes Zeitlimit (ca. 4 Sekunden). Nach Ablauf dieses Zeitlimits wird der Übergang vollständig übersprungen. Wenn ein Übergang initiiert wurde, wird viewTransition.ready mit einem InvalidStateError abgelehnt.
  • Verwenden Sie <link rel="expect"> NICHT, um Elemente zu blockieren, die sich weit unten auf der Seite befinden und im ursprünglichen Darstellungsbereich nicht sichtbar sind. Dadurch wird der Übergang ohne visuellen Vorteil verzögert.
  • Markieren Sie Analyse-, Werbenetzwerk-, Tag-Manager- oder andere Drittanbieter-Loader NICHT mit blocking="render", auch wenn sie Inhalte in den ursprünglichen Darstellungsbereich einfügen. Ihre Netzwerklatenz wird auf das Übergangszeitlimit angerechnet und ihre Ausgabe ist nicht Teil des stabilen Zustands der Seite.
  • Weisen Sie NICHT dieselbe view-transition-name mehreren Elementen auf derselben Seite zu. Doppelte Namen führen dazu, dass die gesamte Umstellung übersprungen wird.
  • Auswirkungen auf das Timing von Hilfstechnologien: Die Verwendung von blocking="render" verzögert visuelle Aktualisierungen und das erste Rendern. Dies verhindert zwar visuelle Fehler für sehende Nutzer, kann aber zu Verarbeitungslatenz oder verzögerter Initialisierung für Screenreader und andere Hilfstechnologien führen, die auf gerenderte Accessibility Trees angewiesen sind. Wägen Sie die Vorteile der visuellen Kontinuität gegen die anfängliche Leselatenz für Nutzer ab, die keine visuellen Inhalte sehen, und achten Sie darauf, dass renderblockierende Skripts minimal und extrem optimiert sind.

Fallback-Strategien

Dokumentübergreifende Ansichtsübergänge sind ein hervorragender Kandidat für die progressive Verbesserung. In Browsern, die sie nicht unterstützen, wird die @view-transition-Regel ignoriert und die Standardnavigation innerhalb desselben Ursprungs erfolgt genau so, wie es ohne die Funktion der Fall wäre. Unterstützte Browser erhalten flüssige Übergänge, alle anderen die Standardnavigation. Die eingeschränkte Browserunterstützung ist kein Grund, die Funktion nicht zu verwenden.

Alle Browser, die dokumentübergreifende Ansichtsübergänge unterstützen, unterstützen auch blocking="render" und <link rel="expect">. Daher ist für die in diesem Leitfaden beschriebenen Funktionen, die das Rendern blockieren, kein separater Fallback erforderlich.

Weitere Überlegungen

  1. Auswirkungen auf die Leistung: Jede renderblockierende Ressource verzögert den Start der Ansichtsübergangsanimation. Minimieren Sie die Anzahl der Scripts, die das Rendering blockieren, und verwenden Sie <link rel="expect"> nur für Elemente, die ohne Scrollen sichtbar sind. Mit der Speculation Rules API können Sie Landingpages vorab rendern, um Ladeverzögerungen vollständig zu vermeiden.
  2. Zeitüberschreitungsverhalten: Wenn die kombinierte Zeit für das Blockieren des Renderings etwa 4 Sekunden überschreitet, überspringt der Browser den Übergang (und alle aktiven viewTransition.ready-Promises werden mit einem InvalidStateError abgelehnt). Achten Sie darauf, dass kritische Ressourcen innerhalb dieses Zeitfensters geladen werden.
  3. Kompatibilität mit dem Back-Forward-Cache: Temporäre view-transition-name-Zuweisungen, die nach dem Übergang nicht bereinigt werden, können verhindern, dass die Seite in den Back-Forward-Cache aufgenommen wird. Entfernen Sie dynamisch zugewiesene Namen immer im finished-Callback.