content-visibilidade: a nova propriedade de CSS que melhora o desempenho de renderização

Melhore o tempo de carregamento inicial ignorando a renderização de conteúdo fora da tela.

A propriedade content-visibility, lançada no Chromium 85, pode ser uma das novas propriedades do CSS mais impactantes para melhorar o desempenho do carregamento de página. content-visibility permite que o user agent pule o trabalho de renderização de um elemento, incluindo o layout e a pintura, até que ele seja necessário. Como a renderização é ignorada, se uma grande parte do conteúdo estiver fora da tela, o uso da propriedade content-visibility vai acelerar o carregamento inicial do usuário. Ele também permite interações mais rápidas com o conteúdo na tela. Muito legal.

demonstração com figuras que representam resultados da rede
Na demonstração do nosso artigo, aplicar content-visibility: auto às áreas de conteúdo divididas proporciona um aumento de 7x no desempenho da renderização no carregamento inicial. Continue lendo para saber mais.

Suporte ao navegador

Compatibilidade com navegadores

  • 85
  • 85
  • 124

Origem

O content-visibility depende de primitivos da especificação de contenção do CSS. Embora, por enquanto, content-visibility seja compatível apenas com o Chromium 85 e seja considerado "vale a prototipagem" para o Firefox, a especificação de contenção é compatível com os navegadores mais modernos.

Contenção de CSS

O principal objetivo da contenção do CSS é possibilitar melhorias no desempenho de renderização do conteúdo da Web, fornecendo isolamento previsível de uma subárvore do DOM do restante da página.

Basicamente, um desenvolvedor pode informar ao navegador quais partes da página são encapsuladas como um conjunto de conteúdo, permitindo que os navegadores analisem o conteúdo sem precisar considerar o estado fora da subárvore. Saber quais partes do conteúdo (subárvores) têm conteúdo isolado significa que o navegador pode tomar decisões de otimização para a renderização da página.

Existem quatro tipos de contenção de CSS, cada um sendo um valor em potencial para a propriedade CSS contain, que podem ser combinados em uma lista de valores separados por espaços:

  • size: a contenção de tamanho em um elemento garante que a caixa do elemento possa ser disposta sem precisar examinar os descendentes. Isso significa que podemos pular o layout dos descendentes se só precisamos do tamanho do elemento.
  • layout: contenção de layout significa que os descendentes não afetam o layout externo de outras caixas na página. Isso nos permite pular o layout dos descendentes se quisermos apenas posicionar outras caixas.
  • style: a contenção de estilo garante que propriedades que podem ter efeitos sobre mais do que apenas seus descendentes não escapem do elemento (por exemplo, contadores). Isso nos permite pular o cálculo de estilo dos descendentes se tudo que queremos for computar estilos em outros elementos.
  • paint: a contenção de pintura garante que os descendentes da caixa que a contém não sejam exibidos fora dos limites. Nada pode visivelmente estourar o elemento. Se um elemento estiver fora da tela ou não visível, os descendentes dele também não ficarão visíveis. Isso nos permite pular a pintura dos descendentes se o elemento estiver fora da tela.

Pulando o trabalho de renderização com content-visibility

Pode ser difícil descobrir quais valores de contenção usar, já que as otimizações de navegador só podem entrar em ação quando um conjunto apropriado é especificado. Você pode testar os valores para ver o que funciona melhor ou usar outra propriedade CSS chamada content-visibility para aplicar a contenção necessária automaticamente. O content-visibility garante que você tenha os maiores ganhos de desempenho que o navegador pode oferecer com o mínimo de esforço da sua parte como desenvolvedor.

A propriedade de visibilidade do conteúdo aceita vários valores, mas auto é aquela que oferece melhorias imediatas de desempenho. Um elemento que tem content-visibility: auto ganha a contenção layout, style e paint. Se o elemento estiver fora da tela e não for relevante para o usuário (elementos relevantes são aqueles que têm foco ou seleção na subárvore), ele também recebe a contenção de size e interrompe a pintura e o teste de hit do próprio conteúdo.

O que isso significa? Em resumo, se o elemento estiver fora da tela, os descendentes dele não serão renderizados. O navegador determina o tamanho do elemento sem considerar nenhum conteúdo e para aí. A maior parte da renderização, como o estilo e o layout da subárvore do elemento, é ignorada.

