📚 Pré-requisitos Teóricos: Este projeto prático aplica os conceitos avançados de estilização e layouts bidimensionais lecionados no Módulo 04: HTML5 e CSS3. Recomendamos sua leitura para domínio de Grid Areas e CSS Custom Properties.

O Painel Administrativo Responsivo com CSS Grid & Dark Mode exemplifica a engenharia de layouts bidimensionais modernos para dashboards analíticos complexos. Sem a sobrecarga de frameworks utilitários pesados, a aplicação emprega CSS Grid puro (grid-template-areas), Flexbox e variáveis CSS para proporcionar alternância fluida entre temas claro e escuro (Dark/Light Mode) e perfeita adaptabilidade em qualquer resolução de tela.


🏛️ Arquitetura do Layout e Grid de Áreas

O diagrama abaixo detalha o mapeamento de áreas estruturais (header, sidebar, main, footer) e a cascata de tokens para suporte ao Dark Mode:

Arquitetura do Painel Administrativo com CSS Grid e Dark Mode

📁 Estrutura de Pastas Padronizada

A base de código segue o padrão canônico com nomenclatura anti-colisão:

catalogo_projetos/web_02_dashboard_css_grid/
├── assets/                   # Galeria de diagramas visuais e mapeamento de Grid
│   └── arquitetura_dashboard_css_grid.svg
├── web-vanilla/              # Aplicação web estática com CSS Grid nativo
│   ├── index.html            # Estrutura com áreas de grid e toggle de tema
│   └── style.css             # Regras de Grid Areas, variáveis de tema e cards
└── index.md                  # Especificação executiva e Padrão Ouro

🧑‍💼 Engenharia de Software & Requisitos

1. Histórias de Usuário (US)

ID Persona História de Usuário Prioridade
US-01 Administrador de Sistema Desejo visualizar painéis de métricas e gráficos em um layout com barra lateral fixa e conteúdo expansivo. Alta
US-02 Operador Noturno Desejo alternar instantaneamente para o Dark Mode para reduzir o cansaço visual em ambientes com pouca luz. Alta
US-03 Usuário de Tablet Desejo que os cartões de métricas se reorganizem automaticamente conforme a largura da tela sem quebras. Média
US-04 Auditor de Acessibilidade Desejo que os elementos gráficos e tipográficos preservem contraste legível em ambos os modos visuais. Alta
US-05 Desenvolvedor Web Desejo customizar facilmente cores e espaçamentos através de variáveis CSS centralizadas no :root. Média

2. Requisitos Funcionais (RF)

ID Nome Descrição
RF-01 Layout com grid-template-areas Definir estrutura macro da interface via áreas nomeadas (sidebar, topbar, content, footer).
RF-02 Grid Auto-Ajustável de KPIs Implementar área de cards com grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)).
RF-03 Alternância de Tema Dinâmica Modificar o atributo data-theme="dark|light" no elemento raiz via script leve, alternando o tema.
RF-04 Persistência de Preferência Gravar o tema selecionado no localStorage do navegador para recarga automática em novas sessões.
RF-05 Menu Lateral Retrátil Permitir colapsar o menu lateral em telas menores através de media queries inteligentes.

3. Requisitos Não-Funcionais (RNF)

ID Nome Descrição
RNF-01 Transição Suave de Cores Transição de 200ms (transition: background 0.2s, color 0.2s) para evitar piscadas visuais bruscas.
RNF-02 Zero Frameworks Externos Construção em HTML5, CSS3 e JavaScript puro sem bibliotecas terceiras para máxima leveza.
RNF-03 Taxa de Quadros a 60 FPS Animações e redimensionamento sem causar layout thrashing ou recalculos caros do DOM.
RNF-04 Contraste de Cores WCAG AA Conformidade com taxa de contraste mínima de 4.5:1 para modo diurno e noturno.
RNF-05 Peso do Pacote Inferior a 25 KB Código HTML, CSS e JS combinados mantidos abaixo do limiar crítico de payload.

4. Diagrama de Casos de Uso

flowchart TD
    subgraph Atores
        ADM["Administrador / Operador"]
        SIS["Sistema / Navegador"]
    end

    subgraph "Painel Administrativo (web-vanilla)"
        UC1["Visualizar Indicadores no Grid de Áreas"]
        UC2["Alternar Tema (Light / Dark Mode)"]
        UC3["Persistir Preferência em localStorage"]
        UC4["Redimensionar Viewport Responsivamente"]
        UC5["Navegar por Itens da Sidebar"]
    end

    ADM --> UC1
    ADM --> UC2
    ADM --> UC5
    UC2 --> UC3
    SIS --> UC4

5. Matriz de Rastreabilidade (RTM)

