25 KiB
Padrões de Estilo - Course Platform
Este documento define os padrões de estilo unificados para todo o sistema, garantindo consistência visual e facilidade de manutenção.
Índice
- Princípios Fundamentais
- Cores e Tema
- Tipografia
- Espaçamento
- Badges e Labels
- Componentes
- Transições e Animações
- Regras CSS Globais
Princípios Fundamentais
- Usar CSS Variables para cores, backgrounds e bordas (definidas em
:roote.dark) - Padronizar em
neutral-*em vez degray-*para consistência - Usar
blue-600como cor primária para botões de ação principal - Manter border-radius de 8px (
rounded-lgourounded-xl) - Usar sombras sutis:
shadow-smpadrão,shadow-mdno hover - Evitar regras CSS globais com
!importantque sobrescrevem classes Tailwind
Cores e Tema
CSS Variables (Definidas em globals.css)
/* Tema Claro (:root) */
--background: #ffffff;
--foreground: #09090b;
--card: #ffffff;
--card-foreground: #09090b;
--primary: #18181b;
--primary-foreground: #fafafa;
--secondary: #f4f4f5;
--secondary-foreground: #18181b;
--muted: #f4f4f5;
--muted-foreground: #71717a;
--accent: #f4f4f5;
--accent-foreground: #18181b;
--destructive: #ef4444;
--destructive-foreground: #fafafa;
--border: #e4e4e7;
--input: #e4e4e7;
--ring: #18181b;
--radius: 0.5rem;
/* Tema Escuro (.dark) */
--background: #09090b;
--foreground: #fafafa;
--card: #09090b;
--card-foreground: #fafafa;
--primary: #fafafa;
--primary-foreground: #18181b;
--secondary: #27272a;
--secondary-foreground: #fafafa;
--muted: #27272a;
--muted-foreground: #a1a1aa;
--accent: #27272a;
--accent-foreground: #fafafa;
--destructive: #7f1d1d;
--destructive-foreground: #fafafa;
--border: #27272a;
--input: #27272a;
--ring: #d4d4d8;
Cores Semânticas (Tailwind)
| Propósito | Light Mode | Dark Mode |
|---|---|---|
| Primary (Ação principal) | blue-600 |
blue-500 |
| Primary Hover | blue-700 |
blue-600 |
| Success | green-600 |
green-500 |
| Success Hover | green-700 |
green-600 |
| Warning | yellow-500 |
yellow-400 |
| Warning Hover | yellow-600 |
yellow-500 |
| Error/Destructive | red-600 |
red-500 |
| Error Hover | red-700 |
red-600 |
| Info | blue-600 |
blue-500 |
Cores Neutras (Padrão)
Use sempre neutral-* em vez de gray-* para consistência:
| Escala | Light Mode | Dark Mode |
|---|---|---|
| 50 | #fafafa |
#18181b |
| 100 | #f5f5f5 |
#27272a |
| 200 | #e5e5e5 |
#3f3f46 |
| 300 | #d4d4d4 |
#52525b |
| 400 | #a3a3a3 |
#71717a |
| 500 | #737373 |
#a1a1aa |
| 600 | #525252 |
#d4d4d8 |
| 700 | #404040 |
#e4e4e7 |
| 800 | #262626 |
#f4f4f5 |
| 900 | #171717 |
#fafafa |
| 950 | #0a0a0a |
#fafafa |
Tipografia
Fonte
- Fonte Principal: Geist Sans (Google Fonts)
- Fonte Monospace: Geist Mono (Google Fonts)
Tamanhos e Pesos
| Elemento | Classe Tailwind | Uso |
|---|---|---|
| Título Principal | text-2xl md:text-3xl font-semibold |
Títulos de página |
| Título de Seção | text-lg font-semibold |
Títulos de seção |
| Subtítulo | text-base font-medium |
Subtítulos |
| Texto Normal | text-sm |
Texto de conteúdo |
| Texto Secundário | text-sm text-muted-foreground |
Texto de apoio |
| Label | text-sm font-medium |
Labels de formulário |
| Caption/Small | text-xs |
Textos pequenos |
Exemplos
// Título de Página
<h1 className="text-2xl md:text-3xl font-semibold text-foreground">
Título da Página
</h1>
// Título de Seção
<h2 className="text-lg font-semibold text-foreground">
Título da Seção
</h2>
// Subtítulo
<p className="text-sm text-muted-foreground">
Descrição ou subtítulo
</p>
// Label
<label className="text-sm font-medium leading-none">
Nome do Campo
</label>
Espaçamento
Padrões de Padding
| Elemento | Padding/Margin |
|---|---|
| Card padding | p-5 |
| Card padding (compacto) | p-4 |
| Button padding | px-4 py-2 |
| Input padding | px-3 py-2 |
| Table cell padding | px-6 py-4 |
| Section gap | gap-3 ou gap-4 |
| Vertical spacing entre elementos | space-y-4 |
| Horizontal spacing entre elementos | space-x-3 |
Exemplos
// Card
<div className="border rounded-xl p-5 shadow-sm bg-card">
{/* conteúdo */}
</div>
// Formulário com espaçamento vertical
<form className="space-y-4">
{/* campos */}
</form>
// Botões com espaçamento horizontal
<div className="flex gap-3">
<button>Cancelar</button>
<button>Salvar</button>
</div>
Badges e Labels
Badges são pequenos labels coloridos usados para indicar status, categorias ou outros metadados. Devem ser usados consistentemente em toda a aplicação.
Estrutura Base
<span className="text-xs px-2 py-1 rounded-full border [badge-classes]">
Label Text
</span>
Padrões de Cores
Success/Green (Disponível, Pago, Ativo, Presente)
Tema Claro: bg-emerald-100 text-emerald-700 border-emerald-200
Tema Escuro: dark:bg-emerald-900/20 dark:text-emerald-300 dark:border-emerald-800
<span className="text-xs px-2 py-1 rounded-full border bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/20 dark:text-emerald-300 dark:border-emerald-800">
Disponível
</span>
Casos de Uso:
- "Disponível" (Available)
- "Pago" (Paid)
- "Ativa" (Active)
- "Presente" (Present)
- Status "Verified"
Info/Blue (Em breve, Aguardando Verificação, Justificado)
Tema Claro: bg-blue-100 text-blue-700 border-blue-200
Tema Escuro: dark:bg-blue-900/20 dark:text-blue-300 dark:border-blue-800
<span className="text-xs px-2 py-1 rounded-full border bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900/20 dark:text-blue-300 dark:border-blue-800">
Em breve
</span>
Casos de Uso:
- "Em breve" (Upcoming)
- "Aguardando Verificação" (Pending Verification)
- "Justificado" (Excused)
- Templates "Public"
Danger/Red (Encerrada, Rejeitado, Ausente)
Tema Claro: bg-red-100 text-red-700 border-red-200
Tema Escuro: dark:bg-red-900/20 dark:text-red-300 dark:border-red-800
<span className="text-xs px-2 py-1 rounded-full border bg-red-100 text-red-700 border-red-200 dark:bg-red-900/20 dark:text-red-300 dark:border-red-800">
Encerrada
</span>
Casos de Uso:
- "Encerrada" (Expired)
- "Rejeitado" (Rejected)
- "Ausente" (Absent)
- Status "Rejected"
Warning/Amber (Aguardando Pagamento, Atrasado, Arquivada)
Tema Claro: bg-amber-100 text-amber-700 border-amber-200
Tema Escuro: dark:bg-amber-900/20 dark:text-amber-300 dark:border-amber-800
<span className="text-xs px-2 py-1 rounded-full border bg-amber-100 text-amber-700 border-amber-200 dark:bg-amber-900/20 dark:text-amber-300 dark:border-amber-800">
Aguardando Pagamento
</span>
Casos de Uso:
- "Aguardando Pagamento" (Pending Payment)
- "Atrasado" (Late)
- "Arquivada" (Archived)
- Status "Pending"
Warning/Yellow (Obrigações)
Tema Claro: bg-yellow-100 text-yellow-800 border-yellow-300
Tema Escuro: dark:bg-yellow-900/50 dark:text-yellow-200 dark:border-yellow-700
<span className="text-xs px-2 py-1 rounded-full border bg-yellow-100 text-yellow-800 border-yellow-300 dark:bg-yellow-900/50 dark:text-yellow-200 dark:border-yellow-700">
Obrigação
</span>
Casos de Uso:
- Obrigações de pagamento
- "Obrigação" (Obligation)
Purple (Leitura)
Tema Claro: bg-purple-100 text-purple-700 border-purple-200
Tema Escuro: dark:bg-purple-900/20 dark:text-purple-300 dark:border-purple-800
<span className="text-xs px-2 py-1 rounded-full border bg-purple-100 text-purple-700 border-purple-200 dark:bg-purple-900/20 dark:text-purple-300 dark:border-purple-800">
Leitura
</span>
Casos de Uso:
- "Leitura" (Reading)
- Categoria "Indigo"
Indigo (Trabalhos)
Tema Claro: bg-indigo-100 text-indigo-700 border-indigo-200
Tema Escuro: dark:bg-indigo-900/20 dark:text-indigo-300 dark:border-indigo-800
<span className="text-xs px-2 py-1 rounded-full border bg-indigo-100 text-indigo-700 border-indigo-200 dark:bg-indigo-900/20 dark:text-indigo-300 dark:border-indigo-800">
Trabalhos
</span>
Casos de Uso:
- "Trabalhos" (Homework)
- Itens relacionados a assignments
Neutral/Gray (Outros, Padrão)
Tema Claro: bg-gray-100 text-gray-700 border-gray-200
Tema Escuro: dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700
<span className="text-xs px-2 py-1 rounded-full border bg-gray-100 text-gray-700 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700">
Outros
</span>
Casos de Uso:
- "Outros" (Other)
- Categorias padrão/desconhecidas
- Status neutro
Badges por Categoria
Categorias de Arquivos
| Categoria | Cor |
|---|---|
| Slides | Blue |
| Exercícios | Green |
| Leitura | Purple |
| Vídeos | Red |
| Áudio | Yellow |
| Trabalhos | Indigo |
| Outros | Gray |
Status de Presença
| Status | Cor |
|---|---|
| Presente | Green |
| Ausente | Red |
| Atrasado | Amber |
| Justificado | Blue |
Status de Pagamento
| Status | Cor |
|---|---|
| Pago | Green |
| Aguardando Verificação | Blue |
| Rejeitado | Red |
| Aguardando Pagamento | Amber |
| Obrigação | Yellow |
Status de Provas
| Status | Cor |
|---|---|
| Disponível | Green |
| Em breve | Blue |
| Encerrada | Red |
Status de Turma
| Status | Cor |
|---|---|
| Ativa | Green |
| Arquivada | Amber |
Exemplo de Implementação
const categoryColors = {
'Slides': 'text-xs px-2 py-1 rounded-full border bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900/20 dark:text-blue-300 dark:border-blue-800',
'Exercícios': 'text-xs px-2 py-1 rounded-full border bg-green-100 text-green-700 border-green-200 dark:bg-green-900/20 dark:text-green-300 dark:border-green-800',
'Leitura': 'text-xs px-2 py-1 rounded-full border bg-purple-100 text-purple-700 border-purple-200 dark:bg-purple-900/20 dark:text-purple-300 dark:border-purple-800',
'Vídeos': 'text-xs px-2 py-1 rounded-full border bg-red-100 text-red-700 border-red-200 dark:bg-red-900/20 dark:text-red-300 dark:border-red-800',
'Áudio': 'text-xs px-2 py-1 rounded-full border bg-yellow-100 text-yellow-700 border-yellow-200 dark:bg-yellow-900/20 dark:text-yellow-300 dark:border-yellow-800',
'Trabalhos': 'text-xs px-2 py-1 rounded-full border bg-indigo-100 text-indigo-700 border-indigo-200 dark:bg-indigo-900/20 dark:text-indigo-300 dark:border-indigo-800',
'Outros': 'text-xs px-2 py-1 rounded-full border bg-gray-100 text-gray-700 border-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-700'
};
<span className={categoryColors[category] || categoryColors['Outros']}>
{category}
</span>
Notas Importantes
- Consistência: Sempre use a string de classes completa incluindo variantes de tema claro e escuro
- Acessibilidade: As combinações de cores fornecem contraste suficiente para ambos os temas
- Opacidade: O tema escuro usa
/20de opacidade para a maioria das cores (exceto yellow que usa/50) - Borda: Sempre inclua borda para melhor separação visual
- Arredondamento: Use
rounded-fullpara badges em formato de pílula - Tamanho: Use
text-xspara badges pequenos,text-smpara badges maiores se necessário
Componentes
Cards
Padrão Unificado:
<div className="border rounded-xl p-5 shadow-sm bg-card dark:bg-card dark:border-border hover:shadow-md hover:border-neutral-300 dark:hover:border-neutral-700 transition-all">
{/* conteúdo */}
</div>
Card com Link:
<Link href="/path" className="block no-underline">
<div className="border rounded-xl p-5 shadow-sm bg-card hover:shadow-md transition-all">
{/* conteúdo */}
</div>
</Link>
Botões
Primary (Ação Principal):
<button
type="submit"
className="inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium bg-blue-600 hover:bg-blue-700 text-white h-10 px-4 py-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
>
Salvar
</button>
Secondary (Ação Secundária):
<button
type="button"
className="inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
>
Cancelar
</button>
Destructive (Ação de Exclusão):
<button
type="button"
className="inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium bg-red-600 hover:bg-red-700 text-white h-10 px-4 py-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-red-500 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
>
Excluir
</button>
Botão com Ícone:
<button className="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium shadow-sm hover:shadow transition-shadow">
<Icon className="w-4 h-4" />
<span>Texto do Botão</span>
</button>
Inputs
Input de Texto:
<input
type="text"
className="flex h-10 w-full rounded-lg border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
placeholder="Digite aqui..."
/>
Select:
<div className="relative">
<select
className="flex h-10 w-full rounded-lg border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 appearance-none"
>
<option value="">Selecione...</option>
{/* opções */}
</select>
<svg className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-5 w-5 text-muted-foreground">
{/* ícone de seta */}
</svg>
</div>
Textarea:
<textarea
className="flex min-h-[80px] w-full rounded-lg border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
placeholder="Digite sua mensagem..."
rows={4}
/>
Tabelas
Tabela Padrão:
<div className="w-full overflow-x-auto rounded-xl border border-border bg-card shadow-sm">
<table className="w-full text-sm text-left">
<thead className="bg-muted text-muted-foreground font-semibold uppercase text-xs">
<tr>
<th className="px-6 py-4 border-b border-border">Coluna 1</th>
<th className="px-6 py-4 border-b border-border">Coluna 2</th>
<th className="px-6 py-4 border-b border-border text-right">Ações</th>
</tr>
</thead>
<tbody className="divide-y divide-border">
<tr className="hover:bg-accent transition-colors">
<td className="px-6 py-4 text-foreground">Valor 1</td>
<td className="px-6 py-4 text-foreground">Valor 2</td>
<td className="px-6 py-4 text-right">
{/* ações */}
</td>
</tr>
</tbody>
</table>
</div>
Badges
Status Badges:
// Success (Pago)
<span className="px-2 py-1 rounded-full text-xs font-medium border bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-200 border-green-300 dark:border-green-700">
Pago
</span>
// Warning (Pendente)
<span className="px-2 py-1 rounded-full text-xs font-medium border bg-yellow-100 dark:bg-yellow-900/50 text-yellow-800 dark:text-yellow-200 border-yellow-300 dark:border-yellow-700">
Pendente
</span>
// Error (Rejeitado)
<span className="px-2 py-1 rounded-full text-xs font-medium border bg-red-100 dark:bg-red-900/50 text-red-800 dark:text-red-200 border-red-300 dark:border-red-700">
Rejeitado
</span>
// Info (Aguardando Verificação)
<span className="px-2 py-1 rounded-full text-xs font-medium border bg-blue-100 dark:bg-blue-900/50 text-blue-800 dark:text-blue-200 border-blue-300 dark:border-blue-700">
Aguardando Verificação
</span>
// Neutral (Não Pago)
<span className="px-2 py-1 rounded-full text-xs font-medium border bg-neutral-100 dark:bg-neutral-700 text-neutral-800 dark:text-neutral-200 border-neutral-300 dark:border-neutral-600">
Não Pago
</span>
Flash Messages
// Success
<div className="px-4 py-3 rounded relative mb-4 text-green-700 dark:text-green-300 bg-green-100 dark:bg-green-900/50 border border-green-400 dark:border-green-700">
<div className="flex items-center">
<strong className="font-bold mr-1">Sucesso:</strong>
<span className="block sm:inline">Mensagem de sucesso</span>
</div>
</div>
// Error
<div className="px-4 py-3 rounded relative mb-4 text-red-700 dark:text-red-300 bg-red-100 dark:bg-red-900/50 border border-red-400 dark:border-red-700">
<div className="flex items-center">
<strong className="font-bold mr-1">Erro:</strong>
<span className="block sm:inline">Mensagem de erro</span>
</div>
</div>
// Warning
<div className="px-4 py-3 rounded relative mb-4 text-yellow-700 dark:text-yellow-300 bg-yellow-100 dark:bg-yellow-900/50 border border-yellow-400 dark:border-yellow-700">
<div className="flex items-center">
<strong className="font-bold mr-1">Aviso:</strong>
<span className="block sm:inline">Mensagem de aviso</span>
</div>
</div>
// Info
<div className="px-4 py-3 rounded relative mb-4 text-blue-700 dark:text-blue-300 bg-blue-100 dark:bg-blue-900/50 border border-blue-400 dark:border-blue-700">
<div className="flex items-center">
<strong className="font-bold mr-1">Info:</strong>
<span className="block sm:inline">Mensagem informativa</span>
</div>
</div>
Sidebar Navigation
<aside className="w-64 flex-shrink-0 border-r border-neutral-200 dark:border-neutral-800 p-4 bg-white dark:bg-neutral-950">
<nav className="mt-2">
<ul className="space-y-1">
{/* itens de navegação */}
</ul>
</nav>
</aside>
Stats Cards
<div className="rounded-lg border border-neutral-200 dark:border-neutral-800 p-3 bg-neutral-50 dark:bg-neutral-800/40">
<p className="text-xs text-neutral-700 dark:text-neutral-300 font-medium">Label</p>
<p className="mt-1 text-lg font-semibold text-neutral-900 dark:text-neutral-100">Valor</p>
</div>
Transições e Animações
Padrões de Transição
// Transição suave para todas as propriedades
className="transition-all duration-200"
// Transição apenas para cores
className="transition-colors"
// Transição para sombra
className="transition-shadow"
// Transição para transformações
className="transition-transform"
Hover Effects
// Card hover
<div className="hover:shadow-md hover:border-neutral-300 dark:hover:border-neutral-700 transition-all">
// Button hover
<button className="hover:bg-blue-700 transition-colors">
// Link hover
<a className="hover:text-blue-600 transition-colors">
Regras CSS Globais
Importante: Evitar !important em regras globais
NÃO FAÇA:
button[type="submit"] {
background-color: var(--primary) !important;
color: var(--primary-foreground) !important;
}
Isso sobrescreve todas as classes Tailwind e causa problemas de manutenção.
FAÇA:
Deixe cada componente definir seu próprio estilo usando classes Tailwind ou CSS variables sem !important.
Estilos Base (Definidos em globals.css)
/* Form elements */
input, select, textarea {
border: 1px solid var(--input);
background-color: var(--background);
border-radius: var(--radius);
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
color: var(--foreground);
transition: all 0.2s;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: var(--ring);
box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
}
/* Tables */
table {
border-collapse: collapse;
width: 100%;
font-size: 0.875rem;
}
thead {
background-color: var(--muted);
}
tbody tr {
border-bottom: 1px solid var(--border);
}
tbody tr:hover {
background-color: var(--accent);
}
th {
text-align: left;
font-weight: 500;
color: var(--muted-foreground);
padding: 0.75rem 1rem;
font-size: 0.75rem;
text-transform: uppercase;
}
td {
padding: 1rem;
vertical-align: middle;
}
/* Cards */
.card {
background-color: var(--card);
color: var(--card-foreground);
border-radius: var(--radius);
border: 1px solid var(--border);
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
}
/* Badges */
.badge {
display: inline-flex;
align-items: center;
border-radius: 9999px;
padding: 0.125rem 0.625rem;
font-size: 0.75rem;
font-weight: 500;
}
/* Links */
a {
color: var(--primary);
text-decoration: none;
}
a:hover,
a:focus,
a:active {
text-decoration: none !important;
}
Checklist de Implementação
Ao criar ou modificar componentes, verifique:
- Usar CSS variables (
bg-card,text-foreground,border-border) - Usar
neutral-*em vez degray-* - Usar
blue-600para botões primary - Usar
rounded-lgourounded-xlpara border-radius - Adicionar
transition-colorsoutransition-allpara elementos interativos - Testar em tema claro e escuro
- Verificar contraste de cores
- Usar classes Tailwind em vez de CSS customizado quando possível
- Evitar
!importantem regras CSS
Exemplos de Componentes Completos
Formulário Padrão
function StandardForm({ onSubmit, onCancel }) {
return (
<form onSubmit={onSubmit} className="bg-card text-card-foreground rounded-xl border shadow-sm p-6 max-w-lg mx-auto space-y-4">
<div className="space-y-2">
<label htmlFor="name" className="text-sm font-medium leading-none">
Nome
</label>
<input
id="name"
name="name"
className="flex h-10 w-full rounded-lg border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
placeholder="Digite o nome"
/>
</div>
<div className="space-y-2">
<label htmlFor="email" className="text-sm font-medium leading-none">
Email
</label>
<input
id="email"
name="email"
type="email"
className="flex h-10 w-full rounded-lg border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
placeholder="Digite o email"
/>
</div>
<div className="flex gap-3 mt-6">
<button
type="submit"
className="inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium bg-blue-600 hover:bg-blue-700 text-white h-10 px-4 py-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 flex-1"
>
Salvar
</button>
<button
type="button"
onClick={onCancel}
className="inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium border border-input bg-background hover:bg-accent hover:text-accent-foreground h-10 px-4 py-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 flex-1"
>
Cancelar
</button>
</div>
</form>
);
}
Card de Dashboard
function DashboardCard({ title, description, buttonText, link, icon: Icon }) {
return (
<Link href={link} className="block no-underline">
<div className="h-full border rounded-xl p-5 shadow-sm bg-card dark:bg-card dark:border-border hover:shadow-md hover:border-neutral-300 dark:hover:border-neutral-700 transition-all">
<div className="flex items-start gap-4">
{Icon && (
<div className="p-2.5 rounded-lg bg-blue-50 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300">
<Icon className="w-5 h-5" />
</div>
)}
<div className="flex-1">
<h3 className="text-lg font-semibold text-foreground mb-2">{title}</h3>
<p className="text-sm text-muted-foreground mb-4">{description}</p>
<span className="inline-flex justify-center text-white text-sm font-medium py-2 px-6 rounded-lg transition-colors bg-blue-600 hover:bg-blue-700">
{buttonText}
</span>
</div>
</div>
</div>
</Link>
);
}
Conclusão
Seguir estes padrões garantirá:
✅ Consistência visual em todo o sistema ✅ Manutenção mais fácil com código previsível ✅ Melhor suporte a tema claro/escuro ✅ Código mais limpo e organizado ✅ Experiência do usuário mais coesa
Para dúvidas ou sugestões de melhoria, consulte a equipe de desenvolvimento.