Executar transições de visualização simultâneas e aninhadas com transições de visualização no escopo do elemento

Publicado em: 27 de março de 2026

As transições de visualização no escopo do elemento permitem que várias transições de visualização sejam executadas simultaneamente, que transições de visualização em andamento sejam aninhadas em outra e resolvem problemas de z-index que você pode encontrar com transições de visualização no escopo do documento, mantendo o restante da página interativo. Leia este guia para saber como usá-los.

Vídeo promocional: repense a Web com as transições de visualização no escopo. Teste uma demonstração ao vivo (Chrome 147 ou mais recente)

A necessidade de transições de visualização com escopo mais restrito

Quando você inicia uma transição de visualização no mesmo documento com document.startViewTransition() (ou com o equivalente entre documentos), o navegador limita a transição de visualização resultante ao documento.

Depois que o callback de atualização é executado e o navegador cria snapshots de todos os elementos necessários, a sobreposição ::view-transition e a árvore de pseudoelementos resultantes são anexadas ao elemento :root, html no exemplo a seguir.

html
  ├─ ::view-transition
  │  └─ ::view-transition-group(root)
  │     └─ ::view-transition-image-pair(root)
  │        ├─ ::view-transition-old(root)
  │        └─ ::view-transition-new(root)
  ├─ head
  └─ body
     └─ …

Como a camada ::view-transition é renderizada sobre a raiz da transição, isso pode levar a situações inesperadas. Por exemplo, elementos que participam de uma transição de visualização podem de repente se sobrepor a outros não participantes, ou elementos podem não ser mais cortados pelo wrapper ancestral durante a transição de visualização.

Demonstração ao vivo

Gravação da demonstração

Reativar pointer-events em ::view-transition ou usar grupos de transição de visualização aninhados pode resolver alguns efeitos colaterais introduzidos pelas transições de visualização no escopo do documento. No entanto, esses métodos não resolvem todos os problemas.

Por exemplo, elementos com position: fixed ou popovers ainda são obscurecidos por uma transição de visualização no escopo do documento enquanto a transição está ativa, também conhecida como problema do z-index.

Ative o popover na demonstração a seguir e selecione o botão Shuffle para iniciar uma transição de visualização no escopo do documento. Grupos de transição de visualização aninhados resolvem o problema de corte, mas o problema de camadas permanece.

Demonstração ao vivo

Gravação da demonstração

Uma solução alternativa é capturar o popover como parte da transição de visualização, atribuindo a ele um view-transition-name. Embora isso possa funcionar para uma única instância, é difícil de manter e sobrecarrega desnecessariamente o processo de criação de snapshots.

Transições de visualização no escopo do elemento

As transições de visualização no escopo do elemento permitem iniciar uma transição de visualização em uma subárvore do DOM. Em vez de chamar document.startViewTransition(), chame element.startViewTransition() em um elemento arbitrário, que escopo a transição de visualização para esse elemento.

No snippet a seguir, o navegador inicia uma transição de visualização no escopo do elemento <ul>.

document.querySelector('ul').startViewTransition({
  callback: () => {
    // … code that manipulates the contents of <ul>
  },
})

O elemento em que você invoca element.startViewTransition(), por exemplo, o <ul>, é chamado de raiz de transição ou escopo.

Quando o navegador define o escopo de uma transição de visualização para um elemento, ele é isolado do restante do DOM:

  • O navegador procura elementos para criar snapshots apenas na subárvore do escopo.
  • Durante o processo de criação de snapshots, enquanto o callback update é executado, apenas a renderização do escopo é interrompida.
  • A pseudárvore ::view-transition resultante é injetada na raiz da transição.

Por exemplo, com o <ul>, a árvore do DOM fica assim enquanto a transição de visualização está ativa:

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

O pseudoelemento ::view-transition tem o mesmo tamanho e formato da raiz de transição e é renderizado apenas sobre ela. Por isso, a ordem de camadas dos elementos fora da raiz da transição é respeitada.

Por exemplo, se você tiver um popover visível acima do elemento <ul> e iniciar uma transição de visualização no escopo do elemento <ul>, o popover não será obscurecido pela pseudárvore da transição de visualização.

Teste na demonstração a seguir. Ele tem dois botões. O primeiro botão alterna o popover, e o segundo reordena os itens da lista usando uma transição de visualização no escopo do elemento.

Demonstração ao vivo

Gravação da demonstração

Como são usadas transições de visualização no escopo do elemento, o popover permanece visível sobre o elemento <ul> enquanto a transição está ativa.

Além disso, os elementos fora do elemento <ul>, como os botões, permanecem interativos porque não fazem parte do escopo.