Requisito Funcional Histórias Atendidas Requisitos Não-Funcionais Status de Cobertura
RF-01 (Grid Areas) US-01, US-03 RNF-02, RNF-03 Implementado ✅
RF-02 (Grid Auto-Ajustável) US-01, US-03 RNF-03, RNF-05 Implementado ✅
RF-03 (Alternância de Tema) US-02, US-05 RNF-01, RNF-04 Implementado ✅
RF-04 (Persistência) US-02 RNF-02, RNF-05 Implementado ✅
RF-05 (Sidebar Responsiva) US-03 RNF-03, RNF-04 Implementado ✅

🛠️ Código-Fonte Implementado

web-vanilla/index.html

<!DOCTYPE html>
<html lang="pt-BR" data-theme="dark">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Enterprise Analytics Dashboard — CSS Grid & Dark Mode</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="layout-grid">
    <!-- Barra Lateral -->
    <aside class="sidebar">
      <div class="brand">
        <span class="brand-icon">⚡</span>
        <span class="brand-name">PortalAdmin</span>
      </div>
      <nav class="sidebar-nav">
        <a href="#" class="nav-item active">📊 Visão Geral</a>
        <a href="#" class="nav-item">📈 Vendas</a>
        <a href="#" class="nav-item">👥 Usuários</a>
        <a href="#" class="nav-item">⚙️ Configurações</a>
      </nav>
    </aside>

    <!-- Cabeçalho Superior -->
    <header class="topbar">
      <h1 class="page-title">Painel de Operações Globais</h1>
      <div class="topbar-actions">
        <button id="themeToggle" class="btn-theme" title="Alternar Modo Claro / Escuro">
          🌓 Alternar Tema
        </button>
      </div>
    </header>

    <!-- Conteúdo Principal -->
    <main class="content">
      <div class="metrics-grid">
        <article class="metric-card">
          <p class="metric-label">Faturamento Hoje</p>
          <p class="metric-value">R$ 54.280,00</p>
          <span class="metric-trend trend-up">↑ +18.4% vs ontem</span>
        </article>

        <article class="metric-card">
          <p class="metric-label">Novos Cadastros</p>
          <p class="metric-value">+ 1.840</p>
          <span class="metric-trend trend-up">↑ +9.1% vs ontem</span>
        </article>

        <article class="metric-card">
          <p class="metric-label">Latência do Cluster</p>
          <p class="metric-value">12 ms</p>
          <span class="metric-trend trend-stable">● SLA Operacional</span>
        </article>

        <article class="metric-card">
          <p class="metric-label">Taxa de Conversão</p>
          <p class="metric-value">4.62%</p>
          <span class="metric-trend trend-up">↑ +0.8% benchmark</span>
        </article>
      </div>

      <section class="panel-section">
        <div class="panel-card">
          <h2>Telemetria de Tráfego de Microsserviços</h2>
          <p class="panel-desc">Visualização simulada de consumo de recursos computacionais por região:</p>
          <div class="server-status-list">
            <div class="status-row"><span>US-East (N. Virginia)</span><span class="badge-status ok">99.99%</span></div>
            <div class="status-row"><span>EU-West (Frankfurt)</span><span class="badge-status ok">100.0%</span></div>
            <div class="status-row"><span>SA-East (São Paulo)</span><span class="badge-status ok">99.95%</span></div>
          </div>
        </div>
      </section>
    </main>

    <!-- Rodapé -->
    <footer class="footer">
      <p>&copy; 2026 PortalAdmin — CSS Grid Architecture · Padrão Ouro Educacional</p>
    </footer>
  </div>

  <script>
    const themeBtn = document.getElementById('themeToggle');
    const root = document.documentElement;

    const savedTheme = localStorage.getItem('theme') || 'dark';
    root.setAttribute('data-theme', savedTheme);

    themeBtn.addEventListener('click', () => {
      const currentTheme = root.getAttribute('data-theme');
      const nextTheme = currentTheme === 'dark' ? 'light' : 'dark';
      root.setAttribute('data-theme', nextTheme);
      localStorage.setItem('theme', nextTheme);
    });
  </script>
</body>
</html>

web-vanilla/style.css

:root {
  --bg-page: #f8fafc;
  --bg-surface: #ffffff;
  --bg-sidebar: #0f172a;
  --text-main: #0f172a;
  --text-muted: #64748b;
  --text-sidebar: #e2e8f0;
  --border-color: #e2e8f0;
  --primary: #3b82f6;
  --primary-hover: #2563eb;
  --trend-up: #10b981;
}

[data-theme="dark"] {
  --bg-page: #090d16;
  --bg-surface: #131b2e;
  --bg-sidebar: #0b1120;
  --text-main: #f1f5f9;
  --text-muted: #94a3b8;
  --text-sidebar: #cbd5e1;
  --border-color: #1e293b;
  --primary: #60a5fa;
  --primary-hover: #3b82f6;
  --trend-up: #34d399;
}

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background-color: var(--bg-page);
  color: var(--text-main);
  transition: background-color 0.2s ease, color 0.2s ease;
}

