Conteúdo deste artigo
- O sonho: uma linha, nomes infinitos
- 100 produtos, 100 nomes, 1 pesadelo
- Nome x classe: a distinção que muda tudo
- Não nomeie tudo antecipadamente
- Padrões práticos para conteúdo real
- Não deixe as pessoas doentes
- Lidar com navegadores antigos (sem fazer basicamente nada)
- Envie
Em Parte 1 , cobrimos as pegadinhas que incomodam você primeiro: a meta tag obsoleta que silenciosamente não faz nada, o tempo limite de 4 segundos que mata as transições sem avisar você, a distorção da imagem que transforma cada mudança de proporção em uma massa boba e o pagereveal/pageswap eventos que fornecem ganchos para o ciclo de vida da transição.
Tudo isso leva você de “nada funciona” para “um elemento fazendo uma boa transição entre duas páginas”. O que é ótimo. Por cerca de cinco minutos. Em seguida, você tenta construir uma página de listagem de produtos com 48 cartões, cada um precisando se transformar em uma visualização detalhada, e percebe que os tutoriais deixaram de fora a parte difícil.
É aqui que tudo se torna real. Vamos dimensionar isso.
Cross-Document View Transitions Series
- The Gotchas Nobody Mentions
- Dimensionando transições de visualização em centenas de elementos (Você está aqui!)
O sonho: uma linha, nomes infinitos
Em um mundo perfeito, você resolveria o problema de dimensionamento com CSS puro. Sem JavaScript. Sem loops do lado do servidor. Apenas isto:
###PRE_4c5b05b40a8c486b0192eeb1351eb4fc###
Isso é ident() — uma função CSS proposta por Bramus (que trabalha no Chrome) para o Grupo de Trabalho CSS. Ele pega strings, números inteiros ou outros identificadores, concatena-os e exibe um nome CSS válido. Combine-o com sibling-index(), que retorna a posição de um elemento entre seus irmãos (1, 2, 3…), e você obterá nomes exclusivos gerados automaticamente para cada elemento em uma lista. Uma regra. Funciona para 10 cartões ou 10.000. O CSS não se importa.
E não se trata apenas de ver transições. The same pattern works for scroll-timeline-name, container-name, view-timeline-name — anywhere you need unique identifiers at scale. Você pode até extrair nomes de atributos HTML com attr() em vez de sibling-index(), construindo identificadores como ident("--item-" attr(id) "-tl"). A flexibilidade é real.
O problema é o seguinte: metade dessa equação já existe. sibling-index() fornecido no Chrome 138 – você pode usá-lo hoje para coisas como animações escalonadas e estilos calculados. A peça que falta é ident(). Há uma Chrome Intent to Prototype de maio de 2025, o que significa que está no radar. Mas “no radar” e “no seu navegador” são coisas muito diferentes. Nenhum navegador foi enviado ident() ainda, e não há um cronograma para quando ele chegará.
Portanto, não podemos usá-lo ainda. Mas vale a pena saber porque assim que ident() for enviado, uma grande parte da complexidade que você está prestes a ver simplesmente… evapora. Até então, veja como você resolve o mesmo problema de forma eficiente hoje – com as ferramentas que realmente existem nos navegadores atualmente.
100 produtos, 100 nomes, 1 pesadelo
Aqui está o que acontece quando você segue um tutorial que mostra uma imagem principal fazendo a transição entre duas páginas e tenta aplicar esse padrão a uma grade:
###PRE_2fa6a312481d14a2556041f4356fe3be###
É isso que você obtém se seguir os tutoriais que mostram apenas um ou dois elementos nomeados. Eles atribuem view-transition-name: hero a uma imagem e encerram o dia. Legal. Agora tente construir uma grade de produtos.
Cada view-transition-name em uma página deve ser exclusivo. Essa é uma regra rígida – se dois elementos compartilham um nome, o navegador não sabe qual deles mapeia para qual na próxima página, então descarta toda a transição. Em uma página de listagem com 48 produtos, você precisa de 48 nomes exclusivos. Em uma galeria de fotos com 200 miniaturas, você precisa de 200. Os nomes não são o problema – você pode gerá-los. O problema é que cada seletor de pseudo-elemento em seu CSS tem como alvo um nome específico , então seus estilos de animação explodem em uma parede incontrolável de seletores.
É aqui que você precisa entender a diferença entre duas propriedades que parecem fazer a mesma coisa, mas absolutamente não fazem.
Nome x classe: a distinção que muda tudo
E sim, a nomenclatura aqui é confusa. Vou ser sincero: a primeira vez que vi view-transition-name e view-transition-class um ao lado do outro, pensei que fossem intercambiáveis. Eles não são, e a diferença é importante.
Nome = identidade. Ele responde: “Qual elemento na página A é o mesmo elemento na página B?” Quando você fornece uma miniatura view-transition-name: card-7 na página da grade e fornece a imagem do herói view-transition-name: card-7 na página de detalhes, você está dizendo ao navegador que são a mesma coisa e para animar entre eles. Os nomes devem ser exclusivos por página. Dois elementos não podem ser card-7 ou tudo quebra.
Classe = gancho de estilo. Ele responde: “Como deve ser a animação ?” Quando todos os cinquenta elementos tiverem view-transition-class: card, você pode escrever uma regra CSS que controle a duração, atenuação e object-fit para todos eles. É o mesmo modelo mental das classes CSS em elementos regulares – .btn não identifica um botão específico, diz “estile-me como um botão”.
Pense nisso como um banco de dados. O name é a chave primária – única, identifica uma linha específica. O class é uma coluna de categoria – agrupa linhas para que você possa executar uma consulta em todas elas de uma vez.
Aqui está o que parece na prática:
Aí está. Seis cartões, seis nomes exclusivos, mas exatamente três regras CSS que tratam de todo o comportamento da animação. Podem ser sessenta cartas. Poderia ser seiscentos. O CSS não muda.
A linha principal é esse seletor: ::view-transition-group(*.card). O asterisco é um curinga para o nome e .card corresponde a view-transition-class. É lido como “qualquer grupo de transição de visualização cujo elemento tenha view-transition-class: card, independentemente de qual seja seu nome específico”.
Para transições de aplicativos de múltiplas páginas (MPA) entre documentos, o padrão é o mesmo, mas você gera os nomes no servidor:
###PRE_3fc28d7e23f0735ce5a7e541eddf7e44###
###PRE_a804522cc912f515e764eeb42f74bd48###
###PRE_5ffa201d5a0239d3152e8bfa2ac685d7###
Essa é toda a folha de estilo de animação de um site com milhares de produtos. Três regras. Não importa quantos itens você tenha no banco de dados, você nunca adiciona outra linha de transição CSS.
Antes de view-transition-class existir, as pessoas faziam coisas horríveis — percorrer itens em JavaScript para gerar <style> blocos com centenas de seletores ou usando CSS pré-processadores para cuspir todas as permutações de nomes possíveis no momento da construção. Funcionou, tecnicamente, da mesma forma que a fita adesiva no para-choque de um carro funciona tecnicamente.
view-transition-class são os autores das especificações reconhecendo que a API original simplesmente não foi escalonada e corrigindo-a da maneira certa.
Uma pegadinha: view-transition-class foi adicionado à especificação posteriormente para corrigir esses problemas exatos de escala. A propriedade chegou ao Chrome 125 e agora está no Chrome, Edge e Safari 18.2+. Versões mais antigas do Chromium e do Firefox ainda não o reconhecerão. As transições ainda funcionarão , elas usarão apenas a animação de fade padrão em vez de seu tempo personalizado. Não é o pior substituto.
Você também pode atribuir várias classes a um único elemento, assim como as classes CSS normais. Algo como view-transition-class: card featured é válido e você pode segmentá-lo com ::view-transition-group(*.card) ou ::view-transition-group(*.featured). Útil quando você deseja que a maioria dos produtos faça a transição da mesma maneira, mas precisa de alguns para se destacarem com um estilo de animação diferente.
Não nomeie tudo antecipadamente
Tudo até agora teve view-transition-name ali mesmo no HTML ou CSS desde o momento em que a página é carregada. Isso funciona. Mas tem um custo que não é óbvio até atingir a escala do mundo real.
Observe o CSS de ambas as páginas. Zero declarações view-transition-name. Nenhum. Cada cartão da grade é anônimo até o exato momento em que o usuário clica em um.
Veja por que isso é importante. Quando você coloca view-transition-name em um elemento em sua folha de estilo – apenas ali em CSS, atribuído a partir do carregamento da página – você está dizendo ao navegador: “Este elemento participa de cada transição que acontece nesta página”. Cada navegação. O navegador precisa tirar um instantâneo dele, calcular sua posição e configurar a árvore de pseudoelementos para ele. Para uma imagem de herói, quem se importa? Para uma grade de 48 cartões de produtos, são 48 elementos sendo capturados, diferenciados e animados individualmente quando o usuário clicou apenas em um deles. Os outros 47 instantâneos são puro desperdício.
Em uma máquina rápida você pode não perceber. Em um telefone Android de gama média carregando uma grade de imagens de produtos via LTE? Você vai sentir isso. A transição falha ou o navegador simplesmente a ignora completamente porque não consegue configurar tudo rápido o suficiente.
A solução é tratar view-transition-name como algo just-in-time. Atribua-o no momento da interação, não no carregamento da página.
O ciclo de vida é assim:
- O usuário clica em um cartão na página de listagem.
- O navegador começa a navegar —
pageswapdispara na página antiga. - Seu
pageswapmanipulador olha para #event.activation.entry.urlpara descobrir onde o o usuário está indo, encontra o cartão clicado e dá um tapaview-transition-name: product-42nele. - O navegador captura instantâneos daquele elemento nomeado (mais a transição padrão
root). - A navegação acontece, uma nova página é carregada.
-
pagerevealé acionado na página recebida. - Seu
pagerevealmanipulador lê o URL, encontra o elemento hero, atribui oview-transition-name: product-42correspondente. - O navegador vê nomes correspondentes em snapshots antigos e novos — transforma-se entre eles.
- A transição termina, sua promessa
.finishedresolve, você limpa os nomes.
É isso. Um elemento nomeado, um elemento transicionado, zero desperdício.
O objeto event.activation é seu melhor amigo aqui. Na página de saída, event.activation.entry.url informa para onde a navegação está indo. Na página de entrada, você acabou de ler window.location. Entre os dois, você tem tudo o que precisa para descobrir qual elemento nomear sem qualquer estado global, sem sessionStorage truques, sem ginástica de parâmetros de consulta além do que seu aplicativo já usa.
E sobre essa etapa de limpeza, remover o nome depois de .finished resolve? Não é apenas arrumação. Se o usuário navegar de volta para a página de listagem e clicar em um cartão diferente , você não deseja que o cartão antigo ainda carregue o nome da transição anterior. Nomes obsoletos causam conflitos de nomes duplicados (morte instantânea da transição) ou correspondência de elementos errados (a nova página se transforma a partir do cartão errado). Limpe você mesmo.
Este padrão é basicamente o que A diretiva transition:name do Astro faz nos bastidores. O mesmo acontece com Suporte à transição de visualização do Nuxt . Eles atribuem e removem nomes dinamicamente em todo o ciclo de vida da navegação. As estruturas apenas ocultam a fiação pageswap/pagereveal atrás de um atributo de componente. Você está fazendo a mesma coisa, só que sem a camada de abstração. Menos peças móveis, mesmo resultado.
Padrões práticos para conteúdo real
O exemplo da grade de produtos cobre o caso mais comum, mas vamos examinar alguns outros padrões que você encontrará no ambiente.
Galerias de fotos com proporções mistas
Galerias são complicadas porque cada miniatura pode ter uma proporção diferente, e a visualização em tamanho real definitivamente terá. A correção do artigo da Parte 1 é essencial aqui, mas você também deseja que a transição pareça intencional, em vez de caótica.
###PRE_0f80d952dfece494b80d81b1c3b2124f###
O truque com galerias é atribuir o view-transition-name ao próprio <img> em vez do cartão ou contêiner ao redor. Você deseja que o navegador transforme a imagem do tamanho da miniatura para o tamanho da lightbox, não o plano de fundo, o preenchimento e a legenda do cartão junto com ela. Dê um nome à imagem. Estilize o cartão. Mantenha-os separados.
Para o fundo lightbox (aquela sobreposição escura), dê a ele seu próprio view-transition-name e view-transition-class. Ele desaparecerá independentemente enquanto a imagem se transforma. Duas transições ocorrendo em paralelo, cada uma com seu tempo próprio. Parece polido e são apenas dois nomes.
Transições de guias ou seções em uma página
Nem tudo é um padrão da grade aos detalhes. Às vezes, você está fazendo a transição entre seções da mesma página, por exemplo, guias do painel, formulários de várias etapas, painéis de conteúdo. As transições de visualização do mesmo documento funcionam muito bem aqui, e a abordagem view-transition-class é dimensionada da mesma maneira.
###PRE_309693b7f486ee33616f6cbfca8100d5###
The animation-duration: 0s on persistent elements is worth calling out. Se o cabeçalho do seu site tiver um view-transition-name (para que ele permaneça no lugar em vez de participar do cross-fade raiz padrão), você provavelmente não deseja que ele seja animado. Zero-duration makes it snap to its new position instantly, which feels like it never moved. That’s the point — stable landmarks make the transitioning content feel grounded.
Conteúdo dinâmico e rolagem infinita
Aqui está um padrão que pega as pessoas desprevenidas. Você tem uma grade de produtos com rolagem infinita, carregando novos itens conforme o usuário rola para baixo. Cada novo lote chega via fetch() e é anexado ao DOM. Esses novos itens precisam de view-transition-name?
Não. Não até que alguém clique em um.
Com o padrão just-in-time, não importa se um elemento existia no carregamento da página ou foi adicionado dinamicamente cinco minutos depois. O manipulador pageswap consulta o DOM no momento da navegação. Se o elemento estiver lá, ele o encontra, nomeia e pronto. Seus itens de rolagem infinita funcionam de forma idêntica aos itens de carregamento da página inicial, sem qualquer configuração extra.
A única coisa a observar: certifique-se de que seus atributos data-id (ou o que você estiver usando para corresponder aos elementos) sejam exclusivos em todos os lotes carregados. Se sua API retorna itens com IDs e você os usa para view-transition-name, você já está bem. Se você estiver gerando IDs no lado do cliente, certifique-se de que eles não colidam quando novos lotes forem carregados.
Não deixe as pessoas doentes
###PRE_e874c8635e9e4c528d7de548c10a839e###
Isso não é algo bom de se ter. Preciso ser franco sobre isso.
Pessoas com distúrbios vestibulares — e há muito mais deles do que a maioria dos desenvolvedores imagina — podem ficar fisicamente enjoadas devido a movimentos inesperados na tela. Não “levemente irritado”. Enjoado. Tonto. Enxaquecas que duram horas. A consulta de mídia prefers-reduced-motion existe porque pessoas reais marcaram uma caixa nas configurações do sistema operacional que diz “por favor, pare de me deixar doente”. Ignorá-lo é o equivalente em acessibilidade a remover uma rampa para cadeiras de rodas porque as escadas parecem mais limpas.
A opção @view-transition pode ficar fora da consulta de mídia. Tudo bem, apenas informa ao navegador: “Quero que as transições entre documentos sejam ativadas”. O navegador ainda fará um corte instantâneo entre as páginas, o que é visualmente idêntico a uma navegação normal. São as personalizações da animação que precisam ser controladas: as durações, as curvas de atenuação, os quadros-chave personalizados. Envolva tudo isso em prefers-reduced-motion: no-preference e você estará coberto.
Aquele prefers-reduced-motion: reduce bloco na parte inferior é uma coisa de cinto e suspensórios. Mesmo se você perder alguma regra de animação, forçar animation-duration: 0s em todos os pseudoelementos de transição garante que nada realmente se mova. O !important é feio, mas justificado aqui. você realmente deseja que isso substitua tudo, sem exceções.
Você já viu o padrão de aceitação condicional na Parte 1:
###PRE_d31bf166d2ff682e78ac2835eae5c4a7###
Qualquer abordagem funciona. Envolver toda a regra @view-transition significa que o navegador nem mesmo tentará a transição – é uma navegação normal, ponto final. Manter @view-transition ativo, mas eliminar as durações da animação, significa que a transição é tecnicamente acionada, mas concluída instantaneamente, o que pode ser importante se você tiver pagereveal lógica que depende de event.viewTransition existente. Escolha o que melhor se adapta à sua configuração. Apenas não envie transições animadas sem verificar.
Algo que vale a pena considerar aqui: “movimento reduzido” não significa necessariamente “sem movimento”. Alguns usuários com sensibilidades vestibulares aceitam fades, mas não deslizam ou ampliam. Você poderia oferecer uma alternativa mais suave em vez de eliminar completamente toda a animação.
###PRE_fe94b655aca6bb538c174794811efc41###
Este é um julgamento. Um cross-fade rápido e sutil tem menos probabilidade de desencadear sintomas do que uma 400ms animação de metamorfose com curvas de atenuação. Mas a opção mais segura é sempre movimento zero e, se você não tiver certeza, escolha animation-duration: 0s. Você sempre pode adicionar uma alternativa mais suave posteriormente, depois de testá-la com usuários reais que dependem da configuração.
Lidar com navegadores antigos (sem fazer basicamente nada)
###PRE_c86e1ec67190754db0e94d1e240beb56###
###PRE_9717c9bc93f79a582bcbfed23dbec61f###
Mas o problema é o seguinte: você provavelmente nem precisa dessa verificação @supports.
As transições de visualização são melhorias progressivas no sentido mais puro do termo. Se um navegador não entender @view-transition { navigation: auto; }, ele ignorará a regra. É assim que o CSS funciona. O usuário clica em um link, o navegador navega normalmente, a nova página carrega. Sem animação, sem metamorfose, sem cross-fade. Apenas um carregamento normal de página. Que é exatamente o que todos os sites da Internet fizeram durante os primeiros 25 anos da web. Está tudo bem.
Nada quebra. Sem erros de JavaScript. Sem mudanças de layout. Nenhum código substituto para escrever. As propriedades view-transition-name são ignoradas. Os ::view-transition-* seletores de pseudoelementos não correspondem a nada. Seus ouvintes de eventos pageswap e pagereveal não disparam ou event.viewTransition é null e sua cláusula de guarda retorna mais cedo. Todo o recurso foi projetado para ficar invisível quando estiver ausente.
Essa é a beleza desta API, honestamente. É um dos raros recursos da plataforma web onde você não precisa escrever uma única linha de código substituto. O Firefox ainda não oferece suporte? Tudo bem – os usuários do Firefox obtêm navegação normal. O Safari está trabalhando nisso, mas ainda não foi enviado? Legal, os usuários do Safari clicam em links e as páginas são carregadas. Ninguém recebe um erro. Ninguém recebe um layout quebrado. Ninguém perde nada. Eles simplesmente não entendem a animação sofisticada, e a maioria deles nunca notará que ela deveria estar lá.
Vale a pena observar onde as coisas realmente estão hoje: o Chrome e o Edge têm suporte completo para transições de visualização entre documentos, incluindo classe de transição de visualização. O Safari também oferece suporte completo para vários documentos a partir do Safari 18.2. O impulso é claramente em direção ao suporte universal, embora o Firefox ainda o mantenha atrás de uma bandeira por enquanto.
A única vez que @supports importa é se você estiver adicionando estilos que apenas façam sentido no contexto de transições de visualização – como contain: paint em elementos para melhorar a qualidade do instantâneo ou ocultar algum estado de carregamento que a transição normalmente cobriria. Bloqueie aqueles que estão por trás de @supports (view-transition-name: none) para que navegadores sem suporte não obtenham os efeitos colaterais sem a recompensa.
A falha é invisível. Esse é o ponto principal.
Envie
Olha, venho construindo sites há muito tempo e sempre houve essa troca tácita: você quer transições suaves, semelhantes a aplicativos, adota uma estrutura e um roteador do lado do cliente e uma etapa de construção e uma estratégia de hidratação e de repente você está mantendo um pequeno porta-aviões apenas para que um cartão possa ser animado em uma imagem de herói.
Essa compensação está se dissolvendo.
As transições de visualização entre documentos permitem que um <a href> pareça uma navegação de aplicativo nativo. Dois arquivos HTML. Algum CSS. Talvez um pouco de JavaScript para coisas sofisticadas. O navegador faz o resto. Isso não é uma coisa pequena – muda quais projetos precisam de uma estrutura e quais apenas assumiram que eles precisavam.
A especificação é jovem. No momento, é apenas Chromium. As arestas são reais – você as viu em ambas as partes desta série. Mas a API foi projetada tão bem que, quando não é suportada, nada quebra. Seu site funciona da maneira que os sites sempre funcionaram. E quando é compatível, parece uma mágica que veio de graça.
Aqui está uma folha de dicas rápida para levar com você:
- Ative com CSS , não com a meta tag obsoleta:
@view-transition { navigation: auto; }. - Ambas as páginas devem ser ativadas ou nenhuma transição acontecerá.
- Tempo limite de 4 segundos começa na navegação, não na renderização – use
pagerevealpara capturarTimeoutError. - As imagens se esticam durante as transições porque os pseudoelementos são padronizados como
object-fit: fill– corrija-o comobject-fit: coverem::view-transition-olde::view-transition-new. -
view-transition-name= identidade (única por página),view-transition-class= gancho de estilo (compartilhado entre elementos). - Não nomeie os elementos antecipadamente – use
pageswapepagerevealpara atribuir nomes just-in-time. Mas mantenha sua lógicapageswaprápida – o navegador oferece uma janela estreita (10-50 ms) antes dos instantâneos. - Limpar nomes após
viewTransition.finishedresolver para evitar conflitos obsoletos. - Animações de portão atrás de
prefers-reduced-motion: no-preference— isso não é opcional. - Aprimoramento progressivo está integrado — navegadores não suportados apenas carregam páginas normais
As melhores animações são aquelas que você não precisa manter uma estrutura para obter.
Série de transições de visualização entre documentos
- As pegadinhas que ninguém menciona
- Dimensionando transições de visualização em centenas de elementos (Você está aqui!)
Transições de visualização entre documentos: dimensionamento em centenas de elementos originalmente escrito à mão e publicado com amor em CSS-Tricks . Você realmente deveria receber o boletim informativo também.
