Container Queries em CSS: Componentes Responsivos Sem Media Queries cover image

Container Queries em CSS: Componentes Responsivos Sem Media Queries

Por muitos anos, design responsivo significou uma coisa: media queries ligadas à viewport. Você definia breakpoints em 768px e 1024px, e todos os componentes da página reagiam ao mesmo sinal global. Isso funcionava quando o layout era pensado por página. Mas quebra quando você constrói UI reutilizável — cards em uma sidebar, tiles de produto em um grid ou widgets em contextos imprevisíveis.

As Container Queries em CSS mudam esse modelo. Em vez de perguntar “qual a largura da janela do navegador?”, o componente pergunta “quanto espaço eu tenho de fato dentro do meu pai?”. Essa mudança torna possível criar componentes modulares e conscientes do contexto, sem ResizeObserver em JavaScript ou prop drilling.

O Problema Que Media Queries Não Resolvem

Imagine um componente ProductCard usado em três lugares: listagem em largura total, grid de duas colunas e sidebar estreita. Com media queries, as três instâncias compartilham a mesma lógica de breakpoint. Em uma tela de 1200px, o card da sidebar pode ter 280px de largura enquanto o do grid principal ocupa 400px — mas ambos recebem o mesmo CSS, porque a viewport é idêntica.

O resultado são compromissos estranhos: empilhar conteúdo cedo demais, deixar espaço vazio tarde demais ou duplicar markup com classes específicas por wrapper. Container queries eliminam esse acoplamento ao escopar o comportamento responsivo ao bloco contenedor do componente.

Como Funcionam as Container Queries

Container queries dependem de duas peças: um query container (o pai que estabelece o contexto de tamanho) e uma container query (a regra que reage a esse contexto).

Primeiro, marque um elemento como container:

.card-wrapper {
  container-type: inline-size;
  /* shorthand: container: card / inline-size; */
}

A propriedade container-type indica ao navegador que deve rastrear as dimensões do elemento. Na maioria dos casos de layout, use inline-size, que observa a largura em modos de escrita horizontal. Use size somente quando precisar de largura e altura — lembre que isso exige altura explícita no container, o que é raro em fluxo de documento.

Depois, em elementos descendentes, escreva regras @container:

