Curso de CSS / Container queries para componentes reutilizáveis
Consultas de tamanho de container permitem que um componente responda ao tamanho de um container ancestral elegível, em vez de apenas ao viewport. Declare um container de consulta com container-type, e então use @container para alterar estilos de descendentes no tamanho apropriado.
Compare condições de viewport e de container
Mesmo que a largura do navegador seja 1200px, o posicionamento correto variará dependendo se o cartão está na área de texto do corpo de 700px ou na área da barra lateral de 300px. É difícil expressar essa diferença usando apenas Media queries e pontos de quebra baseados no conteúdo, onde os dois cartões compartilham as mesmas condições de viewport. Ao reutilizar partes, você pode reduzir regras de exceção específicas de layout alterando sua configuração interna com base na largura do elemento pai.
| Regra | Critérios de julgamento | papel apropriado |
|---|---|---|
| @media | Ambiente como largura do viewport | Número de colunas na página, localização da barra lateral |
| @container | tamanho do contêiner ancestral selecionado, etc. | Posicionamento de imagens e texto dentro do cartão, espaçamento entre as partes |
As consultas de contêiner também comparam a largura da expressão condicional em vez de "dispor horizontalmente se a tela for horizontal." Quando a barra lateral cresce além de 30rem, os cartões dentro dela também podem ser dispostos horizontalmente. Se você sempre quiser uma barra lateral estreita, também deve especificar uma largura máxima no layout pai.
Declare um contêiner de consulta de tamanho
.slot { container: panel / inline-size; }
@container panel (min-width: 30rem) {
.card { display: grid; grid-template-columns: 8rem minmax(0, 1fr); gap: 1rem; }
}
container: panel / inline-size declara o nome panel e a contenção de tamanho no eixo inline. Na escrita horizontal, a dimensão em linha é horizontal; a escrita vertical altera essa correspondência. Forneça a largura do contêiner através de seu layout de bloco externo ou faixa do Grid para que o cartão tenha um tamanho significativo para consulta. Veja Grid básico: faixas, linhas e posicionamento.
O pai para avaliar a condição é .slot e o descendente para receber o estilo condicional é .card. Não escreva em uma estrutura que altere o próprio cartão ao consultar o tamanho do próprio cartão. Para evitar o problema das condições serem revertidas repetidamente à medida que o tamanho muda, as consultas de tamanho operam em relação aos ancestrais apropriados.
A contenção inline-size faz com que o tamanho inline do contêiner seja independente das contribuições de conteúdo de seus descendentes. Um contêiner shrink-to-fit, portanto, pode se tornar inesperadamente pequeno. Aqui, um bloco ou faixa do Grid fornece sua largura. Usar contenção size também afeta o outro eixo e pode colapsar uma altura dimensionada pelo conteúdo, então escolha a contenção deliberadamente. Para mais detalhes, consulte a descrição de container-type na MDN.
Reutilize um cartão em diferentes larguras de contêiner pai
Salve como container-card.html e abra em seu navegador. A caixa de cor é um elemento de exemplo que representa a área da imagem, então não há arquivo de imagem separado. Em telas maiores, separe o texto principal e uma barra lateral de até 20rem, e em telas de 44rem ou menos, coloque as duas áreas em uma coluna.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>container-card.html</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font: 1rem/1.6 sans-serif; color: #243447; }
h1, h2, p { margin-top: 0; } a { color: #145f8f; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 3px solid #ac4100; outline-offset: 3px; }
.layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 20rem); gap: 1rem; width: min(100%, 75rem); margin-inline: auto; padding: 1rem; }
.slot { min-width: 0; container: panel / inline-size; }
.card { min-width: 0; padding: 1rem; background: #e8f3fa; overflow-wrap: anywhere; }
.media { min-height: 7rem; background: #287699; }
.card h2 { font-size: 1.25rem; } .card p { margin-bottom: 0; }
@container panel (min-width: 30rem) {
.card { display: grid; grid-template-columns: 8rem minmax(0, 1fr); gap: 1rem; align-items: start; }
}
@media (max-width: 44rem) { .layout { grid-template-columns: minmax(0, 1fr); } }
</style>
</head>
<body>
<div class="layout">
<main class="slot"><article class="card"><div class="media" aria-hidden="true"></div><div><h2>Card in the main content area</h2><p>The image and text appear side by side when the parent is at least 30rem wide.</p></div></article></main>
<aside class="slot"><article class="card"><div class="media" aria-hidden="true"></div><div><h2>The same card in a sidebar</h2><p>The sidebar in this example stays at or below 20rem even on a wide screen, so the card keeps its vertical layout.</p></div></article></aside>
</div>
</body>
</html>
Em um ambiente padrão onde o tamanho de fonte inicial é 16px, 30rem é 480px. Se o contêiner .slot da área principal tiver pelo menos 480px de largura de conteúdo, com fonte de 16px para a consulta, o interior do card terá duas colunas, e em uma barra lateral de até 320px, o interior do card será disposto verticalmente. Em telas estreitas, à medida que a barra lateral se move para baixo, seu elemento pai pode se tornar mais largo. Portanto, é normal que ambos os cards sejam colocados horizontalmente em uma tela de 600px, desde que a largura do elemento pai satisfaça as condições.
A condição externa de 44rem e a condição interna de 30rem resolvem problemas diferentes. A primeira condição determina como posicionar a área em toda a página, e a segunda condição determina como posicionar imagens e texto dentro do espaço concedido a cada cartão. Mesmo que os nomes dos pais sejam os mesmos, cada cartão seleciona primeiro o ancestral elegível mais próximo chamado panel e depois avalia sua largura. Se esse ancestral não satisfizer a condição, a consulta não continua procurando outro panel mais distante.
Escolha contêineres mais próximos e com nome
Uma consulta de tamanho não nomeada seleciona o contêiner ancestral mais próximo cujas condições podem ser consultadas. Mesmo que a área externa seja ampla, os resultados podem variar se um contêiner mais estreito for adicionado no meio. Você pode ver essa diferença separadamente salvando o seguinte exemplo como container-nested.html.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>container-nested.html</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; font: 1rem/1.6 sans-serif; color: #243447; }
h1, h2, p { margin-top: 0; } a { color: #145f8f; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 3px solid #ac4100; outline-offset: 3px; }
main { width: min(100%, 42rem); padding: 1rem; }
.outer { width: 100%; container: outer / inline-size; border: 2px solid #789; padding: 1rem; }
.inner { width: min(100%, 17rem); container-type: inline-size; }
.card { padding: 1rem; background: #e8f3fa; overflow-wrap: anywhere; }
.media { min-height: 5rem; background: #287699; }
@container (min-width: 30rem) {
.card { display: grid; grid-template-columns: 5rem minmax(0, 1fr); gap: 1rem; }
}
</style>
</head>
<body>
<main><h1>Which parent's width is queried?</h1><div class="outer"><div class="inner"><article class="card"><div class="media" aria-hidden="true"></div><p>The nearest inner container has a maximum of 17rem.</p></article></div></div></main>
</body>
</html>
Em telas largas, o contêiner externo é maior que 30rem, mas o contêiner interno tem no máximo 17rem. A condição sem nome seleciona o mais próximo interno, então os cartões são dispostos verticalmente. Mantenha o outro código e substitua @container (min-width: 30rem) pelo seguinte:
@container outer (min-width: 30rem) {
.card { display: grid; grid-template-columns: 5rem minmax(0, 1fr); gap: 1rem; }
}
Agora selecionamos o ancestral externo chamado outer, então o cartão é disposto horizontalmente na tela larga. O próprio cartão ainda está dentro do máximo de 17rem, então você precisará verificar separadamente se a largura disponível para o texto é suficiente. O nome não é uma propriedade que corrige automaticamente padrões incorretos, mas uma função que especifica qual ancestral usar como padrão. Após comparar, restaure para a condição sem nome.
Diagnostique consultas que não se aplicam
- Verifique se o alvo do seletor condicional é um descendente do contêiner. Certifique-se de que você não está tentando aplicar o mesmo estilo ao próprio pai.
- Verifique container-type do pai e a largura real do conteúdo nas ferramentas de desenvolvedor. Não confunda a largura externa, incluindo padding e borda, com a largura de conteúdo consultada.
- Verifique se um novo contêiner foi criado e, se necessário, especifique os critérios com container-name.
- Se você alterar apenas container-type para normal, poderá ver a diferença no fato de que a consulta de tamanho não é aplicada. Após a confirmação, restaure para inline-size.
Mantenha um cartão vertical legível como referência, depois adicione o posicionamento horizontal quando a consulta de contêiner se aplicar. A referência também serve para navegadores que não suportam a consulta de tamanho. Verifique o suporte e o comportamento nos navegadores que seus leitores realmente usam. Veja MDN: Consultas de tamanho e estilo de contêiner.




