Estudo de caso: como o Google criou uma experiência conectada para o novo modo de IA usando transições de visualização

Publicado em: 28 de agosto de 2025

A Pesquisa Google tem um dos maiores alcance do mundo. Por isso, as mudanças na experiência do usuário podem afetar bilhões de pessoas. Há muito tempo sonhamos com uma experiência na Web mais moderna e parecida com um app. Quando começamos a desenvolver o Modo IA, queríamos criar uma experiência para nossos usuários em que a transição da pesquisa padrão para o Modo IA fosse perfeita e conectada. Quando soubemos das transições de visualização entre documentos, percebemos que era uma combinação perfeita para o recurso. Este estudo de caso mostra o que aprendemos ao adicionar o recurso de transição junto com o lançamento do Modo IA.

Gravação de uma pesquisa na Pesquisa Google, alternando dos resultados da pesquisa para o Modo IA. A transição está usando transições de visualização.

As transições de visualização entre documentos são uma mudança radical quando se trata de ferramentas nativas do navegador, e estamos animados para ver como isso vai moldar a Web no futuro.

Browser Support

  • Chrome: 126.
  • Edge: 126.
  • Firefox: not supported.
  • Safari: 18.2.

Source

Mudando o status quo

A Pesquisa Google tem requisitos de suporte a navegadores rigorosos e conservadores. Em geral, o uso de um recurso de disponibilidade limitada era proibido. Para transições de visualização entre documentos, descobrimos que um polyfill não era viável. O principal bloqueador era que não havia uma API de captura de tela de pixels, e clonar toda a janela de visualização causava grandes problemas de desempenho. Por isso, usar o recurso como um aprimoramento progressivo foi a melhor maneira de lançar junto com o modo de IA. Como as animações criadas pelas transições de visualização não afetam diretamente a funcionalidade do site, para tráfego sem suporte, elas seriam simplesmente desativadas, o que já era o estado de produção atual sem animações de transição.

Primeiro, testamos essa estratégia de melhoria progressiva com nossos usuários internos. Isso nos deu um feedback inicial, que foi extremamente positivo. O feedback recebido também revelou bugs, incluindo problemas de desempenho e interações não intencionais com outros recursos, como contextos de empilhamento sobrepostos.

Achamos essa estratégia bem-sucedida e acredito que vamos testá-la com outros recursos novos do navegador no futuro.

Dificuldades encontradas e como as resolvemos

Latência, bloqueio de renderização e timers watchdog

Em geral, a latência adicionada que vem com as transições de visualização da MPA é insignificante para 99% dos casos de uso, especialmente em hardware moderno. No entanto, a Pesquisa Google tem um padrão extremamente alto quando se trata de latência, e nos esforçamos para criar experiências do usuário que funcionem bem em todos os dispositivos. Para nós, até alguns milissegundos extras são importantes. Por isso, tivemos que investir em como implementar corretamente as transições de visualização entre documentos sem prejudicar a experiência do usuário.

O bloqueio de renderização é uma técnica que funciona bem com transições de visualização entre documentos. Os snapshots de pseudoelementos do documento recebido só podem mostrar conteúdo que já foi renderizado. Portanto, para animar o conteúdo do documento recebido, é necessário renderizar o bloco até que o elemento de destino que você quer animar seja renderizado. Para fazer isso, use o atributo blocking em um HTMLLinkElement. O bloqueio de renderização tem desvantagens, já que esperar por um elemento que está no final da árvore DOM do documento recebido pode ter um impacto substancial na latência. Precisamos equilibrar essa troca de acordo e renderizar o bloco apenas em elementos que são renderizados muito cedo no ciclo de vida da página.

<!-- Link tag in the <head> of the incoming document -->
<link blocking="render" href="#target-id" rel="expect">
<!-- Element you want to animate in the <body> of the incoming document -->
<div id="target-id">
  some content
</div>

Em alguns casos, não era suficiente ser preciso sobre qual elemento você renderiza o bloco. Alguns dispositivos ou conexões ainda teriam latência adicionada, mesmo quando o bloqueio de renderização em um elemento próximo ao início da árvore DOM. Para lidar com esses casos, escrevemos um script de timer watchdog para remover o HTMLLinkElement depois de um determinado período para forçar a renderização sem bloqueio do documento recebido.

Uma maneira simples de fazer isso é a seguinte:

function unblockRendering() {
  const renderBlockingElements = document.querySelectorAll(
    'link[blocking=render]',
  );
  for (const element of renderBlockingElements) {
    element.remove();
  }
}

const timeToUnblockRendering = t - performance.now();

if (timeToUnblockRendering > 0) {
  setTimeout(unblockRendering, timeToUnblockRendering);
} else {
  unblockRendering();
}

Limitações de cobertura

