Publicado: 22 de septiembre de 2025
Cuando inicias una transición de vista, el navegador toma automáticamente instantáneas del antes y el después de los elementos etiquetados con un view-transition-name
. Estas instantáneas se renderizan en un árbol de seudoelementos. De forma predeterminada, el árbol generado es "plano". Esto significa que se pierde la jerarquía original en el DOM y todos los grupos de transición de vista capturados son hermanos bajo un solo seudoelemento ::view-transition
.
Este enfoque de árbol plano es suficiente para muchos casos de uso, pero hay algunos casos de uso de diseño que no se pueden lograr con él. A continuación, se muestran ejemplos de efectos que pueden tener un efecto visual inesperado en un árbol plano:
- Recorte (
overflow
,clip-path
,border-radius
): El recorte afecta a los elementos secundarios del elemento, lo que significa que los elementos secundarios del grupo de transición de vista no pueden recortarse entre sí. opacity
,mask-image
yfilter
: Del mismo modo, estos efectos están diseñados para funcionar en una imagen completamente rasterizada de un árbol, afectando a los elementos secundarios en lugar de afectar a cada elemento de forma individual.- Transformaciones 3D (
transform-style
,transform
,perspective
): Para mostrar el rango completo de animaciones de transformación 3D, se debe mantener cierta jerarquía.
En el siguiente ejemplo, se muestra un seudoárbol plano con elementos que un elemento superior recorta en el árbol del DOM. Estos elementos pierden su recorte durante la transición de vista, lo que genera un efecto visual roto.
Los grupos de transición de vistas anidados son una extensión de las transiciones de vistas que te permiten anidar pseudoelementos ::view-transition-group
entre sí. Cuando los grupos de transición de vistas están anidados, es posible restablecer efectos como el recorte durante la transición.
Browser Support
De un seudoárbol plano a un seudoárbol anidado
En la siguiente demostración, puedes hacer clic en el avatar de una persona para ver más información sobre ella. Las animaciones se controlan con una transición de vista del mismo documento que transforma el botón en el que se hizo clic en el diálogo, mueve el avatar y el nombre por la pantalla, y desliza los párrafos del diálogo hacia arriba o hacia abajo.
Demostración en directo
Grabación de la demostración:
Grabación de la demostración (en cámara lenta)
Si observas de cerca la demostración, verás que hay un problema con la transición: aunque los párrafos con la descripción son elementos secundarios del elemento <dialog>
en el DOM, el cuadro de <dialog>
no recorta el texto durante la transición:
<dialog id="info_bramus" closedby="any">
<h2><img alt="…" class="avatar" height="96" width="96" src="avatar_bramus.jpg"> <span>Bramus</span></h2>
<p>Bramus is …</p>
<p>…</p>
</dialog>
Aplicar overflow: clip
en el <dialog>
tampoco hace nada.
El problema radica en la forma en que las transiciones de vista compilan y renderizan su seudobloque:
- En el seudoárbol, de forma predeterminada, todas las instantáneas son elementos secundarios entre sí.
- El seudoárbol se renderiza en un seudoelemento
::view-transition
que se renderiza sobre todo el documento.
En el caso de esta demostración en particular, el árbol del DOM se ve de la siguiente manera:
html
├─ ::view-transition
│ ├─ ::view-transition-group(card)
│ │ └─ ::view-transition-image-pair(card)
│ │ ├─ ::view-transition-old(card)
│ │ └─ ::view-transition-new(card)
│ ├─ ::view-transition-group(name)
│ │ └─ ::view-transition-image-pair(name)
│ │ ├─ ::view-transition-old(name)
│ │ └─ ::view-transition-new(name)
│ ├─ ::view-transition-group(avatar)
│ │ └─ ::view-transition-image-pair(avatar)
│ │ ├─ ::view-transition-old(avatar)
│ │ └─ ::view-transition-new(avatar)
│ ├─ ::view-transition-group(paragraph1.text)
│ │ └─ ::view-transition-image-pair(paragraph1.text)
│ │ └─ ::view-transition-new(paragraph1.text)
│ └─ ::view-transition-group(paragraph2.text)
│ └─ ::view-transition-image-pair(paragraph2.text)
│ └─ ::view-transition-new(paragraph2.text)
├─ head
└─ body
└─ …
Dado que los seudoelementos ::view-transition-group(.text)
son hermanos posteriores del seudoelemento ::view-transition-group(card)
, se pintan en la parte superior de la tarjeta.
Para que el pseudo ::view-transition-group(.text)
tenga un clip ::view-transition-group(card)
, los pseudos ::view-transition-group(.text)
deben ser elementos secundarios del ::view-transition-group(card)
. Para ello, usa view-transition-group
, que te permite asignar un "grupo principal" para un seudoelemento ::view-transition-group()
generado.
Para cambiar el grupo principal, tienes dos opciones:
- En el elemento principal, establece
view-transition-group
encontain
para que contenga todos los elementos secundarios con unview-transition-name
. - En todos los elementos secundarios, establece
view-transition-group
en elview-transition-name
del elemento principal. También puedes usarnearest
para segmentar a los grupos de nivel superior más cercanos.
Por lo tanto, para esta demostración, el código para usar grupos de transición de vistas anidados se convierte en el siguiente:
button.clicked,
dialog {
view-transition-group: contain;
}
O
button.clicked,
dialog *,
view-transition-group: nearest;
}
Con este código, los seudoelementos ::view-transition-group(.text)
ahora se anidan dentro del seudoelemento ::view-transition-group(card)
. Esto se hace en un seudoelemento ::view-transition-group-children(…)
adicional, que mantiene juntos todos los seudoelementos anidados:
html
├─ ::view-transition
│ ├─ ::view-transition-group(card)
│ │ ├─ ::view-transition-image-pair(card)
│ │ │ ├─ ::view-transition-old(card)
│ │ │ └─ ::view-transition-new(card)
│ │ └─::view-transition-group-children(card)
│ │ ├─ ::view-transition-group(paragraph1.text)
│ │ │ └─ ::view-transition-image-pair(paragraph1.text)
│ │ │ └─ ::view-transition-new(paragraph1.text)
│ │ └─ ::view-transition-group(paragraph2.text)
│ │ └─ ::view-transition-image-pair(paragraph2.text)
│ │ └─ ::view-transition-new(paragraph2.text)
│ ├─ ::view-transition-group(name)
│ │ └─ ::view-transition-image-pair(name)
│ │ ├─ ::view-transition-old(name)
│ │ └─ ::view-transition-new(name)
│ └─ ::view-transition-group(avatar)
│ └─ ::view-transition-image-pair(avatar)
│ ├─ ::view-transition-old(avatar)
│ └─ ::view-transition-new(avatar)
├─ head
└─ body
└─ …
Por último, para que ::view-transition-group(card)
pseudocorte los párrafos, aplica overflow: clip
al seudoelemento ::view-transition-group-children(card)
:
::view-transition-group-children(card) {
overflow: clip;
}
El resultado es el siguiente:
Demostración en directo
Grabación de la demostración:
Grabación de la demostración (en cámara lenta)
El seudónimo ::view-transition-group-children
solo está presente cuando se usan grupos anidados. Tiene el tamaño de la caja de borde del elemento original y se le asigna un borde transparente con la misma forma y grosor que el elemento que generó el seudoelemento (card
en el ejemplo anterior).
Recortes y mucho más
Los grupos de transiciones de vista anidados se usan en otros lugares además de los efectos de recorte. Otro ejemplo son los efectos 3D. En la siguiente demostración, hay una opción para rotar la tarjeta en 3D durante la transición.
html:active-view-transition-type(open) {
&::view-transition-old(card) {
animation-name: rotate-out;
}
&::view-transition-new(card) {
animation-name: rotate-in;
}
}
html:active-view-transition-type(close) {
&::view-transition-old(card) {
animation-name: rotate-in;
}
&::view-transition-new(card) {
animation-name: rotate-out;
}
}
Sin grupos de transición de vistas anidados, el avatar y el nombre no rotan junto con la tarjeta.
Demostración en directo
Grabación de la demostración:
Grabación de la demostración (en cámara lenta)
Si anidas los seudoelementos del avatar y el nombre dentro de la tarjeta, se puede restablecer el efecto 3D. Pero eso no es lo único que debes hacer. Además de rotar los seudónimos ::view-transition-old(card)
y ::view-transition-new(card)
, también debes rotar el ::view-transition-group-children(card)
.
html:active-view-transition-type(open) {
&::view-transition-group-children(card) {
animation: rotate-in var(--duration) ease;
backface-visibility: hidden;
}
}
html:active-view-transition-type(close) {
&::view-transition-group-children(card) {
animation: rotate-out var(--duration) ease;
backface-visibility: hidden;
}
}
Demostración en directo
Grabación de la demostración:
Grabación de la demostración (en cámara lenta)
Más demostraciones
En el siguiente ejemplo, se usan grupos de transición de vistas anidados para garantizar que el elemento de desplazamiento principal recorte las tarjetas. Puedes activar o desactivar el uso de grupos de transición de vista anidados con los controles incluidos.
Demostración en directo
Grabación de la demostración:
Lo interesante de esta demostración es que todos los seudoelementos ::view-transition-group(.card)
se anidan dentro del seudoelemento ::view-transition-group(cards)
superior y se recortan con él. El #targeted-card
se excluye porque su animación de entrada o salida no debe recortarse con el ::view-transition-group(cards)
.
/* The .cards wrapper contains all children */
.cards {
view-transition-name: cards;
view-transition-group: contain;
}
/* Contents that bleed out get clipped */
&::view-transition-group-children(cards) {
overflow: clip;
}
/* Each card is given a v-t-name and v-t-class */
.card {
view-transition-name: match-element;
view-transition-class: card;
}
/* The targeted card is given a unique name (to style the pseudo differently)
and shouldn't be contained by the ::view-transition-group-children(cards) pseudo */
#targeted-card {
view-transition-name: targeted-card;
view-transition-group: none;
}
Resumen
Las transiciones de vista anidadas te permiten conservar parte de la topología del árbol del DOM cuando construyes los seudoelementos. Esto permite una variedad de efectos que antes no eran posibles con las transiciones de vista, algunos de los cuales describimos aquí.
El anidamiento cambia el modelo de cómo se construyen las transiciones de vista y está diseñado para crear efectos avanzados. Como se mencionó, las transiciones de vista con alcance de elemento también pueden lograr un subconjunto de los efectos con un modelo más simple. Te recomendamos que pruebes ambas funciones para decidir cuál se adapta mejor a tus necesidades.