Alert
ProntoBanner persistente com mensagem importante. Diferente de Toast (efêmero): Alert fica no DOM até user dispensar ou contexto resolver.
Usar quando
LGPD/cookies banner, plano vencendo, manutenção agendada, mudança em conformidade. Mensagem que precisa atenção sustentada.
Não usar quando
Feedback de ação (use Toast). Erro de form (use erro inline). Notificação efêmera (use Toast).
Variantes
Variantes — compacto (sem título)
Microinterações
| Microinteração | Disparada por | Comportamento | Timing |
|---|---|---|---|
| Mount slide | componente entra | translateY -8→0 + opacity 0→1 | 250ms cubic-bezier(0.32,0.72,0,1) |
| Dismiss collapse | click X | opacity 1→0 + max-height collapse | 200ms ease |
| Action hover | mouseenter no botão action | underline + color shift | 150ms ease |
Acessibilidade
Acessibilidade — checklist
ARIA esperado
- role="alert" para variantes warning/error (interrompe screen reader)
- role="status" para info/success (não interrompe)
- aria-live="assertive" se urgente, "polite" caso contrário
- Botão dismiss: aria-label="Fechar alerta"
Notas
- Alert NUNCA é o único feedback de erro crítico — combine com mudança de estado.
- Cor SEMPRE acompanha de ícone semântico (a11y).
- Action button no alert é secundário — botão primário fica em outro lugar.
- Em mobile, banner full-width com padding 16px.
Código
'use client';
import { AlertCircle, Info, CheckCircle2, X } from 'lucide-react';
import { T, RADIUS, SP } from '@/lib/tokens';
export function Alert({ variant, title, description, action, dismissible, onDismiss }: AlertProps) {
const cfg = VARIANTS[variant];
const Icon = cfg.icon;
return (
<div role={variant === 'error' || variant === 'warning' ? 'alert' : 'status'}
aria-live={variant === 'error' ? 'assertive' : 'polite'}
style={{
background: cfg.bg, border: `1px solid ${cfg.edge}`, borderLeft: `3px solid ${cfg.fg}`,
borderRadius: RADIUS.md, padding: `${SP[3]}px ${SP[4]}px`,
display: 'flex', gap: SP[3], alignItems: 'flex-start',
}}>
<Icon size={16} color={cfg.fg} />
<div style={{ flex: 1 }}>
{title && <p style={{ fontSize: 13, fontWeight: 500, color: T.ink, margin: 0 }}>{title}</p>}
{description && <p style={{ fontSize: 12, color: T.ink2, margin: title ? '4px 0 0' : 0 }}>{description}</p>}
</div>
</div>
);
}Regras
Faça
- ✓Use cor + ícone semântico (a11y).
- ✓Borda esquerda 3px na cor da variante (signature visual).
- ✓Action button discreto (não primário).
- ✓Dismissible (X) só se mensagem for repetível ou opcional.
- ✓role="alert" para warning/error, role="status" para info/success.
Não faça
- ✗Não use alert como toast (toast é efêmero, alert persiste).
- ✗Não force cor vermelha em info (a11y semantica).
- ✗Não tenha 3+ alerts empilhados (pollui).
- ✗Não use alert para feedback de ação trivial.
- ✗Não esqueça de auto-dismiss alguns (ex: success de save) ou eles ficarão pra sempre.
Tokens usados
| Token | Valor | Papel |
|---|---|---|
T.seloTint | rgba(43,90,138,0.07) | background info |
T.verdeTint | rgba(47,107,60,0.07) | background success |
T.brassTint | rgba(164,124,43,0.08) | background warning |
T.carmimTint | rgba(163,46,46,0.07) | background error |
RADIUS.md | 12 | borderRadius |
SP[3] | 12 | padding |