Publié le : 27 mars 2026
Les transitions de vue à portée d'élément permettent d'exécuter plusieurs transitions de vue simultanément, d'imbriquer des transitions de vue en cours dans une autre et de résoudre les problèmes z-index que vous pourriez rencontrer avec les transitions de vue à portée de document, tout en conservant l'interactivité du reste de la page. Consultez ce guide pour découvrir comment les utiliser.
Nécessité de transitions de vue plus ciblées
Lorsque vous démarrez une transition de vue dans le même document avec document.startViewTransition() (ou son équivalent entre documents), le navigateur étend la transition de vue résultante au document.
Une fois le rappel de mise à jour exécuté et le navigateur ayant créé des instantanés de tous les éléments nécessaires, la couche ::view-transition et son arborescence de pseudo-éléments sont associées à l'élément :root, html dans l'exemple suivant.
html
├─ ::view-transition
│ └─ ::view-transition-group(root)
│ └─ ::view-transition-image-pair(root)
│ ├─ ::view-transition-old(root)
│ └─ ::view-transition-new(root)
├─ head
└─ body
└─ …
Étant donné que le calque ::view-transition s'affiche au-dessus de la racine de la transition, cela peut entraîner des situations inattendues. Par exemple, les éléments qui participent à une transition de vue peuvent soudainement se chevaucher avec d'autres éléments non participants, ou les éléments peuvent ne plus être coupés par leur wrapper ancêtre pendant la transition de vue.
Démo
Enregistrement de la démonstration
Réactiver pointer-events sur ::view-transition ou utiliser des groupes de transitions de vue imbriqués peut résoudre certains effets secondaires introduits par les transitions de vue à portée de document. Toutefois, ces méthodes ne peuvent pas résoudre tous les problèmes.
Par exemple, les éléments avec position: fixed ou les pop-ups sont toujours masqués par une transition de vue à portée de document pendant que la transition est active (également appelée problème z-index).
Basculez le popover dans la démo suivante, puis sélectionnez le bouton Shuffle (Mélanger) pour lancer une transition de vue à l'échelle du document. Les groupes de transition de vue imbriqués résolvent le problème de découpage, mais pas celui de superposition.
Démo
Enregistrement de la démonstration
Une solution de contournement consiste à capturer le popover dans la transition de vue en lui attribuant un view-transition-name. Bien que cela puisse fonctionner pour une seule instance, il est difficile à gérer et sollicite inutilement le processus d'instantané.
Transitions de vue à portée d'élément
Les transitions de vue à portée d'élément vous permettent de démarrer une transition de vue sur un sous-arbre du DOM. Au lieu d'appeler document.startViewTransition(), vous appelez element.startViewTransition() sur un élément arbitraire, ce qui limite la transition de vue à cet élément.
Dans l'extrait suivant, le navigateur lance une transition de vue à portée d'élément sur l'élément <ul>.
document.querySelector('ul').startViewTransition({
callback: () => {
// … code that manipulates the contents of <ul>
},
})
L'élément dans lequel vous appelez element.startViewTransition() (par exemple, <ul>) est appelé racine de transition ou portée.
Lorsque le navigateur étend une transition de vue à un élément, celui-ci est isolé du reste du DOM :
- Le navigateur recherche les éléments à inclure dans l'instantané uniquement dans la sous-arborescence de la portée.
- Pendant le processus d'instantané, seule l'exécution du rendu du champ d'application s'arrête, tandis que le rappel
updates'exécute. - Le pseudo-arbre
::view-transitionrésultant est injecté dans la racine de transition.
Par exemple, avec <ul>, l'arborescence DOM se présente comme suit lorsque la transition de vue est active :
html
├─ head
└─ body
├─ ul
│ ├─ ::view-transition
│ │ └─ ::view-transition-group(root)
│ │ ├─ ::view-transition-group-children(root)
│ │ │ └─ …
│ │ └─ ::view-transition-image-pair(root)
│ │ ├─ ::view-transition-old(root)
│ │ └─ ::view-transition-new(root)
│ ├─ li
│ ├─ li
│ └─ li
├─ button#showpopover
├─ button#reorder
└─ div#popover
└─ p
Le pseudo ::view-transition a la même taille et la même forme que la racine de la transition et ne s'affiche qu'au-dessus de la racine de la transition. Pour cette raison, l'ordre de superposition des éléments en dehors de la racine de la transition est respecté.
Par exemple, si vous avez un pop-over visible au-dessus de l'élément <ul> et que vous lancez ensuite une transition de vue à portée d'élément sur l'élément <ul>, le pop-over n'est pas masqué par le pseudo-arbre de la transition de vue.
Essayez-le dans la démo suivante. Il comporte deux boutons. Le premier bouton active ou désactive le pop-up, et le second réorganise les éléments de la liste à l'aide d'une transition de vue à portée d'élément.
Démo
Enregistrement de la démonstration
Comme des transitions de vue à portée d'élément sont utilisées, le pop-up reste visible au-dessus de l'élément <ul> pendant que la transition est active.
De plus, les éléments en dehors de l'élément <ul> (par exemple, les boutons) restent interactifs, car ils ne font pas partie du champ d'application.
Portées auto-participantes et groupes de transition de vue imbriqués
Lorsque vous démarrez une transition de vue à portée d'élément sur un élément qui masque son contenu en cas de dépassement (c'est-à-dire lorsque son overflow est défini sur hidden, scroll ou clip), vous remarquez que le contenu de la transition de vue reste visuellement masqué.
En effet, les transitions de vue à portée d'élément gèrent automatiquement les éléments suivants :
- La portée
view-transition-name: rootest automatiquement appliquée, ce qui la rend auto-participative. - Le champ d'application
view-transition-group: containest automatiquement appliqué pour activer les groupes de transitions de vue imbriqués. - Le pseudo-élément
::view-transition-group-children(root)résultant coupe automatiquement son contenu à l'aide deoverflow: clipsi la racine de portée coupe son overflow, ce qui empêche les pseudo-éléments de déborder visuellement de la racine de transition.
Par conséquent, vous pouvez limiter le CSS que vous utilisez avec les transitions de vue à portée d'élément aux éléments que vous souhaitez capturer. Par exemple, dans la démo de liste, le CSS n'ajoute que des noms aux éléments de la liste :
ul li {
view-transition-name: match-element;
view-transition-class: album;
}
Essayez-le dans la démo suivante. Il vous permet d'ignorer l'auto-participation. Lorsque le champ d'application est "auto-participant" (comportement par défaut), tout fonctionne comme prévu. Lorsque le champ d'application n'est pas auto-participant, sa bordure change immédiatement et son contenu déborde de l'encapsuleur pendant la transition.
Démo
Enregistrement de la démonstration
Pour référence, le pseudo-arbre de cette démo avec auto-participation se présente comme suit :
html
├─ head
└─ body
├─ ul
│ ├─ ::view-transition
│ │ └─ ::view-transition-group(root)
│ │ ├─ ::view-transition-group-children(root)
│ │ │ ├─ ::view-transition-group(item1)
│ │ │ │ └─ ::view-transition-image-pair(item1)
│ │ │ │ ├─ ::view-transition-old(item1)
│ │ │ │ └─ ::view-transition-new(item1)
│ │ │ ├─ ::view-transition-group(item2)
│ │ │ │ └─ …
│ │ │ …
│ │ └─ ::view-transition-image-pair(root)
│ │ ├─ ::view-transition-old(root)
│ │ └─ ::view-transition-new(root)
│ ├─ li
│ ├─ li
│ └─ li
└─ button#reorder
Étant donné que la racine de la transition, l'élément <ul>, coupe verticalement son contenu, ::view-transition-group-children(root) applique également automatiquement un clip.
Transitions de vue simultanées à portée d'élément
Étant donné que les transitions de vue à portée d'élément s'exécutent de manière isolée, plusieurs transitions de vue à portée d'élément peuvent s'exécuter simultanément si elles ont une portée différente.
La démo suivante comporte deux boutons de réorganisation, un pour chaque liste. Chaque bouton lance une transition de vue à portée d'élément uniquement sur sa liste respective. Étant donné que les arbres DOM des deux listes ne se chevauchent pas, les deux transitions de vue à portée d'élément peuvent s'exécuter simultanément de manière isolée.
Démo
Enregistrement de la démonstration
Cette nature isolée vous permet également de réutiliser les valeurs view-transition-name dans différents niveaux. Tant qu'un nom reste unique dans son champ d'application, il n'y a pas de conflit.
Transitions de vue à portée d'élément imbriquées et confinement de view-transition-name
Lorsque les arbres DOM de plusieurs transitions de vue à portée d'élément se chevauchent, il existe un risque de collision de valeurs view-transition-name. Pour cette raison, le navigateur attribue automatiquement view-transition-scope: all aux transitions de vue actives à portée d'élément afin d'atténuer ce risque.
De la même manière que les portées anchor-scope définissent les valeurs anchor-name, la propriété view-transition-scope garantit que les valeurs view-transition-name sont limitées à la sous-arborescence de l'élément. La propriété accepte none, une liste de noms que vous souhaitez définir comme portée, ou all pour définir toutes les valeurs comme portée.
En plus d'empêcher les noms de déborder, view-transition-scope empêche également un élément et son contenu d'être capturés par une transition de vue externe et simultanée. Lorsque le processus d'instantané parcourt le sous-arbre pour trouver un élément à inclure dans l'instantané, il ignore les éléments (et l'ensemble de leur sous-arbre) auxquels view-transition-scope: all est appliqué. Cela suppose que ces éléments participent déjà à une autre transition de vue à portée d'élément.
La démo suivante est une variante de la précédente. En plus des deux boutons qui mélangent le contenu de la liste, il comporte également un bouton Inverser pour inverser les listes. L'échange est géré par l'activation/désactivation d'une classe .reversed sur le #lists-wrapper.
Démo
Enregistrement de la démonstration
Comme view-transition-scope: all s'applique automatiquement pendant la transition de lecture aléatoire, vous pouvez démarrer une transition de permutation externe simultanée pendant que la transition de lecture aléatoire est toujours en cours.
Étant donné que view-transition-scope: all empêche également un élément d'être instantané dans une transition externe, la démo ajoute également des valeurs view-transition-name aux éléments qui encapsulent les éléments <ul>.
#list1-wrapper, #list2-wrapper {
view-transition-name: attr(id type(<custom-ident>));
}
Le pseudo-arbre de cette démo, après avoir lancé un mélange sur la deuxième liste, puis inversé les deux listes, ressemble à ceci :
html
├─ head
└─ body
└─ #lists-wrapper.reversed (SCOPE)
├─ ::view-transition
│ └─ ::view-transition-group(lists-wrapper)
│ ├─ ::view-transition-group-children(lists-wrapper)
│ │ ├─ ::view-transition-group(list1-wrapper)
│ │ │ └─ ::view-transition-image-pair(list1-wrapper)
│ │ │ ├─ ::view-transition-old(list1-wrapper)
│ │ │ └─ ::view-transition-new(list1-wrapper)
│ │ └─ ::view-transition-group(list2-wrapper)
│ │ └─ ::view-transition-image-pair(list2-wrapper)
│ │ ├─ ::view-transition-old(list2-wrapper)
│ │ └─ ::view-transition-new(list2-wrapper)
│ └─ ::view-transition-image-pair(lists-wrapper)
│ ├─ ::view-transition-old(lists-wrapper)
│ └─ ::view-transition-new(lists-wrapper)
├─ div#list1-wrapper
│ ├─ ul
│ │ ├─ li#item1
│ │ ├─ li#item2
│ │ └─ li#item3
│ └─ button.reorder
└─ div#list2-wrapper
├─ ul (SCOPE)
│ ├─ ::view-transition
│ │ └─ ::view-transition-group(list)
│ │ ├─ ::view-transition-group-children(list )
│ │ │ ├─ ::view-transition-group(item4)
│ │ │ │ └─ ::view-transition-image-pair(item4)
│ │ │ │ ├─ ::view-transition-old(item4)
│ │ │ │ └─ ::view-transition-new(item4)
│ │ │ ├─ ::view-transition-group(item5)
│ │ │ │ └─ …
│ │ │ …
│ │ └─ ::view-transition-image-pair(list)
│ │ ├─ ::view-transition-old(list)
│ │ └─ ::view-transition-new(list)
│ ├─ li#item4
│ ├─ li#item5
│ └─ li#item6
└─ button.reorder
En savoir plus
Pour en savoir plus sur les transitions de vue à portée d'élément, consultez l'explication, la spécification css-view-transitions-2 et la liste des modifications de spécification ouvertes.