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.

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 acelerar o carregamento inicial do usuário. 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 que representam resultados da rede
Na demonstração do nosso artigo, aplicar content-visibility: auto a áreas de conteúdo divididas proporciona um aumento de 7x no desempenho da renderização no carregamento inicial. Continue lendo para saber mais.

Confinamento de CSS

O objetivo principal e geral do contenção de CSS é permitir melhorias de desempenho de renderização do conteúdo da Web, fornecendo isolamento previsível de uma subárvore 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 ele analise 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.

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: 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 as propriedades que podem ter efeitos em outros elementos além dos 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 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 do navegador só são ativadas quando um conjunto adequado é 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ê receba os maiores ganhos de desempenho que o navegador pode oferecer com o mínimo de esforço do desenvolvedor.

A propriedade content-visibility aceita vários valores, mas auto é o único que oferece melhorias imediatas no 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 por aí. A maior parte da renderização, como o estilo e o layout do 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 na hora certa para 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, o que pode causar confusão, adicione 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 232ms a 30ms 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, junto 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 é renderizar trabalho. 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 a visibilidade do conteúdo, ele vai estilizar e definir 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, houve um aumento de um tempo de renderização de 232 ms para 30 ms. Isso representa um aumento de 7 vezes na performance.

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 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. Confira a 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 benefícios potenciais do content-visibility, o navegador precisa aplicar a contenção de tamanho para garantir que os resultados de renderização do conteúdo não afetem o tamanho do elemento de nenhuma forma. 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á altura 0.

Isso pode não ser ideal, já que o tamanho da barra de rolagem vai mudar, dependendo de cada história ter 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á 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 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 rolagem infinita, que agora pode 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 todos os mesmos benefícios de conteúdo não renderizado e estado de renderização em cache que content-visibility: auto 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 que você oculte o conteúdo de um elemento e o mostre novamente rapidamente.

Compare com outras maneiras 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 verdade, já que ele (e a subárvore) ainda ocupa espaço geométrico na página e ainda pode ser clicado. Ele também atualiza o estado de renderização sempre que necessário, mesmo quando 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 torna a renderização da visualização rápida quando ela é ativada 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, e o uso da propriedade CSS content-visiblity, quando apropriado, pode reduzir o trabalho de renderização, especialmente durante a inicialização, quando a maioria do trabalho de renderização e layout é feita.

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 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 alguns recursos de desempenho incríveis estão chegando ao seu arquivo CSS. Para mais informações sobre essas propriedades, confira: