Mobile-first UI com React: Imagens, Layout e Contexto

Roteiro com picture, img fluida, Flexbox, Grid, viewport e media queries por contexto

Por Fábio Linhares • Instituto Infnet

Como usar esta página

À primeira vista, este conjunto parece apenas uma sequência de exercícios de HTML e CSS. Não é. Ele cobra a passagem do “eu consigo montar algo que parece funcionar” para o “eu consigo interpretar um requisito, escolher a técnica correta, implementar com critério, testar em cenários diferentes e explicar por que a solução funciona”.

O percurso passa por cinco decisões centrais: escolher entre picture e img fluida, escolher entre Flexbox e Grid, tratar viewport e media queries como sistema, adaptar a interface ao contexto de interação e traduzir wireframe em estrutura funcional.

Leia cada exercício como um pequeno laboratório. O objetivo não é decorar propriedade solta; é aprender a transformar requisito em estrutura, condição e comportamento observável.

BASE
Mapa mental para todo o roteiro
Objetivo: decidir por técnica e contexto, não por tentativa aleatória.
Como pensar

Este conjunto treina passagem de um nível “parece funcionar” para um nível mais maduro: “consigo interpretar o requisito, escolher a técnica correta, testar em cenários diferentes e explicar por que a solução funciona”.

A ordem mental certa é: requisito -> técnica -> HTML mínimo -> CSS mínimo -> validação no navegador -> explicação curta e defensável.

Decisões que destravam o roteiro
  • picture troca arquivo; img fluida mantém a mesma mídia e só muda o encaixe.
  • Flexbox resolve fluxo principal em uma direção; Grid resolve linhas, colunas e áreas.
  • @media, <link media> e @import são formas diferentes de aplicar condição; escolha a pedida no enunciado.
  • pointer, hover e prefers-color-scheme tratam contexto real de uso, não só largura de tela.
SETUP
Estrutura mínima para quase todas as questões
Objetivo: manter HTML limpo, CSS externo e viewport correta.
<!-- index.html -->
<!doctype html>
<html lang="pt-BR">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- conteúdo da questão -->
</body>
</html>

/* styles.css */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body { font-family: Arial, Helvetica, sans-serif; line-height: 1.5; }
img { display: block; max-width: 100%; }
Como usar a base

Quando o exercício não mandar outra estrutura, comece daqui. Isso evita erro estrutural e deixa o foco no problema técnico da questão.

EX.01
Imagem fluída com picture
Objetivo: trocar o arquivo da imagem por breakpoint e ocupar 100% da largura visível.
O que o enunciado pede

Usar picture com três saídas: até 576px, até 992px e acima disso, sempre ocupando 100% da largura do viewport.

Conceito cobrado

Escolha correta entre troca real de arquivo e simples fluidez visual.

Estrutura dos dados

HTML mínimo: um picture, dois source com media/srcset e um img fallback.

<!-- index.html -->
<picture class="hero-picture">
  <source media="(max-width: 576px)" srcset="img-small.jpg">
  <source media="(max-width: 992px)" srcset="img-medium.jpg">
  <img src="img-large.jpg" alt="Imagem responsiva">
</picture>

/* styles.css */
.hero-picture,
.hero-picture img {
  width: 100vw;
  height: auto;
}
Como pensar

Se o requisito muda a imagem entregue por faixa de tela, a técnica correta é picture, não só img fluida.

Como validar

Reduza e aumente a largura da tela. A imagem deve continuar ocupando a largura visível, e o arquivo deve mudar conforme o breakpoint.

Tabela de Evidência
  • Até 576px -> carrega img-small.jpg.
  • 577px a 992px -> carrega img-medium.jpg.
  • Acima de 992px -> usa o img fallback.
Variáveis-chave
  • media
  • srcset
  • width: 100vw
Como explicar

Usei picture porque o enunciado pede arquivos diferentes por faixa. O img final funciona como fallback e cobre a última resolução.

Erro comum

Usar só img com max-width: 100%. Isso deixa a imagem fluida, mas não troca o arquivo.

EX.02
Imagem fluída com CSS e img
Objetivo: manter a mesma imagem e ajustá-la via CSS.
O que o enunciado pede

Usar a tag img e CSS para que a imagem ocupe a largura visível sem distorção.

Conceito cobrado

Diferença entre imagem fluida e troca de arquivo.