À medida que o elemento se aproxima da janela de visualização, o navegador não adiciona mais a contenção size e começa a pintar e testar o conteúdo do elemento. Isso permite que o trabalho de renderização seja feito apenas a tempo de ser visto pelo usuário.

Observação sobre acessibilidade

Um dos recursos do content-visibility: auto é que o conteúdo fora da tela permanece disponível no modelo de objeto do documento e, portanto, na árvore de acessibilidade (ao contrário do visibility: hidden). Isso significa que é possível pesquisar e navegar pelo conteúdo na página sem esperar que ele seja carregado nem prejudicar o desempenho da renderização.

O lado inverso disso, no entanto, é que elementos de ponto de referência com recursos de estilo como display: none ou visibility: hidden também aparecerão na árvore de acessibilidade quando estiverem fora da tela, já que o navegador não renderizará esses estilos até que eles entrem na janela de visualização. Para evitar que eles fiquem visíveis na árvore de acessibilidade, possivelmente causando confusão, adicione também aria-hidden="true".

Exemplo: um blog de viagens

Neste exemplo, definimos o valor de referência do nosso blog de viagens à direita e aplicamos content-visibility: auto às áreas fragmentadas à esquerda. Os resultados mostram tempos de renderização de 232 ms a 30 ms no carregamento inicial da página.

Um blog de viagens normalmente contém um conjunto de histórias com algumas imagens e um texto descritivo. Veja o que acontece em um navegador comum quando ele acessa um blog de viagens:

  1. Uma parte da página é transferida por download da rede com os recursos necessários.
  2. O navegador define e mostra todo o conteúdo da página, sem considerar se o conteúdo está visível para o usuário.
  3. O navegador volta à etapa 1 até que toda a página e todos os recursos sejam baixados.

Na etapa 2, o navegador processa todo o conteúdo procurando itens que podem ter mudado. Ela atualiza o estilo e o layout de todos os novos elementos, além dos elementos que podem ter mudado como resultado das novas atualizações. Isso é o trabalho de renderização. Isso leva tempo.

Uma captura de tela de um blog de viagens.
Um exemplo de blog de viagens. Consulte Demonstração no Codepen

Agora, considere o que acontece se você colocar content-visibility: auto em cada uma das histórias individuais no blog. O loop geral é o mesmo: o navegador faz o download e renderiza partes da página. No entanto, a diferença está na quantidade de trabalho realizada na etapa 2.

Com a visibilidade do conteúdo, ele estiliza e define o layout de todos os conteúdos que estão visíveis para o usuário no momento (que estão na tela). No entanto, ao processar a história que está totalmente fora da tela, o navegador pula o trabalho de renderização e só define o estilo e o layout da própria caixa do elemento.

O carregamento dessa página seria como se ela contivesse histórias completas e caixas vazias para cada uma delas. Isso tem um desempenho muito melhor, com redução esperada de 50% ou mais do custo de renderização de carregamento. No nosso exemplo, notamos um aumento de um tempo de renderização de 232 ms para um tempo de renderização de 30 ms. Isso é um aumento de 7 vezes no desempenho.

Qual é o trabalho que você precisa fazer para colher esses benefícios? Primeiro, dividimos o conteúdo em seções:

Uma captura de tela anotada da divisão de conteúdo em seções com uma classe CSS.
Exemplo de divisão de conteúdo em seções com a classe story aplicada para receber content-visibility: auto. Consulte Demonstração no Codepen

Em seguida, aplicamos a seguinte regra de estilo às seções:

.story {
  content-visibility: auto;
  contain-intrinsic-size: 1000px; /* Explained in the next section. */
}

Como especificar o tamanho natural de um elemento com contain-intrinsic-size

Para aproveitar os possíveis benefícios do content-visibility, o navegador precisa aplicar contenção de tamanho para garantir que os resultados da renderização do conteúdo não afetem o tamanho do elemento. Isso significa que o elemento será disposto como se estivesse vazio. Se o elemento não tiver uma altura especificada em um layout de bloco regular, ele terá a altura 0.

Isso pode não ser o ideal, já que o tamanho da barra de rolagem vai mudar, dependendo de que cada matéria tenha uma altura diferente de zero.

Felizmente, o CSS oferece outra propriedade, contain-intrinsic-size, que especifica efetivamente o tamanho natural do elemento se ele for afetado pela contenção de tamanho. No nosso exemplo, estamos a definindo como 1000px como uma estimativa para a altura e a largura das seções.

