Instruction file imported from andviana23/forjasas (
.github/instructions/frontend-ui.instructions.md). Copyright stays with the author.
Frontend UI & Design System — FORJA
Stack: Next.js 16, Tailwind CSS v4, shadcn/ui, OKLCH Versão do Sistema: V5 — Sapphire Luxe + Champagne Gold (fev/2026) Referências visuais: Linear, Stripe Dashboard, Vercel, Notion
0. Filosofia Visual
O FORJA é uma ferramenta de gestão enterprise-grade para barbearias premium. Sua linguagem visual deve comunicar:
- Confiança — hierarquia clara, espaçamento respira, sem ruído
- Sofisticação — identidade de cor distinta, tipografia intencional
- Precisão — dados densos mas legíveis, contraste correto sempre
- Consistência — o mesmo sistema de tokens, do sidebar ao gráfico
O sistema NÃO deve parecer:
- Template genérico shadcn/ui com cores padrão
- Dashboard "tech verde" genérico (a era do verde acabou)
- UI neon ou excessivamente vibrante
- Mistura de estilos entre telas
1. Identidade de Cor — OKLCH Sapphire Luxe
Espaço de Cor
Todos os tokens usam OKLCH (perceptualmente uniforme). A diferença de L é visualmente consistente em qualquer hue.
oklch(L C H)
L = Luminosidade 0.0 (preto) → 1.0 (branco)
C = Croma 0.0 (acinzentado) → ~0.30 (saturado)
H = Matiz 25° (vermelho) → 50° (laranja) → 78° (dourado)
→ 145° (esmeralda) → 200° (teal) → 245° (azure)
→ 264° (safira) → 310° (violeta)
Matizes Canônicos do FORJA
| Papel | Hue | Cor | Token |
|---|---|---|---|
| Primary (Brand) | H=264 | Safira Royal Blue | --primary |
| Accent Premium | H=78 | Dourado Champagne | --accent-gold |
| Success | H=145 | Esmeralda | --success |
| Insight | H=200 | Teal analítico | --insight |
| Warning | H=50 | Laranja-âmbar | --warning |
| Critical | H=25 | Vermelho-carmim | --critical |
| Status Pending | H=245 | Azure elétrico | --status-pending |
Regra inviolável: nenhum token usa o mesmo hue que outro token de papel diferente. Isso garante distinção semântica imediata.
2. Tokens de Cor — Referência Completa
Categorias de tokens (globals.css)
Base → --background, --foreground
Surfaces → --surface, --surface-2, --surface-3
Borders → --border, --border-strong, --input
Primary → --primary, --primary-foreground, --primary-soft
Accent → --accent, --accent-foreground, --accent-gold, --accent-gold-foreground
Focus → --ring, --focus-ring
Secondary → --secondary, --secondary-foreground
Muted → --muted, --muted-foreground
Card → --card, --card-foreground
Popover → --popover, --popover-foreground
Semantic → --critical/--destructive, --warning, --success, --insight
(cada um com -foreground)
Status → --status-{pending|confirmed|in-progress|alert|completed}
(cada um com -bg e -border)
Charts → --chart-{1-5}, --chart-axis, --chart-grid,
--chart-tooltip-bg, --chart-tooltip-fg
Sidebar → --sidebar-{foreground|primary|primary-foreground|
accent|accent-foreground|border|ring}
Tabela Rápida — Light vs Dark
| Token | Light | Dark | Role |
|---|---|---|---|
--background |
oklch(1.0 0 0) branco |
oklch(0.10 0.022 264) abissal |
Canvas da página |
--foreground |
oklch(0.14 0.020 264) |
oklch(0.97 0.005 260) |
Texto principal |
--muted-foreground |
oklch(0.44 0.015 264) |
oklch(0.62 0.022 264) |
Texto secundário |
--card |
oklch(1.0 0 0) |
oklch(0.15 0.022 264) |
Fundo de cards |
--border |
oklch(0.872 0.008 264) |
oklch(0.28 0.030 264) |
Bordas gerais |
--primary |
oklch(0.44 0.19 264) safira |
oklch(0.70 0.22 264) vívido |
CTAs, foco |
--accent |
oklch(0.962 0.018 78) ouro tênue |
oklch(0.20 0.042 78) ouro profundo |
Hover backgrounds |
--accent-gold |
oklch(0.63 0.13 78) |
oklch(0.82 0.14 80) |
Highlights premium |
--success |
oklch(0.46 0.19 145) |
oklch(0.65 0.22 145) |
Confirmação, ok |
--destructive |
oklch(0.47 0.22 25) |
oklch(0.68 0.26 25) |
Erros, exclusão |
--warning |
oklch(0.70 0.17 50) |
oklch(0.78 0.20 50) |
Alertas |
--sidebar |
oklch(0.16 0.025 264) |
oklch(0.08 0.018 264) |
Sidebar escuro |
Hierarquia de Superfícies (Dark Mode)
L=0.08 ████ Sidebar (mais escuro de tudo)
L=0.10 ████ Background de página
L=0.14 ████ Surface
L=0.15 ████ Card
L=0.18 ████ Surface-2
L=0.20 ████ Popover / Secondary / Muted
L=0.22 ████ Surface-3
Cada nível tem ~0.03-0.05 de diferença em L → perceptível ao olho, sem precisar de sombra.
3. Tipografia
// Headings — sempre tracking-tight + semibold
<h1 className="text-2xl font-semibold tracking-tight">Título</h1>
<h2 className="text-xl font-semibold tracking-tight text-balance">Subtítulo longo</h2>
// Body — text-sm para dashboards, text-base para formulários
<p className="text-sm text-foreground leading-relaxed">Descrição</p>
// Labels / helpers
<span className="text-xs text-muted-foreground leading-none">Dica</span>
// Valores numéricos / tabelas
<span className="text-sm font-mono tabular-nums">R$ 1.234,56</span>
// Section labels no sidebar (uppercase tracking)
<span className="text-[0.6rem] font-semibold uppercase tracking-[0.14em] text-sidebar-foreground/35">
OPERAÇÃO
</span>
Regras:
h1..h3: sempretracking-tight- Multiline: sempre
text-balance - Texto secundário:
text-muted-foreground(nuncatext-gray-*) - Dados financeiros:
font-mono tabular-nums
4. Espaçamento
Escala preferencial (não usar gap-4 cegamente):
| Uso | Classe |
|---|---|
| Entre ícone e texto | gap-1.5 |
| Elementos próximos | gap-2 ou gap-3 |
| Grupos de campo | gap-4 |
| Seções internas | gap-6 |
| Seções de página | gap-8 ou gap-12 |
| Padding de card | p-4 ou p-6 |
5. Componentes — Regras de Uso
Button
// CTA principal
<Button>Salvar</Button>
// → bg-primary text-primary-foreground hover:bg-primary/90 active:scale-[0.98]
// Ação secundária
<Button variant="secondary">Cancelar</Button>
// Ação destrutiva
<Button variant="destructive">Excluir</Button>
// Ghost (hover dourado)
<Button variant="ghost">Ver detalhes</Button>
// → hover:bg-accent hover:text-accent-foreground (dourado tênue)
// Outline
<Button variant="outline">Filtrar</Button>
// Tamanhos
<Button size="sm"> // h-8 — ações em tabelas
<Button size="default"> // h-9 — padrão
<Button size="lg"> // h-10 — CTAs de destaque
<Button size="icon"> // size-9 — ações de ícone
Evitar:
Buttonsemactive:scale-*→ o componente já temactive:scale-[0.98]na base- Variante custom com
bg-blue-*hardcoded
Input / Select / Textarea
<Input className="h-9" />
// → border-input focus-visible:ring-ring/50 focus-visible:ring-[3px]
// Anel de foco = safira (--ring = --primary)
Evitar:
className="border border-gray-300"direto no input- Foco sem ring
Card
// Card padrão (branco/elevado)
<Card>
<CardHeader>
<CardTitle className="tracking-tight">Título</CardTitle>
<CardDescription>Descrição secundária</CardDescription>
</CardHeader>
<CardContent>...</CardContent>
</Card>
// Card de KPI (mais denso)
<div className="bg-card border border-border rounded-lg p-4">...</div>
// Fundo neutro para seção
<div className="bg-muted rounded-lg p-4">...</div>
Evitar:
bg-white dark:bg-zinc-900(usebg-card)border border-gray-200(useborder border-border)
Badge
// Genérico — usa primary
<Badge>Principal</Badge>
// Status de agendamento — use StatusBadge
<StatusBadge variant="confirmed">Confirmado</StatusBadge>
<StatusBadge variant="pending">Pendente</StatusBadge>
<StatusBadge variant="in-progress">Em andamento</StatusBadge>
<StatusBadge variant="alert">Atrasado</StatusBadge>
<StatusBadge variant="completed">Concluído</StatusBadge>
// Highlight premium (dourado)
<span className="text-accent-gold font-semibold text-xs">Premium</span>
// ou com fundo:
<span className="bg-accent text-accent-foreground rounded-md px-2 py-0.5 text-xs">Pro</span>
Evitar:
<Badge className="bg-green-500">para status- Misturar semantic tokens em badges de branding
Table
<Table>
<TableHeader>
<TableRow>
<TableHead>Coluna</TableHead> {/* text-foreground h-10 */}
</TableRow>
</TableHeader>
<TableBody>
<TableRow> {/* hover:bg-muted/50 */}
<TableCell>Dado</TableCell>
</TableRow>
</TableBody>
<TableFooter> {/* bg-muted/50 */}
...
</TableFooter>
</Table>
Tabs
<Tabs defaultValue="geral">
<TabsList> {/* bg-muted, h-9 */}
<TabsTrigger value="geral">Geral</TabsTrigger>
<TabsTrigger value="avancado">Avançado</TabsTrigger>
</TabsList>
<TabsContent value="geral">...</TabsContent>
</Tabs>
Dialog / Popover
<Dialog>
<DialogTrigger asChild>
<Button>Abrir</Button>
</DialogTrigger>
<DialogContent> {/* bg-card text-card-foreground shadow-lg */}
<DialogHeader>
<DialogTitle className="tracking-tight">Título</DialogTitle>
<DialogDescription>Descrição opcional</DialogDescription>
</DialogHeader>
{/* conteúdo */}
<DialogFooter>
<Button variant="outline">Cancelar</Button>
<Button>Confirmar</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Overlay: bg-black/50 — aceitável como exceção semântica de opacidade.
Sidebar
O sidebar usa tokens sidebar-* exclusivos (sempre escuro em ambos os modos):
// Tokens disponíveis para o sidebar
bg-sidebar // fundo escuro
text-sidebar-foreground // texto principal
bg-sidebar-primary // item ativo (safira médio)
bg-sidebar-accent // hover
border-sidebar-border // bordas internas
ring-sidebar-ring // foco dentro do sidebar
Evitar:
- Usar
bg-gray-*oubg-zinc-*no sidebar - Usar tokens de
background/carddentro do sidebar
6. Cores Semânticas — Quando Usar
| Token | Usar quando | Não usar para |
|---|---|---|
success / status-confirmed |
Operação concluída, status OK | Branding / destaque de features |
warning / status-alert |
Atenção necessária, prazo | Informação neutra |
critical / destructive |
Erro, exclusão, perigo real | Simples alertas informativos |
insight |
Informação analítica, dica | Substituir warning/critical |
accent-gold |
Badge premium, plano Pro, destaque de valor | Ações operacionais |
primary |
CTAs, links de ação, foco | Status de workflow |
7. Charts (Recharts)
Paleta de Gráficos
| Token | Hue | Uso típico |
|---|---|---|
--chart-1 |
H=264 Safira | Receita principal, barra primária |
--chart-2 |
H=78 Dourado | Metas, projeções, saldo projetado |
--chart-3 |
H=145 Esmeralda | Entradas, positivo |
--chart-4 |
H=50 Laranja | Saídas, custos, negativo |
--chart-5 |
H=310 Violeta | Quinto eixo, dados adicionais |
--chart-axis |
— | Labels de eixo X/Y |
--chart-grid |
— | Linhas de grade |
--chart-tooltip-bg/fg |
— | Fundo e texto de tooltip |
Uso em Recharts (nunca hex direto)
// Constantes no topo do componente de gráfico
const CHART_PRIMARY = "var(--chart-1)";
const CHART_INCOME = "var(--chart-3)";
const CHART_EXPENSE = "var(--destructive)";
// Uso nos props Recharts (SVG aceita CSS vars)
<Bar fill={CHART_PRIMARY} />
<Line stroke={CHART_INCOME} />
<XAxis tick={{ fill: "var(--chart-axis)", fontSize: 10 }} />
// Opacidade: usar fillOpacity/strokeOpacity como props numéricos
<Bar fill={CHART_INCOME} fillOpacity={0.32} />
// Tooltip customizado: style={{ color: "var(--chart-3)" }}
Nunca fazer:
// ❌ hex hardcoded
<Bar fill="#059669" />
<Line stroke="#2563eb" />
<XAxis tick={{ fill: "#94a3b8" }} />
8. Motion (Tailwind + Framer Motion)
// Hover base
className="transition-all duration-200 ease-in-out"
// Micro-interação em botões
className="active:scale-[0.98]" // links de nav
className="active:scale-95" // botões de ação
// Entrada de componente (shadcn/ui animate-in)
className="animate-in fade-in zoom-in-95 duration-200"
// Lista (Framer Motion)
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{ height: { duration: 0.25, ease: [0.25, 0.46, 0.45, 0.94] } }}
/>
// Dashboard stagger (CSS classes utilitárias)
<div className="kpi-card-stagger"> // usa @keyframes fadeUp com delay
9. Acessibilidade
focus-visible:ring-2oufocus-visible:ring-[3px] focus-visible:ring-ring/50em TODOS os elementos interativos- Contraste mínimo 4.5:1 para texto normal
- Contraste mínimo 3:1 para bordas de foco e elementos não-textuais
- Semantic HTML:
<section>,<nav>,<article>,<main>,<button type="button"> aria-current="page"em links de navegação ativaaria-labelem botões sem texto visível (ícones)aria-expandedem accordions e dropdowns
10. Anti-Padrões (NUNCA FAZER)
// ❌ Hex hardcoded em componentes
className="text-[#2563eb]"
style={{ color: "#059669" }}
fill="#e11d48"
// ❌ Tailwind utility colors genéricos em produto
className="text-gray-500 bg-zinc-100 border-slate-200"
// ❌ Usar success igual ao primary (colisão semântica)
// O sistema antigo usava H=145 para os dois — RESOLVIDO na V5
// ❌ Sombra como substituto de hierarquia
className="shadow-lg shadow-xl" // prefira --surface tokens e border
// ❌ gap-4 por padrão sem pensar
className="flex flex-col gap-4" // use gap-3 ou gap-6 com intenção
// ❌ text-base em dashboards
// Use text-sm — dados densos precisam de legibilidade em tamanho menor
// ❌ heading sem tracking-tight
<h1 className="text-2xl font-bold"> // ❌
<h1 className="text-2xl font-semibold tracking-tight"> // ✅
11. Checklist de PR — Design System
Antes de submeter PR com mudanças de UI, verificar:
- Sem hex hardcoded? (
#HEX,rgb(...),rgba(...)) - Sem classes Tailwind de cor direta? (
text-gray-*,bg-zinc-*,bg-blue-*) - Tokens corretos para o contexto? (semantic vs branding vs status)
- Dark mode testado? (toggle de tema no browser)
- Sidebar com tokens
sidebar-*? (não misturar com tokens de página) - Hover/focus visíveis? (
focus-visible:ring-*presente) - Typography com
tracking-tightem headings? - Charts sem hex? (usar constantes
var(--chart-X)) -
aria-labelem ícones? (sr-onlyouaria-label) -
active:scale-*em elementos clicáveis? - Texto secundário com
text-muted-foreground?
12. Guia de Migração — V4 → V5
Mudanças Principais
| Antes (V4) | Depois (V5) | Motivo |
|---|---|---|
--primary: oklch(0.42 0.16 145) verde |
--primary: oklch(0.44 0.19 264) safira |
Identidade enterprise |
--primary = --success no dark |
Distintos (H=264 vs H=145) | Colisão semântica corrigida |
--accent = verde tênue (H=145) |
--accent = dourado tênue (H=78) |
Hover com personalidade |
Sem --accent-gold |
--accent-gold: oklch(0.63 0.13 78) |
Highlights premium |
Sem --primary-soft |
--primary-soft: oklch(0.960 0.022 264) |
Fundo de pills/avatars |
| Sidebar H=250 (roxo-acinzentado) | Sidebar H=264 (safira slate) | Coerência com primary |
| chart-1 = verde (H=145) | chart-1 = safira (H=264) | Alinha com branding |
| chart-2 = azul (H=240) | chart-2 = dourado (H=78) | Destaque de metas/projeção |
--surface/2/3 não mapeados |
Mapeados no @theme inline |
Disponíveis como bg-surface |
--border-strong não mapeado |
Mapeado | border-border-strong disponível |
Classes que mudaram de comportamento
// hover:bg-accent antes = hover verde tênue
// hover:bg-accent agora = hover dourado tênue (premium)
// Nenhuma mudança de código necessária — o token mudou, o uso permanece
// bg-primary antes = verde
// bg-primary agora = safira
// Nenhuma mudança de código necessária
// text-success = esmeralda (antes podia parecer igual ao primary verde)
// text-success agora = esmeralda, claramente distinto do primary safira
Novos utilitários disponíveis
bg-surface // --surface (1° nível acima do background)
bg-surface-2 // --surface-2
bg-surface-3 // --surface-3
border-border-strong // --border-strong
bg-primary-soft // --primary-soft (suave azul para avatars/pills)
bg-accent-gold // --accent-gold (dourado champagne)
text-accent-gold // idem
text-accent-gold-foreground // texto sobre fundo dourado