Mobile-first UI com React: Guia de CSS Responsivo

Implementação prática do zero com HTML mínimo, CSS mínimo e raciocínio técnico por exercício

Por Fábio Linhares • Instituto Infnet

Como usar esta página

Estas questões não são apenas uma revisão de CSS. Elas treinam uma mudança de postura: sair do “ajustei até ficar bonito” e entrar no “consigo prever o comportamento do layout em condições diferentes e justificar tecnicamente cada decisão”. O foco é menos decorar propriedade e mais raciocinar como quem projeta interface para contexto real.

No primeiro bloco, o núcleo é engenharia de viewport. Você precisa distinguir 100% do container e 100vw do viewport, aplicar vh com critério e controlar efeitos colaterais de mobile. O objetivo é fazer imagem e texto obedecerem regra mensurável: ocupar largura visível, usar altura relativa e reagir a rotação sem quebrar.

No segundo bloco, responsividade vira sistema de condições, não chute de breakpoint. Você vai combinar media queries por orientação, mídia de saída, capacidade de entrada e tema do sistema. Isso evita erros comuns: interface dependente de hover em touch, estilo de tela vazando para impressão e dark mode inconsistente.

No terceiro bloco, entram páginas completas com conteúdo real: perfil, produto e plataforma de vídeos. A exigência passa por composição de seções, hierarquia visual, componentes reutilizáveis e reorganização de layout conforme a tela cresce. Também entra disciplina de engenharia: CSS externo, separação correta de arquivos e comportamento previsível em cada estado.

Se você percorrer o conjunto completo com método, as competências finais ficam objetivas:

  • Domínio de viewport engineering: quando usar 100vw, 80vh, 100%, object-fit e aspect-ratio com estabilidade visual.
  • Tipografia responsiva com clamp(), calibrando limites mínimos e máximos com legibilidade em diferentes telas.
  • Layout mobile-first com evolução por necessidade de conteúdo, usando progressive enhancement em vez de ajustes aleatórios.
  • Leitura de contexto de execução: orientação, pointer/hover, impressão e tema do sistema para definir regras certas no lugar certo.

Trate cada exercício como um pequeno experimento técnico: implemente, valide no DevTools e explique a cadeia regra → condição → comportamento observado. Esse ciclo é o que transforma CSS em método de construção, depuração e comunicação técnica.

BASE
Modelo mental para todas as questões
Objetivo: resolver por condição técnica, não por ajuste visual aleatório.
Sequência padrão
1. Leia o requisito e identifique a condição principal (viewport, orientação, mídia, input ou tema).
2. Monte HTML mínimo antes de estilizar.
3. Aplique CSS por menor escopo possível e só depois abra media queries.
4. Valide em tela pequena e grande para confirmar mudança de estado.
SETUP
Estrutura mínima para qualquer questão
Objetivo: evitar erro estrutural básico e manter CSS externo.
<!-- index.html -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="styles.css">

/* styles.css */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
img { display: block; max-width: 100%; }
QUESTÃO 01
Imagem com 100% da largura do viewport
Objetivo: usar img + classe e controlar largura com vw.
<img class="hero-full" src="img.jpg" alt="Imagem principal">

.hero-full {
  width: 100vw;
  height: auto;
}
Como explicar em 3 frases
1. Usei a tag de imagem com classe dedicada.
2. 100vw significa largura total do viewport.
3. Mantive height: auto para preservar proporção.
Erros comuns
1. Usar apenas width: 100% sem entender o container.
2. Esquecer margens/padding e gerar barra horizontal.
QUESTÃO 02
Imagem com 80% da altura do viewport
Objetivo: usar vh com controle de recorte.
<img class="hero-tall" src="img.jpg" alt="Imagem principal">

