--- name: notifications-and-recovery description: Toasts, inline errors, banners and recovery paths. Use when designing error states, success confirmations, async feedback or in-place editing. metadata: priority: 8 pathPatterns: - "components/**" - "src/components/**" - "**/*.tsx" - "**/*.jsx" - "design-system/**" - "ui/**" promptSignals: phrases: - "toast" - "notification" - "error message" - "success message" - "in-place editing" - "inline error" - "alert" - "banner" - "recovery" - "retry" - "notification center" - "notification preferences" - "notification settings" retrieval: aliases: - toast notification - error recovery - inline error - notification pattern - in-place editing - alert banner - retry pattern - notification center - notification preferences intents: - show a success message - handle an error gracefully - design a toast notification - add retry to a failed action - design inline editing feedback - design a notification center - let users control which notifications they get examples: - show a toast when the form is saved - what happens when the API call fails - design the error state for this form - add a retry button when network fails - design a notification center for this app - let users mute or filter notification categories --- # Notifications and Recovery When something changes — success, failure, or anything in between — the user must know. And when something goes wrong, they must always have a path forward. A notification without a recovery action is just an apology. --- ## Pattern Selection | Pattern | When to use | Dismissal | |---|---|---| | **Toast** | Transient result of a user action (saved, sent, deleted) | Auto-dismiss 4–6s, manual close | | **Inline error** | Field-level validation, form errors | Clears on correction | | **Alert banner** | Persistent issue affecting the current context | Manual dismiss or resolved state | | **Modal / dialog** | Blocking error requiring a decision before continuing | User action required | | **Empty state** | No data yet — guide the user to the first action | Replaced by the first item | | **Skeleton / loading** | Async content pending | Replaced by content | | **In-place confirmation** | Inline edit saved, row updated, item toggled | Auto-clears after 2–3s | --- ## Toast Notifications Toasts confirm that a background action completed. They appear without interrupting the user's flow. **Placement:** bottom-center or bottom-right. Never top-center — it competes with page content and navigation. **Duration:** 4–6 seconds for information. Errors should persist until dismissed — the user needs time to read and act. **Anatomy:** ``` [Icon] Message text [Action] [×] ``` - Icon: colour-coded (green ✓ success, red ✗ error, orange ⚠ warning, blue ℹ info) - Message: one sentence, plain language - Action (optional): "Undo", "Retry", "View" — one action maximum - Close button: always present on errors; optional on success ``` ✓ "Changes saved." ✓ "Message sent. [Undo]" ✗ "Could not save. Check your connection. [Retry]" ← persists until dismissed ``` **Never:** multiple simultaneous toasts. Queue them; show one at a time. --- ## Inline Errors Inline errors appear adjacent to the element that caused them. They are the most contextual and actionable form of error feedback. **Form validation:** - Validate on blur (leaving a field), not on every keystroke — keystroke validation is noisy - Validate on submit for the complete form - Show the error message directly below the field, in red, with an icon - The field border changes to `--color-error` - Error message is associated via `aria-describedby` for screen readers ```html
Enter a valid email address.
``` **In-place editing:** - When a field is edited inline (table cell, card title), show save/cancel controls adjacent to the field - On save: brief success indicator ("✓ Saved") that fades after 2s — do not navigate away - On error: inline error message below the field with a retry option - On cancel: restore the original value immediately --- ## Alert Banners Banners are persistent — they stay until the condition is resolved or the user dismisses them. **Use for:** - Service degradation ("Some features are temporarily unavailable") - Account issues requiring action ("Your subscription expires in 3 days. [Renew]") - Ongoing sync errors ("Changes are not saving. [Retry]") - Important announcements tied to the current page **Placement:** top of the affected section, not the entire page unless the issue is truly global. **Anatomy:** ``` [Icon] [Message — describes the issue and its scope] [Action] [×] ``` - One banner at a time per region — multiple simultaneous banners create alarm fatigue - Dismissible unless the condition is blocking - Colour follows status colour conventions: red (error), orange (warning), blue (info), green (success/resolved) --- ## Recovery Patterns Every error state must have a path forward. Design the recovery action at the same time as the error message. ### Retry For transient failures (network, timeout, rate limit): ``` "Could not load results." [Try again] ``` - Retry button triggers the same action - After 3 failed retries, escalate: "Still having trouble? [Contact support]" - Show a spinner during retry — do not let the user click multiple times ### Undo For destructive or irreversible actions (delete, archive, send): ``` "Message sent. [Undo] ×" ``` - Undo window: 5–10 seconds. Toast persists for this duration. - After the window closes, the action is final - Undo is preferable to confirmation dialogs for low-stakes actions — it is faster and less disruptive ### Autosave and Draft Recovery For long-form inputs (forms, documents, editors): - Autosave every 30–60 seconds silently - On save failure: "Autosave failed — your changes are stored locally. [Retry save]" - On return after crash or close: "You have unsaved changes from [time]. [Restore] [Discard]" ### Graceful Degradation When a feature fails but the rest of the product still works: - Show an error state for the failed section only — do not blank the entire page - Offer a fallback: "Could not load recommendations. [Browse all products →]" - Log the error silently; surface only what the user needs to know --- ## Loading and Skeleton States Loading is not an error, but it is a state that needs design. - **Skeleton screens** for content-heavy pages — show the layout shape while data loads - **Spinners** for targeted async actions (button loading, inline refresh) - **Progress bars** for long operations with known duration (file upload, multi-step processing) - Never show a blank screen while loading — always show something Skeleton screens reduce perceived wait time compared to spinners. Match the skeleton shape to the actual content layout. --- ## The Notification Center Toasts and banners are *transient*; a **notification center** is the persistent place a user goes to review and control what reaches them. Two things make it work. **Easy to reach, and easy to control.** The user must get to their notification preferences with almost no digging (a bell icon → the center → settings). And keep the control model **coarse — 1 to 3 categories at most**, each with a simple level: - `Off (0)` — none of this category - `Minimal` — only the important ones - `All / granular` — everything Don't build a wall of per-event toggles. Two real needs dominate: **silence everything**, or **keep one or two categories**. Design straight for those — a prominent "mute all" / "mark all read" plus 1–3 category switches. **Minimise reading.** The center is scanned, not read. Group by category, lead each item with a recognisable icon and the entity/action (not a paragraph), show unread state clearly, and let the whole list be cleared in one action. Reading is time (see [[ui-density]]) — a notification center that demands careful reading defeats its purpose. ## Notification Accessibility - Errors use `role="alert"` — announced immediately by screen readers - Status updates use `role="status"` — announced politely (after current speech) - Toasts must be reachable by keyboard — do not use `pointer-events: none` on the close button - Auto-dismissing toasts must have sufficient duration (`prefers-reduced-motion` users may need more time to read) ```html