View Transitions API : état et navigation
Coordonnez document.startViewTransition avec l’état du DOM, ready et finished, l’historique, l’abandon et la réduction des mouvements.
BotBrowser Team
Vous voulez la documentation structurée pour Plateforme ?
Cet article fait partie de la bibliothèque éditoriale. Pour les étapes de configuration, la référence et les mises à jour continues, passez directement à la section docs.
View Transitions API peut rendre fluide une navigation dans le même document, mais l’animation n’est qu’une partie du contrat. BotBrowser peut rejouer un scénario autorisé et enregistrer des états bornés du DOM, du cycle de vie et des préférences ; il ne peut pas ajouter l’API à un navigateur qui ne l’implémente pas ni certifier ce qu’annonce une technologie d’assistance. Associez ce guide à l’historique et la confidentialité de Navigation API et aux préférences media query et au contrôle utilisateur lorsque la transition décide aussi d’une route ou du mouvement autorisé.
TL;DR
- Appelez
document.startViewTransition()autour d’une seule mise à jour synchrone du DOM et rendez le callback idempotent. - Considérez
transition.readycomme le moment où les pseudo-éléments capturés peuvent s’animer ;transition.finishedindique la phase visuelle stabilisée. - Une mise à jour rejetée ou une transition abandonnée est un repli contrôlé, pas la preuve que la route a échoué. Le nouveau DOM doit rester utilisable.
- Mettez à jour l’état de l’application avant de mesurer la nouvelle disposition et n’enregistrez pas deux fois la même navigation ou le même rectangle.
- Respectez
prefers-reduced-motion: reduce: gardez l’état et l’entrée d’historique, mais désactivez ou raccourcissez l’animation.
Sommaire
- Modéliser le cycle de vie
- Gérer ready, finished et abort
- Compter une seule fois l’historique et les mesures
- Créer un repli pour réduire les mouvements
- Exécuter une matrice bornée
- Limite de capacité de BotBrowser
- Conclusion pratique
Modéliser le cycle de vie
La spécification CSS View Transitions décrit une capture de l’ancien rendu, un callback de mise à jour puis un nouveau rendu animé par des pseudo-éléments. Le point d’entrée est document.startViewTransition(updateCallback). Il renvoie immédiatement un objet ViewTransition, même si le callback et les captures arrivent ensuite. Cet objet pilote le cycle de vie ; il ne garantit pas que la route est terminée.
Rendez les états explicites :
| État | Signification | Enregistrement sûr |
|---|---|---|
requested | L’application accepte une demande | navigationId, route, préférence de mouvement |
updating | Le callback modifie le DOM possédé | updateStarted: true |
ready | Les nouveaux pseudo-éléments sont disponibles | ready: true, sans prétendre que la peinture est finie |
animating | L’animation ou la voie instantanée s’exécute | animationMode |
finished | La transition est stabilisée ou ignorée | finished: true |
aborted | Capture, mise à jour ou animation interrompue | abortReason, état de repli |
Ainsi requested, ready et finished ne deviennent pas trois navigations. Ce sont les phases d’un événement. Gardez un navigationId stable et écrivez une seule issue terminale. Le navigateur peut ignorer l’animation tout en validant le DOM ; finished ne signifie pas qu’une durée non nulle a été visible.
Détectez la méthode à la frontière. Si document.startViewTransition manque, choisissez la même mise à jour de route sans capture. Ne modifiez pas les règles d’historique selon la présence de l’amélioration.
Valider l’état du DOM une seule fois
Le callback de mise à jour est l’autorité du nouveau DOM. Gardez-le synchrone et court. Chargez les données avant de demander la transition, ou validez d’abord un état de chargement. Un second clic doit être ignoré ou recevoir un navigationId voulu ; il ne doit pas créer deux entrées.
let navigationId = 0;
function showRoute(route, render) {
const id = ++navigationId;
const commit = () => {
render(route);
document.documentElement.dataset.route = route;
};
if (!document.startViewTransition) {
commit();
return Promise.resolve({ id, mode: 'fallback', outcome: 'committed' });
}
const transition = document.startViewTransition(commit);
return transition.finished.then(
() => ({ id, mode: 'view-transition', outcome: 'finished' }),
error => ({ id, mode: 'view-transition', outcome: 'aborted', error: error.name })
);
}
Rendez le rendu déterministe à partir d’un enregistrement de route gelé et mettez le magasin à jour une seule fois. Un observateur qui modifie un autre élément durant la capture peut mélanger deux routes : c’est une course applicative, pas une raison de réessayer aveuglément. Utilisez view-transition-name seulement pour des objets sémantiquement identiques ; un nom dupliqué peut faire échouer la capture. La mise en page sans nom doit rester lisible.
Gérer ready, finished et abort
transition.ready se résout quand l’arbre de pseudo-éléments est prêt à s’animer. Il sert à ajouter une classe, mais ne prouve pas qu’une personne a vu la nouvelle page. transition.finished se résout après l’animation ou une voie sans animation. Utilisez-le pour retirer les classes temporaires et libérer le verrou, pas pour émettre un second événement de route.
const transition = document.startViewTransition(updateRoute);
const record = { navigationId, ready: false, outcome: 'updating' };
transition.ready.then(
() => {
record.ready = true;
record.outcome = 'animating';
},
error => {
record.outcome = 'aborted';
record.abortReason = error.name;
}
);
transition.finished.then(
() => {
record.outcome = 'finished';
releaseRouteLock();
},
error => {
record.outcome = 'aborted';
record.abortReason = error.name;
releaseRouteLock();
}
);
Si le callback lève une exception, gardez le document utilisable et affichez le mécanisme d’erreur ou de nouvelle tentative existant. Séparez domCommitted de animationOutcome. Une mise à jour réussie ne doit pas être annulée seulement parce que son animation a échoué. Utilisez une liste bornée de raisons : unsupported, duplicate-name, update-error, reduced-motion, superseded.
Quand une nouvelle navigation remplace une transition active, le callback tardif de l’ancienne ne doit plus écrire. Le remplacement rend l’état courant plutôt que de restaurer une capture obsolète.
Compter une seule fois l’historique et les mesures
pushState() ou Navigation API décide si la route crée une entrée ; startViewTransition() fournit seulement le traitement visuel. Exécutez l’opération d’historique une fois, puis transmettez la route au callback. Un événement popstate doit conserver la sémantique retour/avance du routeur, sans ajouter une entrée parce qu’une animation commence.
Le guide Navigation API et historique explique la propriété des entrées. Gardez son navigationId dans le registre de transition au lieu d’un second compteur. Une route possède une tentative de transition et une issue visuelle. Sans API, utilisez la même opération et mode: "fallback".
Mesurez après ready uniquement pour le nouveau rendu capturé, ou après finished uniquement pour le rendu stabilisé. Ne faites jamais la somme des deux rectangles.
function measureOnce(phase, navigationId, element) {
const rect = element.getBoundingClientRect();
return {
navigationId,
phase,
rect: {
top: Math.round(rect.top),
left: Math.round(rect.left),
width: Math.round(rect.width),
height: Math.round(rect.height),
},
};
}
Les coordonnées dépendent du défilement, du zoom, des polices et de la route. Enregistrez le viewport et l’offset visualViewport si la géométrie est le critère ; n’en faites pas une identité. Pour retour/avance, laissez l’événement historique choisir l’entrée et marquez une demande plus récente superseded avant son rendu.
Créer un repli pour réduire les mouvements
La référence MDN de prefers-reduced-motion décrit une préférence utilisateur. Respectez-la avant un long déplacement. L’état, le focus, la route et l’historique restent identiques ; seule l’animation change.
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 0.18s;
animation-timing-function: ease-out;
}
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation-duration: 0s;
animation-iteration-count: 1;
}
}
Relisez la préférence à chaque navigation. La branche JavaScript qui omet l’API doit exécuter le même commit. Enregistrez animationMode: "instant" et motionPreference: "reduce", sans stocker cette préférence comme identité.
Une transition ne crée ni piège de focus, ni nom accessible, ni annonce de lecteur d’écran. Déplacez le focus selon le contrat de route et testez clavier et technologie d’assistance séparément. Gardez le titre dans le DOM avant de déclarer la route prête et évitez les flashs.
Exécuter une matrice bornée
Employez un scénario same-origin, des données stables et un profil neuf par ligne.
| Ligne | Condition | Résultat attendu | Preuve |
|---|---|---|---|
| A | API disponible, mouvement normal | DOM, ready et finished une fois | Cycle préféré |
| B | API absente | Même DOM et historique, mode: fallback | Compatibilité |
| C | Nom dupliqué | Récupération utilisable et raison d’abandon | Échec de capture |
| D | prefers-reduced-motion: reduce | Même route et focus, animation instantanée | Contrôle utilisateur |
| E | Entrée retour/avance | Un événement, aucun pushState supplémentaire | Propriété de l’historique |
| F | Nouvelle demande en attente | Ancien registre superseded | Callback périmé |
{
"fixtureId": "vt-nav-01",
"navigationId": "route-17",
"mode": "view-transition",
"motionPreference": "no-preference",
"domCommitted": true,
"ready": true,
"outcome": "finished",
"historyAction": "push",
"measurementPhase": "ready",
"browserVersion": "155.0"
}
Conservez un historyAction, un navigationId et au plus une phase de mesure. N’exportez pas les requêtes, le texte de page ou les transcriptions de focus. Si E crée deux entrées, corrigez le routeur avant le CSS ; si D change le focus, c’est une régression d’accessibilité même si l’image semble bonne.
Limite de capacité de BotBrowser
BotBrowser peut charger ce scénario autorisé, conserver route et préférence et enregistrer les états visibles. Il ne peut pas forcer startViewTransition, garantir le temps du compositeur ni certifier une annonce d’assistance. Le rejeu décrit ce navigateur, ce viewport, ce profil et cette feuille de style.
Sources
- CSS View Transitions Module Level 1 (CSSWG)
- MDN :
Document.startViewTransition() - Chrome for Developers : View Transitions
- MDN :
prefers-reduced-motion - Capacités de la plateforme BotBrowser
Conclusion pratique
Attribuez une décision de route, une validation du DOM et une issue terminale. Utilisez ready pour la phase capturée, finished pour le nettoyage et un abandon explicite pour les voies incompatibles ou échouées. Conservez historique et focus lorsque le mouvement est réduit ou absent, et mesurez la géométrie dans une seule phase nommée. BotBrowser peut rejouer cette matrice ; l’application et une revue d’accessibilité restent responsables de l’expérience.
Cet article fait partie du guide : Surfaces de la plateforme web : carte de revue
Articles Connexes
Navigation API, historique et limites de confidentialité
PlateformePréférences des media queries : respecter les choix des utilisateurs
PlateformeRevue de compatibilité navigateur pour les Web Components
PlateformeBack-Forward Cache : cycle de vie, confidentialité et cohérence
PlateformeCompute Pressure API : charges adaptatives et vie privée
PlateformeIsolation des content scripts d’extensions de navigateur
Faites passer BotBrowser de la recherche à la production
Utilisez ces guides pour comprendre le modèle, puis passez à la validation multi-plateforme, aux contextes isolés et au déploiement navigateur prêt pour l'échelle.