Estrutura dos dados

HTML mínimo: uma única img com classe dedicada.

<!-- index.html -->
<img class="hero-img" src="img.jpg" alt="Imagem responsiva">

/* styles.css */
.hero-img {
  width: 100vw;
  max-width: 100%;
  height: auto;
}
Como pensar

Aqui a mídia é a mesma. O trabalho do CSS é só fazer a imagem se adaptar sem overflow nem deformação.

Como validar

A imagem precisa continuar proporcional em qualquer largura e não pode estourar horizontalmente.

Tabela de Evidência
  • 320px -> imagem ocupa a largura visível.
  • 768px -> continua proporcional.
  • Sem barra horizontal -> max-width: 100% cumpriu o papel.
Variáveis-chave
  • width: 100vw
  • max-width: 100%
  • height: auto
Como explicar

Usei img porque o enunciado não pede troca de arquivo. width: 100vw ocupa a largura visível e height: auto preserva a proporção.

Erro comum

Confundir este caso com picture e complicar uma questão que só pede imagem fluida.

EX.03
Reproduzir o layout mobile com Flexbox
Objetivo: montar um card vertical, centralizado e mobile-first até 576px.
O que o enunciado pede

Reproduzir um layout mobile com ícone de menu, avatar circular, título, parágrafo e botão usando Flexbox.

Conceito cobrado

Flexbox para fluxo vertical e alinhamento em um eixo.

Estrutura dos dados

HTML mínimo: container .profile-card, .menu-icon, .avatar, título, parágrafo e botão.

<!-- index.html -->
<div class="profile-card">
  <div class="menu-icon">☰</div>
  <img class="avatar" src="avatar.jpg" alt="Foto de John">
  <h1>Hi, I am John,<br>Creative Technologist</h1>
  <p>Amet minim mollit non deserunt ullamco est sit aliqua dolor do amet sint.</p>
  <a class="btn" href="#">Download Resume</a>
</div>

/* styles.css */
.profile-card {
  width: 100vw;
  max-width: 576px;
  min-height: 100vh;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  padding: 2rem 1.5rem;
  text-align: center;
}
.menu-icon { align-self: flex-end; font-size: 1.5rem; }
.avatar {
  width: 120px;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  object-fit: cover;
}
Como pensar

O eixo dominante é vertical. Isso pede Flexbox em coluna, não Grid e nem posicionamento absoluto.

Como validar

Entre 360px e 576px, tudo precisa ficar empilhado, centralizado e sem scroll horizontal.

Tabela de Evidência
  • 360px -> ícone, avatar, texto e botão empilhados.
  • 576px -> card continua centralizado.
  • Sem scroll horizontal -> largura e padding estão sob controle.
Variáveis-chave
  • flex-direction: column
  • align-self: flex-end
  • max-width: 576px
Como explicar

Usei Flexbox em coluna porque o protótipo é vertical. O ícone foi jogado para a direita com align-self, e o card respeita a faixa mobile pedida.

Erro comum

Tentar resolver com coordenadas absolutas e perder o fluxo natural do layout.

EX.04
Ajustar o layout anterior para 992px+
Objetivo: reorganizar o mesmo conteúdo para horizontal em tela grande.
O que o enunciado pede

Pegar a estrutura do exercício anterior e reorganizar o layout a partir de 992px com media query.

Conceito cobrado

Progressive enhancement com Flexbox e min-width.

Estrutura dos dados

HTML mínimo: navegação .top-nav e hero com dois blocos, .hero-text e .hero-image.

<!-- index.html -->
<div class="page">
  <nav class="top-nav">
    <a href="#">Works</a>
    <a href="#">Blog</a>
    <a href="#">Contact</a>
  </nav>

  <section class="hero">
    <div class="hero-text">
      <h1>Hi, I am John,<br>Creative Technologist</h1>
      <p>Amet minim mollit non deserunt ullamco est sit aliqua dolor do amet sint.</p>
      <a class="btn" href="#">Download Resume</a>
    </div>
    <div class="hero-image">
      <img class="avatar" src="avatar.jpg" alt="Foto de John">
    </div>
  </section>
</div>

