Layout Shift causado por transições CSS

Aprenda a encontrar e remover transições CSS que criam layout shifts

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-03-27

Por que transições CSS causam layout shifts

Cumulative Layout Shifts causados por transições CSS frequentemente ocorrem cedo durante a fase de carregamento da página. Esses layout shifts não acontecem de forma consistente, o que os torna difíceis de depurar.

Última revisão por Arjen Karel em março de 2026

O problema do transition: all

A causa mais comum de layout shifts relacionados a transições é o transition: all. De acordo com o capítulo de CSS do Web Almanac de 2022, 53% das páginas usam transition: all. Isso significa que toda mudança de propriedade é animada, incluindo aquelas que afetam o layout como width, height, margin e padding.

Uma transição CSS possui uma propriedade, duração, timing-function e um delay. A forma abreviada é assim:

/* property | duration | timing-function | delay */
transition: margin-right 4s ease-in-out 1s;

O problema começa quando os desenvolvedores escrevem transition: all .3s ease em vez de especificar a propriedade exata que desejam animar. Durante o carregamento da página, um elemento above-the-fold como um menu de navegação transita do seu estado inicial (sem estilo) para o estado final (estilizado ou até mesmo oculto). Se a propriedade de transição for all, isso inclui width, height e até visibility. O resultado: um layout shift que só acontece durante o carregamento e é quase impossível de reproduzir consistentemente.

Quais propriedades CSS causam layout shift?

Nem todas as transições CSS causam layout shifts. Apenas as propriedades que alteram a geometria ou a posição de um elemento acionam a etapa de layout do navegador. De acordo com a pesquisa do Google, páginas que animam propriedades CSS que induzem layout têm 15% menos probabilidade de passar no CLS. Páginas que animam margin ou border-width têm um CLS ruim em quase o dobro da taxa normal.

Propriedades que causam layout shift: width, height, margin, padding, top, left, right, bottom, border-width, font-size, display

Propriedades seguras (apenas composição, sem layout shift): transform, opacity, filter, clip-path

Propriedades seguras (apenas pintura, sem layout shift): background-color, color, box-shadow, outline

A principal descoberta: transform e opacity são totalmente tratados pela thread do compositor do navegador. Eles nunca acionam o recálculo de layout, portanto, nunca contam para o CLS. Se você precisar mover um elemento, use transform: translate() em vez de top/left. Se precisar redimensionar visualmente, use transform: scale() em vez de width/height.

Dê uma olhada no exemplo abaixo. Isso demonstra um layout shift causado por transições CSS que ocorrem durante a fase de carregamento de uma página. Eu vejo esse padrão o tempo todo e encontrar e corrigir esse tipo de problema pode ser difícil.

Encontre e corrija transições CSS

Para encontrar e corrigir todos os layout shifts causados por transições CSS, precisamos executar um teste rápido. Primeiro, encontramos todas as transições CSS na página. Em seguida, verificamos se alguma delas altera as propriedades que afetam o layout. Por fim, medimos o impacto de desabilitar ou modificar essas transições para confirmar se elas estavam causando CLS.

Dica de Core Web Vitals: Cumulative Layout Shifts que são causados por transições CSS frequentemente ocorrem cedo durante a fase de carregamento da página. Esses layout shifts não acontecem de forma consistente, o que os torna difíceis de depurar. Desacelerar sua rede emulando um dispositivo móvel e desativando o cache tornará mais fácil encontrá-los!

Passo 1: Encontre as transições CSS

Encontrar transições CSS pode ser feito manualmente: inspecione todas as folhas de estilo e procure pela palavra 'transition'. Isso não deve dar mais de 10 minutos de trabalho, mas existe uma maneira melhor! Basta colar este snippet no console e pressionar enter

