Indo mais fundo nas consultas de estilo de contêiner

Nó Fonte: 1765194

eu escrevi alguns pensamentos iniciais sobre consultas de estilo de contêiner um pouco atrás. Ainda é cedo. Eles já estão definidos no Especificação de Nível 1 do Módulo de Contenção CSS (atualmente no status de Rascunho do Editor), mas ainda há algumas discussões pendentes acontecendo.

A ideia básica é que podemos definir um contêiner e aplicar estilos condicionalmente a seus descendentes com base em seu estilo computado.

@container ?  {
  /* conditional styles */
}

O melhor exemplo que vi até agora é remover o itálico de algo como , e quando são usados ​​em um contexto em que o conteúdo já está em itálico:

em, i, q {
  font-style: italic; /* default UA behavior */
}

/* When the container's font-style is italic, remove italics from these elements. */
@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

Essa é a ideia geral. Mas se você não sabia, Miriam Suzanne, que é editora da especificação, mantém um conjunto contínuo e completo de notas pessoais sobre consultas de estilo de contêiner que está disponível publicamente. Ele foi atualizado outro dia e passei algum tempo tentando entender aspectos mais sutis das consultas de estilo. É algo não oficial, mas pensei em anotar algumas coisas que me chamaram a atenção. Quem sabe? Talvez sejam coisas pelas quais possamos esperar!

Cada elemento é um contêiner de estilo

Nem precisamos atribuir explicitamente um container-name or container-type para definir um contêiner de estilo porque tudo é um contêiner de estilo por padrão.

Então, você vê o exemplo acima que remove o itálico? Observe que ele não identifica um contêiner. Ele vai direto para a consulta usando o style() função. Então, qual contêiner está sendo consultado? vai ser o pai direto dos elementos recebendo os estilos aplicados. E se não for isso, então é o próximo contêiner relativo mais próximo que tem precedência.

Eu gosto disso. É muito CSS-y para a consulta procurar uma correspondência e, em seguida, continuar a crescer até encontrar uma condição correspondente.

Foi difícil para meu pequeno cérebro entender por que podemos nos safar com um contêiner implícito baseado em estilos, mas não tanto quando estamos lidando com consultas dimensionais, como size e inline-size. Miriam explica bem:

Consultas dimensionais requerem css contenção no tamanho, layout e estilo do contêiner para evitar loops de layout. A contenção é uma coisa invasiva para ser aplicada de forma ampla, por isso era importante que os autores tivessem um controle cuidadoso sobre quais elementos são (ou não são) recipientes de tamanho.

As consultas baseadas em estilo não têm a mesma limitação. Já não há nenhuma maneira em CSS de estilos descendentes terem impacto nos estilos calculados de um ancestral. Portanto, nenhuma contenção é necessária e não há efeitos colaterais invasivos ou inesperados ao estabelecer um elemento como um contêiner de consulta de estilo.

(Ênfase minha)

Tudo se resume a consequências - das quais não há nenhuma, desde que tudo seja um contêiner de consulta de estilo pronto para uso.

  • Se um contêiner for encontrado: as condições serão resolvidas nesse contêiner.
  • Se vários contêineres corresponderem: o contêiner relativo mais próximo tem precedência.
  • Se nenhuma correspondência for encontrada: unknown devolvida.

Isso é o mesmo espírito “perdoador” como o resto do CSS.

Um contêiner pode suportar consultas dimensionais e de estilo

Digamos que queremos definir uma consulta de estilo sem um explícito container-name:

@container style(font-style: italic) {
  em {
    font-style: normal;
  }
}

Isso funciona porque todos os elementos são contêineres de estilo, não importa o container-type. Isso é o que nos permite consultar estilos implicitamente e confiar na correspondência mais próxima. E isso é totalmente bom, pois, novamente, não há efeitos colaterais adversos ao estabelecer contêineres de estilo.

Temos que usar um explícito container-type para consultas dimensionais, mas não tanto para consultas de estilo, pois cada elemento é uma consulta de estilo. Isso também significa que este contêiner é um estilo e consulta dimensional:

.card-container {
  container: card / inline-size; /* implictly a style query container as well */
}

Excluir um container da consulta

Talvez não desejemos que um contêiner participe do processo de correspondência. É aí que pode ser possível definir container-type: none em um elemento.

.some-element {
  container-type: none;
}

Contêineres de consulta de estilo explícito oferecem mais controle sobre o que é consultado

Se, digamos, escrevermos uma consulta de estilo para padding , não há uma maneira confiável de determinar o melhor contêiner correspondente, independentemente de estarmos trabalhando com um contêiner nomeado explicitamente ou com o pai direto mais próximo. Isso é porque padding não é uma propriedade herdada.

Então, nesses casos, devemos usar container-name para informar explicitamente ao navegador de quais contêineres eles podem extrair. Podemos até dar a um contêiner vários nomes explícitos para que ele corresponda a mais condições:

.card {
  container-name: card layout theme;
}

