Mobile-first UI com React

Migração completa para ReactJS: requisitos, implementação por componente, validação e defesa técnica.

Por Fábio Linhares • Instituto Infnet

BASE
Escopo Real do TP3
Consolidar o que o enunciado cobra: migrar HTML/CSS estático para ReactJS com componentização, CSS3 e responsividade.
Este material foi organizado para funcionar como um guia de estudo orientado ao TP3 de Mobile-first UI com React. A proposta aqui não é apenas mostrar "a resposta pronta", mas ajudar você a entender o que o enunciado realmente pede, como quebrar o problema em partes menores e como migrar, com método, um site HTML/CSS estático para uma aplicação ReactJS organizada em componentes.
Ao longo desta página, você vai trabalhar exatamente as competências cobradas no teste: criação do projeto React, organização de arquivos CSS por componente, reaproveitamento da pasta assets, construção do menu com comportamento mobile-first, implementação do menu hamburger com exibir/ocultar, migração do header, manutenção dos breakpoints do projeto anterior, reaproveitamento do sistema de container, row e grid, criação dos componentes de Banner, Card, Partners e Footer e montagem da página Home com os itens mínimos exigidos no enunciado.
O desafio central deste TP não é "inventar um site novo" nem "escrever React avançado". O desafio é estruturar bem o que já existia, separar a interface em componentes reutilizáveis, manter a responsividade do projeto anterior e usar React de forma coerente para controlar comportamento, repetição de conteúdo e composição da página. Em outras palavras: sair do HTML/CSS estático e entrar em uma arquitetura de componentes com CSS3, sem perder clareza, organização e previsibilidade.
Por isso, esta página foi montada em uma ordem intencional. Primeiro, você vai ver o escopo real do trabalho e os requisitos obrigatórios. Depois, os pré-requisitos e o vocabulário mínimo para não se perder na leitura. Em seguida, a estrutura recomendada do projeto, a fundação visual com container, row e grid, e só então os componentes principais: Menu, Header, Banner, Card, Partners, Footer e Home. No fim, você encontra uma lista de validação e um roteiro de defesa técnica para explicar sua solução com segurança.
A melhor forma de usar este material é simples: não tente absorver tudo de uma vez. Leia cada seção como uma etapa do trabalho. Entenda o objetivo, observe o código, monte a parte correspondente no seu projeto, valide o comportamento no navegador e só depois avance. Se fizer isso, você não estará apenas "seguindo um tutorial"; estará aprendendo a pensar o projeto em camadas, que é exatamente o que o TP quer avaliar.
  • Disciplina: Mobile-first UI com React (TP3).
  • Objetivo: componentização com comportamento real (menu hamburger), grid responsivo e organização de código.
  • Entrega: trabalho no https://codesandbox.io/ + link em documento Google + PDF no padrão nome_sobrenome_DR3_TP3.
Para fins de estudo técnico, o conteúdo foi organizado como guia de revisão e checklist de implementação.
EX.01
Checklist Técnico dos 15 Itens Obrigatórios
Transformar o enunciado em lista verificável para não deixar requisito de fora.
  • 1. Criar projeto ReactJS.
  • 2. Criar CSS ou SCSS por componente.
  • 3. Adicionar pasta assets do projeto estático.
  • 4. Criar componente Menu com base no menu anterior.
  • 5. Implementar menu hamburger abrindo/fechando ao clique.
  • 6. Criar componente de cabeçalho/Header.
  • 7. Definir breakpoints em arquivo CSS/SCSS.
  • 8. Migrar container, row e grid.
  • 9. Criar componente Card para notícias.
  • 10. Criar componente Banner.
  • 11. Renderizar 2 itens de banner na Home.
  • 12. Renderizar 4 notícias na Home.
  • 13. Criar componente de rodapé/Footer.
  • 14. Criar componente de parceiros.
  • 15. Renderizar 4 parceiros acima do rodapé.
Se qualquer item acima faltar, a entrega está incompleta.
EX.02
Pré-requisitos e Glossário Mínimo
Garantir base de entendimento para quem ainda está inseguro com React.
Pré-requisitos: HTML/CSS do projeto anterior, noção de JSX, import/export, props, useState, map e organização de pastas.
  • Componente: peça reutilizável (Menu, Header, Card, Banner, Partners, Footer).
  • Props: dados recebidos pelo componente.
  • State: dado que muda por interação (ex.: menu aberto/fechado).
  • useState: hook para controlar estado local.
  • map: renderização de listas (2 banners, 4 notícias, 4 parceiros).
  • Mobile-first: começar pela tela pequena e expandir com breakpoints.
EX.03
Estrutura Recomendada de Projeto
Padronizar a arquitetura antes de implementar para evitar retrabalho.
src/
  assets/
  components/
    Menu/Menu.jsx + Menu.css
    Header/Header.jsx + Header.css
    Banner/Banner.jsx + Banner.css
    Card/Card.jsx + Card.css
    Partners/Partners.jsx + Partners.css
    Footer/Footer.jsx + Footer.css
  pages/Home/Home.jsx + Home.css
  styles/grid.css
  App.jsx
  main.jsx
