O fato
Em 16 de setembro de 2026, Victor Ayomipo publicou no Smashing Magazine o artigo Stop Treating CSS Container Queries Like Traditional Media Queries. A tese é direta: container queries e media queries se parecem na sintaxe, e por isso quase todo mundo usa container queries errado, tratando como se fosse a mesma coisa. A adoção reforça o argumento. Segundo a mesma matéria, citando o State of CSS, 86% dos desenvolvedores conhecem container queries, mas só 41,4% usam. E não é falta de suporte de navegador: o suporte cobre Chrome 105+, Firefox 110+, Safari 16+ e Edge 105+, com cobertura global de aproximadamente 90% em meados de 2026.
Este artigo é sobre por que as duas ferramentas resolvem problemas diferentes, e como parar de aplicar o modelo mental errado.
Como as duas coisas funcionam por baixo
Media query pergunta para a janela
Quando você escreve @media (min-width: 1024px), você está fazendo uma pergunta ao navegador sobre o viewport, ou seja, sobre a janela inteira do navegador. Nada mais. Se a janela tem 1920px de largura, a condição dispara, independente de onde o componente que você está estilizando está posicionado dentro dessa janela.
Isso é o que Ayomipo chama de layout macro no artigo original, e é uma boa forma de pensar: media query enxerga estrutura de página. Cabeçalho que atravessa a tela, grid principal, preferência de tema escuro via prefers-color-scheme, orientação do dispositivo. Coisas que são verdade sobre a página inteira.
Container query pergunta para o pai
Container query inverte o eixo. Em vez de perguntar sobre a janela, o componente pergunta sobre o espaço que o pai dele oferece. Diferente de media queries, container queries se estabelecem em duas partes: você define um container, e escreve estilos para um elemento filho que se aplicam quando o container pai satisfaz as condições da consulta. Você define um container usando a propriedade container-type. Um container-type de inline-size permite consultar o eixo inline do container.
Na prática, o CSS fica assim:
.card-wrapper {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 450px) {
.card {
display: flex;
flex-direction: row;
}
}
Duas coisas importantes de entender aqui, porque é onde as pessoas escorregam.
Primeiro, inline-size é um valor lógico, então se refere à largura quando você usa modo de escrita horizontal (o padrão), ou à altura para modo de escrita vertical. Não é sinônimo de width. É o eixo de fluxo do texto. Em português e inglês, é largura. Em japonês vertical, seria altura.
Segundo, o container-type: inline-size aplica containment, que é o que torna a consulta possível. Ambos os valores de container-type aplicam diferentes tipos de containment de tamanho. Containment inline-size em um elemento impede que seus descendentes afetem seu inline-size. Esse é o pulo do gato: o navegador só consegue calcular o tamanho do container de forma isolada, sem consultar os filhos, porque senão você teria um loop (o filho depende do tamanho do pai, o tamanho do pai depende do filho).
A analogia que ajuda, e onde ela quebra
Pense numa loja de shopping. A media query é o gerente que decide o layout inteiro do shopping olhando pelo telhado: hoje é sábado, então abre corredor extra, muda o palco central de lugar, ativa iluminação de fim de semana. Ele não sabe o que está acontecendo dentro de cada loja específica.
A container query é o gerente da loja individual. Ele não liga se o shopping é grande ou pequeno. Ele mede o próprio ponto comercial dele e decide: aqui cabem duas prateleiras lado a lado, ou só uma empilhada. A decisão é local, e vale só ali dentro.
Onde a analogia quebra: no shopping, cada loja escolhe o próprio layout sem ninguém obrigar. No CSS, o container precisa ser declarado explicitamente com container-type, senão o filho não tem como consultar nada. Se você esquecer essa declaração, o @container simplesmente não dispara, e o pior é que não dá erro visível. Falha silenciosa é o pior tipo de falha, porque o navegador aceitou seu código e não te avisou onde parou.
O que muda na prática: componente reutilizável
O cenário que justifica container query é o componente que aparece em mais de um contexto. Um card que existe no grid principal com 800px de largura, e também no sidebar com 280px. Com media query, esse card não tem como saber onde ele está. Você acaba escrevendo variantes: .card--wide, .card--sidebar, e passando a decisão para quem monta a página. Isso funciona, mas empurra complexidade para fora do componente.
Com container query, o card se resolve sozinho:
<div class="card-slot">
<article class="card">
<img src="..." alt="...">
<div class="card-body">
<h3>Título</h3>
<p>Texto</p>
</div>
</article>
</div>
.card-slot {
container-type: inline-size;
}
.card {
display: block;
}
@container (min-width: 420px) {
.card {
display: grid;
grid-template-columns: 120px 1fr;
gap: 1rem;
}
}
Repare que o container é o .card-slot, não o .card. Isso é obrigatório: um elemento não pode consultar a si mesmo. Você sempre precisa de um wrapper que sirva de referência de tamanho. Se você tentar botar container-type no próprio .card e consultar dentro, o navegador ignora e você fica horas procurando o bug.
Unidades de container: cqi, cqw, cqb
Junto com container queries vieram unidades relativas ao container, análogas ao vw e vh. A mais útil no dia a dia é cqi, que é 1% do inline-size do container mais próximo. Isso permite tipografia que escala com o componente:
.card-title {
font-size: clamp(1rem, 0.5rem + 3cqi, 2rem);
}
Aqui a fonte tem chão de 1rem, teto de 2rem, e no meio escala com 3% do inline-size do container. Se o card está num sidebar de 300px, a fonte fica no chão. Se está num grid de 900px, encosta no teto. Sem depender do tamanho da janela.
Isso resolve um problema que atormentava tipografia responsiva: você usava vw, o componente ia parar num sidebar, e a fonte ficava minúscula porque a janela continuava grande mas o espaço real era pequeno. A responsividade estava ancorada no lugar errado.
Aqui vale um caso que vivi. Publicando design system e style guide de interface, uma das primeiras dores que aparece é justamente essa: o mesmo componente precisa viver em contextos com larguras muito diferentes, e antes de container queries a única saída era gerar variantes tipadas do componente. Isso funciona, mas o custo de manter as variantes coerentes cresce em cada tela nova. Container query encolhe esse custo porque o componente vira responsável pela própria adaptação, e o design system fica com menos regras condicionais de composição.
Onde container queries quebram
Consultar o eixo block colapsa o layout
Se você usar container-type: size em vez de inline-size, o navegador aplica containment nos dois eixos. E aí o container passa a ignorar os filhos para calcular a altura. Sem altura explícita, o container fica com 0px, e o layout desmonta. Sim, dá para consultar altura, mas você precisa usar container-type: size em vez de inline-size. O containment de tamanho total se aplica em ambas as dimensões, o que impede o container de derivar sua altura dos filhos. Use isso apenas quando realmente precisar de consultas baseadas em altura, como em painéis de dashboard redimensionáveis pelo usuário. Regra prática: use inline-size por padrão, e só suba para size quando você tem uma altura fixa ou aspect-ratio definida.
Custom properties não funcionam como valor de query
Você não consegue fazer isto:
:root { --breakpoint-lg: 1600px; }
/* NÃO FUNCIONA */
@container (min-width: var(--breakpoint-lg)) {
.card { display: flex; }
}
A razão é estrutural: custom properties cascateiam pela árvore do DOM, e uma query que depende de custom property poderia disparar uma mudança que altera essa mesma custom property. O sistema declara onde ele para em vez de tentar resolver o loop. É a coisa certa a fazer, mas quebra a expectativa de quem quer centralizar breakpoints em variáveis.
Style queries com custom properties ainda são limitadas
Existem também style queries, que consultam estilos computados do container em vez do tamanho. Mas o estado ainda não é uniforme: quanto a container style queries usando custom properties como condição, cabe ao Firefox adicionar suporte. Eu estou otimista de que veremos suporte chegar no Firefox em 2026. Se você quer aplicar estilos com base nos estilos do container, você precisa esperar até que a disponibilidade limitada entre navegadores melhore. Se você não pode escolher o navegador do usuário, style queries com custom properties ainda são território de progressive enhancement.
Quando não vale a pena
Se o componente só existe num contexto (por exemplo, o header principal do site, que só aparece uma vez e sempre com a largura total da janela), container query só adiciona uma camada de indireção sem benefício. Media query resolve, e você economiza um wrapper. Não é para substituir todas as media queries por container queries. Use media queries para decisões de layout no nível da página e container queries para responsividade no nível do componente. Muitos projetos se beneficiam de ambos: um grid de topo troca colunas com @media, enquanto cards individuais dentro desse grid se adaptam com @container.
O que ainda não dá para afirmar
Duas coisas o material não esclarece bem.
Primeiro, o custo de performance de ter muitos containers ativos numa página. Cada container-type: inline-size aplica containment, que envolve isolamento de layout. Em teoria isso pode ajudar performance (o navegador pode recalcular menos coisas), mas em páginas com centenas de containers ninguém publicou benchmark claro. Não testei em escala e o artigo do Smashing não entra nesse ponto.
Segundo, a interação com frameworks que fazem hidratação parcial ou streaming SSR. Se o container é renderizado depois do filho, a primeira medida pode disparar estilos que somem no próximo frame. Isso é problema resolvível, mas não vi ainda um padrão consolidado.
Como testar isso você mesmo
Um caminho pequeno para experimentar em 15 minutos:
- Crie um HTML com dois wrappers: um com
width: 300pxe outro comwidth: 800px. Dentro de cada um, coloque o mesmo componente card com imagem e texto. - Aplique
container-type: inline-sizenos wrappers. - Escreva um
@container (min-width: 500px)que muda o card dedisplay: blockparadisplay: gridcom duas colunas. - Abra no navegador. O card no wrapper de 300px vai ficar empilhado. O de 800px vai ficar em duas colunas. Sem tocar em media query, sem redimensionar a janela.
- Agora abra o DevTools do Chrome, inspecione um dos wrappers, e observe que aparece um badge de container. Você consegue redimensionar direto ali e ver o breakpoint disparando.
Feito isso, o modelo mental gruda. O componente para de perguntar sobre a janela e passa a perguntar sobre o próprio espaço. É uma mudança pequena de sintaxe, e uma mudança grande de responsabilidade.