Ah, e container-name aceita qualquer número de opcionais e reutilizável nomes para um contêiner! Isso é ainda mais flexibilidade quando se trata de ajudar o navegador a fazer uma escolha ao procurar correspondências.

.theme {
  container-name: theme;
}
.grid {
  container-name: layout;
}
.card {
  container-name: card layout theme;
}

Eu meio que me pergunto se isso também pode ser considerado um “retrocesso” no caso de um contêiner ser ignorado.

Consultas de estilo podem ser combinadas

A or e and operadores nos permitem combinar wueries para manter as coisas DRY:

@container bubble style(--arrow-position: start start) or style(--arrow-position: end start) {
  .bubble::after {
    border-block-end-color: inherit;
    inset-block-end: 100%;
  }
}

/* is the same as... */
@container bubble style(--arrow-position: start start) {
  /* etc. */
}
@container bubble style(--arrow-position: end start) {
  /* etc. */
}

Alternando estilos

Há uma pequena sobreposição entre consultas de estilo de contêiner e trabalho que está sendo feito para definir um toggle() função. Por exemplo, podemos percorrer dois font-style valores, diga italic e normal:

em, i, q {
  font-style: italic;
}

@container style(font-style: italic) {
  em, i, q {
    font-style: normal;
  }
}

Frio. Mas a proposta do CSS Toggles sugere que o toggle() função seria uma abordagem mais simples:

em, i, q {
  font-style: toggle(italic, normal);
}

Mas qualquer coisa além desse tipo de caso de uso binário é onde toggle() é menos adequado. Consultas de estilo, no entanto, estão prontas. Miriam identifica três instâncias em que as consultas de estilo são mais adequadas do que uma toggle():

/* When font-style is italic, apply background color. */
/* Toggles can only handle one property at a time. */
@container style(font-style: italic) {
  em, i, q {
    background: lightpink;
  }
}

/* When font-style is italic and --color-mode equals light */
/* Toggles can only evaluate one condition at a time */
@container style((font-style: italic) and (--color-mode: light)) {
  em, i, q {
    background: lightpink;
  }
}

/* Apply the same query condition to multiple properties */
/* Toggles have to set each one individually as separate toggles */
@container style(font-style: italic) {
  em, i, q {
    /* clipped gradient text */
    background: var(--feature-gradient);
    background-clip: text;
    box-decoration-break: clone;
    color: transparent;
    text-shadow: none;
  }
}

Consultas de estilo resolvem o “Hack de alternância de propriedade personalizada”

Observe que as consultas de estilo são uma solução formal para o “Truque de alternância de propriedade personalizada CSS”. Lá, definimos uma propriedade personalizada vazia (--foo: ;) e use o método de fallback separado por vírgula para “ativar” e desativar as propriedades quando a propriedade personalizada for definida como um valor real.

button {
  --is-raised: ; /* off by default */
  
  border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1));
  box-shadow: var(
    --is-raised,
    0 1px hsl(0 0% 100% / 0.8) inset,
    0 0.1em 0.1em -0.1em rgb(0 0 0 / 0.2)
  );
  text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3));
}

button:active {
  box-shadow: var(--is-raised, 0 1px 0.2em black inset);
}

#foo {
  --is-raised: initial; /* turned on, all fallbacks take effect. */
}

Isso é super legal, também muito trabalho que as consultas de contêiner de estilo tornam triviais.

Consultas de estilo e conteúdo gerado por CSS

Para o conteúdo gerado produzido pelo content propriedade de ::before e ::after pseudoelementos, o contêiner correspondente é o elemento no qual o conteúdo é gerado.

.bubble {
  --arrow-position: end end;
  container: bubble;
  border: medium solid green;
  position: relative;
}

.bubble::after {
  content: "";
  border: 1em solid transparent;
  position: absolute;
}

@container bubble style(--arrow-position: end end) {
  .bubble::after {
    border-block-start-color: inherit;
    inset-block-start: 100%;
    inset-inline-end: 1em;
  }
}

Consultas de estilo e componentes da web

Podemos definir um componente da Web como um contêiner e consultá-lo por estilo. Primeiro, temos o do componente:


  
… …

Então usamos o :host pseudo-elemento como um contêiner para definir um container-name, um container-type, e alguns atributos de alto nível nele:

:host {
  container: media-host / inline-size;
  --media-location: before;
  --media-style: square;
  --theme: light;
}

Elementos dentro do pode consultar os parâmetros do elemento:

@container media-host style(--media-style: round) {
  [part='img'] {
    border-radius: 100%;
  }
}

Qual é o próximo?

Novamente, todas as coisas que anotei aqui são baseadas nas anotações de Miriam, e essas anotações não substituem as especificações oficiais. Mas eles são uma indicação do que está sendo discutido e onde as coisas podem chegar no futuro. Agradeço que Miriam tenha vinculado um punhado de discussões pendentes que ainda estão ocorrendo e que podemos seguir para ficar por dentro de tudo:

Carimbo de hora:

Mais de Truques CSS