Isso significa que ele será apresentado como se tivesse um único filho de dimensões "tamanho intrínseco", garantindo que os divs sem tamanho ainda ocupem espaço. contain-intrinsic-size atua como um tamanho de marcador de posição em vez de conteúdo renderizado.

No Chromium 98 e em diante, há uma nova palavra-chave auto para contain-intrinsic-size. Quando especificado, o navegador se lembra do tamanho da última renderização, se houver, e o usa em vez do tamanho do marcador de posição fornecido pelo desenvolvedor. Por exemplo, se você especificou contain-intrinsic-size: auto 300px, o elemento vai começar com um dimensionamento intrínseco de 300px em cada dimensão, mas, quando o conteúdo do elemento for renderizado, ele vai manter o tamanho intrínseco renderizado. Qualquer alteração subsequente no tamanho de renderização também será lembrada. Na prática, isso significa que, se você rolar um elemento com content-visibility: auto aplicado e depois rolar de volta para fora da tela, ele vai manter a largura e a altura ideais, sem reverter para o tamanho do marcador. Esse recurso é especialmente útil para rolagem infinita, que agora pode melhorar automaticamente a estimativa de dimensionamento ao longo do tempo à medida que o usuário explora a página.

Ocultando conteúdo com o content-visibility: hidden

E se você quiser manter o conteúdo não renderizado, independente de ele estar ou não na tela, aproveitando os benefícios do estado de renderização em cache? Insira: content-visibility: hidden.

A propriedade content-visibility: hidden oferece os mesmos benefícios de conteúdo não renderizado e estado de renderização em cache que o content-visibility: auto oferece fora da tela. No entanto, ao contrário do auto, ele não começa a ser renderizado automaticamente na tela.

Isso oferece mais controle, permitindo ocultar o conteúdo de um elemento e depois reexibir rapidamente.

Compare-o com outras formas comuns de ocultar o conteúdo do elemento:

  • display: none: oculta o elemento e destrói o estado de renderização. Isso significa que reexibir o elemento é tão caro quanto renderizar um novo elemento com o mesmo conteúdo.
  • visibility: hidden: oculta o elemento e mantém o estado de renderização. Isso não remove o elemento do documento de fato, já que ele (e a subárvore) ainda ocupa espaço geométrico na página e ainda pode ser clicado. Ela também atualiza o estado de renderização sempre que é necessário, mesmo quando está oculto.

content-visibility: hidden, por outro lado, oculta o elemento enquanto preserva o estado de renderização. Portanto, se houver alguma mudança que precise acontecer, ela só vai acontecer quando o elemento for mostrado novamente (ou seja, a propriedade content-visibility: hidden é removida).

Alguns ótimos casos de uso para content-visibility: hidden são ao implementar rolagens virtuais avançadas e medir o layout. Elas também são ótimas para aplicativos de página única (SPAs). As visualizações de apps inativas podem ser deixadas no DOM com content-visibility: hidden aplicado para evitar a exibição, mas manter o estado armazenado em cache. Isso acelera a renderização da visualização quando ela fica ativa novamente.

Efeitos na interação com a próxima exibição (INP, na sigla em inglês)

INP é uma métrica que avalia a capacidade de uma página de responder de forma confiável à entrada do usuário. A capacidade de resposta pode ser afetada por qualquer quantidade excessiva de trabalho que ocorra na linha de execução principal, incluindo o trabalho de renderização.

Sempre que você pode reduzir o trabalho de renderização em qualquer página, está oferecendo à linha de execução principal a oportunidade de responder às entradas do usuário mais rapidamente. Isso inclui o trabalho de renderização. O uso da propriedade CSS content-visiblity quando apropriado pode reduzir o trabalho de renderização, especialmente durante a inicialização, quando a maior parte dos trabalhos de renderização e layout é concluído.

Reduzir o trabalho de renderização tem um efeito direto no INP. Quando os usuários tentam interagir com uma página que usa a propriedade content-visibility corretamente para adiar o layout e a renderização de elementos fora da tela, você dá à linha de execução principal a chance de responder a trabalhos essenciais visíveis ao usuário. Isso pode melhorar o INP da sua página em algumas situações.

Conclusão

content-visibility e a especificação de contenção do CSS significam que seu arquivo CSS oferece melhorias de desempenho incríveis. Para mais informações sobre essas propriedades, confira: