## Toast Stack (S—016)

Notifications that stack into one card, fan out on hover, swipe away and follow a promise.

- URL: https://sundaeui.dev/components/toast-stack
- Category: UI components · Art direction: Refined
- Access: Free (no account needed)
- Stack: React 18+, TypeScript, plain CSS; dependencies: none
- Install: `npx shadcn@latest add https://sundaeui.dev/r/toast-stack.json`
- Tags: toast, animated, notification, react, feedback

New toasts slide in at the front, and older ones tuck behind, scaled down, so a burst of notifications reads as a single object instead of a tower. Hovering or focusing the stack fans it into a list using each toast’s measured height, and pauses every timer until you leave.
Drag a toast sideways to dismiss it. toast.promise() shows a spinner that resolves into a success or error state in place, with the icon springing in. Actions such as Retry or Undo sit inline.
The store is a tiny module-level subscription consumed with useSyncExternalStore. There is no context provider and no dependency. Render <Toaster /> once, then call toast() from anywhere, including outside React.

### Usage

```tsx
// app/layout.tsx
import { Toaster } from '@/components/sundae/toast-stack'
<body>{children}<Toaster /></body>

// anywhere
import { toast } from '@/components/sundae/toast-stack'

toast.success('Payment received', { description: '$48.00 from Kinfolk Market' })
toast.error('Upload failed', { action: { label: 'Retry', onClick: retry } })
toast.promise(publish(), { loading: 'Publishing…', success: 'Published', error: 'Publish failed' })
```

### Props

- `position` (`'bottom-right' | 'bottom-left' | 'bottom-center'`, default `'bottom-right'`): Toaster anchor.
- `toast(title, opts?)` (`(string, { description?, duration?, action? }) => id`): Shows a default toast. Also available as toast.success and toast.error.
- `toast.promise(p, msgs)` (`(Promise, { loading, success, error }) => Promise`): A loading toast that resolves in place.
- `toast.dismiss(id)` (`(id: number) => void`): Dismisses a toast programmatically.
- `createToaster()` (`() => { toast, Toaster }`): Creates an isolated toaster, for example for an embedded widget.

### Accessibility

- The list is an aria-live="polite" region inside a labelled section, so new toasts are announced without stealing focus.
- Timers pause while the stack is hovered or contains focus, giving people time to read and act.
- Every toast has a labelled dismiss button, and you don’t need to swipe.
- Reduced motion removes the slide and stack animations.