/* styles.css */
.top-nav, .hero { display: flex; }
.top-nav { flex-direction: column; gap: .75rem; align-items: center; }
.hero { flex-direction: column; gap: 1.5rem; align-items: center; }
.hero-text, .hero-image { width: 100%; text-align: center; }
@media (min-width: 992px) {
  .top-nav { flex-direction: row; justify-content: center; }
  .hero { flex-direction: row; justify-content: space-between; align-items: center; }
  .hero-text, .hero-image { width: 48%; text-align: left; }
}
Como pensar

O layout base continua mobile-first. A media query só reorganiza o que já existia, em vez de criar outra página.

Como validar

Abaixo de 992px tudo empilha. A partir daí, hero e navegação precisam virar linha.

Tabela de Evidência
  • 768px -> menu e hero em coluna.
  • 992px -> menu em linha.
  • 992px+ -> texto e imagem lado a lado.
Variáveis-chave
  • flex-direction
  • min-width: 992px
  • width: 48%
Como explicar

A solução nasce mobile-first e a media query de 992px só redistribui os blocos. Isso deixa o CSS incremental e previsível.

Erro comum

Começar pelo desktop e tentar “encolher” depois, acumulando exceções.

EX.05
Centralização com Grid
Objetivo: centralizar container e três filhos com Grid.
O que o enunciado pede

Um container com três filhos centralizados vertical e horizontalmente, ocupando a altura da tela e se adaptando no mobile.

Conceito cobrado

Grid para centralização e fluxo automático.

Estrutura dos dados

HTML mínimo: um .container com três filhos, por exemplo .b1, .b2 e .b3.

<!-- index.html -->
<div class="container">
  <div class="box b1"></div>
  <div class="box b2"></div>
  <div class="box b3"></div>
</div>

/* styles.css */
.container {
  min-height: 100vh;
  display: grid;
  place-items: center;
  grid-auto-flow: column;
  gap: 1rem;
}
@media (max-width: 576px) {
  .container { grid-auto-flow: row; }
}
Como pensar

A exigência é centralizar no eixo vertical e horizontal ao mesmo tempo. place-items resolve isso de forma direta.

Como validar

Os três filhos precisam ficar centralizados na tela. No mobile, devem passar de horizontal para vertical.

Tabela de Evidência
  • Desktop -> três filhos no centro, lado a lado.
  • Até 576px -> filhos empilhados.
  • Altura da tela ocupada -> min-height: 100vh.
Variáveis-chave
  • display: grid
  • place-items: center
  • grid-auto-flow
Como explicar

Usei Grid porque queria centralização bidimensional simples e um fluxo que pudesse trocar de coluna para linha no mobile.

Erro comum

Responder com Flexbox sem perceber que a própria questão pede Grid.

EX.06
Layout básico com CSS Grid
Objetivo: distribuir header, nav, main, aside e footer em grade.
O que o enunciado pede

Um layout com header, nav, main, aside e footer em 3 colunas e ao menos 3 linhas.

Conceito cobrado

Grid para áreas e spans entre colunas.

Estrutura dos dados

HTML mínimo: um container .layout contendo exatamente header, nav, main, aside e footer.

<!-- index.html -->
<div class="layout">
  <header>Header</header>
  <nav>Nav</nav>
  <main>Main</main>
  <aside>Aside</aside>
  <footer>Footer</footer>
</div>

/* styles.css */
.layout {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 220px 1fr 220px;
  grid-template-rows: auto 1fr auto;
  gap: 12px;
}
header { grid-column: 1 / -1; }
nav    { grid-column: 1; }
main   { grid-column: 2; }
aside  { grid-column: 3; }
footer { grid-column: 1 / -1; }
Como pensar

O problema é claramente bidimensional: há linhas, colunas e elementos ocupando toda a largura em cima e embaixo.

Como validar

Header e footer devem atravessar as três colunas; nav, main e aside ficam lado a lado no meio.

Tabela de Evidência
  • Header -> grid-column: 1 / -1.
  • Footer -> grid-column: 1 / -1.
  • Miolo -> nav na 1, main na 2, aside na 3.
Variáveis-chave
  • grid-template-columns
  • grid-template-rows
  • grid-column
Como explicar

Usei Grid porque a página precisa de áreas em linhas e colunas. Header e footer ocupam toda a largura, e o miolo fica distribuído por coluna.

Erro comum

Criar tudo com Flexbox e depois forçar spans que o Grid resolveria de forma natural.

EX.07
Meta viewport e observação experimental
Objetivo: alterar viewport e explicar o efeito observado no navegador.
O que o enunciado pede

