📚 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.
O diagrama abaixo detalha o mapeamento de áreas estruturais (header, sidebar, main, footer) e a cascata de tokens para suporte ao Dark Mode:
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
| 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 |
| 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. |
| 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. |
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
| 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 ✅ |
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>© 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;
}
}
cd catalogo_projetos/web_02_dashboard_css_grid/web-vanilla
python -m http.server 8081
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.
| 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 ✅ |