.hero-tall {
  width: 100vw;
  height: 80vh;
  object-fit: cover;
}
Como explicar em 3 frases
1. 80vh fixa a altura em 80% da área visível.
2. object-fit: cover evita deformação.
3. A largura continua responsiva com 100vw.
Erros comuns
1. Definir altura fixa em px e perder adaptação.
2. Não usar object-fit e distorcer a imagem.
QUESTÃO 03
Texto relativo ao tamanho padrão
Objetivo: aplicar font-size: 100% em texto real.
<p class="texto-base">Pensar mobile-first é começar pelo cenário mais restrito.</p>

.texto-base { font-size: 100%; }
Como explicar em 3 frases
1. Usei parágrafo com classe específica.
2. 100% mantém o tamanho padrão do documento.
3. A regra foi aplicada sem CSS inline.
Erros comuns
1. Alterar html { font-size } e afirmar que continua padrão.
2. Aplicar unidade absoluta sem necessidade.
QUESTÃO 04
Tipografia responsiva entre 10px e 30px
Objetivo: usar clamp com limite inferior e superior.
<p class="texto-fluido">Mobile-first reduz retrabalho e melhora estabilidade visual.</p>

.texto-fluido {
  font-size: clamp(10px, 2.2vw, 30px);
}
Como explicar em 3 frases
1. clamp controla mínimo, valor fluido e máximo.
2. O termo em vw reage à largura.
3. O texto não passa de 30px nem cai abaixo de 10px.
Erros comuns
1. Usar valor fixo no termo central e perder fluidez.
2. Definir limites fora do intervalo pedido.
QUESTÃO 05
Imagem por orientação em um único CSS
Objetivo: alternar comportamento com @media (orientation: portrait|landscape).
<img class="hero" src="img.jpg" alt="Imagem orientada">

@media (orientation: portrait) {
  .hero { width: 100vw; height: auto; }
}

@media (orientation: landscape) {
  .hero { width: 100vw; height: 80vh; object-fit: cover; }
}
Como explicar em 3 frases
1. Usei duas regras por orientação dentro do mesmo arquivo.
2. Portrait prioriza largura, landscape prioriza altura.
3. A troca acontece quando a tela é rotacionada.
Erros comuns
1. Misturar requisito da questão 06 aqui.
2. Esquecer object-fit no modo landscape.
QUESTÃO 06
Dois arquivos CSS com link condicional
Objetivo: separar portrait e landscape em arquivos distintos.
<link rel="stylesheet" href="base.css">
<link rel="stylesheet" href="portrait-screen.css" media="(orientation: portrait)">
<link rel="stylesheet" href="landscape-screen.css" media="(orientation: landscape)">

/* portrait-screen.css */
.hero { width: 100vw; height: auto; }

/* landscape-screen.css */
.hero { width: 100vw; height: 80vh; object-fit: cover; }
Como explicar em 3 frases
1. Separei estilo por orientação em arquivos diferentes.
2. A tag link com media escolhe automaticamente a regra.
3. A solução atende o requisito de separação explícita.
Erros comuns
1. Colocar tudo em um único arquivo e não cumprir o requisito.
2. Nomear arquivos diferente do padrão pedido.
QUESTÃO 07
Layout vertical para horizontal
Objetivo: aplicar mobile-first com mudança de direção por breakpoint.
<div class="wrap">
  <img class="hero" src="img.jpg" alt="Imagem">
  <p class="txt">Pense mobile-first.</p>
</div>

.wrap { display: flex; flex-direction: column; gap: 12px; }
.hero { width: 100vw; margin-left: calc(50% - 50vw); }

@media (min-width: 768px) {
  .wrap { flex-direction: row; align-items: center; }
  .hero, .txt { width: 50%; margin-left: 0; }
}
Como explicar em 3 frases
1. O padrão é coluna para telas menores.
2. Em largura maior, o layout vira linha.
3. A imagem ocupa a largura completa no modo vertical.
Erros comuns
1. Começar pelo desktop e remendar mobile no fim.
2. Esquecer de zerar margens no layout horizontal.
QUESTÃO 08
Light e dark mode com tokens
Objetivo: alternar paleta com prefers-color-scheme.
<article class="card">
  <h2>Resumo da aula</h2>
  <p>Mobile-first começa no menor contexto e escala para telas maiores.</p>
