Build a Toast Queue Manager widget that provides a programmatic toast queue with dismiss-all action, per-toast action support, configurable stacking behavior, and auto-dismiss with configurable duration. It enhances the existing toast.tsx primitive and toast.store.ts with queue management, stacking limits, and a persistent toast history panel.
Introduction
Overview
The design system provides a toast.tsx primitive with react-toastify integration and a Zustand-based toast store. The current implementation supports showing toasts with tones (success, error, warning, info) and auto-dismiss. However, it lacks queue management (limiting concurrent toasts), a dismiss-all action, per-toast action buttons (e.g., “Undo”, “View”), stacking behavior configuration, and a toast history panel. SaaS applications with high-frequency events (deployments, CI runs, real-time collaboration) need these capabilities.
Goals
Provide a ToastQueueManager widget that wraps the existing toast system with queue management.
Support configurable max concurrent toasts with overflow queuing.
Add a “Dismiss all” action when multiple toasts are visible.
Support per-toast action buttons (primary and secondary actions).
Configure stacking behavior (stack, replace, or queue).
Ship with Storybook stories demonstrating all behaviors.
Non-Goals
Replacing the existing toast primitive or react-toastify integration.
Building a notification center (persistent notification list with read/unread state).
Push notification integration.
Scope
In Scope
Item
Description
Queue management
Configurable max concurrent toasts; overflow toasts are queued and shown as slots open.
Dismiss all
A “Dismiss all” button appears when 2+ toasts are visible.
Per-toast actions
Toasts can include a primary action button (e.g., “Undo”) and optional secondary action.
Stacking behavior
Configurable: “stack” (all visible up to max), “replace” (new toast replaces oldest), “queue” (FIFO).
Auto-dismiss
Configurable duration per toast; persistent toasts (no auto-dismiss) supported.
Toast counter
When queued toasts exceed max, show “N more” indicator.
Stories and tests
Storybook stories for all behaviors; unit tests for queue logic.
Out of Scope
Item
Reason
Notification center
Different UX pattern with persistence, read/unread state, and filtering.
Push notifications
Browser notification API is a separate concern.
Toast positioning
Already handled by react-toastify; not changing.
Users and Pain Points
User
Pain Point
SaaS developers
No way to limit concurrent toasts; rapid events flood the screen.
CI/CD dashboard teams
Deployment toasts stack infinitely; users cannot dismiss them all at once.
E-commerce teams
Toast for “Item added to cart” needs an “Undo” action button.
Accessibility-focused teams
Too many simultaneous toasts overwhelm screen readers.
Definitions
Term
Definition
Toast queue
An ordered list of pending toasts waiting to be displayed when a slot opens.
Max concurrent
The maximum number of toasts visible simultaneously.
Stacking behavior
How new toasts interact with existing ones: stack (add), replace (swap oldest), or queue (wait).
Persistent toast
A toast that does not auto-dismiss; requires manual dismissal.
Toast action
A button within a toast that triggers a callback (e.g., “Undo”, “View details”).
Current State
toast.tsx (src/components/ui/toast.tsx): Toast component using react-toastify with tone variants (success, error, warning, info). Supports auto-dismiss with DEFAULT_TOAST_DURATION (5000ms). Uses a Zustand store for toast state.
toast.store.ts (src/stores/toast.store.ts): Re-exports toast utilities from toast.tsx.
ToastContainer: react-toastify container with custom transition (motion-toast-enter, motion-toast-exit).
Current limitations: No max concurrent limit, no dismiss-all, no per-toast actions, no queue management, no stacking configuration.
Proposed Solution
Enhanced Toast API
Extend the existing toast() function to accept action buttons:
Toasts use role="status" with aria-live="polite" (existing behavior).
A11Y-02
Error toasts use role="alert" with aria-live="assertive".
A11Y-03
Action buttons within toasts are keyboard accessible (Tab, Enter).
A11Y-04
”Dismiss all” button has aria-label="Dismiss all notifications".
A11Y-05
Queue counter announces “N more notifications pending” to screen readers.
A11Y-06
Max 3 concurrent toasts to avoid overwhelming screen reader users.
Content and Documentation Requirements
ID
Requirement
DOC-01
Storybook docs page with usage guidelines, prop table, and interactive examples.
DOC-02
Migration guide from existing toast() calls (backward compatible; new props are optional).
DOC-03
Recipe showing toast with undo action for destructive operations.
DOC-04
Guidelines on when to use persistent vs. auto-dismiss toasts.
Dependencies
Dependency
Type
Risk
src/components/ui/toast.tsx
Internal
Low — enhancing existing component.
react-toastify
Existing
Low — wrapping, not replacing.
zustand
Existing
Low — extending existing toast store.
src/styles/motion.css
Internal
Low — existing toast animations.
Risks and Tradeoffs
Risk
Likelihood
Impact
Mitigation
Queue logic adds complexity to the toast system
Medium
Medium
Keep queue manager as an optional wrapper; existing toast() works without it.
react-toastify’s internal queue conflicts with our queue logic
Low
High
Use react-toastify in manual mode (disable its internal queue); our manager controls visibility.
Per-toast actions increase toast height and visual density
Low
Low
Actions render as compact text buttons; limit to 2 actions per toast.
Open Questions
#
Question
Owner
Status
OQ-01
Should the queue manager support a toast history panel (viewable after dismissal)?
David Holmes
Open
OQ-02
Should persistent toasts count toward the max concurrent limit?
David Holmes
Open
OQ-03
Should the “dismiss all” button also cancel queued toasts or only visible ones?
David Holmes
Open
Acceptance Criteria
#
Criterion
AC-01
toast() accepts action and secondaryAction props; action buttons render inside the toast.
AC-02
toast({ persistent: true }) creates a toast that does not auto-dismiss.
AC-03
ToastQueueManager limits concurrent toasts to maxConcurrent.
AC-04
Toasts exceeding the limit enter a queue and display when slots open.
AC-05
”Dismiss all” appears when 2+ toasts are visible and clears all toasts.
AC-06
Existing toast() calls continue to work without changes (backward compatible).
AC-07
All components pass axe-core checks with zero violations.
AC-08
Storybook stories exist for: basic toast with action, persistent toast, queue overflow, dismiss all.
AC-09
pnpm typecheck and pnpm vitest run --project unit pass with zero errors.
LLM Handoff Instructions
When implementing this FRD:
Start with the toast() API enhancement in src/components/ui/toast.tsx. Add action, secondaryAction, persistent, and duration to the toast options. Render action buttons inside the toast surface. This is backward compatible — all new props are optional.
Then build ToastQueueManager in src/components/widgets/toast-queue-manager.tsx. It wraps ToastContainer and manages a queue via the Zustand toast store.
Queue logic: Intercept toast creation in the store. If visible count >= maxConcurrent, push to queue array. On toast dismiss, shift from queue and display.
Dismiss all: Render a small “Dismiss all” button positioned above the toast stack. It calls reactToastifyToast.dismiss() for all active IDs and clears the queue.
Counter badge: Render “+N more” below the toast stack when queue is non-empty.
Stories in src/components/widgets/toast-queue-manager.stories.tsx. Use interactive stories with buttons to trigger toasts and demonstrate queue behavior.
Tests in src/components/widgets/toast-queue-manager.test.tsx. Test queue limit, dismiss-all, action button clicks, persistent toast.