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

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

Publicado em 5 de agosto de 2020

A propriedade content-visibility permite que o agente do usuário pule o trabalho de renderização de um elemento, incluindo layout e pintura, até que 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 tornar a carga inicial do usuário muito mais rápida. Ele também permite interações mais rápidas com o conteúdo na tela. Muito legal.

Compatibilidade com navegadores

  • Chrome: 85.
  • Edge: 85.
  • Firefox: 125.
  • Safari: 18.

Origem

demonstração com figuras representando os resultados da rede
Na demonstração do artigo, a aplicação de content-visibility: auto a áreas de conteúdo divididas em blocos aumenta a sete vezes o desempenho de renderização no carregamento inicial. Continue lendo para saber mais.

Confinamento de CSS

O objetivo principal e abrangente 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 a um navegador quais partes da página estão encapsuladas como um conjunto de conteúdo, permitindo que os navegadores pensem sobre o conteúdo sem precisar considerar o estado fora do 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.

Há quatro tipos de conteiner de CSS, cada um um valor potencial para a propriedade CSS contain, que pode ser combinada 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 tudo o que precisamos é o tamanho do elemento.
  • layout: o isolamento de layout significa que os descendentes não afetam o layout externo de outras caixas na página. Isso permite que possamos pular o layout dos descendentes se quisermos apenas posicionar outras caixas.
  • style: o contenção de estilo garante que as propriedades que podem ter efeitos em mais de um elemento não escapem do elemento (por exemplo, contadores). Isso permite que possamos pular a computação de estilo para os descendentes se tudo o que queremos é calcular estilos em outros elementos.
  • paint: a contenção de pintura garante que os descendentes da caixa que contém não apareçam fora dos limites dela. Nada pode transbordar o elemento de forma visível, e, se um elemento estiver fora da tela ou não estiver visível, os elementos filhos também não estarão. Isso permite que possamos pular a pintura dos descendentes se o elemento estiver fora da tela.

Pular a 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 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 contenção layout, style e paint. Se o elemento estiver fora da tela (e não for relevante para o usuário. Os elementos relevantes são aqueles que têm foco ou seleção no subárvore), ele também vai receber contenção size (e vai parar de pintar e testar acertos no 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 viewport, o navegador não adiciona mais o contêiner 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 (diferente do visibility: hidden). Isso significa que o conteúdo pode ser pesquisado na página e navegado sem esperar o carregamento ou sacrificar o desempenho da renderização.

No entanto, o lado negativo é que os elementos de referência com recursos de estilo, como display: none ou visibility: hidden, também aparecem na árvore de acessibilidade quando estão fora da tela, já que o navegador não renderiza esses estilos até que eles entrem na viewport. 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 geralmente contém um conjunto de histórias com algumas imagens e texto descritivo. Veja o que acontece em um navegador típico quando ele navega até um blog de viagens:

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

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

Captura de tela de um blog de viagens.
Exemplo de um blog de viagens. Confira a 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á no quanto de trabalho ele realiza na etapa 2.

Com content-visibility, ele vai estilizar e organizar todo o conteúdo que está visível para o usuário (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, houve um aumento de um tempo de renderização de 232 ms para 30 ms. Isso representa um aumento de 7 vezes na performance.

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

Uma captura de tela anotada de como dividir o 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. */
}

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á exibido 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 histó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, definimos como 1000px como uma estimativa para a altura e a largura das seções.

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

A palavra-chave auto para contain-intrinsic-size faz com que o navegador lembre o tamanho renderizado pela última vez, se houver, e o use em vez do tamanho do marcador de posição fornecido pelo desenvolvedor. Por exemplo, se você especificar contain-intrinsic-size: auto 300px, o elemento vai começar com um tamanho intrínseco 300px em cada dimensão, mas, depois que 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 automaticamente a largura e a altura ideais, sem reverter para o tamanho do marcador de posição. Esse recurso é especialmente útil para rolagens infinitas, que agora podem melhorar automaticamente a estimativa de dimensionamento ao longo do tempo à medida que o usuário explora a página.

Ocultar conteúdo com content-visibility: hidden

E se você quiser manter o conteúdo sem renderização, independentemente de 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 de auto, ele não começa a renderização automaticamente na tela.

Isso oferece mais controle, permitindo que você oculte o conteúdo de um elemento e o mostre novamente 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 dele. 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 verdade, 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 mudanças que precisem acontecer, elas só vão acontecer quando o elemento for mostrado novamente (ou seja, quando a propriedade content-visibility: hidden for removida).

Alguns casos de uso excelentes para content-visibility: hidden são quando você implementa roladores virtuais avançados e mede o layout. Elas também são ótimas para aplicativos de página única (SPAs, na sigla em inglês). As visualizações de apps inativas podem ser deixadas no DOM com content-visibility: hidden aplicado para impedir a exibição, mas manter o estado em cache. Isso acelera a renderização da visualização quando ela fica ativa novamente.

Efeitos na interação com a próxima pintura (INP)

O INP é uma métrica que avalia a capacidade de resposta confiável de uma página à 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ê reduz o trabalho de renderização em uma página, você dá à linha de execução principal uma 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 na 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 críticos visíveis para o 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 alguns recursos de desempenho incríveis estão chegando ao seu arquivo CSS. Para mais informações sobre essas propriedades, consulte: