<!-- Protocore Design System — Toast -->
# Toast

- **Category:** Feedback (`feedback`)
- **Slug:** `feedback/toast`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { PdsToastProvider, useToast } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/feedback/toast

> An imperative notification queue — mount PdsToastProvider, raise toasts anywhere via useToast().

## When to use it

Use a **Toast** for a *transient* reaction to a *discrete* action the user just took — "Node provisioned", "Couldn't save", "Copied". It's non-blocking, self-dismissing, and raised imperatively from an event handler.

- A persistent, surface-wide condition? Use a [Banner](/feedback/banner) — a toast will vanish before it's read.
- Feedback tied to a specific field or row? Use an [InlineMessage](/feedback/inline-message).
- Something that must be acknowledged or blocks the flow? Use a [Dialog](/overlay/dialog) — never a toast, which can be missed entirely.

Don't put essential information or the only copy of an action in a toast; assume it can be missed.

## The provider API

The queue is context-backed: `useToast()` returns `{ toast, dismiss }` and throws if called outside a `PdsToastProvider`. Configure the provider once — `duration` (default 5000ms), `swipeDirection`, and the region `label` — and every toast inherits it unless it overrides `duration` per call. The provider owns the bottom-right viewport and the enter/exit animations; you never render a `<Toast>` element yourself.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | `ReactNode` | yes | — | App subtree that can raise toasts. |
| `duration` | `number` | no | `5000` | Default auto-dismiss delay in ms for toasts that don't set their own. |
| `label` | `string` | no | `Notifications` | Accessible region label announced to assistive tech. |
| `swipeDirection` | `enum` | no | `right` | Swipe direction to dismiss. |

## Examples

### Basics

Mount a single `PdsToastProvider` near the app root. Any component beneath it calls `useToast()` and fires `toast({ title, description, tone })` — no JSX to render, no open state to hold.

```tsx
import { Button, PdsToastProvider, useToast } from "@protocore/pds";

function RaiseButton() {
  const { toast } = useToast();
  return (
    <Button
      onClick={() =>
        toast({
          title: "NODE PROVISIONED",
          description: "validator-eu-01 is now attesting.",
          tone: "success",
        })
      }
    >
      Provision node
    </Button>
  );
}

// Mount one PdsToastProvider near the app root; anything under it can raise
// toasts imperatively via useToast().
export default function Basics() {
  return (
    <PdsToastProvider>
      <RaiseButton />
    </PdsToastProvider>
  );
}
```

### Tones

A `tone` sets the 2px left rule. The title is short and mono-uppercase (the outcome); the optional description carries the sans supporting line.

```tsx
import { Button, HStack, PdsToastProvider, useToast } from "@protocore/pds";
import type { ToastOptions } from "@protocore/pds";

const events: Array<{ label: string; opts: ToastOptions }> = [
  { label: "Success", opts: { title: "BLOCK FINALISED", tone: "success" } },
  { label: "Warning", opts: { title: "STAKE LOW", tone: "warning" } },
  { label: "Danger", opts: { title: "SLASHING EVENT", tone: "danger" } },
  { label: "Info", opts: { title: "EPOCH ROLLED OVER", tone: "info" } },
];

function Buttons() {
  const { toast } = useToast();
  return (
    <HStack gap={2} wrap>
      {events.map((e) => (
        <Button key={e.label} variant="secondary" size="sm" onClick={() => toast(e.opts)}>
          {e.label}
        </Button>
      ))}
    </HStack>
  );
}

export default function Tones() {
  return (
    <PdsToastProvider>
      <Buttons />
    </PdsToastProvider>
  );
}
```

## Do & don't

**Do**

- Mount exactly one PdsToastProvider near the app root.
- Raise toasts from event handlers for discrete, completed actions.
- Keep the title to a short outcome; put detail in the description.
- Hold the returned id to dismiss a long-running toast when its job ends.

**Don't**

- Put essential or must-read information in a toast — it disappears.
- Use a toast where the user must confirm or is blocked — that's a Dialog.
- Nest multiple providers or render Toast internals directly.
- Fire a burst of toasts for one logical action.

## Accessibility

**Keyboard**

| Keys | Action |
| --- | --- |
| `F8` | Moves focus into the toast viewport region (Radix hotkey). |
| `Tab` | Cycles through the focused toast's controls, including its close button. |
| `Escape` | Dismisses the focused toast. |

**Notes**

- Built on Radix Toast: the viewport is an `aria-live` region labelled by the provider `label` (default "Notifications"), so toasts are announced.
- `success`/`info`/`neutral` toasts announce politely; the underlying region role escalates for higher-urgency content.
- Each toast has a keyboard- and pointer-accessible close button (`aria-label` "Dismiss") plus swipe-to-dismiss.
- Auto-dismiss timers pause on hover and focus, so keyboard and screen-reader users aren't rushed.

## Related

`banner`, `inline-message`, `dialog`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