Reutilizar o arquivo anterior, incluir a meta viewport, alterar condições e escrever três observações no main.

Conceito cobrado

Viewport como interpretação inicial de largura e zoom, não como detalhe decorativo.

Estrutura dos dados

HTML mínimo: a meta tag no head e um main com três parágrafos curtos de observação.

<!-- index.html -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<main>
  <p>Sem a meta viewport, a página parece encolhida em dispositivos móveis porque o navegador assume uma largura virtual maior.</p>
  <p>Ao alterar <code>initial-scale</code>, o zoom inicial muda e a leitura começa mais ampliada ou mais afastada.</p>
  <p>Ao testar combinações como <code>width=500</code> ou <code>initial-scale=2</code>, a interface passa a ser percebida com outra referência de largura e escala.</p>
</main>
Como pensar

Aqui a resposta não é só técnica de CSS. É experimento controlado: mudar condição, observar e explicar o efeito.

Como validar

Abra o Device Toolbar, compare com e sem a meta tag e altere initial-scale para confirmar a mudança de percepção inicial.

Tabela de Evidência
  • Sem meta viewport -> página “encolhida”.
  • initial-scale=2 -> abertura mais ampliada.
  • width=500 -> outra referência inicial de largura.
Variáveis-chave
  • width=device-width
  • initial-scale
  • Device Toolbar
Como explicar

A meta viewport informa ao navegador móvel como interpretar largura e zoom inicial. Ao mudá-la, eu mudo a forma como a página aparece logo na abertura.

Erro comum

Tratar a questão como se bastasse copiar a meta tag sem observar nem redigir o efeito percebido.

EX.08
Criar 5 breakpoints mobile-first
Objetivo: crescer do menor para o maior com min-width.
O que o enunciado pede

Criar um arquivo CSS com cinco breakpoints do menor para o maior, em lógica mobile-first.

Conceito cobrado

Crescimento incremental com min-width.

Estrutura dos dados

Estrutura mínima: um arquivo CSS com base sem media query e cinco media queries em ordem crescente.

/* styles.css */
/* base: mobile pequeno */

@media (min-width: 360px) { }
@media (min-width: 576px) { }
@media (min-width: 768px) { }
@media (min-width: 992px) { }
@media (min-width: 1200px) { }
Como pensar

O layout nasce sem media query. Cada breakpoint só acrescenta comportamento quando a largura cresce.

Como validar

Verifique que os breakpoints estão em ordem crescente e que a base sem media query representa o cenário menor.

Tabela de Evidência
  • Base -> mobile pequeno.
  • 768px -> estado tablet.
  • 1200px -> estado desktop maior.
Variáveis-chave
  • min-width
  • ordem crescente
  • base sem media query
Como explicar

Comecei sem media query e fui ampliando com min-width. Isso é mobile-first de verdade, porque o layout cresce sem inverter a lógica.

Erro comum

Misturar max-width e min-width sem critério e perder a progressão incremental.

EX.09
Três arquivos CSS via link media
Objetivo: carregar CSS diferente por cenário, incluindo impressão.
O que o enunciado pede

Três CSS chamados por <link>, sendo um deles específico para impressão.

Conceito cobrado

Carga condicional de CSS pelo atributo media.

Estrutura dos dados

HTML mínimo: três tags link, cada uma com um arquivo e uma condição de mídia.

<!-- index.html -->
<link rel="stylesheet" href="screen-small.css" media="screen and (max-width: 767px)">
<link rel="stylesheet" href="screen-large.css" media="screen and (min-width: 768px)">
<link rel="stylesheet" href="print.css" media="print">
Como pensar

A decisão aqui é separar o CSS no próprio carregamento, e não usar um arquivo único com tudo misturado.

Como validar

Inspecione o HTML e confirme que cada link está condicionado ao cenário correto: tela pequena, tela grande e impressão.

Tabela de Evidência
  • screen-small.css -> max-width: 767px.
  • screen-large.css -> min-width: 768px.
  • print.css -> media="print".
Variáveis-chave
  • <link>
  • media
  • print
Como explicar

Usei o atributo media na própria tag link, então o navegador decide quando cada arquivo entra em cena.

Erro comum

Criar três arquivos mas carregar todos sem condição, perdendo o objetivo da questão.

