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.
Navigation programmatique
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
| Export | Description |
|---|---|
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
- L'utilisateur clique un lien interne (ou vous appelez
navigateavecviewTransition: true). - EmberKit démarre une view transition.
history.pushState(oureplaceState) exécute le routeur patché.renderCurrentRoute()met à jour le HTML de la racine app.- Un
MutationObserversur la racine app se résout quand les nœuds enfants changent. - 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
| Navigateur | Support |
|---|---|
| Chrome / Edge 111+ | API complète |
| Safari / Firefox | Retour à 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 });
}