Primeiros passos com consultas de estilo

A capacidade de consultar o tamanho inline de um elemento pai e os valores da unidade de consulta de contêiner recentemente atingiu o suporte estável em todos os mecanismos de navegador modernos.

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 110.
  • Safari: 16.

Source

No entanto, a especificação de contenção inclui mais do que apenas consultas de tamanho. Ela também permite consultar os valores de estilo de um elemento pai. A partir do Chromium 111, será possível aplicar a contenção de estilo para valores de propriedade personalizados e consultar um elemento pai para o valor de uma propriedade personalizada.

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: 151.
  • Safari: 18.

Isso significa que temos ainda mais controle lógico de estilos em CSS e permite uma melhor separação da lógica e da camada de dados de um aplicativo dos estilos dele.

A especificação do módulo de contenção de CSS nível 3, que abrange consultas de tamanho e estilo, permite que qualquer estilo seja consultado de um elemento pai, incluindo pares de propriedade e valor, como font-weight: 800. No entanto, no lançamento desse recurso, as consultas de estilo só funcionam com valores de propriedade personalizados de CSS. Isso ainda é muito útil para combinar estilos e separar dados do design. Vamos conferir como usar consultas de estilo com propriedades personalizadas de CSS:

Introdução às consultas de estilo

Digamos que temos o seguinte HTML:

<ul class="card-list">
  <li class="card-container">
    <div class="card">
      ...
    </div>
  </li>
</ul>

Para usar consultas de estilo, primeiro é necessário configurar um elemento de contêiner. Isso exige uma abordagem um pouco diferente, dependendo se você está consultando um pai direto ou indireto.

Consultar pais diretos

Diagrama de uma consulta de estilo.

Ao contrário das consultas de estilo, não é necessário aplicar a contenção usando a propriedade container-type ou container a .card-container para que .card possa consultar os estilos do pai direto. No entanto, precisamos aplicar os estilos (valores de propriedade personalizados nesse caso) a um contêiner (.card-container nesse caso) ou a qualquer elemento que contenha o elemento que estamos estilizando no DOM. Não é possível aplicar estilos que estamos consultando no elemento direto que estamos estilizando usando essa consulta, porque isso pode causar loops infinitos.

Para consultar um pai diretamente, escreva:

/* styling .card based on the value of --theme on .card-container */
@container style(--theme: warm) {
  .card {
    background-color: wheat;
    border-color: brown; 
    ...
  }
}

Você pode ter notado que a consulta de estilo envolve a consulta com style(). Isso serve para desambiguar valores de tamanho de estilos. Por exemplo, é possível escrever uma consulta para a largura do contêiner como @container (min-width: 200px) { … }. Isso aplicaria estilos se o contêiner pai tivesse pelo menos 200 px de largura. No entanto, min-width também pode ser uma propriedade CSS, e você pode consultar o valor CSS de min-width usando consultas de estilo. É por isso que você usaria o wrapper style() para deixar a diferença clara: @container style(min-width: 200px) { … }.

Estilizar pais não diretos

Se você quiser consultar estilos para qualquer elemento que não seja um pai direto, é necessário atribuir um container-name a esse elemento. Por exemplo, podemos aplicar estilos a .card com base nos estilos de .card-list atribuindo um container-name a .card-list e referenciando-o na consulta de estilo.

/* styling .card based on the value of --moreGlobalVar on .card-list */
@container cards style(--moreGlobalVar: value) {
  .card {
    ...
  }
}

Geralmente, é recomendável dar nomes aos contêineres para deixar claro o que você está consultando e desbloquear a capacidade de acessar esses contêineres com mais facilidade. Um exemplo de quando isso é útil é se você quiser estilizar elementos diretamente em .card. Sem um contêiner nomeado em .card-container, eles não podem consultá-lo diretamente.

Mas tudo isso faz muito mais sentido na prática. Vamos conferir alguns exemplos:

Consultas de estilo em ação

Imagem de demonstração com vários cards de produtos, alguns com tags &quot;novo&quot; ou &quot;estoque baixo&quot;, e o card &quot;estoque baixo&quot; com um plano de fundo vermelho.

