Task: Toast / Snackbar Component
Type: Component
Milestone: M0.5 — Shared Component Library
Estimate: M
Component Type
Props Interface
interface Toast {
id: string;
message: string;
variant?: 'success' | 'error' | 'warning' | 'info';
duration?: number; // ms before auto-dismiss, default 2500
action?: {
label: string;
onClick: () => void;
};
}
// Imperative API (via Zustand toastStore) — not a prop
toast.success('Action applied to RPT-0003');
toast.error('Failed to moderate report');
toast.warning('Another moderator is reviewing this');
Variants / States
| Variant |
Usage in your app |
success |
Action applied (Remove, Ban, Warn, Dismiss) |
error |
API failure, invalid state transition |
warning |
Claim-lock — another moderator is reviewing |
info |
AI screening triggered, processing |
| With action |
"Undo" link for reversible actions (future) |
| Queue |
Multiple toasts stack vertically, each dismisses independently |
Acceptance Criteria
Notes
- Consumed via
useToast() hook, not by passing props — <ToastContainer /> is placed once in root layout, individual toasts are triggered imperatively from anywhere
ModerationQueue calls toast.success() after every moderation action — this must work without re-rendering the queue
Task: Toast / Snackbar Component
Type: Component
Milestone: M0.5 — Shared Component Library
Estimate: M
Component Type
Props Interface
Variants / States
successerrorwarninginfoAcceptance Criteria
createPortalfixed totop-rightof viewportdurationms with a progress bar animation×buttonactionbutton renders inline if providedrole="alert"so screen readers announce ituseToast()hook backed by a Zustand slice — no prop drillingNotes
useToast()hook, not by passing props —<ToastContainer />is placed once in root layout, individual toasts are triggered imperatively from anywhereModerationQueuecallstoast.success()after every moderation action — this must work without re-rendering the queue