(() => {

  let nodeTable = [];
  let nodeArray = [];

  // Get the name of the node
  function getName(node) {
    const name = node.nodeName;
    return node.nodeType === 1
      ? name.toLowerCase()
      : name.toUpperCase().replace(/^#/, '');
  }

  // Get the selector
  const getSelector = (node) => {
    let sel = '';

    try {
      while (node && node.nodeType !== 9) {
        const el = node;
        const part = el.id
          ? '#' + el.id
          : getName(el) +
          (el.classList &&
            el.classList.value &&
            el.classList.value.trim() &&
            el.classList.value.trim().length
            ? '.' + el.classList.value.trim().replace(/\s+/g, '.')
            : '');
        if (sel.length + part.length > (100) - 1) return sel || part;
        sel = sel ? part + '>' + sel : part;
        if (el.id) break;
        node = el.parentNode;
      }
    } catch (err) {
      // Do nothing...
    }
    return sel;
  };

  const getNodesWithTransition = (node) => {

    // Get the computed style
    let cs = window.getComputedStyle(node);
    let tp = cs['transition-property'];
    let td = cs['transition-duration'];

    // If there is a transition, add it to the table
    if (tp !== '' && tp !== 'none' && td != '0s') {
      nodeTable.push({ selector: getSelector(node), transition: cs['transition'] });
      nodeArray.push(node);
    }

    // Recursively call this function for each child node
    for (let i = 0; i < node.children.length; i++) {
      getNodesWithTransition(node.children[i]);
    }
  }

  // find all transitions
  getNodesWithTransition(document.body);

  // Display the results in the console
  console.log('%cReadable table of selectors and their transitions', 'color: red; font-weight: bold;');
  console.table(nodeTable);

  console.log('%cNodeList for you to inspect (harder to read but more info)', 'color: red; font-weight: bold;');
  console.log(nodeArray);


  // styles to temporarity override the transitions
  let selectors = nodeTable.map((item) => item.selector).join(', ');

  console.log('%cSpecific CSS to disable all transitions on this page', 'color: red; font-weight: bold;');
  console.log(`<style>${selectors}{transition-property: none !important;}</style>`);

  console.log('%cGlobal CSS to disable all transitions on this page (not suggested on production)', 'color: red; font-weight: bold;');
  console.log(`<style>*{transition-property: none !important;}</style>`);

})()

Ele mostrará uma tabela de todas as transições, os elementos nos quais elas estão atuando e mais detalhes sobre as transições.

Para encontrar layout shifts, procure propriedades de transição como width, height, margin, padding, top, left, display e especialmente all (já que all inclui todas as propriedades de transição válidas).

Passo 2: Modifique as transições CSS

O snippet JavaScript acima mostrará todas as transições, bem como fornecerá um código de exemplo de como desabilitar essas transições. Para fins de testes rápidos, sugiro que você siga pelo caminho mais fácil e desabilite todas as transições com uma simples linha de código CSS

<style>*{transition-property: none !important;}</style>

Claro que, para ambientes de produção, é necessário um pouco mais de cuidado. Remova cuidadosamente apenas as propriedades de transição desnecessárias por seletor. Por exemplo, altere #button{transition: all .2s} para #button{transition: background-color .2s}

Passo 3: Meça a mudança no layout shift

O próximo e último passo é medir o impacto. Você pode usar minha extensão do Chrome Core Web Vitals Visualizer ou uma ferramenta de RUM como o CoreDash para medir o impacto na vida real dessas alterações de código. Nos dados de monitoramento do CoreDash, os sites que substituíram transition: all por propriedades específicas viram seu CLS melhorar em 40% em média.

Melhores práticas para transições

  1. Sempre especifique a propriedade exata: Nunca use transition: all. Em vez disso, escreva transition: background-color .2s ease. Isso evita layout shifts acidentais de propriedades que você não pretendia animar.
  2. Use transform e opacity para animações: Essas duas propriedades são tratadas pelo compositor e nunca acionam o layout. Use transform: translate() para mover elementos, transform: scale() para redimensioná-los visualmente e opacity para esmaecê-los. Isso é o que o Google recomenda para animações de alto desempenho.
  3. Defina dimensões explícitas em elementos animados: Se um elemento precisa de uma transição, certifique-se de que ele tenha width e height explícitos (ou um aspect ratio) antes e depois da transição. Isso evita que o conteúdo ao redor sofra um shift. Para saber mais sobre isso, veja como corrigir layout shifts causados por imagens com dimensionamento automático.
  4. Tenha cuidado com will-change: A propriedade CSS will-change avisa o navegador para se preparar para uma animação promovendo o elemento à sua própria camada de composição. Mas isso traz desvantagens: cria um novo contexto de empilhamento (stacking context), aumenta o uso de memória da GPU e estabelece um bloco de contenção para descendentes posicionados. Aplique dinamicamente com JavaScript logo antes do início da animação e remova-o quando a animação terminar. Não deixe will-change na sua folha de estilos permanentemente.

Apesar de tudo isso, o Web Almanac de 2025 relata que 40% das páginas para dispositivos móveis ainda executam animações não compostas. A boa notícia: o CLS é o Core Web Vital com a maior taxa de aprovação, de 81% em dispositivos móveis. A má notícia: se o seu site estiver nos 19% que falham, as transições CSS podem ser a causa que você ainda não verificou.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

CoreDash já vem com MCP.

Conecta no Claude ou em qualquer agente de IA. Pergunta pra ele por que seu INP disparou terça passada.

Vê como funciona
Layout Shift causado por transições CSSCore Web Vitals Layout Shift causado por transições CSS