Skip to content

Padrões de UI — Frontend (SSOT)

Documento mestre dos padrões visuais e de composição de componentes do frontend OLP. Regra de ouro: quando um componente da biblioteca (src/components/ui/*) precisa de ajuste consistente, corrija na primitiva, nunca caso a caso no consumer.


1. Princípios

  1. shadcn/Radix como base. Toda primitiva visual vive em src/components/ui/*. Consumers compõem, não reescrevem.
  2. Correção na raiz. Se um padrão (ex: altura, espaçamento, cor) precisa valer em todo o sistema, ajuste o componente base — não o cliente.
  3. Override consciente. Consumers podem sobrescrever via className apenas com justificativa clara (ex: lista que precisa de mais altura por agrupamento). Documentar inline.
  4. Tokens semânticos. Cores via tokens HSL (bg-primary, text-foreground), nunca cores diretas (bg-blue-500).

2. Select / Dropdown — max-h-[200px]

Regra: todo <SelectContent> exibe no máximo ~6 itens (200px) antes de ativar scroll interno.

tsx
// src/components/ui/select.tsx — SelectContent
className={cn(
  "... max-h-[200px] ...",
  className,
)}

Por quê: listas longas (ex: 10+ olimpíadas) "vazam" o layout do modal pai. Cap fixo garante UX previsível e sem regressões em qualquer modal/popover.

Cálculo: SelectItem (~32px com py-1.5 + text-sm) × 6 + Viewport p-1 (8px) ≈ 200px.

Override autorizado:

tsx
<SelectContent className="max-h-[280px]">  {/* +2 itens — listas com agrupamento */}

Anti-padrão:

tsx
{/* ❌ Decidir altura ad-hoc em cada consumer (gera inconsistência) */}
<SelectContent className="max-h-72">
<SelectContent className="max-h-[350px]">
<SelectContent>  {/* sem cap → vaza layout */}

3. Tooltips em primitivas Radix — Proibido

Não envolver SelectItem, DropdownMenuItem ou similares com <TooltipTrigger>. Radix exige controle interno de ref/foco; o wrapper de Tooltip quebra a navegação por teclado e gera warning cannot be given refs.

Alternativa: usar title="..." HTML nativo no próprio item.

Memória relacionada: mem://ui/radix-primitive-composition-restriction


4. Cabeçalhos de Seção (TutorialModal)

O container que envolve <title + subtítulo> e <TutorialModal /> deve usar items-start (não items-center) para alinhar o botão "Como usar" ao topo do título — cria linha horizontal harmoniosa.

tsx
{/* ✅ Correto */}
<div className="flex items-start justify-between">
  <div>
    <h1>Agenda</h1>
    <p className="text-muted-foreground">Subtítulo...</p>
  </div>
  <TutorialModal slug="..." />
</div>

5. Modais (Dialog)

  • Usar <Dialog> da biblioteca; nunca rolar manual.
  • Conteúdo extenso: aplicar max-h-[80vh] overflow-y-auto no DialogContent interno (não no body).
  • hideCloseButton apenas em fluxos onde o fechamento é controlado por ação de negócio (ex: confirmação obrigatória).

6. Toast — olpToast

Sempre olpToast (@/lib/olp-toast). Proibido sonner direto, toast() cru ou alert(). Em onError: getUserFriendlyError(error) antes do toast — nunca error.message.


7. Loading / Anti-Flash

Todo componente com fetch+cache deve guardar com:

tsx
if (isLoading && !data) return <Skeleton />;

Evita flash quando a query é re-buscada em background com cache pré-populado.

Referência completa: ATOMIC_RENDERING.md


8. Tokens Visuais — Referência Rápida

ContextoToken / Classe
Background appbg-background
Texto principaltext-foreground
Texto secundáriotext-muted-foreground
Cardbg-card text-card-foreground
Ação primáriabg-primary text-primary-foreground
Ação destrutivabg-destructive text-destructive-foreground
Header de tabela (Controle)bg-blue-400 (decisão de negócio — coordenador)
Borda padrãoborder-border
Focofocus-visible:ring-ring

9. Espaçamento Vertical Global

Container de página (src/App.tsx): pt-3 / pb-3 no header de novidades para reduzir gap exagerado. Conteúdo interno usa space-y-6 por padrão.


10. Quando criar/atualizar este documento

  • Nova primitiva em src/components/ui/* → adicionar seção aqui.
  • Decisão visual aplicada em ≥3 telas → padronizar e documentar aqui.
  • Override de regra existente → registrar exceção com justificativa nesta seção.

Referências cruzadas:

  • CODING_STANDARDS.md — padrões gerais de código
  • ATOMIC_RENDERING.md — renderização atômica
  • mem://ui/select-dropdown-max-height-standard — regra do cap em Select
  • mem://ui/radix-primitive-composition-restriction — restrições de composição Radix