Esse arranjo atende ao requisito "CSS/SCSS por componente" e mantém rastreabilidade da migração.
EX.04
Setup Inicial + Entrega (CodeSandbox/PDF)
Criar base ReactJS, importar assets e fechar formato de submissão sem ambiguidade.
npm create vite@latest mobile-react-tp3 -- --template react
cd mobile-react-tp3
npm install
npm run dev

# depois
# 1) copiar assets para src/assets
# 2) criar componentes e css por componente
# 3) subir no CodeSandbox
# 4) gerar PDF com nome_sobrenome_DR3_TP3
A entrega deve mencionar explicitamente: CodeSandbox, documento Google com link e PDF no padrão solicitado.
EX.05
Base CSS: Container, Row e Grid
Migrar a fundação visual antes de montar componentes específicos.
/* src/styles/grid.css */
* { box-sizing: border-box; }
html, body, #root { margin: 0; padding: 0; min-height: 100%; }
img { display: block; max-width: 100%; }

.container { width: min(1120px, calc(100% - 2rem)); margin-inline: auto; }
.row { display: grid; grid-template-columns: repeat(12, 1fr); gap: 1rem; }
.col-12 { grid-column: span 12; }

@media (min-width: 768px) { .col-md-6 { grid-column: span 6; } }
@media (min-width: 1024px) {
  .col-lg-3 { grid-column: span 3; }
  .col-lg-6 { grid-column: span 6; }
}
Sem essa base, Banner/Card/Partners ficam sem consistência de layout.
EX.06
Menu com Flexbox + Hamburger (useState)
Implementar comportamento real do menu no ReactJS.
/* Menu.jsx */
import { useState } from "react";
import "./Menu.css";

export default function Menu() {
  const [open, setOpen] = useState(false);
  return (
    <nav className="menu">
      <button onClick={() => setOpen(!open)} aria-expanded={open}>☰</button>
      <ul className={`menu__list ${open ? "menu__list--open" : ""}`}>...</ul>
    </nav>
  );
}
  • Mobile: botão hamburger visível e lista recolhida.
  • Clique: alterna estado com setOpen(!open).
  • Desktop: menu horizontal via media query.
EX.07
Header Component + Breakpoints
Separar cabeçalho como componente e manter evolução mobile-first.
/* Header.jsx */
import Menu from "../Menu/Menu";

export default function Header() {
  return (
    <header className="site-header" id="home">
      <div className="container site-header__content">
        <h1>Meu Site</h1>
        <Menu />
      </div>
    </header>
  );
}
/* breakpoints exemplo */
@media (min-width: 768px) { ...tablet... }
@media (min-width: 1024px) { ...desktop... }
EX.08
Banner Component (2 Itens na Home)
Cumprir o requisito de dois banners com reuso via props e map.
/* Banner.jsx */
export default function Banner({ items }) {
  return (
    <section className="banner">
      <div className="container">
        <div className="row">
          {items.map((item, index) => (
            <article className="col-12 col-lg-6" key={index}>...</article>
          ))}
        </div>
      </div>
    </section>
  );
}
Condição de conformidade: 2 banners renderizados na Home.
EX.09
Card de Notícias (4 Itens Mínimos)
Aplicar componente reutilizável para conteúdo repetido.
/* Card.jsx */
export default function Card({ image, title, description }) {
  return (
    <article className="news-card">
      <img src={image} alt={title} />
      <div className="news-card__body">
        <h3>{title}</h3>
        <p>{description}</p>
      </div>
    </article>
  );
}
Condição de conformidade: 4 notícias exibidas na Home com map.
EX.10
Partners + Footer
Fechar a composição da página com parceiros e rodapé componentizados.
/* Partners.jsx */
import "./Partners.css";

