View Transitions

EmberKit intègre la View Transitions API pour une navigation SPA fluide. Les transitions attendent que le HTML de route soit écrit dans la racine app avant que le navigateur capture les snapshots.

Démarrage rapide

Activez les transitions au bootstrap du routeur client :

// src/index.tsx
import { render } from '@emberkit/core';
import App from './routes/_layout.tsx';
import { routes } from 'virtual:emberkit-routes';

render(App, document.getElementById('app')!, {
  routes,
  viewTransitions: true,
});

Les liens internes sont interceptés en phase capture pour que la navigation s'exécute une fois dans startViewTransition.

import { navigate, navigateWithViewTransition } from '@emberkit/core';

// Per navigation
await navigate('/docs/api', { viewTransition: true });

// Dedicated helper
await navigateWithViewTransition('/docs/api');

CSS

Stylisez le cross-fade par défaut sur la racine :

::view-transition-old(root) {
  animation: fade-out 0.3s ease-in-out forwards;
}
::view-transition-new(root) {
  animation: fade-in 0.3s ease-in-out forwards;
}

Ce site docs utilise des règles similaires dans src/styles/globals.css.

Référence API

ExportDescription
supportsViewTransitions()Si document.startViewTransition existe
withViewTransition(callback)Enveloppe du travail async dans une transition si supporté
waitForAppUpdate(href, options?)Navigation SPA qui se résout après mutation de #app
initViewTransitions(options?)Intercepteur de clics sur liens (appelé automatiquement par render)
navigateWithViewTransition(href, options?)Naviguer avec transition + attente mise à jour DOM

Option de render() :

viewTransitions?: boolean | { rootId?: string };

Utilisez rootId si votre élément de montage n'est pas id="app".

Fonctionnement du timing

  1. L'utilisateur clique un lien interne (ou vous appelez navigate avec viewTransition: true).
  2. EmberKit démarre une view transition.
  3. history.pushState (ou replaceState) exécute le routeur patché.
  4. renderCurrentRoute() met à jour le HTML de la racine app.
  5. Un MutationObserver sur la racine app se résout quand les nœuds enfants changent.
  6. Le navigateur anime de l'ancien snapshot au nouveau.

Cela évite les flashs de contenu vide entre routes.

Désactivation par lien

Ajoutez data-no-transition sur une ancre, ou utilisez les touches modificateurs (Ctrl/Cmd clic). Liens externes, target="_blank" et liens hash-only sur la même page sont ignorés automatiquement.

Support navigateurs

NavigateurSupport
Chrome / Edge 111+API complète
Safari / FirefoxRetour à navigation instantanée (sans erreurs)

Les utilisateurs avec prefers-reduced-motion doivent obtenir un mouvement réduit du navigateur.

Pattern du site docs

Cette app utilise un wrapper fin pour que chaque appel useNavigate() active les transitions par défaut :

// apps/docs/src/hooks/useNavigate.ts
import { navigate as coreNavigate } from '@emberkit/core';

export function useNavigate() {
  return (path: string, options = {}) =>
    coreNavigate(path, { ...options, viewTransition: options.skipTransition ? false : true });
}

Liens connexes