Les View Transitions, je les ai longtemps rangées dans la case “démo sympa”. Pas parce que l’idée est mauvaise. Parce que dès que tu as une UI un peu sérieuse, avec un overlay, une stack de modals, un panel qui s’ouvre pendant qu’une autre animation tourne, ça se met à clignoter, à se battre, ou à animer beaucoup trop de choses.
Chrome 147 change un point crucial : tu n’es plus obligé de faire une transition “globale” au niveau du document. Tu peux scoper une View Transition à un élément, et Chrome gère aussi mieux le fait d’en avoir plusieurs en même temps (concurrent) et imbriquées (nested). Dit autrement : ça devient enfin un outil de vraie UI, pas juste un effet de page.
Pourquoi les View Transitions « globales » étaient vite inutilisables en prod
Le modèle historique (transition au niveau du document) est brutalement simple : tu fais une capture de l’état “avant”, tu appliques ton update DOM, puis le navigateur anime entre les deux. Sur une navigation de page ou un petit toggle isolé, c’est magique.
Mais sur une app qui vit, tu as rarement un seul changement à la fois. Tu as des composants qui se montent et se démontent, un backdrop qui apparaît, un scroll container interne, un header sticky, des toasts, parfois un player vidéo, souvent un focus qui bouge. Une transition globale a tendance à embarquer trop de surface, donc trop de rendu, donc trop de risques. Et surtout, elle impose une “autorité” unique : si deux actions veulent animer deux zones différentes au même moment, tu te retrouves à arbitrer au chausse-pied, ou à renoncer.
Le résultat classique, c’est la foire : une modal qui fait “fondre” tout le layout alors que tu voulais juste animer la boîte, un mini-router qui déclenche une transition alors qu’un drawer est en train de s’ouvrir, ou l’inverse. Et quand tu essaies de “rattraper” avec du CSS sur les pseudo-éléments ::view-transition-*, tu finis par écrire des hacks spécifiques à chaque écran.
Sponsorisé par Le Scribouillard
Besoin de contenu optimisé SEO ?
Utilisez la meilleure plateforme française de création de contenu assistée par IA ! Et générez des articles pour moins de 1€ !
Ce que Chrome 147 apporte : element-scoped + transitions nested et concurrentes
L’idée des element-scoped view transitions est simple et très attendue : au lieu de dire “anime la page”, tu dis “anime cette zone”. Typiquement un panel, un conteneur de contenu, une liste, un composant master-detail. Tu réduis l’impact visuel, tu réduis le coût de rendu, et tu évites d’animer des trucs qui n’ont rien demandé (header, background, overlay global, etc.).
Et surtout, ça ouvre la porte à deux scénarios qui étaient pénibles voire impossibles à rendre propres : les transitions concurrentes (deux zones différentes qui transizionnent en même temps sans se marcher dessus) et les transitions imbriquées (un composant interne qui fait sa transition sans casser celle du composant parent). Dit comme ça, ça fait “note de release”. En pratique, ça veut dire que tu peux construire une UI moderne sans te battre avec un seul “verrou global” de transition.
Le point important à garder en tête : une view transition, même scopée, reste un outil qui manipule une représentation “capturée” de l’UI. Donc tu gagnes en contrôle, mais tu dois toujours être clean sur le DOM, le focus et les cas d’accessibilité.
Pattern : animer un panel (drawer, side sheet) sans faire bouger le reste
Le cas le plus rentable, c’est le panel. Dans la vraie vie, un drawer est rarement “une page”. C’est une zone qui glisse, parfois au-dessus d’un contenu scrollable, parfois avec un backdrop, parfois avec un formulaire. Si tu déclenches une transition globale, tu prends le risque d’animer le backdrop, le header, et tout le contenu derrière. Visuellement c’est souvent too much, et côté perf c’est une invitation au jank sur mobile.
Avec une transition scopée, tu peux isoler le panel : tu fais transizionner le conteneur du panel, et tu laisses le reste vivre sa vie. Ça rend aussi plus simple un comportement attendu : l’arrière-plan ne doit pas “morpher”, il doit juste être là, éventuellement assombri par le backdrop.
// Exemple volontairement simple : une transition scopée sur un drawer
const drawer = document.querySelector('[data-drawer]');
const button = document.querySelector('[data-drawer-toggle]');
button.addEventListener('click', async () => {
// Selon l’API disponible (Chrome 147+), l’idée est : transition sur l’élément.
// Si ton navigateur n’a pas l’API, tu retombes sur une animation CSS classique.
if (drawer.startViewTransition) {
await drawer.startViewTransition(() => {
drawer.classList.toggle('is-open');
}).finished;
} else {
drawer.classList.toggle('is-open');
}
// Après l’anim, tu gères le focus proprement (cf section accessibilité)
});Je préfère largement ce pattern à “faire une transition de page” sur l’ouverture d’un drawer. Une UI qui bouge trop donne vite une sensation cheap, même si techniquement c’est “smooth”. L’animation doit raconter quelque chose. Un panel se déploie. Le reste ne change pas d’état, donc ne devrait pas se mettre à flouter, cross-fader, ou faire semblant d’être remplacé.
Pattern : mini-router dans une zone (tabs, master-detail, search results)
Deuxième cas très concret : tu as une zone de contenu qui change, mais le chrome de l’app ne change pas. Exemple classique : un layout avec une colonne de gauche (liste) et un contenu à droite (détails). Ou un écran de recherche avec filtres persistants en haut et résultats en dessous. Dans une SPA, tu changes souvent une partie du DOM, pas “la page”.
Si tu gardes une transition globale, tu te retrouves à animer des zones qui ne bougent pas. Et tu te mets à corriger à la main : “ne transizione pas le header”, “ne transizione pas le sidebar”, “ne transizione pas le footer”. Bref, tu te bats contre ton propre layout. Une transition scopée te laisse faire exactement ce que tu veux : le container des résultats cross-fade / slide / morph, pendant que les filtres restent solides.
Terrain : ça rend aussi plus robuste la cohabitation avec des composants qui vivent en continu (un lecteur audio, un chat, des toasts). Le contenu change, pas les services autour.
Pattern : stack de modals, overlays, et états « au-dessus » de l’app
Le cas qui faisait le plus mal avec les transitions globales, c’est la modal. Déjà parce que tu as souvent un backdrop qui apparaît. Ensuite parce que tu as des “couches” : modal + popover + dropdown à l’intérieur de la modal, parfois une deuxième modal (oui, c’est discutable, mais ça existe), parfois un date picker portal qui sort du DOM “logique”.
Avec une transition globale, tu peux facilement capturer un état intermédiaire et obtenir un flash. Ou animer l’arrière-plan alors que tu voulais animer uniquement la boîte. Ou te retrouver avec des problèmes de z-index parce que la transition crée ses propres couches de rendu.
Une transition scopée, quand elle est bien choisie, te permet de traiter la modal comme un monde à part : tu transizionnes le conteneur de la modal, ou même juste la “card”, et tu laisses le reste stable. Et si tu as une stack, tu peux imaginer des transitions séparées pour la modal de niveau 1 et celle de niveau 2, au lieu d’une seule animation globale qui essaye de raconter une histoire incohérente.
Accessibilité : si tu rates le focus, ta transition est “jolie” mais mauvaise
Une view transition ne remplace pas les règles d’accessibilité, elle les rend juste plus visibles quand ça casse. Le premier piège, c’est le focus. Si tu ouvres une modal et que le focus reste derrière, tu as beau avoir la plus belle animation du monde, le clavier et les lecteurs d’écran vont naviguer dans le vide. Ça arrive très vite quand tu fais des montages/démontages DOM pendant la transition, ou quand tu relies l’ouverture à un state manager qui déclenche plusieurs renders.
Mon approche : je considère que l’animation est un bonus. La fonctionnalité d’abord. Donc je mets toujours le focus explicitement à la fin de l’update (ou juste après la transition si je veux éviter un focus ring qui “saute” au milieu). Et je ne triche pas sur le backdrop : si le reste de l’UI doit être “bloqué”, j’utilise inert (quand possible) ou un vrai focus trap, et j’assume une structure de DOM qui ne laisse pas des éléments interactifs accessibles derrière.
Deuxième piège : prefers-reduced-motion. Ce n’est pas un détail. Si l’utilisateur a demandé moins de mouvement, tu dois réduire ou désactiver l’anim. Le bon réflexe, ce n’est pas forcément “pas d’animation du tout”, c’est souvent “pas de mouvement spatial, pas de zoom, pas de morph”, mais garder un micro fondu très court si ça aide la compréhension. Dans tous les cas, tu dois le décider, pas le laisser au hasard.
@media (prefers-reduced-motion: reduce) {
/* Le but : éviter les mouvements amples.
Selon ton design, tu peux couper net ou réduire à un fondu court. */
::view-transition-group(*) {
animation-duration: 1ms;
}
}Dernier point : n’utilise pas les transitions pour cacher un mauvais état ARIA. Une modal doit avoir des attributs corrects, un ordre de focus correct, et une fermeture clavier qui marche. La transition vient ensuite. Sinon tu vas shipper un truc “premium” au scroll et inutilisable au clavier.
Perf : ce que tu snapshot, c’est ce que tu payes (surtout sur mobile)
Une view transition implique une capture et une animation. Même si le navigateur optimise, tu es en train de demander du boulot GPU/CPU. Quand tu scopes à un élément, tu limites le rayon d’explosion, mais ça ne rend pas l’opération gratuite.
Le piège le plus courant, c’est de scoper trop large “par facilité” : tu scopes le main layout entier alors que tu voulais juste animer la liste. Ou tu scopes un container qui contient une vidéo, un canvas, une map, ou un énorme tableau. Là, tu vas le sentir sur un Android milieu de gamme, pas sur ton MacBook.
Mon conseil terrain : scope petit, et structure ton DOM pour que ce soit possible. Un container de contenu bien isolé, avec du contain quand c’est pertinent, des zones scrollables qui ne débordent pas, et pas un monolithe qui englobe toute la page. Les transitions “jolies” ne valent rien si elles ajoutent une latence perceptible sur les interactions.
Mon protocole QA : la transition doit survivre aux cas pénibles
Je ne valide jamais une view transition juste “en cliquant une fois sur desktop”. Je la teste au clavier, je la teste avec une modal déjà ouverte, je la teste quand un toast apparaît pendant l’anim, je la teste avec un scroll interne pas en haut, et je la teste avec un réseau/device lent en throttling. Souvent, c’est là que tu vois les vrais bugs : focus perdu, scroll qui saute, élément capturé au mauvais moment, overlay qui passe derrière, ou animation qui donne la nausée.
Je vérifie aussi la dégradation. Une UI ne doit pas dépendre du support de l’API. Si la transition n’est pas dispo, l’app doit rester nickel, juste moins “cinéma”. Ce point est encore plus important si tu as une audience non-Chrome, ou si tu es dans un contexte WebView.
Conclusion : les View Transitions deviennent un outil d’UI, pas un effet “page”
Avec le scoped par élément et une meilleure gestion des transitions imbriquées/concurrentes, Chrome 147 rapproche les View Transitions d’un usage réaliste : animer une zone précise, sans transformer toute l’app en film d’animation.
Ça ne dispense pas de faire le boulot d’UI (focus, inert, reduced motion, perfs). Mais au moins, on n’est plus obligé de choisir entre “pas de transition” et “transition globale qui casse dès que l’UI devient complexe”. Et ça, franchement, ça change le jeu.