export default function Partners({ items }) {
  return (
    <section className="partners" id="partners">
      <div className="container">
        <h2>Parceiros</h2>
        <div className="row">
          {items.map((partner, index) => (
            <article className="col-12 col-md-6 col-lg-3 partner-card" key={index}>
              <img src={partner.image} alt={partner.name} />
              <h3>{partner.name}</h3>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}
/* Partners.css */
.partners {
  padding: 2rem 0;
  background: #f9fafb;
}

.partner-card {
  background: #fff;
  border-radius: 12px;
  padding: 1rem;
  text-align: center;
  border: 1px solid #e5e7eb;
}
/* Footer.jsx */
import "./Footer.css";

export default function Footer() {
  return (
    <footer className="site-footer" id="footer">
      <div className="container site-footer__content">
        <div>
          <h3>Meu Site</h3>
          <p>Projeto React migrado do HTML/CSS estático.</p>
        </div>

        <div>
          <h4>Links</h4>
          <ul>
            <li><a href="#home">Home</a></li>
            <li><a href="#news">Notícias</a></li>
            <li><a href="#partners">Parceiros</a></li>
          </ul>
        </div>
      </div>
    </footer>
  );
}
/* Footer.css */
.site-footer {
  background: #111827;
  color: #fff;
  padding: 2rem 0;
}

.site-footer__content {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.site-footer a {
  color: #fff;
  text-decoration: none;
}

.site-footer ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

@media (min-width: 768px) {
  .site-footer__content {
    flex-direction: row;
    justify-content: space-between;
  }
}
Condição de conformidade: 4 parceiros acima do rodapé.
EX.11
Montagem da Home + App Final
Orquestrar componentes e dados em fluxo único.
/* Home.jsx */
import Banner from "../../components/Banner/Banner";
import Card from "../../components/Card/Card";
import Partners from "../../components/Partners/Partners";
import Footer from "../../components/Footer/Footer";
import "./Home.css";

import banner1 from "../../assets/banner1.jpg";
import banner2 from "../../assets/banner2.jpg";
import news1 from "../../assets/news1.jpg";
import news2 from "../../assets/news2.jpg";
import news3 from "../../assets/news3.jpg";
import news4 from "../../assets/news4.jpg";
import partner1 from "../../assets/partner1.jpg";
import partner2 from "../../assets/partner2.jpg";
import partner3 from "../../assets/partner3.jpg";
import partner4 from "../../assets/partner4.jpg";

const bannerItems = [
  {
    image: banner1,
    title: "Banner 1",
    description: "Primeiro destaque da página inicial."
  },
  {
    image: banner2,
    title: "Banner 2",
    description: "Segundo destaque da página inicial."
  }
];

const newsItems = [
  { image: news1, title: "Notícia 1", description: "Resumo da notícia 1." },
  { image: news2, title: "Notícia 2", description: "Resumo da notícia 2." },
  { image: news3, title: "Notícia 3", description: "Resumo da notícia 3." },
  { image: news4, title: "Notícia 4", description: "Resumo da notícia 4." }
];

const partnerItems = [
  { image: partner1, name: "Parceiro 1" },
  { image: partner2, name: "Parceiro 2" },
  { image: partner3, name: "Parceiro 3" },
  { image: partner4, name: "Parceiro 4" }
];

export default function Home() {
  return (
    <>
      <Banner items={bannerItems} />

      <main className="home-main container" id="news">
        <h2>Notícias</h2>
        <div className="row">
          {newsItems.map((item, index) => (
            <div className="col-12 col-md-6 col-lg-3" key={index}>
              <Card
                image={item.image}
                title={item.title}
                description={item.description}
              />
            </div>
          ))}
        </div>
      </main>

      <Partners items={partnerItems} />
      <Footer />
    </>
  );
}
/* Home.css */
.home-main {
  padding: 2rem 0;
}

.home-main h2 {
  margin-top: 0;
}
/* App.jsx */
import "./styles/grid.css";
import Header from "./components/Header/Header";
import Home from "./pages/Home/Home";

export default function App() {
  return (
    <>
      <Header />
      <Home />
    </>
  );
}
CHECK
Validação Final + Defesa Técnica
Fechar a entrega com evidência verificável, explicação clara e prevenção dos erros mais comuns.
  • Validação 1: projeto ReactJS abre sem erro.
  • Validação 2: assets usados em banners/cards/parceiros.
  • Validação 3: existe CSS ou SCSS por componente.
  • Validação 4: hamburger aparece no mobile e alterna ao clique.
  • Validação 5: menu desktop horizontal em breakpoint maior.
  • Validação 6: Home renderiza 2 banners, 4 notícias e 4 parceiros.
  • Validação 7: Footer componentizado e posicionado corretamente.
Como explicar a sua solução:
"Eu parti do site estático do TP anterior e não reescrevi o layout do zero. Primeiro migrei a base do CSS, com container, row e grid, para preservar a responsividade. Depois quebrei o HTML em componentes reutilizáveis: Menu, Header, Banner, Card, Partners e Footer. O menu hamburger foi resolvido com useState, e a Home foi montada com arrays para renderizar 2 banners, 4 notícias e 4 parceiros."

Se perguntarem "por que isso é React e não só HTML espalhado?", responda:
"Porque cada bloco repetível ou semanticamente isolado virou componente, e os conteúdos variáveis são passados por props ou renderizados com map."
Erros mais comuns:
  • Fazer tudo dentro de App.jsx (perde componentização).
  • Usar um único arquivo CSS gigante (o enunciado pede CSS/SCSS por componente).
  • Hamburger visual sem abrir/fechar de verdade ao clique.
  • Copiar 4 cards e 4 parceiros manualmente, sem props/map.
  • Inventar um layout totalmente novo em vez de migrar o estático.
  • Não validar em larguras diferentes (mobile, tablet e desktop).
Conclusão: com código completo nos componentes e na Home, mais validação e defesa técnica, esta página passa a cumprir integralmente o papel pedagógico e técnico do arquivo de respostas.