EX.10
Três imports com @import
Objetivo: combinar orientação, largura e altura em imports condicionais.
O que o enunciado pede

Três chamadas com @import, combinando condições com and e alternativa equivalente a “ou”.

Conceito cobrado

Consultas de mídia compostas em CSS.

Estrutura dos dados

Estrutura mínima: um arquivo CSS com três linhas @import e combinações claras de condição.

/* styles.css */
@import url("portrait.css") screen and (orientation: portrait);
@import url("wide.css") screen and (min-width: 992px) and (min-height: 700px);
@import url("combo.css") screen and (orientation: landscape), screen and (min-width: 1200px);
Como pensar

Em CSS, o “ou” não é a palavra or; ele é representado por vírgula.

Como validar

Cheque se há pelo menos uma combinação com and e uma alternativa separada por vírgula.

Tabela de Evidência
  • Import 1 -> orientação portrait.
  • Import 2 -> largura mínima + altura mínima.
  • Import 3 -> landscape ou tela bem larga.
Variáveis-chave
  • @import
  • and
  • vírgula como alternativa
Como explicar

Usei and para combinar condições na mesma consulta e vírgula para representar alternativas equivalentes a “ou”.

Erro comum

Escrever or literalmente, como se CSS aceitasse a palavra.

EX.11
Imagem em cima, texto embaixo; depois lado a lado
Objetivo: manter estrutura única e reorganizar com media query.
O que o enunciado pede

Um layout vertical por padrão e horizontal quando houver largura suficiente.

Conceito cobrado

Flexbox com mudança de direção por breakpoint.

Estrutura dos dados

HTML mínimo: um wrapper com uma imagem e um bloco de texto.

<!-- index.html -->
<div class="wrap">
  <img class="hero" src="img.jpg" alt="Imagem">
  <div class="text">
    <h1>Responsividade</h1>
    <p>Uma interface responsiva melhora leitura, uso e adaptação.</p>
  </div>
</div>

/* styles.css */
.wrap { display: flex; flex-direction: column; gap: 1rem; }
.hero { width: 100%; height: auto; }
@media (min-width: 768px) {
  .wrap { flex-direction: row; align-items: center; }
  .hero, .text { width: 50%; }
}
Como pensar

A mesma estrutura HTML deve servir aos dois estados. A media query só muda a direção do fluxo.

Como validar

No mobile, imagem e texto ficam empilhados. A partir do breakpoint, os dois blocos ficam lado a lado.

Tabela de Evidência
  • 375px -> imagem em cima, texto embaixo.
  • 768px -> dois blocos na mesma linha.
  • Sem duplicar HTML -> a estrutura continua única.
Variáveis-chave
  • flex-direction
  • min-width: 768px
  • width: 50%
Como explicar

Começo em coluna porque o padrão é mobile-first. Quando a largura cresce, a media query vira o layout para linha.

Erro comum

Criar dois HTMLs diferentes, um para mobile e outro para desktop.

EX.12
Light e dark com texto e imagem
Objetivo: trocar o tema sem CSS inline.
O que o enunciado pede

Uma página simples com texto e imagem, em light e dark, sem CSS inline.

Conceito cobrado

Tokens de cor com prefers-color-scheme.

Estrutura dos dados

HTML mínimo: um container .card com título, imagem e parágrafo.

<!-- index.html -->
<div class="card">
  <h1>Benefícios da responsividade</h1>
  <img src="img.jpg" alt="Interface responsiva">
  <p>Sites responsivos melhoram leitura, navegação e adaptação entre dispositivos.</p>
</div>

/* styles.css */
:root {
  --bg: #ffffff;
  --fg: #111111;
  --card: #f1f3f5;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #111111;
    --fg: #f8f9fa;
    --card: #1c1f24;
  }
}
body { background: var(--bg); color: var(--fg); }
.card { background: var(--card); padding: 1rem; border-radius: 12px; }
Como pensar

A estrutura HTML não muda. O tema troca apenas os tokens de cor no CSS.

Como validar

Force light e dark no navegador e confira se texto e imagem continuam legíveis com o mesmo HTML.

Tabela de Evidência
  • Light -> fundo claro, texto escuro.
  • Dark -> fundo escuro, texto claro.
  • Mesmo HTML -> só os tokens mudam.
Variáveis-chave
  • :root
  • prefers-color-scheme
  • --bg, --fg, --card
Como explicar