.card {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

@container (min-width: 400px) {
  .card {
    flex-direction: row;
    align-items: center;
  }

  .card__image {
    flex: 0 0 160px;
  }
}

Quando .card-wrapper atinge 400px de largura, o card passa de layout empilhado para horizontal — independentemente do tamanho da viewport.

Containers Nomeados e Queries Aninhadas

Quando componentes aninham profundamente, containers anônimos ficam ambíguos. Use container-name para mirar em um ancestral específico:

.sidebar {
  container: sidebar / inline-size;
}

.main-grid {
  container: main / inline-size;
}

@container sidebar (max-width: 320px) {
  .product-card {
    padding: 0.5rem;
  }
}

O shorthand container: nome / inline-size combina nome e tipo em uma declaração. Containers nomeados são especialmente úteis em design systems onde o mesmo átomo (badge, grupo de avatares, bloco de estatística) precisa se comportar de forma diferente dentro de um modal versus uma célula de tabela.

Unidades de Comprimento para Container Queries

Container queries introduzem unidades relativas ligadas às dimensões do query container:

  • cqw — 1% da largura do container
  • cqh — 1% da altura do container
  • cqi e cqb — porcentagens dos eixos inline e block (conscientes do writing-mode)
  • cqmin e cqmax — o menor ou o maior valor entre cqi e cqb

Essas unidades brilham em tipografia e espaçamento que devem escalar com o tamanho do componente, não da página. Um título de card pode usar font-size: clamp(1rem, 3cqi, 1.5rem) para crescer sutilmente conforme o card alarga — sem breakpoints fixos.

Container Queries vs. Media Queries

As duas ferramentas são válidas; respondem perguntas diferentes.

  • Media queries controlam layout de página: padrões de navegação, contagem de colunas, espaçamento global, dark mode e estilos de impressão.
  • Container queries controlam layout de componente: orientação do card, limiares de truncamento, proporção de imagem e campos de formulário inline versus empilhados.

Uma regra prática: se remover o componente da página tornaria a query sem sentido, provavelmente ela pertence a uma container query. Se a regra governa a casca em torno dos componentes, mantenha em media query.

Padrões Práticos em Vue e Nuxt

No Vue 3 e no Nuxt, componentes já são naturalmente encapsulados. Container queries alinham-se a esse modelo mental — você estiliza a raiz do componente ou um wrapper interno sem vazar classes dependentes de contexto para os pais.

Um padrão típico em um SFC ProductCard.vue:

<template>
  <article class="product-card">
    <img class="product-card__image" :src="image" :alt="title" />
    <div class="product-card__body">
      <h3>{{ title }}</h3>
      <p>{{ price }}</p>
    </div>
  </article>
</template>

<style scoped>
.product-card {
  container-type: inline-size;
  display: grid;
  gap: 1rem;
}

@container (min-width: 28rem) {
  .product-card {
    grid-template-columns: 120px 1fr;
  }
}
</style>

Com estilos scoped, o container é o próprio componente. Os pais apenas posicionam o card em grid ou flex; o card se adapta sozinho. Sem bindings de :class baseados em composables de breakpoint — embora você ainda possa combinar ambos quando comportamento via JavaScript (ativação de carrossel, lazy hydration) depende de tamanho.

No Nuxt, nada de especial é necessário além de targets de navegador modernos. Se você ainda suporta browsers antigos, trate layouts aprimorados com container queries como progressive enhancement: o layout base funciona em todo lugar; o layout horizontal do card ativa quando há suporte.

Style Queries e o Futuro

A especificação de container queries também inclui style queries — regras que reagem a custom properties no container, como @container style(--theme: compact). O suporte ainda está amadurecendo, mas o padrão aponta para componentes que respondem a design tokens definidos pelos pais, não só a dimensões.

Suporte nos Navegadores e Fallbacks

Container queries são suportadas em todos os navegadores evergreen principais lançados desde 2023. Para projetos com requisitos legados, use @supports para isolar layouts aprimorados:

@supports container-type: inline-size {
  .card {
    container-type: inline-size;
  }

  @container (min-width: 400px) {
    .card__layout {
      flex-direction: row;
    }
  }
}

Alternativamente, duplique regras críticas de layout com media queries como fallback grosseiro e deixe as container queries refinar a experiência onde houver suporte.

Armadilhas Comuns

  • Esquecer de definir container-type em um ancestral. Sem isso, regras @container nunca correspondem.
  • Usar size em vez de inline-size sem necessidade. Exigir altura explícita impede que muitos layouts flex e grid virem query containers.
  • Consultar o elemento errado. O container deve envolver o conteúdo cujo layout muda. Colocar container-type em um filho que não abrange irmãos gera resultados confusos.
  • Exagerar nos micro-breakpoints. Container queries reduzem a proliferação de breakpoints, mas dez limiares por componente ainda prejudicam a manutenção. Prefira poucos thresholds significativos.

Quando Usar Container Queries

Aposte em container queries ao construir componentes reutilizáveis que aparecem em várias regiões de layout, ao desenhar bibliotecas de componentes ou primitivos headless, e quando quiser comportamento responsivo que sobreviva a layouts dirigidos por CMS, onde editores reorganizam blocos de forma imprevisível.

Mantenha media queries para navegação global, grids de página e qualquer coisa que dependa de características do dispositivo, como orientação da viewport ou prefers-reduced-motion.

Conclusão

Container queries completam a história do design responsivo que as media queries começaram. Elas permitem escrever CSS alinhado à forma como de fato construímos front ends modernos: peças pequenas e composáveis montadas em páginas por frameworks como Vue e Nuxt. Comece com container-type: inline-size nas raízes dos componentes, adicione um ou dois limiares significativos em @container e aposente as classes wrapper frágeis que existiam só para simular responsividade no nível do componente. Seu eu do futuro — e todo consumidor do seu design system — agradece.