.layout-grid {
  display: grid;
  grid-template-areas:
    "sidebar topbar"
    "sidebar content"
    "sidebar footer";
  grid-template-columns: 240px 1fr;
  grid-template-rows: auto 1fr auto;
  min-height: 100vh;
}

.sidebar {
  grid-area: sidebar;
  background-color: var(--bg-sidebar);
  color: var(--text-sidebar);
  padding: 1.5rem;
  border-right: 1px solid var(--border-color);
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  font-size: 1.2rem;
  font-weight: 800;
  margin-bottom: 2rem;
}

.sidebar-nav {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.nav-item {
  color: var(--text-sidebar);
  text-decoration: none;
  padding: 0.75rem 1rem;
  border-radius: 6px;
  font-size: 0.95rem;
  font-weight: 500;
  transition: background 0.15s ease;
}

.nav-item:hover, .nav-item.active {
  background-color: rgba(255, 255, 255, 0.08);
}

.topbar {
  grid-area: topbar;
  background-color: var(--bg-surface);
  border-bottom: 1px solid var(--border-color);
  padding: 1rem 2rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.page-title {
  font-size: 1.25rem;
  font-weight: 700;
}

.btn-theme {
  background: var(--bg-page);
  border: 1px solid var(--border-color);
  color: var(--text-main);
  padding: 0.5rem 1rem;
  border-radius: 6px;
  cursor: pointer;
  font-weight: 600;
  font-size: 0.85rem;
  transition: all 0.2s ease;
}

.btn-theme:hover {
  border-color: var(--primary);
}

.content {
  grid-area: content;
  padding: 2rem;
}

.metrics-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 1.5rem;
  margin-bottom: 2rem;
}

.metric-card {
  background-color: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: 10px;
  padding: 1.5rem;
}

.metric-label {
  font-size: 0.875rem;
  color: var(--text-muted);
}

.metric-value {
  font-size: 1.75rem;
  font-weight: 800;
  margin: 0.5rem 0;
}

.metric-trend {
  font-size: 0.8rem;
  font-weight: 600;
}

.trend-up {
  color: var(--trend-up);
}

.trend-stable {
  color: var(--primary);
}

.panel-section {
  display: grid;
  gap: 1.5rem;
}

.panel-card {
  background-color: var(--bg-surface);
  border: 1px solid var(--border-color);
  border-radius: 10px;
  padding: 1.5rem;
}

.panel-card h2 {
  font-size: 1.15rem;
  margin-bottom: 0.5rem;
}

.panel-desc {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin-bottom: 1.25rem;
}

.server-status-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.status-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem;
  background-color: var(--bg-page);
  border-radius: 6px;
  font-size: 0.9rem;
}

.badge-status {
  padding: 0.25rem 0.6rem;
  border-radius: 12px;
  font-size: 0.75rem;
  font-weight: 700;
}

.badge-status.ok {
  background-color: rgba(16, 185, 129, 0.15);
  color: var(--trend-up);
}

.footer {
  grid-area: footer;
  background-color: var(--bg-surface);
  border-top: 1px solid var(--border-color);
  padding: 1rem 2rem;
  text-align: center;
  font-size: 0.8rem;
  color: var(--text-muted);
}

@media (max-width: 768px) {
  .layout-grid {
    grid-template-areas:
      "topbar"
      "content"
      "footer";
    grid-template-columns: 1fr;
  }
  .sidebar {
    display: none;
  }
}

🚀 Roteiro de Execução

1. Iniciar Servidor Local ou Abrir no Navegador

cd catalogo_projetos/web_02_dashboard_css_grid/web-vanilla
python -m http.server 8081

2. Validar Alternância de Tema

Abra http://localhost:8081. Clique no botão 🌓 Alternar Tema para alternar entre os modos claro e escuro. Recarregue a página com F5 para comprovar a persistência via localStorage.


🎯 Matriz de Checkpoints de Entrega

Checkpoint Critério de Aceite Validação Status
CP-01 Estrutura macro com grid-template-areas nomeadas Layout bidimensional renderizado sem quebras PASS ✅
CP-02 Grid auto-ajustável de cartões de métricas (repeat(auto-fit)) Redimensionamento responsivo fluido PASS ✅
CP-03 Alternância dinâmica de tema via CSS Custom Properties Transição instantânea das paletas de cores PASS ✅
CP-04 Persistência de tema no localStorage do navegador Estado mantido após recarregamento de página PASS ✅
CP-05 Media queries colapsando a sidebar em dispositivos móveis Layout 100% responsivo abaixo de 768px PASS ✅