Primeiros passos com consultas de estilo

A capacidade de consultar o tamanho inline de um pai e os valores da unidade de consulta do contêiner recentemente alcançou uma compatibilidade estável em todos os mecanismos de navegador modernos.

Compatibilidade com navegadores

  • 105
  • 105
  • 110
  • 16

Origem

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 pai. A partir do Chromium 111, você poderá aplicar a contenção de estilos para valores de propriedades personalizadas e consultar um elemento pai para o valor de uma propriedade personalizada.

Compatibilidade com navegadores

  • 111
  • 111
  • x
  • x

Origem

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

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

Primeiros passos com 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 configure um elemento de contêiner. Isso exige uma abordagem um pouco diferente, dependendo se você está consultando um pai direto ou indireto.

Consulta de pais diretos

Diagrama de uma consulta de estilo.

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

Para consultar diretamente um pai, escreva:

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

Talvez você tenha notado que a consulta de estilo envolve a consulta com style(). Isso serve para diferenciar valores de tamanho dos 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 é possível consultar o valor CSS de min-width usando consultas de estilo. É por isso que use o wrapper style() para esclarecer a diferença: @container style(min-width: 200px) { … }.

Como definir o estilo de pais não diretos

Se você quiser consultar estilos para qualquer elemento que não seja um pai direto, dê a esse elemento um container-name. Por exemplo, podemos aplicar estilos a .card com base nos estilos de .card-list fornecendo a .card-list um container-name e fazendo referência a ele na consulta de estilo.

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

Geralmente, é uma prática recomendada nomear os contêineres para deixar claro o que você está consultando e ter acesso a eles com mais facilidade. Isso pode ser útil, por exemplo, quando você quer definir o estilo de elementos diretamente na .card. Sem um contêiner nomeado em .card-container, não é possível consultá-lo diretamente.

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

Consultas de estilo em ação

Imagem de demonstração com vários cards de produtos, alguns com tags de &quot;novo&quot; ou &quot;estoque baixo&quot; e o cartão &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 diversas variações ou quando não tem controle sobre todos os estilos, mas precisa aplicar mudanças em certos casos. Neste exemplo, mostramos 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 "Pouco estoque", acionados por uma propriedade personalizada chamada --detail. Além disso, se um produto estiver em "Pouco estoque", ele vai ter um plano de fundo com borda vermelha. Esse tipo de informação provavelmente é renderizado pelo servidor e pode ser aplicado aos cards com estilos inline, como:

 <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 nos permite aplicar um ícone 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. Esse recurso está atualmente 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. Veja este exemplo de cartão de previsão do tempo:

Demonstração dos cards de previsão do tempo.

Para estilizar os gradientes de plano de fundo e os ícones desses cards, procure características do clima, 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 únicas dele. Também é possível definir o estilo para combinações de características (propriedade personalizada), usando o combinador and da mesma forma que para consultas de mídia. Por exemplo, um dia nublado e ensolarado teria esta aparência:

@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;
  }
}
.

Como separar os dados do design

Em ambas as 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 seriam usados para estilizar o componente. Você pode 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 o melhor de tudo é que você também pode combinar esses valores. Ao verificar --sunny e --cloudy, você pode mostrar um estilo parcialmente nublado.

A atualização dos valores de propriedades personalizadas com JavaScript pode ser feita sem problemas, seja ao configurar o modelo DOM (por exemplo, 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):

.

Defina o estilo do cartão usando consultas de estilo. O JavaScript usado para atualizar os valores da propriedade personalizada é:

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. As consultas de contêiner podem ajudar você a criar interfaces dinâmicas e responsivas com mais recursos. Especificamente para consultas de estilo, ainda há alguns problemas em aberto. Uma delas é a implementação de consultas de estilo para estilos CSS além das 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 deverá 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.