Outro problema que encontramos é que a regra at navigation: auto de transições de visualização entre documentos acontece em um nível global dentro do documento. Não há uma maneira integrada de limitar a ativação das transições de visualização entre documentos apenas a destinos de clique específicos. Como essa é uma mudança muito grande, não foi possível ativar as transições de visualização entre documentos em 100% das navegações na Pesquisa Google. Precisávamos de uma maneira de ativar ou desativar dinamicamente as transições de visualização entre documentos, dependendo do recurso com que o usuário estava interagindo. No nosso caso, só ativamos as mudanças de modo para o modo de IA e vice-versa. Fizemos isso atualizando programaticamente a regra at de navegação, dependendo de qual destino foi clicado ou tocado.

Uma maneira de alternar a regra at de transição de visualização é a seguinte:

let viewTransitionAtRule: HTMLElement | undefined;
const DISABLED_VIEW_TRANSITION = '@view-transition{navigation:none;}';
const ENABLED_VIEW_TRANSITION = '@view-transition{navigation:auto;}';

function getVtAtRule(): HTMLElement {
  if (!viewTransitionAtRule) {
    viewTransitionAtRule = document.createElement('style');
    document.head.append(viewTransitionAtRule);
  }
  return viewTransitionAtRule;
}

function disableVt() {
  getVtAtRule().textContent = DISABLED_VIEW_TRANSITION;
}

function enableVt() {
  getVtAtRule().textContent = ENABLED_VIEW_TRANSITION;
}

Instabilidade e animações compostas

Algumas das animações geradas automaticamente nos pseudoelementos de transição de visualização causavam quedas de frames em dispositivos mais antigos, prejudicando a experiência limpa e perfeita que queremos oferecer aos usuários. Para melhorar a performance das animações, reescrevemos elas usando técnicas que podem ser executadas no compositor. Para isso, inspecionamos os keyframes para receber as dimensões dos pseudoelementos de snapshot antes e depois e usamos a matemática de matrizes para reescrever os keyframes de acordo. O exemplo a seguir mostra como capturar a animação de cada pseudoelemento de transição de visualização:

const pseudoElement = `::view-transition-group(${name})`;
const animation = document
  .getAnimations()
  .find(
    (animation) =>
      (animation.effect as KeyframeEffect)?.pseudoElement === pseudoElement,
  );

Leia mais sobre como escrever keyframes de transição de visualização eficientes em Transições de visualização aplicadas: como lidar com o bloco de instantâneo.

Outros pontos importantes

Um dos problemas mais evidentes é que a inclusão de tags em elementos com a propriedade CSS view-transition-name afeta o contexto de empilhamento (Especificação de transições de visualização: seção 2.1.1). Essa foi a origem de vários bugs que exigiram a modificação do z-index de elementos de contêiner.

Outra coisa importante é que talvez você não queira adicionar valores view-transition-name aos elementos por padrão. Há muitas pessoas trabalhando na Pesquisa Google. Para evitar que os valores view-transition-name definidos pela nossa equipe em elementos entrem em conflito com os valores que pessoas de outras equipes podem usar, usamos tipos de transição de visualização para adicionar condicionalmente a propriedade view-transition-name somente enquanto um tipo de transição de visualização específico está ativo.

Exemplo de CSS para adicionar o view-transition-name de the-element a um elemento somente quando o tipo de transição de visualização de ai-mode estiver ativo:

html:active-view-transition-type(ai-mode) {
  #target {
    view-transition-name: the-element;
  }
}

Depois de ter essas regras de CSS para todas as transições de visualização, você pode mudar dinamicamente o tipo de transição de visualização atual para qualquer navegação durante os eventos pageswap e pagereveal.

Exemplo de atualização do tipo de transição de visualização para ai-mode durante o evento pageswap.

function updateViewTransitionTypes(
  event: ViewTransitionEvent,
  types: string[],
): void {
  event.viewTransition.types.clear();
  for (const type of types) {
    event.viewTransition.types.add(type);
  }
}

window.addEventListener(
  'pageswap',
  (e) => {
    updateViewTransitionTypes(
      e as ViewTransitionEvent,
      ['ai-mode'],
    );
  }
);

Assim, evitamos conflitos de nomenclatura e não criamos snapshots desnecessários de elementos que não precisam ser incluídos nesse processo.

Por fim, problemas de contexto de empilhamento só vão aparecer durante a transição de visualização. Para resolver esses problemas, podemos segmentar os z-índices dos pseudoelementos gerados, em vez de modificar arbitrariamente os z-índices dos elementos originais apenas para resolver esse problema ao usar transições de visualização.

::view-transition-group(the-element) {
  z-index: 100;
}

A seguir

Temos planos de usar transições de visualização entre documentos na Pesquisa Google, incluindo a integração com a API Navigation quando ela estiver disponível em vários navegadores. Fique de olho para saber o que vamos criar em seguida!