</article>

:root { --bg:#fff; --fg:#111; --card:#f3f3f3; }
@media (prefers-color-scheme: dark) {
  :root { --bg:#0b0b0b; --fg:#eaeaea; --card:#1a1a1a; }
}
body { background: var(--bg); color: var(--fg); }
.card { background: var(--card); }
Como explicar em 3 frases
1. Centralizei cores em variáveis.
2. Troquei apenas os tokens no modo dark.
3. O HTML permanece igual, muda só o tema.
Erros comuns
1. Duplicar regras inteiras para cada tema.
2. Ignorar contraste e legibilidade do texto.
QUESTÃO 09
Ocultar resposta em mídia de impressão
Objetivo: manter resposta em tela e ocultar na impressão.
<section class="quiz">
  <p class="question">Qual regra deixa a resposta oculta apenas na impressão?</p>
  <ol class="options">
    <li>.answer { visibility: hidden; }</li>
    <li>@media print { .answer { display: none; } }</li>
    <li>@media screen { .answer { display: none; } }</li>
    <li>.quiz { display: none; }</li>
    <li>.options { opacity: 0; }</li>
  </ol>
  <p class="answer">Alternativa correta: 2, porque a regra atua somente na mídia print.</p>
</section>

@media print {
  .answer { display: none; }
}
Como explicar em 3 frases
1. O bloco de resposta existe na tela normal.
2. Em mídia de impressão, ele é removido.
3. A pergunta e as opções permanecem visíveis.
Erros comuns
1. Esconder a seção inteira em vez da resposta.
2. Colocar a regra fora de @media print.
QUESTÃO 10
Cursor de interação para pointer fino
Objetivo: aplicar cursor apenas quando o input for preciso.
<div class="options">
  <label><input type="radio" name="q"> Opção A</label>
  <label><input type="radio" name="q"> Opção B</label>
</div>

@media (pointer: fine) {
  .options label { cursor: pointer; }
}
Como explicar em 3 frases
1. A regra depende do tipo de ponteiro.
2. Em mouse, o cursor sinaliza interatividade.
3. Em toque, o comportamento visual não depende de cursor.
Erros comuns
1. Aplicar cursor sempre, sem media query.
2. Estilizar o input e esquecer o label clicável.
QUESTÃO 11
Rótulo visível somente no hover em desktop
Objetivo: manter ícone+rótulo e ocultar rótulo apenas em contexto com hover real.
<nav class="menu">
  <a class="item" href="#"><span class="icon">🏠</span><span class="label">Home</span></a>
  <a class="item" href="#"><span class="icon">📚</span><span class="label">Cursos</span></a>
  <a class="item" href="#"><span class="icon">🧰</span><span class="label">Ferramentas</span></a>
  <a class="item" href="#"><span class="icon">☎️</span><span class="label">Contato</span></a>
</nav>

.label { display: inline; }
@media (hover: hover) and (pointer: fine) {
  .label { display: none; }
  .item:hover .label { display: inline; }
}
Como explicar em 3 frases
1. O fallback padrão mantém rótulo visível.
2. Só no contexto com hover real o rótulo é ocultado.
3. O hover reexibe o rótulo do item ativo.
Erros comuns
1. Ocultar rótulo também em touch e perder clareza.
2. Tentar resolver com JavaScript desnecessário.
QUESTÃO 12
Tooltip no hover com rótulo invisível
Objetivo: mostrar tooltip em desktop com rótulo semântico via atributo.
<nav class="menu">
  <a class="item" href="#" data-label="Home" aria-label="Home"><span class="icon">🏠</span></a>
  <a class="item" href="#" data-label="Cursos" aria-label="Cursos"><span class="icon">📚</span></a>
</nav>

.item { position: relative; }
@media (hover: hover) and (pointer: fine) {
  .item::after {
    content: attr(data-label);
    position: absolute;
    top: 110%;
    left: 50%;
    transform: translateX(-50%);
    opacity: 0;
    background: #111;
    color: #fff;
    padding: 6px 10px;
    border-radius: 8px;
    white-space: nowrap;
  }
  .item:hover::after { opacity: 1; }
}
Como explicar em 3 frases
1. O texto do tooltip vem de data-label.
2. A pseudo-elemento aparece só em hover com pointer fino.
3. position: relative ancora o tooltip no item correto.
Erros comuns
1. Esquecer position: relative no item.
2. Remover aria-label e perder acessibilidade.
QUESTÃO 13
Cinco questões com três estados de layout
Objetivo: estruturar question/options/answer com 1, 2 e 3 colunas.
<!-- index.html -->
<section class="quiz-grid">
  <!-- Repita este article .q 5 vezes (mínimo) -->
  <article class="q">
    <h3 class="q__question">Qual media query limita 3 colunas para tela?</h3>
    <ul class="q__options">
      <li>@media print</li>
      <li>@media (min-width: 768px)</li>
      <li>@media screen and (min-width: 1024px)</li>
      <li>@media (hover: hover)</li>
    </ul>
    <p class="q__answer">Resposta: usar @media screen and (min-width: 1024px).</p>
  </article>
</section>

/* styles.css */
body { font-size: clamp(10px, 2.2vw, 30px); }
.quiz-grid { display: grid; gap: 12px; }
.q { display: grid; grid-template-columns: 1fr; gap: 10px; }
@media (min-width: 768px) {
  .q { grid-template-columns: 1fr 1fr; }
  .q__answer { grid-column: 1 / -1; }
}
@media screen and (min-width: 1024px) {
  .q { grid-template-columns: 1fr 1fr 1fr; }
  .q__answer { grid-column: auto; }
}
Pontos essenciais de estudo
1. Criar no mínimo 5 blocos .q.
2. Cada questão precisa de 4 alternativas.
3. A regra de 3 colunas deve ficar somente em @media screen ....
Como explicar em 3 frases
1. O componente nasce em coluna única.
2. No médio, pergunta e opções ficam lado a lado.
3. No grande (somente screen), as três áreas ocupam três colunas.
Erros comuns
1. Esquecer screen e aplicar 3 colunas na impressão.
2. Não usar grid-column: 1 / -1 no estado intermediário.
QUESTÃO 14
Página "Meu Perfil" responsiva
Objetivo: combinar perfil, hobbies, amigos, menu expande-colapsa e tema.
<!-- index.html -->
<header class="top">
  <h1>Meu Perfil</h1>
  <details class="nav">
    <summary>Menu</summary>
    <nav class="nav__list">
      <a href="#bio">Bio</a>
      <a href="#hobbies">Hobbies</a>
      <a href="#amigos">Amigos</a>
      <a href="#contato">Contato</a>
    </nav>
  </details>
</header>
<main class="layout">
  <section id="bio" class="card profile"><img src="avatar.jpg" alt="Foto de perfil"><p>Apresentação curta.</p></section>
  <section id="hobbies" class="card hobbies"><img src="h1.jpg" alt="Hobby 1"><img src="h2.jpg" alt="Hobby 2"><img src="h3.jpg" alt="Hobby 3"></section>
  <section id="amigos" class="card friends"><img src="a1.jpg" alt="Amigo 1"><img src="a2.jpg" alt="Amigo 2"><img src="a3.jpg" alt="Amigo 3"></section>
  <section id="contato" class="card contact"><p>contato@exemplo.com</p></section>
</main>

/* styles.css */
:root { --bg:#fff; --fg:#111; --card:#f3f3f3; --bd:#ccc; }
@media (prefers-color-scheme: dark) {
  :root { --bg:#0b0b0b; --fg:#eaeaea; --card:#1a1a1a; --bd:#333; }
}
body { background: var(--bg); color: var(--fg); }
.top { display:flex; justify-content:space-between; align-items:center; gap:12px; }
.nav__list { display:flex; flex-direction:column; gap:8px; }
.layout { display: grid; gap: 12px; }
.card { padding: 12px; border: 1px solid var(--bd); border-radius: 10px; background: var(--card); }
.profile img { width: 96px; height: 96px; border-radius: 50%; object-fit: cover; }
.hobbies, .friends { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr)); gap: 8px; }
.hobbies img, .friends img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 8px; }
@media (min-width: 900px) {
  .layout { grid-template-columns: 1.2fr 1fr; }
  .friends, .contact { grid-column: 1 / -1; }
}
Pontos essenciais de estudo
1. Menu expande/colapsa com mínimo de 4 itens.
2. Hobbies e amigos devem usar imagens no HTML.
3. Manter light/dark por tokens em CSS externo.
Como explicar em 3 frases
1. Estruturei o conteúdo por blocos com semântica clara.
2. O layout cresce de uma coluna para múltiplas colunas.
3. O tema usa tokens para light/dark sem duplicação extensa.
Erros comuns
1. Menu horizontal rígido estourando no celular.
2. Galeria sem proporção estável e com saltos visuais.
QUESTÃO 15
Página de produto com galeria e listas
Objetivo: montar e-commerce responsivo com produto, relacionados e comentários.
<!-- index.html -->
<header class="top">
  <h1>Produto</h1>
  <details class="nav"><summary>Menu</summary><nav class="nav__list"><a href="#produto">Produto</a><a href="#especificacoes">Specs</a><a href="#relacionados">Relacionados</a><a href="#comentarios">Comentários</a></nav></details>
</header>
<section id="produto" class="card product">
  <div class="gallery">
    <input id="p1" type="radio" name="gal" checked><input id="p2" type="radio" name="gal"><input id="p3" type="radio" name="gal">
    <div class="thumbs"><label for="p1">Frente</label><label for="p2">Lado</label><label for="p3">Detalhe</label></div>
    <div class="main"><img class="p1" src="produto-1.jpg" alt="Frente"><img class="p2" src="produto-2.jpg" alt="Lado"><img class="p3" src="produto-3.jpg" alt="Detalhe"></div>
  </div>
  <div class="info" id="especificacoes"><h2>Notebook X</h2><p class="price">R$ 4.999</p><ul class="specs"><li>16GB RAM</li><li>512GB SSD</li><li>Tela 14"</li></ul></div>
</section>
<section id="relacionados" class="card related">
  <article class="item"><img src="r1.jpg" alt="Mouse sem fio"><h3>Mouse sem fio</h3><p>R$ 149</p></article>
  <article class="item"><img src="r2.jpg" alt="Mochila"><h3>Mochila</h3><p>R$ 199</p></article>
</section>
<section id="comentarios" class="card comments">
  <article class="comment"><strong>Ana</strong> <time datetime="2026-03-10">10/03/2026</time><p>Ótimo desempenho no dia a dia.</p></article>
  <article class="comment"><strong>Bruno</strong> <time datetime="2026-03-11">11/03/2026</time><p>Bateria estável para trabalho remoto.</p></article>
</section>

/* styles.css */
:root { --bg:#fff; --fg:#111; --card:#f3f3f3; --bd:#ccc; }
@media (prefers-color-scheme: dark) { :root { --bg:#0b0b0b; --fg:#eaeaea; --card:#1a1a1a; --bd:#333; } }
body { background: var(--bg); color: var(--fg); }
.gallery input { display: none; }
.main img { display: none; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
#p1:checked ~ .main .p1, #p2:checked ~ .main .p2, #p3:checked ~ .main .p3 { display: block; }
.thumbs { display: flex; gap: 8px; flex-wrap: wrap; }
.product, .related, .comments { display: grid; gap: 12px; }
.related .item img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 8px; }
@media (min-width: 900px) { .product { grid-template-columns: 1fr 1fr; } .related { grid-template-columns: 1fr 1fr; } }
Pontos essenciais de estudo
1. Relacionados precisam de imagem + nome + valor.
2. Comentários precisam de nome + data + comentário.
3. Não esquecer menu expande/colapsa e light/dark no mesmo exercício.
Como explicar em 3 frases
1. A galeria troca imagem com radios, sem JavaScript.
2. Informações do produto ficam separadas da mídia.
3. Em telas maiores, o bloco principal vira duas colunas.
Erros comuns
1. Não limitar proporção das imagens e quebrar o layout.
2. Omitir dados essenciais de relacionados/comentários.
QUESTÃO 16
Plataforma de vídeos com sidebar
Objetivo: combinar player, ações, descrição, propaganda, relacionados e comentários.
<!-- index.html -->
<header class="top">
  <h1>Plataforma de Vídeos</h1>
  <details class="nav"><summary>Menu</summary><nav class="nav__list"><a href="#video">Vídeo</a><a href="#acoes">Ações</a><a href="#relacionados">Relacionados</a><a href="#comentarios">Comentários</a></nav></details>
</header>
<main class="video-layout">
  <section id="video" class="card main">
    <iframe class="player" src="https://www.youtube.com/embed/dQw4w9WgXcQ" title="Vídeo principal" allowfullscreen></iframe>
    <h2>Título do vídeo</h2>
    <p class="author">Autor: Canal Exemplo</p>
    <div id="acoes" class="actions"><button>Curtir</button><button>Compartilhar</button><button>Salvar</button><button>Denunciar</button></div>
    <p>Descrição curta do conteúdo.</p>
  </section>
  <aside class="card sidebar">
    <section class="ad">Espaço de propaganda</section>
    <section id="relacionados" class="related">
      <article class="video-item"><img src="capa-1.jpg" alt="Capa 1"><h3>Vídeo relacionado 1</h3><p>Autor A</p></article>
      <article class="video-item"><img src="capa-2.jpg" alt="Capa 2"><h3>Vídeo relacionado 2</h3><p>Autor B</p></article>
    </section>
    <section id="comentarios" class="comments">
      <article class="comment"><strong>Ana</strong> <time datetime="2026-03-12">12/03/2026</time><p>Explicação clara e direta.</p></article>
      <article class="comment"><strong>Bruno</strong> <time datetime="2026-03-13">13/03/2026</time><p>Gostei do exemplo de layout responsivo.</p></article>
    </section>
  </aside>
</main>

/* styles.css */
:root { --bg:#fff; --fg:#111; --card:#f3f3f3; --bd:#ccc; }
@media (prefers-color-scheme: dark) { :root { --bg:#0b0b0b; --fg:#eaeaea; --card:#1a1a1a; --bd:#333; } }
body { background: var(--bg); color: var(--fg); }
.player { width: 100%; aspect-ratio: 16 / 9; border: 0; }
.actions { display: flex; flex-wrap: wrap; gap: 8px; }
.video-item img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; border-radius: 8px; }
.video-layout { display: grid; gap: 12px; }
@media (min-width: 1000px) { .video-layout { grid-template-columns: 2fr 1fr; align-items: start; } }
Pontos essenciais de estudo
1. Mostrar autor do vídeo principal.
2. Incluir 4 ações: curtir, compartilhar, salvar e denunciar.
3. Relacionados: capa + nome + autor; comentários: nome + data + comentário.
4. Manter menu expande/colapsa e light/dark também nesta página.
Como explicar em 3 frases
1. A página organiza conteúdo principal e barra lateral.
2. O player mantém proporção estável com aspect-ratio.
3. As ações quebram linha com flex-wrap em telas menores.
Erros comuns
1. Botões em linha única estourando a largura.
2. Sidebar fixa sem adaptação para mobile.
REVISÃO FINAL
Roteiro técnico de revisão
Objetivo: consolidar entendimento técnico nos cenários principais de execução.
Roteiro de estudo
1. Revise o comportamento em 320, 375, 768 e 1024.
2. Observe a troca portrait/landscape nas questões de orientação.
3. Compare light/dark nas questões de tema.
4. Verifique o cenário de impressão na questão de mídia print.
5. Analise pointer/hover nas questões de menu interativo.
Quando cada bloco tem HTML mínimo, CSS mínimo e explicação curta, você reduz erro de interpretação e ganha velocidade de implementação.