Escopos de participação automática e grupos de transição de visualização aninhados

Quando você inicia uma transição de visualização no escopo do elemento em um elemento que corta o estouro (ou seja, quando o overflow é definido como hidden, scroll ou clip), o conteúdo da transição de visualização permanece visualmente cortado.

Isso ocorre porque as transições de visualização no escopo do elemento processam automaticamente o seguinte:

  • O escopo recebe automaticamente a aplicação de view-transition-name: root, o que o torna auto-participante.
  • O escopo recebe automaticamente view-transition-group: contain aplicado para ativar grupos de transição de visualização aninhados.
  • O pseudoelemento ::view-transition-group-children(root) resultante corta automaticamente o conteúdo usando overflow: clip se a raiz do escopo cortar o estouro, o que impede que os pseudoelementos sangrem visualmente da raiz da transição.

Assim, você pode manter o CSS usado com transições de visualização no escopo do elemento focado apenas nos elementos que quer capturar. Por exemplo, na demonstração da lista, o CSS só adiciona nomes aos itens da lista:

ul li {
  view-transition-name: match-element;
  view-transition-class: album;
}

Teste na demonstração a seguir. Ela permite substituir a autoparticipação. Quando o escopo é de participação própria (o comportamento padrão), tudo funciona como esperado. Quando o escopo não é auto-participante, a borda muda imediatamente, e o conteúdo vaza do wrapper durante a transição.

Demonstração ao vivo

Gravação da demonstração

Para referência, a pseudárvore desta demonstração com participação própria tem esta aparência:

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

Como a raiz da transição, o elemento <ul>, corta verticalmente o conteúdo, o ::view-transition-group-children(root) também aplica um corte automaticamente.

Transições de visualização simultâneas no escopo do elemento

Como as transições de visualização no escopo do elemento são executadas isoladamente, várias transições desse tipo podem ser executadas simultaneamente se tiverem um escopo diferente.

A demonstração a seguir tem dois botões de reordenação, um para cada lista. Cada botão inicia uma transição de visualização no escopo do elemento apenas na respectiva lista. Como as árvores DOM das duas listas não se sobrepõem, as duas transições de visualização no escopo do elemento podem ser executadas simultaneamente de forma isolada.

Demonstração ao vivo

Gravação da demonstração

Essa natureza isolada também permite reutilizar valores view-transition-name em diferentes escopos. Desde que um nome permaneça exclusivo no escopo dele, não haverá conflito.

Transições de visualização aninhadas no escopo do elemento e contenção de view-transition-name

Quando as árvores DOM de várias transições de visualização no escopo do elemento se sobrepõem, há risco de colisão de valores view-transition-name. Por isso, o navegador atribui automaticamente view-transition-scope: all a transições de visualização ativas no escopo do elemento para mitigar esse risco.

Assim como anchor-scope escopo valores anchor-name, a propriedade view-transition-scope garante que os valores view-transition-name sejam escopo para a subárvore do elemento. A propriedade aceita none, uma lista de nomes que você quer escopo, ou all para escopo de todos os valores.

Além de evitar que os nomes sangrem, o view-transition-scope também impede que um elemento e o conteúdo dele sejam capturados por uma transição de visualização externa e simultânea. Quando o processo de criação de snapshots percorre a subárvore para encontrar um elemento a ser incluído no snapshot, ele ignora elementos (e toda a subárvore deles) que têm view-transition-scope: all aplicado. Isso pressupõe que esses elementos já estão participando de uma transição de visualização diferente no escopo do elemento.

A demonstração a seguir é uma variação da anterior. Além dos dois botões que embaralham o conteúdo da lista, ele também tem um botão Trocar para trocar as listas. Alternar uma classe .reversed no #lists-wrapper processa a troca.

Demonstração ao vivo

Gravação da demonstração

Como view-transition-scope: all é aplicado automaticamente durante a transição de reprodução aleatória, você pode iniciar uma transição de troca externa simultânea enquanto a transição de reprodução aleatória ainda está em andamento.

Como view-transition-scope: all também impede que um elemento seja capturado em uma transição externa, a demonstração também adiciona valores view-transition-name aos elementos que envolvem os elementos <ul>.

#list1-wrapper, #list2-wrapper {
  view-transition-name: attr(id type(<custom-ident>));
}

A pseudárvore desta demonstração, depois de iniciar uma redistribuição na segunda lista e trocar as duas listas, fica assim:

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

Saiba mais

Para saber mais sobre transições de visualização no escopo do elemento, consulte a explicação, a especificação css-view-transitions-2 e a lista de edições de especificações abertas.