Usei variáveis CSS para centralizar a paleta. O prefers-color-scheme troca só os tokens, então o restante continua consistente.

Erro comum

Duplicar um bloco grande de CSS inteiro em vez de trocar só as cores.

EX.13
pointer: none | coarse | fine
Objetivo: estilizar três botões conforme o tipo de apontador.
O que o enunciado pede

Três botões com aparência diferente conforme pointer: none, coarse e fine.

Conceito cobrado

Adaptação por precisão do dispositivo de entrada.

Estrutura dos dados

HTML mínimo: um wrapper com três button.

<!-- index.html -->
<div class="buttons">
  <button>Botão 1</button>
  <button>Botão 2</button>
  <button>Botão 3</button>
</div>

/* styles.css */
.buttons { display: flex; gap: 1rem; padding: 1rem; }
button { padding: 1rem 1.25rem; border: 0; border-radius: 10px; }
@media (pointer: none)   { button { background: #adb5bd; color: #111; } }
@media (pointer: coarse) { button { background: #74c0fc; font-size: 1.1rem; } }
@media (pointer: fine)   { button { background: #51cf66; cursor: pointer; } }
Como pensar

pointer fala da precisão do dispositivo de entrada. A interface deve deixar essa diferença evidente.

Como validar

Em um contexto fino, os botões mostram cursor; em coarse, ficam maiores; em none, ficam neutros e sem depender de ponteiro.

Tabela de Evidência
  • pointer: none -> visual neutro.
  • pointer: coarse -> toque favorecido.
  • pointer: fine -> cursor e precisão.
Variáveis-chave
  • pointer
  • font-size
  • cursor
Como explicar

Usei um estilo para ausência de apontador, outro para toque e outro para mouse ou touchpad. Assim a página evidencia o contexto de uso.

Erro comum

Usar o mesmo estilo nos três cenários e perder a distinção que a questão pede.

EX.14
hover: none | hover
Objetivo: diferenciar interface com hover real da interface sem hover.
O que o enunciado pede

Dois botões com diferença visual conforme exista ou não a capacidade de hover.

Conceito cobrado

Separar capacidade de hover da precisão do apontador.

Estrutura dos dados

HTML mínimo: um wrapper com dois button.

<!-- index.html -->
<div class="buttons">
  <button>Comprar</button>
  <button>Saiba mais</button>
</div>

/* styles.css */
.buttons { display: flex; gap: 1rem; padding: 1rem; }
button { padding: 1rem 1.25rem; border: 0; border-radius: 10px; transition: .2s ease; }
@media (hover: none) {
  button { background: #dee2e6; color: #111; }
}
@media (hover: hover) {
  button { background: #343a40; color: #fff; }
  button:hover { background: #0d6efd; transform: translateY(-2px); }
}
Como pensar

Aqui o foco não é precisão de clique, e sim capacidade real de passar o mouse e ganhar um estado extra.

Como validar

Quando houver hover, o botão deve reagir ao passar do mouse. Quando não houver, o botão precisa continuar legível sem esse efeito.

Tabela de Evidência
  • hover: none -> botão estável.
  • hover: hover -> estado visual ao passar o mouse.
  • Mesmo HTML -> só a condição muda.
Variáveis-chave
  • hover
  • estado :hover
  • transition
Como explicar

Quando há hover real, faz sentido adicionar resposta visual ao passar o mouse. Quando não há, o botão continua claro sem depender desse efeito.

Erro comum

Confundir hover com pointer e tratar as duas coisas como se fossem idênticas.

EX.15
prefers-color-scheme com header, main e footer
Objetivo: detectar preferência do sistema e adaptar a paleta da página.
O que o enunciado pede

Uma página com header, main e footer, em light e dark.

Conceito cobrado

Preferência de tema do sistema aplicada à estrutura da página.

Estrutura dos dados

HTML mínimo: um wrapper .page com header, main e footer.

<!-- index.html -->
<div class="page">
  <header>Header</header>
  <main>Main</main>
  <footer>Footer</footer>
</div>

/* styles.css */
:root { --bg: #ffffff; --fg: #111111; --panel: #f1f3f5; }
@media (prefers-color-scheme: dark) {
  :root { --bg: #0b0d10; --fg: #f8f9fa; --panel: #1a1d22; }
}
body { margin: 0; background: var(--bg); color: var(--fg); }
.page { min-height: 100vh; display: grid; grid-template-rows: auto 1fr auto; }
header, main, footer { padding: 1rem; background: var(--panel); margin: .5rem; border-radius: 12px; }
Como pensar

A estrutura continua igual. O que muda é só a paleta lida da preferência do usuário.

Como validar

Force light e dark no navegador e verifique se header, main e footer preservam legibilidade nos dois temas.

Tabela de Evidência
  • Light -> painéis claros.
  • Dark -> painéis escuros.
  • Mesma grade vertical -> estrutura preservada.
Variáveis-chave
  • prefers-color-scheme
  • --bg
  • --panel
Como explicar

Usei prefers-color-scheme porque a questão pede detectar a preferência do usuário. O layout continua o mesmo; o que muda é a paleta.

Erro comum

Montar um segundo layout inteiro para dark em vez de trocar só os tokens.

EX.16
Transmutação mobile-only do wireframe em 414px
Objetivo: preservar a hierarquia do wireframe em uma composição mobile funcional.
O que o enunciado pede

Implementar apenas a versão mobile, em 414px, traduzindo o wireframe em blocos estruturais coerentes.

Conceito cobrado

Leitura de wireframe e organização hierárquica de layout mobile-only.

Estrutura dos dados

HTML mínimo completo: topo com marca e menu, hero, artigo com imagem, faixa de logos, CTA e rodapé em blocos empilhados.

<!-- index.html -->
<div class="mobile-page">
  <header class="top">
    <div class="brand">HOMCO</div>
    <button class="menu-btn">☰</button>
  </header>

  <section class="hero-post">
    <h1>Single Post</h1>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  </section>

  <section class="post-card">
    <img src="decor.jpg" alt="Imagem do artigo">
    <h2>Tips Choice Best Agency for House Decoration</h2>
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
    <p>Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
  </section>

  <section class="logos">
    <span>Logo 1</span><span>Logo 2</span><span>Logo 3</span><span>Logo 4</span>
  </section>

  <section class="cta">
    <h3>Lets Change Your Own Home Interior Design Now</h3>
    <a href="#">Contact Us</a>
  </section>

  <footer class="footer">
    <div><h4>Information</h4><p>Texto institucional.</p></div>
    <div><h4>Navigation</h4><p>Home<br>About<br>Services</p></div>
    <div><h4>Contact Us</h4><p>hello@email.com</p></div>
  </footer>
</div>

/* styles.css */
.mobile-page { width: 414px; max-width: 100%; background: white; }
.top, .logos { display: flex; justify-content: space-between; align-items: center; }
.top { padding: 1rem; }
.hero-post { padding: 2rem 1rem; }
.post-card, .cta, .footer { padding: 1rem; }
.post-card img { width: 100%; height: auto; margin-bottom: 1rem; }
.footer { display: grid; gap: 1rem; }
Como pensar

O objetivo não é copiar pixel por pixel, e sim preservar a hierarquia do wireframe em uma largura fixa de 414px.

Como validar

Fixe o Device Toolbar em 414px. A página precisa funcionar inteira em uma coluna, sem depender de expansão para desktop.

Tabela de Evidência
  • Topo -> marca + menu.
  • Miolo -> hero, artigo, logos e CTA empilhados.
  • Rodapé -> blocos finais ainda legíveis em 414px.
Variáveis-chave
  • width: 414px
  • blocos empilhados
  • hierarquia do wireframe
Como explicar

Na transmutação mobile-only, o principal é preservar a hierarquia do desenho. Eu traduzi o wireframe em blocos empilhados e mantive a leitura clara em 414px.

Erro comum

Abrir breakpoints de desktop quando o exercício pede só a versão mobile.

CHECK
Como estudar sem se perder
Objetivo: consolidar a ordem de prática e a competência final do roteiro.
Roteiro de estudo

01-02: picture vs img. 03-04: Flexbox a partir de protótipo. 05-06: Grid para centralização e áreas. 07-10: viewport e arquitetura de media queries. 11-15: contextos reais de adaptação. 16: tradução de wireframe.

Ao final, você deve conseguir dizer

Eu sei escolher entre picture e img, sei quando usar Flexbox e quando usar Grid, consigo montar media queries mobile-first, carregar CSS condicionalmente, adaptar interface por pointer, hover e prefers-color-scheme, e explicar o que muda quando altero a meta viewport.