Capítulo 10 — Design Responsivo¶
Vídeo curto explicativo (link será adicionado posteriormente)
10.1 — O que é design responsivo¶
Vídeo curto explicativo (link será adicionado posteriormente)
O design responsivo (responsive web design) é a abordagem de desenvolvimento que permite que uma página web se adapte e apresente uma experiência adequada em qualquer dispositivo — independentemente do tamanho da tela, da resolução, da orientação ou das capacidades do navegador. O termo foi cunhado por Ethan Marcotte em um artigo seminal publicado na revista A List Apart em maio de 2010, e rapidamente se tornou o paradigma dominante do desenvolvimento front-end moderno.
A motivação para o design responsivo é direta: ao contrário de uma publicação impressa — que possui dimensões físicas fixas e conhecidas no momento da criação —, uma página web é acessada em um espectro de dispositivos de características radicalmente diferentes. Segundo dados do StatCounter (2025), o tráfego web mobile representa globalmente mais de 60% do total de acessos. No Brasil, esse percentual é ainda mais expressivo, com dispositivos móveis respondendo por aproximadamente 65% das sessões.
10.1.1 — O problema da multiplicidade de dispositivos¶
O desenvolvedor web contemporâneo projeta para um espectro que inclui:
- Smartphones com telas de 320px a 430px de largura
- Tablets entre 600px e 1024px
- Laptops entre 1024px e 1440px
- Monitores widescreen de 1440px a 2560px ou mais
- TVs com navegadores embutidos
- Dispositivos wearable com telas mínimas
- Leitores de tela sem dimensão visual
Antes do design responsivo, a resposta comum a essa diversidade era manter duas versões separadas do site — uma para desktop (www.site.com) e uma para mobile (m.site.com). Essa abordagem gerou problemas graves: duplicação de conteúdo, inconsistência entre versões, custo de manutenção dobrado e ausência de cobertura para o vasto espaço entre os dois extremos.
O design responsivo resolve esse problema com uma única base de código que se adapta fluidamente a qualquer contexto.
10.1.2 — Os três pilares do design responsivo¶
Ethan Marcotte definiu o design responsivo como a combinação de três técnicas fundamentais:
1. Grade fluida (fluid grid): utilizar unidades relativas (%, fr, em, rem) em vez de pixels fixos para dimensionamento e layout — permitindo que a estrutura da página se expanda e contraia proporcionalmente ao viewport.
2. Imagens flexíveis (flexible images): garantir que imagens e outros elementos de mídia nunca ultrapassem os limites do seu container — evitando overflow horizontal em telas pequenas.
3. Media queries: aplicar regras CSS específicas condicionalmente, com base nas características do dispositivo — permitindo ajustes de layout, tipografia e apresentação em breakpoints definidos.
Os Capítulos 8 e 9 já cobriram a grade fluida com Flexbox e Grid. Este capítulo aprofunda as media queries e as técnicas modernas que complementam e às vezes substituem a abordagem clássica de breakpoints.
10.1.3 — Viewport: o que é e por que importa¶
O viewport é a área retangular do navegador onde o conteúdo web é renderizado — em essência, a janela visível da página. Em desktops, o viewport corresponde aproximadamente à área da janela do navegador descontando barras de ferramentas.
Em dispositivos móveis, contudo, existe uma distinção importante entre dois tipos de viewport:
Viewport de layout (layout viewport): a área em que o navegador renderiza a página. Por padrão, a maioria dos navegadores móveis define o layout viewport como 980px de largura — uma herança do design desktop — e depois escala o resultado para caber na tela física. Isso faz com que páginas não otimizadas para mobile apareçam "encolhidas" e ilegíveis.
Viewport visual (visual viewport): a área efetivamente visível na tela do dispositivo, que pode ser menor que o layout viewport quando o usuário dá zoom.
A solução para o layout viewport inflado é a meta tag viewport.
10.1.4 — A meta tag viewport e seu papel no mobile¶
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
Esta declaração, que deve estar presente em todo documento HTML responsivo, instrui o navegador a:
width=device-width: definir o layout viewport com a largura real do dispositivo (em vez dos 980px padrão)initial-scale=1.0: não aplicar zoom inicial — a página é exibida em escala 1:1
Sem essa meta tag, as media queries baseadas em max-width simplesmente não funcionam corretamente em dispositivos móveis — o navegador aplica a versão desktop porque o layout viewport reportado é de 980px, não da largura real do dispositivo.
<!-- Obrigatório em todo projeto responsivo -->
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Página Responsiva</title>
<link rel="stylesheet" href="css/style.css" />
</head>
Referência: MDN — Viewport meta tag
10.2 — Estratégia mobile-first vs desktop-first¶
Vídeo curto explicativo (link será adicionado posteriormente)
A escolha entre escrever CSS para mobile primeiro (mobile-first) ou para desktop primeiro (desktop-first) não é apenas uma preferência estilística — ela define a direção das media queries, a ordem de sobrescrita de estilos e, em última análise, a arquitetura de todo o CSS responsivo do projeto.
10.2.1 — O que significa cada abordagem¶
Mobile-first: os estilos base do CSS são escritos para telas pequenas. Media queries com min-width adicionam progressivamente complexidade para telas maiores:
/* Mobile-first: estilos base para telas pequenas */
.container {
display: flex;
flex-direction: column; /* empilhado em mobile */
padding: 1rem;
}
/* Tablet: a partir de 768px, muda para linha */
@media (min-width: 768px) {
.container {
flex-direction: row;
padding: 2rem;
}
}
/* Desktop: a partir de 1024px, adiciona mais espaço */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
padding: 3rem;
}
}
Desktop-first: os estilos base são escritos para telas grandes. Media queries com max-width removem ou simplificam para telas menores:
/* Desktop-first: estilos base para telas grandes */
.container {
display: flex;
flex-direction: row;
max-width: 1200px;
margin: 0 auto;
padding: 3rem;
}
/* Tablet: até 1024px, reduz espaço */
@media (max-width: 1024px) {
.container {
padding: 2rem;
}
}
/* Mobile: até 768px, empilha */
@media (max-width: 768px) {
.container {
flex-direction: column;
padding: 1rem;
}
}
10.2.2 — Por que mobile-first é a abordagem recomendada¶
A preferência pelo mobile-first não é arbitrária — ela tem justificativas técnicas, estratégicas e de desempenho:
Desempenho em dispositivos móveis: navegadores móveis baixam e processam todo o CSS antes de renderizar a página. Com desktop-first, um dispositivo móvel processa todos os estilos complexos de desktop e depois os sobrescreve com simplificações — trabalho desnecessário. Com mobile-first, os estilos simples são aplicados primeiro; as media queries de desktop são ignoradas em dispositivos que não as atendem.
Priorização do conteúdo: forçar-se a projetar para a menor tela primeiro obriga o desenvolvedor a identificar o conteúdo verdadeiramente essencial — aquele que merece espaço na tela de 375px. Esta disciplina tende a produzir interfaces mais focadas e objetivas em todos os tamanhos de tela.
Progressão natural: é conceitualmente mais simples adicionar complexidade (colunas, espaçamentos maiores, elementos adicionais) para telas maiores do que remover complexidade para telas menores.
Alinhamento com o mercado: com mobile representando a maioria dos acessos globais, projetar mobile como experiência primária e desktop como aprimoramento é a ordem correta de prioridade.
10.2.3 — Impacto na escrita das media queries¶
A direção da abordagem define diretamente os operadores das media queries:
| Abordagem | Operador | Direção |
|---|---|---|
| Mobile-first | min-width |
Pequeno → Grande (additive) |
| Desktop-first | max-width |
Grande → Pequeno (subtractive) |
/* Mobile-first: cada media query ADICIONA ao anterior */
/* Estilos base → mobile */
.nav { flex-direction: column; }
/* + tablet */
@media (min-width: 768px) {
.nav { flex-direction: row; }
}
/* + desktop */
@media (min-width: 1024px) {
.nav { gap: 2rem; }
}
10.3 — Media queries¶
Vídeo curto explicativo (link será adicionado posteriormente)
As media queries são o mecanismo do CSS que permite aplicar regras condicionalmente com base nas características do dispositivo ou do ambiente de exibição. Elas são o terceiro pilar do design responsivo e a ferramenta mais direta para adaptar layouts a diferentes contextos.
10.3.1 — Sintaxe básica: @media¶
@media tipo-de-midia and (feature: valor) {
/* regras CSS aplicadas somente quando a condição é verdadeira */
}
/* Exemplos */
@media screen and (min-width: 768px) {
.container { max-width: 1200px; }
}
@media print {
.navbar, .sidebar { display: none; }
body { font-size: 12pt; color: black; }
}
A estrutura de uma media query é composta por:
- @media — declaração de início
- Tipo de mídia (opcional) — screen, print, all
- and — operador lógico (quando tipo + feature)
- Feature query entre parênteses — a condição a verificar
10.3.2 — Tipos de mídia¶
| Tipo | Descrição |
|---|---|
all |
Todos os dispositivos (padrão quando omitido) |
screen |
Telas: monitores, smartphones, tablets |
print |
Impressão e pré-visualização de impressão |
speech |
Sintetizadores de voz (leitores de tela) |
/* Estilos específicos para impressão */
@media print {
/* Oculta elementos desnecessários na impressão */
nav, aside, .btn, .cookie-banner {
display: none !important;
}
/* Tipografia adequada para papel */
body {
font-family: Georgia, serif;
font-size: 12pt;
line-height: 1.5;
color: black;
background: white;
}
/* Garante que links sejam identificáveis */
a[href]::after {
content: " (" attr(href) ")";
font-size: 0.8em;
color: #555;
}
/* Evita quebras de página dentro de elementos importantes */
article, figure, table {
page-break-inside: avoid;
}
}
10.3.3 — Feature queries: dimensão, orientação e preferências do usuário¶
Dimensão do viewport
/* min-width: aplica a partir de N pixels (mobile-first) */
@media (min-width: 768px) { /* tablet e acima */ }
@media (min-width: 1024px) { /* desktop e acima */ }
@media (min-width: 1440px) { /* widescreen */ }
/* max-width: aplica até N pixels (desktop-first) */
@media (max-width: 1023px) { /* abaixo de desktop */ }
@media (max-width: 767px) { /* abaixo de tablet = mobile */ }
/* Intervalo: combinação de min e max */
@media (min-width: 768px) and (max-width: 1023px) {
/* apenas tablet */
}
/* Altura do viewport */
@media (min-height: 800px) {
.hero { min-height: 100vh; }
}
Orientação
@media (orientation: portrait) {
/* tela mais alta do que larga — típico de mobile em pé */
.galeria { grid-template-columns: repeat(2, 1fr); }
}
@media (orientation: landscape) {
/* tela mais larga do que alta — mobile deitado, desktop */
.galeria { grid-template-columns: repeat(4, 1fr); }
}
prefers-color-scheme — tema claro ou escuro
Permite adaptar o visual às preferências de tema do sistema operacional do usuário — uma funcionalidade com impacto direto em acessibilidade e conforto visual:
/* Tema claro: padrão */
:root {
--cor-fundo: #F7F5F2;
--cor-texto: #333333;
--cor-primaria: #12243A;
--cor-card: #FFFFFF;
}
/* Tema escuro: aplicado automaticamente quando o SO está em dark mode */
@media (prefers-color-scheme: dark) {
:root {
--cor-fundo: #1a1a2e;
--cor-texto: #e0e0e0;
--cor-primaria: #A8D8EA;
--cor-card: #16213e;
}
/* Com variáveis CSS, toda a paleta muda com apenas estas linhas */
}
Esta abordagem, combinada com variáveis CSS (Capítulo 7, seção 7.12), é o padrão moderno para implementação de tema escuro — sem duplicar regras CSS.
prefers-reduced-motion — respeito por preferências de movimento
Usuários com epilepsia fotossensível, vertigem ou distúrbios vestibulares podem configurar seu sistema operacional para reduzir animações. Esta media query permite respeitar essa preferência:
/* Animações padrão */
.btn {
transition: transform 300ms ease, background-color 200ms ease;
}
.btn:hover {
transform: scale(1.05);
}
.loading-spinner {
animation: girar 1s linear infinite;
}
/* Remove ou simplifica animações quando o usuário prefere menos movimento */
@media (prefers-reduced-motion: reduce) {
.btn {
transition: background-color 200ms ease; /* mantém apenas cor */
}
.btn:hover {
transform: none; /* remove escala */
}
.loading-spinner {
animation: none; /* remove animação contínua */
}
/* Regra global: desabilita todas as transições e animações */
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Conexão com acessibilidade:
prefers-reduced-motioné relevante para o critério WCAG 2.1 2.3.3 — Animação por Interação (nível AAA) e é considerada boa prática mesmo no nível AA. Sua implementação é simples e o impacto para usuários afetados é significativo.
prefers-contrast — contraste elevado
@media (prefers-contrast: more) {
:root {
--cor-texto: #000000;
--cor-fundo: #FFFFFF;
--cor-borda: #000000;
}
.btn {
border: 2px solid currentColor;
}
}
10.3.4 — Operadores lógicos: and, not e or (,)¶
/* and: todas as condições devem ser verdadeiras */
@media screen and (min-width: 768px) and (orientation: landscape) {
/* tela, largura ≥ 768px E orientação paisagem */
}
/* , (vírgula = or): pelo menos uma condição deve ser verdadeira */
@media (max-width: 767px), (orientation: portrait) {
/* mobile OU orientação retrato */
}
/* not: nega a condição */
@media not print {
/* tudo exceto impressão */
}
/* not em feature específica */
@media (not (prefers-color-scheme: dark)) {
/* apenas quando NÃO está em dark mode */
}
10.3.5 — Breakpoints: o que são, como definir e valores comuns¶
Breakpoints são os valores de largura nos quais o layout muda de forma significativa. Não existe um conjunto universalmente "correto" de breakpoints — eles devem emergir do conteúdo e do design, não de modelos de dispositivos específicos.
Valores comuns (referência, não prescrição):
/* Sistema de breakpoints típico — mobile-first */
:root {
/* sm: dispositivos móveis grandes / landscape */
/* @media (min-width: 480px) */
/* md: tablets */
/* @media (min-width: 768px) */
/* lg: laptops / desktop pequeno */
/* @media (min-width: 1024px) */
/* xl: desktop */
/* @media (min-width: 1280px) */
/* 2xl: widescreen */
/* @media (min-width: 1536px) */
}
A abordagem correta: definir breakpoints onde o layout precisa mudar, não onde um dispositivo específico começa. O processo recomendado é redimensionar o navegador lentamente e adicionar um breakpoint quando o layout "quebrar" — quando o texto ficar ilegível, as colunas ficarem estreitas demais ou o espaçamento inadequado.
/* Breakpoints baseados no conteúdo, não em dispositivos */
@media (min-width: 600px) {
/* o layout de coluna única fica inadequado a partir daqui */
.grade { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 900px) {
/* três colunas cabem confortavelmente a partir daqui */
.grade { grid-template-columns: repeat(3, 1fr); }
}
10.3.6 — A sintaxe moderna de range¶
A especificação Media Queries Level 4 introduziu uma sintaxe de intervalo mais legível e expressiva, já suportada pelos navegadores modernos:
/* Sintaxe clássica */
@media (min-width: 768px) and (max-width: 1023px) { }
/* Sintaxe moderna de range — equivalente e mais legível */
@media (768px <= width <= 1023px) { }
@media (width >= 768px) { }
@media (width < 1024px) { }
/* Exemplos */
@media (width >= 768px) {
.container { max-width: 1200px; }
}
@media (600px <= width <= 900px) {
.grade { grid-template-columns: repeat(2, 1fr); }
}
Compatibilidade: a sintaxe de range tem suporte em Chrome 113+, Firefox 63+, Safari 16.4+. Para projetos que precisam suportar navegadores mais antigos, a sintaxe clássica com
min-width/max-widthainda é necessária. Verifique o suporte atual em caniuse.com/css-media-range-syntax.
10.4 — Layout adaptativo¶
Vídeo curto explicativo (link será adicionado posteriormente)
10.4.1 — Mudança de layout com Flexbox em breakpoints¶
/* Mobile-first: empilhado por padrão */
.secao-hero {
display: flex;
flex-direction: column;
gap: 2rem;
padding: 2rem 1rem;
}
.secao-hero__texto { order: 2; } /* texto abaixo da imagem em mobile */
.secao-hero__imagem { order: 1; }
/* Tablet e acima: lado a lado */
@media (min-width: 768px) {
.secao-hero {
flex-direction: row;
align-items: center;
padding: 4rem 2rem;
}
.secao-hero__texto {
flex: 1;
order: 1; /* texto volta à esquerda */
}
.secao-hero__imagem {
flex: 0 0 400px;
order: 2;
}
}
/* Desktop: mais espaçamento */
@media (min-width: 1024px) {
.secao-hero {
padding: 6rem 4rem;
gap: 4rem;
}
}
10.4.2 — Mudança de layout com Grid e grid-template-areas¶
O padrão mais elegante para layouts responsivos complexos — o container muda, os itens permanecem intocados:
/* Mobile: coluna única */
.pagina {
display: grid;
grid-template-areas:
"header"
"main "
"aside "
"footer";
grid-template-rows: auto 1fr auto auto;
}
/* Tablet: sidebar ao lado */
@media (min-width: 768px) {
.pagina {
grid-template-columns: 1fr 240px;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header"
"main aside "
"footer footer";
}
}
/* Desktop: sidebar mais larga */
@media (min-width: 1024px) {
.pagina {
grid-template-columns: 1fr 300px;
max-width: 1400px;
margin: 0 auto;
}
}
/* Itens: não mudam em nenhum breakpoint */
.cabecalho { grid-area: header; }
.conteudo { grid-area: main; }
.lateral { grid-area: aside; }
.rodape { grid-area: footer; }
10.4.3 — Ocultando e revelando elementos por breakpoint¶
/* Ocultar em mobile, mostrar a partir de tablet */
.apenas-desktop {
display: none;
}
@media (min-width: 768px) {
.apenas-desktop {
display: block; /* ou flex, grid, inline, etc. */
}
}
/* Ocultar a partir de tablet (visível apenas em mobile) */
.apenas-mobile {
display: block;
}
@media (min-width: 768px) {
.apenas-mobile {
display: none;
}
}
⚠️ Acessibilidade:
display: noneremove o elemento tanto visualmente quanto da árvore de acessibilidade — leitores de tela não o percebem. Se o conteúdo é importante para acessibilidade mas deve ser ocultado visualmente, use a técnica de visually hidden:.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }Esta classe oculta o elemento visualmente mas o mantém acessível para leitores de tela — usada para rótulos e textos de contexto que só fazem sentido auditivamente.
10.4.4 — Reordenação responsiva com order e suas implicações¶
Como discutido nos Capítulos 8 e 9, order altera apenas a apresentação visual — leitores de tela e navegação por teclado seguem a ordem do DOM. A recomendação é:
Quando a reordenação é para conveniência visual (ex.: imagem antes do texto em mobile), order é aceitável desde que a experiência de leitura faça sentido em qualquer ordem:
/* Imagem aparece visualmente antes do texto em mobile */
.hero__imagem { order: 1; }
.hero__texto { order: 2; }
/* Em desktop, inverte: texto à esquerda, imagem à direita */
@media (min-width: 768px) {
.hero__texto { order: 1; }
.hero__imagem { order: 2; }
}
Quando a ordem tem significado semântico, a solução correta é organizar o DOM na ordem que faz sentido para leitura e usar CSS para o layout visual — não usar order para simular uma ordem diferente.
10.5 — Técnicas modernas de responsividade¶
Vídeo curto explicativo (link será adicionado posteriormente)
As técnicas desta seção representam a evolução do design responsivo além das media queries — abordagens que permitem layouts fluidos e adaptativos sem (ou com menos) breakpoints explícitos.
10.5.1 — Imagens responsivas: max-width e object-fit¶
max-width: 100% — a regra fundamental
A regra mais simples e mais importante para imagens responsivas: nunca deixar uma imagem ultrapassar o seu container:
/* Reset de imagens responsivas — deve estar no CSS global */
img,
video,
canvas,
svg {
display: block;
max-width: 100%;
height: auto; /* mantém proporção */
}
Com max-width: 100%, a imagem ocupa no máximo 100% da largura do seu container — se o container encolher, a imagem encolhe proporcionalmente. Se a imagem for naturalmente menor que o container, ela permanece em seu tamanho original.
object-fit — controle de proporção em containers dimensionados
Quando uma imagem precisa preencher um container com dimensões fixas (como cards de altura igual), object-fit controla como a imagem se comporta:
.card__imagem {
width: 100%;
height: 220px; /* altura fixa */
object-fit: cover; /* preenche o container, pode cortar */
object-position: center top; /* foca no topo da imagem */
}
/* Valores de object-fit */
img {
object-fit: fill; /* estica para preencher — distorce */
object-fit: contain; /* cabe inteira — pode deixar espaço */
object-fit: cover; /* preenche e corta — mais usado */
object-fit: none; /* tamanho original — pode transbordar */
object-fit: scale-down; /* menor entre none e contain */
}
10.5.2 — Tipografia fluida com clamp()¶
A abordagem clássica de tipografia responsiva usa media queries para definir tamanhos em breakpoints:
/* Abordagem clássica: saltos abruptos nos breakpoints */
h1 { font-size: 1.75rem; }
@media (min-width: 768px) { h1 { font-size: 2.25rem; } }
@media (min-width: 1024px) { h1 { font-size: 3rem; } }
A função clamp() permite tipografia que escala continuamente entre um mínimo e um máximo, sem saltos:
/* clamp(mínimo, preferido, máximo) */
h1 {
font-size: clamp(1.75rem, 4vw, 3rem);
/* Em 320px: 1.75rem (mínimo ativo: 4vw = 12.8px < 28px) */
/* Em 700px: ~1.75rem (4vw ≈ 28px = 1.75rem) */
/* Em 900px: ~2.25rem (4vw = 36px) */
/* Em 1200px: 3rem (máximo ativo: 4vw = 48px > 48px) */
}
h2 { font-size: clamp(1.375rem, 3vw, 2.25rem); }
h3 { font-size: clamp(1.125rem, 2.5vw, 1.75rem); }
p { font-size: clamp(1rem, 1.5vw, 1.125rem); }
Calculando o valor preferido para clamp()
O valor intermediário de clamp() geralmente usa vw (ou uma combinação de vw + rem) para criar uma escala suave. Uma fórmula útil para calcular o valor preferido:
valor-vw = (tamanho-max - tamanho-min) / (viewport-max - viewport-min) × 100
Ferramentas online como Fluid Type Scale (utopia.fyi) geram automaticamente escalas tipográficas fluidas com clamp().
10.5.3 — Espaçamento fluido com clamp()¶
O mesmo princípio se aplica a margens, paddings e gaps — criando espaçamentos que escalam suavemente com o viewport:
:root {
/* Espaçamentos fluidos */
--espaco-sm: clamp(0.75rem, 2vw, 1rem);
--espaco-md: clamp(1rem, 3vw, 2rem);
--espaco-lg: clamp(2rem, 5vw, 4rem);
--espaco-xl: clamp(3rem, 8vw, 6rem);
}
.secao {
padding: var(--espaco-xl) var(--espaco-lg);
}
.grade {
gap: var(--espaco-md);
}
h1 {
margin-bottom: var(--espaco-sm);
}
Esta abordagem, combinada com variáveis CSS, cria um sistema de espaçamento que se adapta a qualquer viewport sem uma única media query para espaçamento.
10.5.4 — Layout fluido com unidades relativas¶
Layouts baseados em % e fr são intrinsecamente fluidos — eles se adaptam ao viewport sem media queries. A combinação de unidades relativas com limites (max-width, min-width, clamp()) cria layouts que funcionam em toda a faixa de tamanhos:
/* Container fluido com largura máxima */
.container {
width: 90%; /* fluido */
max-width: 1200px; /* nunca passa disso */
margin: 0 auto; /* centralizado */
}
/* Grade de cards fluida — sem media queries */
.grade-fluida {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
gap: clamp(1rem, 3vw, 2rem);
}
/* min(280px, 100%): em viewports < 280px, o card ocupa 100% em vez de 280px */
O padrão min(280px, 100%) é um refinamento importante: em vez de minmax(280px, 1fr) — que pode forçar overflow horizontal em viewports menores que 280px —, min(280px, 100%) garante que o item nunca seja maior que seu container:
/* Mais robusto que minmax(280px, 1fr) */
.grade-robusta {
grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
}
10.5.5 — Container queries: responsividade baseada no container¶
As container queries são uma das adições mais significativas ao CSS nos últimos anos — suportadas em todos os navegadores modernos desde 2023. Elas permitem que um componente se adapte ao tamanho do seu container imediato, não do viewport global.
Por que container queries importam
O problema das media queries tradicionais é que elas são globais: um componente de card não sabe em qual contexto está sendo usado. O mesmo card pode aparecer numa coluna larga na página inicial e numa coluna estreita na sidebar — mas com media queries, ele só consegue reagir ao viewport, não ao seu container real.
/* Media query tradicional: reage ao viewport */
@media (min-width: 768px) {
.card { flex-direction: row; }
}
/* Problema: o card usa layout de linha mesmo quando está numa coluna estreita */
Com container queries, o card reage ao seu próprio container:
/* 1. Definir o container de referência */
.card-wrapper {
container-type: inline-size; /* monitora a largura inline */
container-name: card; /* nome opcional para referência */
}
/* 2. Estilos base do card (mobile-first) */
.card {
display: flex;
flex-direction: column;
}
/* 3. Container query: quando o container tiver ≥ 400px */
@container card (min-width: 400px) {
.card {
flex-direction: row;
align-items: center;
}
.card__imagem {
flex: 0 0 160px;
}
}
Sintaxe completa:
/* container-type: define como o container é medido */
.wrapper {
container-type: inline-size; /* monitora largura (mais comum) */
container-type: size; /* monitora largura E altura */
container-type: normal; /* padrão: não é container */
}
/* @container: a query em si */
@container (min-width: 600px) {
/* sem nome: aplica ao container mais próximo */
}
@container card (min-width: 400px) {
/* com nome: aplica ao container chamado "card" */
}
/* Unidades de container: cqi, cqb, cqw, cqh */
.card__titulo {
font-size: clamp(1rem, 5cqi, 1.5rem);
/* 5cqi = 5% da largura inline do container */
}
Exemplo prático: card adaptativo
<!-- Mesmo componente em dois contextos diferentes -->
<div class="grade-principal">
<div class="card-wrapper">
<article class="card">...</article>
</div>
</div>
<aside class="sidebar">
<div class="card-wrapper">
<article class="card">...</article> <!-- mesmo HTML -->
</div>
</aside>
.card-wrapper {
container-type: inline-size;
}
/* Card: layout coluna por padrão */
.card {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
}
/* Card: layout linha quando container ≥ 380px */
@container (min-width: 380px) {
.card {
flex-direction: row;
align-items: center;
}
.card__imagem {
flex: 0 0 120px;
height: 120px;
}
}
/* Card: mais espaçamento quando container ≥ 600px */
@container (min-width: 600px) {
.card {
padding: 1.5rem;
gap: 1.5rem;
}
.card__imagem {
flex: 0 0 200px;
height: 160px;
}
}
Na grade principal (container largo), o card usa layout horizontal com imagem grande. Na sidebar (container estreito), o mesmo card usa layout vertical — automaticamente, sem media queries baseadas no viewport.
Referências: - MDN — Container queries - Can I Use — Container queries - Una Kravets — Container queries explained
Referências gerais do capítulo: - MDN — Design responsivo - MDN — Media queries - Ethan Marcotte — Responsive Web Design (artigo original) - Utopia — Fluid type & space scales - Every Layout — Layouts sem media queries
Atividades — Capítulo 10¶
1. Por que a meta tag <meta name="viewport" content="width=device-width, initial-scale=1.0"> é obrigatória em páginas responsivas?
2. Qual é a vantagem técnica da abordagem mobile-first em relação ao desktop-first para dispositivos móveis?
3. Qual é a principal vantagem das container queries em relação às media queries tradicionais para componentes reutilizáveis como cards?
4. O valor font-size: clamp(1rem, 4vw, 2.5rem) em um viewport de 400px resulta em qual tamanho (considerando 1rem = 16px)?
- GitHub Classroom: Tornar o projeto do bimestre completamente responsivo aplicando: meta tag viewport, estratégia mobile-first, ao menos três breakpoints com media queries, tipografia fluida com
clamp()nos títulos, eprefers-color-schemepara suporte a tema escuro via variáveis CSS. (link será adicionado)
:material-arrow-left: Voltar ao Capítulo 9 — Layout com Grid :material-arrow-right: Ir ao Capítulo 11 — Variáveis CSS e Design System