Files
course-plat/STYLE_GUIDE.md
2026-08-31 14:10:20 -03:00

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

  1. Usar CSS Variables para cores, backgrounds e bordas (definidas em :root e .dark)
  2. Padronizar em neutral-* em vez de gray-* para consistência
  3. Usar blue-600 como cor primária para botões de ação principal
  4. Manter border-radius de 8px (rounded-lg ou rounded-xl)
  5. Usar sombras sutis: shadow-sm padrão, shadow-md no hover
  6. Evitar regras CSS globais com !important que 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

  1. Consistência: Sempre use a string de classes completa incluindo variantes de tema claro e escuro
  2. Acessibilidade: As combinações de cores fornecem contraste suficiente para ambos os temas
  3. Opacidade: O tema escuro usa /20 de opacidade para a maioria das cores (exceto yellow que usa /50)
  4. Borda: Sempre inclua borda para melhor separação visual
  5. Arredondamento: Use rounded-full para badges em formato de pílula
  6. Tamanho: Use text-xs para badges pequenos, text-sm para 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 de gray-*
  • Usar blue-600 para botões primary
  • Usar rounded-lg ou rounded-xl para border-radius
  • Adicionar transition-colors ou transition-all para elementos interativos
  • Testar em tema claro e escuro
  • Verificar contraste de cores
  • Usar classes Tailwind em vez de CSS customizado quando possível
  • Evitar !important em 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.