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 CSS mais impactantes para melhorar o desempenho do carregamento de página. content-visibility permite que o user agent ignore o trabalho de renderização de um elemento, incluindo layout e pintura, até que seja necessário. Como a renderização é ignorada, se grande parte do conteúdo estiver fora da tela, o uso da propriedade content-visibility acelera muito 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 representando resultados de rede
Na nossa demonstração do artigo, aplicar content-visibility: auto a áreas de conteúdo em partes proporciona um aumento de 7 vezes 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 na especificação CSS de contenção (link em inglês). Embora content-visibility seja compatível apenas com o Chromium 85 por enquanto (e considerado "worth prototipagem" para o Firefox), a especificação de contenção é compatível com a maioria dos navegadores modernos.

Contenção do CSS

O objetivo principal e abrangente da contenção de CSS é permitir melhorias no desempenho da 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 são encapsuladas como um conjunto de conteúdo. Isso permite que os navegadores considerem 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 um valor potencial para a propriedade CSS contain, que pode ser combinado 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 dispostas sem precisar examinar os descendentes. Isso significa que podemos ignorar o layout dos descendentes se só precisamos do tamanho do elemento.
  • layout: a contenção de layout significa que os descendentes não afetam o layout externo de outras caixas na página. Isso nos permitirá 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 mais do que apenas os descendentes não escapem do elemento (por exemplo, contadores). Isso permite pular a computação de estilo para os descendentes se quisermos calcular 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 estourar visivelmente o elemento e, se um elemento estiver fora da tela ou não visível, os descendentes também não estarão visíveis. Isso permite pular a pintura dos descendentes se o elemento estiver fora da tela.

Como pular o trabalho de 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 iniciadas quando um conjunto apropriado é especificado. Faça testes com os valores para ver o que funciona melhor ou use outra propriedade CSS chamada content-visibility para aplicar a contenção necessária automaticamente. O content-visibility garante os maiores ganhos de desempenho que o navegador pode proporcionar 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 é a que fornece melhorias imediatas de desempenho. Um elemento que tem content-visibility: auto recebe contenção de layout, style e paint. Se o elemento estiver fora da tela (e não for relevante para o usuário, porque os elementos relevantes seriam aqueles com foco ou seleção na subárvore), ele também receberá contenção de size (e interrompe a pintura e testes de hit do conteúdo).

O que isso significa? Em resumo, se o elemento estiver fora da tela, os descendentes 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 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 de size e começa a pintar e testar o conteúdo do elemento. Isso permite que o trabalho de renderização seja feito a tempo 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 (ao contrário do visibility: hidden). Isso significa que esse conteúdo pode ser pesquisado na página e navegado sem esperar que ele seja carregado ou sacrificar o desempenho da renderização.

O lado negativo disso, no entanto, é que os elementos de ponto de referência com recursos de estilo, como display: none ou visibility: hidden, também aparecem na árvore de acessibilidade quando fora da tela, já que o navegador não renderiza esses estilos até entrar na janela de visualização. Para evitar que eles fiquem visíveis na árvore de acessibilidade, o que pode causar desorganização, adicione também aria-hidden="true".

Exemplo: um blog de viagens

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

Um blog de viagens normalmente contém um conjunto de histórias com algumas fotos e texto descritivo. Veja o que acontece em um navegador comum ao acessar um blog de viagens:

  1. É feito o download de uma parte da página pela rede, com todos os recursos necessários.
  2. O navegador define o estilo e layout de 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é concluir o download de toda a página e de todos os recursos.

Na etapa 2, o navegador processa todo o conteúdo à procura de aspectos que podem ter mudado. Ela atualiza o estilo e o layout de todos os novos elementos, com os elementos que podem ter mudado como resultado de novas atualizações. Esse é 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 do 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 feito na etapa 2.

Com a visibilidade do conteúdo, ela aplica estilo e layout a todos os conteúdos que estão visíveis para o usuário (estão na tela). No entanto, ao processar a história que está totalmente fora da tela, o navegador vai ignorar o trabalho de renderização e apenas aplicar estilo e layout à própria caixa do elemento.

O carregamento dessa página seria como se ela contivesse matérias inteiras e caixas vazias para cada uma das histórias fora da tela. Isso tem um desempenho muito melhor, com redução esperada de 50% ou mais do custo de carregamento de renderização. No exemplo, observamos um aumento de tempo de renderização de 232ms para 30ms. Isso representa 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 com anotações do agrupamento do conteúdo em seções com uma classe CSS.
Exemplo de divisão do 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á apresentado como se estivesse vazio. Se o elemento não tiver uma altura especificada em um layout de blocos normal, ele terá uma 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 fornece outra propriedade, contain-intrinsic-size, que especifica o tamanho natural do elemento se ele for afetado pela contenção de tamanho. No nosso exemplo, definimos como 1000px como uma estimativa da altura e largura das seções.

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

No Chromium 98 e versões mais recentes, há uma nova palavra-chave auto para contain-intrinsic-size. Quando especificado, o navegador vai se lembrar do último tamanho renderizado, se houver, e usar esse valor em vez do tamanho do marcador fornecido pelo desenvolvedor. Por exemplo, se você especificou contain-intrinsic-size: auto 300px, o elemento começa com um dimensionamento intrínseco 300px em cada dimensão, mas, quando o conteúdo do elemento é renderizado, ele mantém o tamanho intrínseco renderizado. Qualquer mudança 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 e não será revertido para o dimensionamento 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 content-visibility: hidden

E se você quiser manter o conteúdo não renderizado, independente de estar ou não na tela, aproveitando os benefícios do estado de renderização em cache? Digite: 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, ela não começa a ser renderizada automaticamente na tela.

Isso proporciona mais controle, permitindo que você oculte o conteúdo de um elemento e, mais tarde, exiba-o rapidamente.

Compare-o com outras formas comuns de ocultar o conteúdo de um 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.

Por outro lado, content-visibility: hidden oculta o elemento e, ao mesmo tempo, 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 for removida).

Alguns ótimos casos de uso para content-visibility: hidden são a implementação de controles de rolagem virtuais avançados e a medição do layout. Elas também são ideais para aplicativos de página única (SPA, na sigla em inglês). As visualizações inativas de apps podem ser deixadas no DOM com content-visibility: hidden aplicado para evitar a exibição, mas manter o estado em cache. Isso agiliza a renderização da visualização quando ela é reativada.

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

A INP é uma métrica que avalia a capacidade de uma página ser responsiva à entrada do usuário de maneira confiável. A capacidade de resposta pode ser afetada por qualquer quantidade excessiva de trabalho 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, oferece à linha de execução principal a 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 adequado, pode reduzir o trabalho de renderização, especialmente durante a inicialização, quando a maior parte do trabalho de renderização e layout é feita.

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 ao trabalho crítico visível para o usuário. Isso pode melhorar o INP da sua página em algumas situações.

Conclusão

Com content-visibility e a especificação de contenção de CSS, algumas melhorias de desempenho interessantes estão sendo aplicadas ao seu arquivo CSS. Para mais informações sobre essas propriedades, confira: