View Transitions JavaScript

Scope same-document view switching, history, focus, and native View Transition enhancement to each router.

Switch views with explicit button targets

Buttons can target views with `to`; the initializer updates active state, history, ARIA, focus, and animation together.

Summary

Focus moves here after the transition update.

Details

This target does not require a URL hash.

More examples

Each example introduces a different part of the shipped API.

The enhancement synchronizes URL, focus, and state

After activation, the current trigger and visible page agree with the hash and focus moves to the new heading region.

href="#activity"aria-current="page"section[active][tabindex="-1"]

view-transition.js enhances the CSS hash-link router. It is not required for cross-document transitions.

Setup

<div data-view-transitions>
<nav aria-label="Account views">
<a data-view-trigger href="#profile">Profile</a>
<a data-view-trigger href="#security">Security</a>
</nav>
<section data-view-page id="profile" active>
<h2>Profile</h2>
</section>
<section data-view-page id="security">
<h2>Security</h2>
</section>
</div>
<script type="module" src="/view-transition.js"></script>

Each router manages only its own pages and triggers. Multiple routers can coexist without toggling one another’s content.

Supported hosts

  • Router: view-transitions, view-transition, [data-view-transitions], .view-transitions
  • Page: view-page, [data-view-page], .view-page
  • Trigger: nav-trigger, [data-view-trigger], .view-trigger

Triggers resolve their target from to="page-id" or a hash href="#page-id". Prefer native hash links.

Behavior

On activation, the enhancement:

  1. Marks one page active.
  2. Updates matching triggers with aria-current="page".
  3. Adds a hash history entry.
  4. Uses document.startViewTransition() when supported and motion is allowed.
  5. Focuses the new page after the transition update callback.

Back and forward navigation resynchronizes the active view from the URL hash. Unsupported browsers and reduced-motion users receive the same update without animation.

Programmatic initialization

The module initializes document routers automatically and exports its initializer for content rendered later:

import { initializeViewTransitions } from "/view-transition.js";
initializeViewTransitions(document.querySelector("#new-content"));

Initialization is idempotent; routers already enhanced are skipped.

Custom elements

The optional custom trigger remains supported:

<nav-trigger to="profile" role="link" tabindex="0">Profile</nav-trigger>

The enhancement supplies Enter and Space handling for this host, but native <a> and <button> elements remain preferable because their semantics work before JavaScript.