Container Queries: @container Responde ao Pai
3 min de leitura
Por décadas, o CSS teve um único ponto de referência para
"como eu me adapto": a tela inteira (@media). Container
queries mudam isso. Agora, um componente pode se adaptar ao
tamanho do seu próprio pai, e não ao tamanho da janela.
É a feature que destrava componentes verdadeiramente reutilizáveis: o mesmo card funciona em uma sidebar estreita e em uma página cheia, reagindo ao espaço que tem disponível.
O problema antes das container queries
Imagine um card de produto que aparece em vários contextos:
- Homepage: 3 colunas, card tem 400px.
- Sidebar: 1 coluna estreita, card tem 200px.
- Página de detalhe: 1 coluna larga, card tem 800px.
Com media queries, você estilizava pela tela. Mas o breakpoint que
importa é o tamanho do card, não o da tela. E a tela é uma
proxy ruim - o mesmo 1200px de tela tem o card em 200px na
sidebar e em 800px no conteúdo principal.
/* Media queries - proxy ruim */
@media (max-width: 600px) {
.card { /* ... */ }
}
/* Se o card está numa sidebar de 200px dentro de uma tela de 1500px,
a media query não dispara. */
A solução: container queries
.card-wrapper {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
@container card (max-width: 399px) {
.card {
display: block; /* imagem em cima, texto embaixo */
}
}
A @container olha o tamanho do container (no caso, o
.card-wrapper com container-type: inline-size) e aplica regras
baseadas nisso. O card passa a ser autônomo - não importa
onde ele está, ele se adapta.
container-type: definindo o que medir
.card-wrapper {
container-type: inline-size; /* mede largura inline (X) */
/* ou: size (mede largura E altura) */
/* ou: normal (default - sem container query) */
}
inline-size: o mais comum. Mede a largura (no caso de LTR, o eixo horizontal). É o que você precisa em 95% dos casos.size: mede largura E altura. Útil em casos raros (componente que precisa saber se está "alto" ou "largo").
container-name: identificando o container
.sidebar { container-name: sidebar; }
.main { container-name: main; }
@container sidebar (min-width: 300px) { /* ... */ }
@container main (min-width: 800px) { /* ... */ }
Quando o mesmo elemento tem vários ancestrais candidatos a
container, nomeá-los permite escolher qual consultar. Sem
nome, a @container olha o ancestral container mais próximo.
A regra de ouro dos containers
.card-wrapper {
container-type: inline-size;
}
Tem que ser o ancestral do elemento que vai reagir à query. O container tem que ser direto (ou ancestral comum) dos elementos estilizados. Filhos ou netos do wrapper respondem à query; o próprio wrapper não responde a si mesmo.
cqi e cqw: unidades relativas ao container
.card {
font-size: clamp(1rem, 3cqi, 1.5rem); /* 3% da largura inline do container */
padding: 2cqw; /* 2% da largura do container */
}
cqi(container query inline): 1% da largura inline do container.cqw(container query width): 1% da largura do container.- São equivalentes para LTR; diferem em escritas verticais.
Combinado com clamp(), dá pra fazer tipografia que escala com o
container, não com a tela:
.card h2 {
font-size: clamp(1.2rem, 5cqi, 2.5rem);
}
O título é grande quando o card tem muito espaço, e pequeno quando está apertado - sozinho, sem media query.
Suporte e fallback
Container queries têm suporte amplo em navegadores modernos
(Chrome 105+, Safari 16+, Firefox 110+). Para projetos que
precisam suportar navegadores muito antigos, a abordagem é
progressive enhancement: o CSS padrão (sem @container) já
precisa estar usável, e o @container "melhora" em navegadores
que suportam.
/* Funciona em qualquer lugar */
.card {
display: block;
}
/* Melhora em navegadores com container query */
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
container-type: inline-sizedefine o que medir.@container (min-width: N)aplica regras baseadas no container.cqi/cqwsão unidades relativas ao container.- Combinar com
clamp()faz tipografia que escala com o componente, não com a tela.
Dica: a primeira vez que você usa container query em produção, a sensação é "isso sempre deveria ter existido". É o tipo de feature que muda a forma de pensar componentes. Um
.carddeixa de ser "estilizado pra 400px" e passa a ser "se adapta a qualquer largura". É a mesma ideia dos componentes de frameworks JS, mas sem o framework.
No próximo nó, vamos ver o :has() - o "seletor pai" que o CSS
prometeu há anos e finalmente entregou.