Toast
ProntoFeedback efêmero após uma ação. Aparece, comunica, desaparece. Suporta variantes (success, error, info), action (Desfazer) e empilhamento.
Usar quando
Confirmação de ação não-bloqueante (item criado, salvo, excluído). Erro recuperável (não crítico).
Não usar quando
Erro crítico que precisa atenção (use modal). Mensagem permanente (use banner). Validação de form (use erro inline).
Variantes
3 tipos: success, error, info. Cor é o sinal — sempre acompanhada de ícone.
Com action (Undo)
Para ações destrutivas reversíveis. Auto-dismiss desabilitado enquanto action existe.
Demo interativa
Clique para disparar. Veja a animação real (slide in, auto-dismiss em 4s, slide out).
Empilhamento
Múltiplos toasts empilham bottom-up com offset 8px.
Microinterações
| Microinteração | Disparada por | Comportamento | Timing |
|---|---|---|---|
| Slide in | show | translateY(20px)→0 + opacity 0→1 | 250ms cubic-bezier(0.32,0.72,0,1) |
| Auto-dismiss countdown | show | Barra inferior reduz de 100% a 0% em 4s | 4000ms linear |
| Hover paused | mouseenter | Pausa o auto-dismiss enquanto mouse está em cima | instant |
| Slide out | dismiss (auto ou manual) | opacity 1→0 + scale 1→0.95 | 200ms cubic-bezier(0.4,0,1,1) |
| Action click | clique no botão Desfazer | Toast desaparece imediatamente, action.onClick chamada | 150ms (sem delay) |
| Stack reflow | novo toast quando já há outros | Toasts existentes deslizam 8px para cima | 250ms ease |
| Swipe to dismiss (mobile) | touchmove horizontal > 80px | Toast segue o dedo, opacity reduz, dismiss ao soltar | follows finger |
Acessibilidade
Acessibilidade — checklist
Teclado
| Esc | Fecha o toast atual (top da pilha) |
| Tab | Move foco para o action (se houver) |
| Enter | Aciona o action (se em foco) |
ARIA esperado
- role="status" // para success/info (assertive=false)
- role="alert" // para error (interrompe screen reader)
- aria-live="polite" // para success/info
- aria-live="assertive" // para error
- aria-atomic="true" // garante leitura completa
Notas
- Toast NUNCA é o único feedback de sucesso de operação crítica — combine com mudança visual no contexto.
- Auto-dismiss não pode ser <4s (acessibilidade WCAG 2.2.1).
- Erro NUNCA usa auto-dismiss curto — dá pelo menos 8s ou requer dismiss manual.
- Em mobile, swipe-to-dismiss horizontal é padrão iOS/Android.
Código
'use client';
import { useEffect, useState } from 'react';
import { CheckCircle2 } from 'lucide-react';
import { T, TYPE, SP, RADIUS, MOTION } from '@/lib/tokens';
interface ToastProps {
variant: 'success' | 'error' | 'info';
message: string;
action?: { label: string; onClick: () => void };
duration?: number; // ms, default 4000 (8000 para error)
onDismiss: () => void;
}
export function Toast({ variant, message, action, duration, onDismiss }: ToastProps) {
const dur = duration ?? (variant === 'error' ? 8000 : 4000);
const [paused, setPaused] = useState(false);
useEffect(() => {
if (action || paused) return; // não auto-dismiss se houver action ou hover
const t = setTimeout(onDismiss, dur);
return () => clearTimeout(t);
}, [paused, action, dur, onDismiss]);
return (
<div
role={variant === 'error' ? 'alert' : 'status'}
aria-live={variant === 'error' ? 'assertive' : 'polite'}
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
style={{
background: T.surface,
border: `1px solid ${T.border}`,
borderRadius: RADIUS.md,
boxShadow: T.shadowLg,
padding: `${SP[3]}px ${SP[4]}px`,
// ... resto dos estilos
}}
>
{/* ícone + mensagem + action + close */}
</div>
);
}Regras
Faça
- ✓Mensagem em 1 frase curta (≤80 caracteres). Diga o que aconteceu.
- ✓Success: 4s de auto-dismiss. Error: 8s ou manual. Info: 4s.
- ✓Use action (Desfazer) em ações destrutivas reversíveis (excluir).
- ✓Pausa auto-dismiss em hover/focus.
- ✓Stack bottom-right com offset 8px entre toasts.
- ✓Use role="alert" para errors (interrompe screen reader).
Não faça
- ✗Não use toast para erro crítico (use modal).
- ✗Não use toast como única confirmação de ação importante.
- ✗Não passe mensagem em parágrafo — toast é flash, não aviso.
- ✗Não use cor sem ícone (a11y).
- ✗Não exiba mais que 3 toasts simultâneos (corte mais antigos).
- ✗Não desabilite o botão de fechar (X) — usuário sempre pode dismiss.
Tokens usados
| Token | Valor | Papel |
|---|---|---|
T.surface | #FFFFFF | background do card |
T.border | #E3DFD2 | borda fina |
T.shadowLg | 0 16px 40px ... | sombra de elevação |
T.verde | #2F6B3C | ícone success |
T.carmim | #A32E2E | ícone error |
T.selo | #2B5A8A | ícone info |
RADIUS.md | 12 | borderRadius |
TYPE.base | 13 | fontSize message |
MOTION.base | 250ms | slide in |