As consultas de estilo são particularmente úteis quando você tem um componente reutilizável com várias variações ou quando não tem controle sobre todos os estilos, mas precisa aplicar mudanças em determinados casos. Este exemplo mostra um conjunto de cards de produtos que compartilham o mesmo componente de card. Alguns cards de produtos têm detalhes/observações adicionais, como "Novo" ou "Estoque baixo", acionados por uma propriedade personalizada chamada --detail. Além disso, se um produto estiver com "Estoque baixo", ele receberá um plano de fundo de borda vermelho escuro. É provável que esse tipo de informação seja renderizado pelo servidor e possa ser aplicado aos cards usando estilos inline, como este:

 <div class="product-list">
  <div class="product-card-container" style="--detail: new">
    <div class="product-card">
      <div class="media">
        <img .../>
      <div class="comment-block"></div>
    </div>
  </div>
  <div class="meta">
    ...
  </div>
  </div>
  <div class="product-card-container" style="--detail: low-stock">
    ...
  </div>
  <div class="product-card-container">
    ...
  </div>
  ...
</div>

Com esses dados estruturados, é possível transmitir valores para --detail e usar essa propriedade personalizada de CSS para aplicar os estilos:

@container style(--detail: new) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'New';
    border: 1px solid currentColor;
    background: white;
    ...
  }
}

@container style(--detail: low-stock) {
  .comment-block {
    display: block;
  }
  
  .comment-block::after {
    content: 'Low Stock';
    border: 1px solid currentColor;
    background: white;
    ...
  }
  
  .media-img {
    border: 2px solid brickred;
  }
}

O código acima permite aplicar um chip para --detail: low-stock e --detail: new, mas você pode ter notado alguma redundância no bloco de código. No momento, não há como consultar apenas a presença de --detail com @container style(--detail), o que permitiria um melhor compartilhamento de estilos e menos repetição. Essa capacidade está em discussão no grupo de trabalho.

Cards de clima

O exemplo anterior usou uma única propriedade personalizada com vários valores possíveis para aplicar estilos. Mas você também pode misturar usando e consultando várias propriedades personalizadas. Confira este exemplo de card de clima:

Demonstração de cards de clima.

Para estilizar os gradientes e ícones de plano de fundo desses cards, procure características climáticas, como "nublado", "chuvoso" ou "ensolarado":

@container style(--sunny: true) {
  .weather-card {
    background: linear-gradient(-30deg, yellow, orange);
  }
  
  .weather-card:after {
    content: url(<data-uri-for-demo-brevity>);
    background: gold;
  }
}

Dessa forma, é possível estilizar cada card com base nas características exclusivas dele. Mas também é possível estilizar combinações de características (propriedades personalizadas) usando o and combinador da mesma forma que para consultas de mídia. Por exemplo, um dia nublado e ensolarado seria assim:

@container style(--sunny: true) and style(--cloudy: true) {
    .weather-card {
      background: linear-gradient(24deg, pink, violet);
    }
  
  .weather-card:after {
      content: url(<data-uri-for-demo-brevity>);
      background: violet;
  }
}

Separar dados do design

Nessas duas demonstrações, há um benefício estrutural de separar a camada de dados (DOM que seria renderizado na página) dos estilos aplicados. Os estilos são escritos como variantes possíveis que residem no estilo dos componentes, enquanto um endpoint pode enviar os dados que ele usaria para estilizar o componente. É possível usar um único valor, como no primeiro caso, atualizando o valor --detail, ou várias variáveis, como no segundo caso (definindo --rainy, --cloudy ou --sunny). E a melhor parte é que você também pode combinar esses valores. A verificação de --sunny e --cloudy pode mostrar um estilo parcialmente nublado.

A atualização de valores de propriedades personalizadas usando JavaScript pode ser feita sem problemas, seja ao configurar o modelo DOM (ou seja, ao criar o componente em um framework) ou atualizada a qualquer momento usando <parentElem>.style.setProperty('--myProperty’, <value>). I

Confira uma demonstração que, em algumas linhas de código, atualiza o --theme de um botão e aplica estilos usando consultas de estilo e essa propriedade personalizada (--theme):

Estilize o card usando consultas de estilo. O JavaScript usado para atualizar os valores de propriedades personalizadas é:

const themePicker = document.querySelector('#theme-picker')
const btnParent = document.querySelector('.btn-section');

themePicker.addEventListener('input', (e) => {
  btnParent.style.setProperty('--theme', e.target.value);
})

Os recursos detalhados neste artigo são apenas o começo. Você pode esperar mais coisas das consultas de contêiner para ajudar a criar interfaces dinâmicas e responsivas. Quanto às consultas de estilo especificamente, ainda há alguns problemas em aberto. Um deles é a implementação de consultas de estilo para estilos CSS além de propriedades personalizadas. Isso já faz parte do nível de especificação atual, mas ainda não foi implementado em nenhum navegador. A avaliação de contexto booleano será adicionada ao nível de especificação atual quando o problema pendente for resolvido, enquanto a consulta de intervalo está planejada para o próximo nível da especificação.