# Toast

A cross-platform toast notification system matching the base Figma toast. Use it for short, non-blocking feedback with a title, optional description, optional action, and automatic or manual dismissal.

<!-- BEGIN:xui-mcp-instructions:toast -->
A toast is a short, non-blocking message that appears temporarily to inform the user about the result of an action or a change in system state. It appears as a compact bar that slides in from the top of the screen, stays briefly, then dismisses automatically — or can be dismissed manually.

### When to use
- To confirm that an action was completed successfully (e.g. *"Profile updated"*)
- To notify the user of a background event they didn't directly trigger (e.g. *"New rewards available"*)
- To surface a non-critical warning that doesn't block the user from continuing
- To inform about an error that occurred in the background (e.g. *"Connection lost"*)

### When not to use
- For critical errors that require the user to take action before continuing — use a modal or inline error instead
- For information that the user needs to refer back to — toasts disappear and can't be recalled
- For complex messages with multiple actions — keep it to one optional CTA at most
- As a substitute for form validation errors — those belong inline next to the relevant field

### Content guidelines
- Title should be short and specific — 1 to 5 words: *"Profile saved"*, *"Connection lost"*, *"Reward claimed"*
- Subtitle is optional — use it to add one line of supporting context, not to repeat the title
- Button label should be a clear verb: *"Undo"*, *"Retry"*, *"View"* — not *"OK"* or *"Click here"*
- Avoid exclamation marks or dramatic language — keep the tone calm and factual
- Match the type to the message: use Alert only for errors, Warning for caution, Success for positive outcomes, Neutral for informational messages

### Behaviour guidelines
- Toasts appear at the top of the viewport, offset from the status bar or navigation
- They auto-dismiss after a set duration (typically 3–5 seconds) — the exact timing is set at the product layer
- The description is not truncated: the toast grows to fit its content. Opt into a line limit with `descriptionLines` only when the layout cannot absorb the extra height
- When Button=False, a close (×) icon is shown — tapping it dismisses the toast immediately
- When Button=True, the action button replaces the close icon — the toast can be dismissed by tapping the button or waiting for auto-dismiss
- Multiple toasts stack in a ToastGroup — see below
- Toasts do not block interaction with the rest of the screen

### Accessibility
- Use role=*"status"* for Neutral and Success toasts — screen readers announce them without interrupting the user
- Use role=*"alert"* for Warning and Alert toasts — these are announced immediately
- The close button must have aria-label=*"Dismiss notification"*
- Do not rely on color alone to convey the type — the icon and text must also communicate it
<!-- END:xui-mcp-instructions:toast -->

## Installation

```bash
npm install @xsolla/xui-toast
```

## Imports

```tsx
import {
  Toast,
  ToastProvider,
  ToastGroup,
  ToastContext,
  useToast,
  type ToastProps,
  type ToastOptions,
  type ToastType,
  type ToastPosition,
  type ToastAlign,
  type UseToastReturn,
} from "@xsolla/xui-toast";
```

## Quick start

Wrap your app with `ToastProvider`, then call `useToast` from any descendant.

```tsx
import * as React from "react";
import { XUIProvider } from "@xsolla/xui-core";
import { ToastProvider, useToast } from "@xsolla/xui-toast";

function SaveButton() {
  const toast = useToast();
  return (
    <button onClick={() => toast.success({ title: "Saved successfully" })}>
      Save
    </button>
  );
}

export default function App() {
  return (
    <XUIProvider initialMode="dark">
      <ToastProvider>
        <SaveButton />
      </ToastProvider>
    </XUIProvider>
  );
}
```

> **React Native:** ensure the root container under `ToastProvider` has `flex: 1` so absolute-positioned toasts have a reference height.

## API Reference

### `<ToastProvider>`

Root provider. Manages toast state, auto-dismiss timers, and renders the `ToastGroup`.

| Prop              | Type                            | Default    | Description                                                                                                   |
| ----------------- | ------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------- |
| `testID`          | `string`                        | -          | Test ID for testing frameworks. On web this renders as `data-testid`; on React Native it renders as `testID`. |
| `children`        | `ReactNode`                     | -          | Application content.                                                                                          |
| `position`        | `"top" \| "bottom"`             | `"top"`    | Vertical anchor of the toast container.                                                                       |
| `align`           | `"left" \| "center" \| "right"` | `"center"` | Horizontal alignment of toasts within the container.                                                          |
| `defaultDuration` | `number`                        | `5000`     | Default auto-dismiss duration in ms. Set `0` to disable auto-dismiss globally.                                |
| `maxWidth`        | `number`                        | -          | Maximum width in px of the toast stack.                                                                       |

### `<Toast>`

The individual toast cell. Used internally by `ToastGroup`, but can be rendered standalone for static display.

| Prop               | Type                                             | Default     | Description                                                        |
| ------------------ | ------------------------------------------------ | ----------- | ------------------------------------------------------------------ |
| `id`               | `string`                                         | -           | Required unique identifier.                                        |
| `type`             | `"alert" \| "success" \| "neutral" \| "warning"` | `"neutral"` | Visual and accessibility type.                                     |
| `title`            | `string`                                         | -           | Short primary text, ideally 1 to 5 words. Always clamped to one line. |
| `description`      | `string`                                         | -           | Optional supporting text. Not clamped by default.                  |
| `descriptionLines` | `number`                                         | -           | Clamp the description to this many lines, cutting overflow with an ellipsis. Omitted = no clamp; the toast grows to fit. |
| `icon`             | `boolean \| ReactNode`                           | `true`      | Show the default icon, hide it with `false`, or pass a custom one. |
| `action`           | `ReactElement`                                   | -           | Optional action element, typically a `FlexButton`.                 |
| `showCloseButton`  | `boolean`                                        | `true`      | Show the trailing dismiss control when `onClose` is provided.      |
| `progress`         | `boolean`                                        | `false`     | Show a bottom countdown strip when `duration` is positive; sticky toasts keep a static strip. |
| `duration`         | `number`                                         | -           | Auto-dismiss duration in ms. `0` or omitted = no auto-dismiss.     |
| `onClose`          | `() => void`                                     | -           | Close handler. Required for the close button to render.            |

Inherits `ThemeOverrideProps` (`themeMode`, `themeProductContext`).

### `<ToastGroup>`

Renders the stack of visible toasts. On web uses `ReactDOM.createPortal` to escape stacking contexts; on React Native uses absolute positioning.

| Prop        | Type                            | Default    | Description                 |
| ----------- | ------------------------------- | ---------- | --------------------------- |
| `toasts`    | `ToastData[]`                   | -          | Toasts to render.           |
| `position`  | `"top" \| "bottom"`             | `"top"`    | Vertical anchor.            |
| `align`     | `"left" \| "center" \| "right"` | `"center"` | Horizontal alignment.       |
| `maxWidth`  | `number`                        | -          | Maximum width in px.        |
| `onDismiss` | `(id: string) => void`          | -          | Dismiss callback per toast. |

Inherits `ThemeOverrideProps`.

### `useToast()`

Hook that returns toast helpers. Throws if called outside a `ToastProvider`.

| Method       | Signature                                         | Description                                           |
| ------------ | ------------------------------------------------- | ----------------------------------------------------- |
| `toast`      | `(options: ToastOptions) => string`               | Show a toast with full options. Returns the toast ID. |
| `alert`      | `(options: Omit<ToastOptions, "type">) => string` | Show an alert toast.                                  |
| `success`    | `(options: Omit<ToastOptions, "type">) => string` | Show a success toast.                                 |
| `neutral`    | `(options: Omit<ToastOptions, "type">) => string` | Show a neutral toast.                                 |
| `warning`    | `(options: Omit<ToastOptions, "type">) => string` | Show a warning toast.                                 |
| `dismiss`    | `(id: string) => void`                            | Dismiss a specific toast by ID.                       |
| `dismissAll` | `() => void`                                      | Dismiss all visible toasts.                           |

### `ToastOptions`

| Property           | Type                                             | Default                      | Description                                                        |
| ------------------ | ------------------------------------------------ | ---------------------------- | ------------------------------------------------------------------ |
| `type`             | `"alert" \| "success" \| "neutral" \| "warning"` | `"neutral"`                  | Visual and accessibility type.                                     |
| `title`            | `string`                                         | -                            | Short primary text.                                                |
| `description`      | `string`                                         | -                            | Optional supporting text. Not clamped by default.                  |
| `descriptionLines` | `number`                                         | -                            | Clamp the description to this many lines. Omitted = no clamp.      |
| `icon`             | `boolean \| ReactNode`                           | `true`                       | Show, hide, or override the icon.                                  |
| `action`           | `ReactElement`                                   | -                            | Optional action element.                                           |
| `showCloseButton`  | `boolean`                                        | `true`                       | Show the close button when the toast is dismissible.               |
| `progress`         | `boolean`                                        | `false`                      | Show a bottom countdown strip when `duration` is positive; sticky toasts keep a static strip. |
| `duration`         | `number`                                         | provider's `defaultDuration` | Auto-dismiss duration. `0` keeps the toast until dismissed manually. |

### `ToastData`

Internal shape exposed via `ToastContext.toasts`.

| Property           | Type        | Description                                  |
| ------------------ | ----------- | -------------------------------------------- |
| `id`               | `string`    | Unique identifier generated by the provider. |
| `type`             | `ToastType` | Toast type.                                  |
| `title`            | `string`    | Primary text.                                |
| `description`      | `string`    | Supporting text.                             |
| `descriptionLines` | `number`    | Optional description line clamp.             |
| `duration`         | `number`    | Auto-dismiss duration.                       |

### `ToastContext`

React context with `{ toasts, addToast, dismissToast, dismissAllToasts }`. Typically consumed via `useToast`; available for advanced use cases:

```tsx
import { useContext } from "react";
import { ToastContext } from "@xsolla/xui-toast";

function ActiveCount() {
  const ctx = useContext(ToastContext);
  if (!ctx) throw new Error("Must be within ToastProvider");
  return <span>Active toasts: {ctx.toasts.length}</span>;
}
```

## Examples

### Types

```tsx
import * as React from "react";
import { useToast } from "@xsolla/xui-toast";

export default function Types() {
  const toast = useToast();
  return (
    <div style={{ display: "flex", gap: 8 }}>
      <button onClick={() => toast.neutral({ title: "Reward claimed" })}>
        Neutral
      </button>
      <button onClick={() => toast.success({ title: "Quest activated" })}>
        Success
      </button>
      <button onClick={() => toast.warning({ title: "Session expires soon" })}>
        Warning
      </button>
      <button onClick={() => toast.alert({ title: "Connection lost" })}>
        Alert
      </button>
    </div>
  );
}
```

### Long descriptions

The description grows to fit its content, so a long or localised message is
shown in full rather than cut off. Pass `descriptionLines` only when the
surrounding layout cannot absorb a taller toast — translations run longer than
their English source, so a clamp that fits today may truncate tomorrow.

```tsx
import * as React from "react";
import { useToast } from "@xsolla/xui-toast";

export default function LongDescription() {
  const toast = useToast();
  return (
    <div style={{ display: "flex", gap: 8 }}>
      <button
        onClick={() =>
          toast.alert({
            title: "Could not link account",
            description:
              "This account is already connected to a different Xsolla ID. Try a different account.",
          })
        }
      >
        Full message (default)
      </button>
      <button
        onClick={() =>
          toast.alert({
            title: "Could not link account",
            description:
              "This account is already connected to a different Xsolla ID. Try a different account.",
            descriptionLines: 2,
          })
        }
      >
        Clamped to 2 lines
      </button>
    </div>
  );
}
```

### Standalone Toast With Action

```tsx
import * as React from "react";
import { FlexButton } from "@xsolla/xui-button";
import { Toast } from "@xsolla/xui-toast";

export default function Standalone() {
  return (
    <Toast
      id="retry"
      type="alert"
      title="Connection lost"
      description="Check your network and try again"
      action={<FlexButton>Retry</FlexButton>}
      progress
      duration={5000}
      onClose={() => {}}
    />
  );
}
```

### Custom duration

```tsx
import * as React from "react";
import { useToast } from "@xsolla/xui-toast";

export default function Duration() {
  const toast = useToast();
  return (
    <button
      onClick={() =>
        toast.toast({
          type: "neutral",
          title: "Manual dismiss",
          duration: 0,
        })
      }
    >
      Show sticky toast
    </button>
  );
}
```

### Position and alignment

```tsx
import * as React from "react";
import { ToastProvider, useToast } from "@xsolla/xui-toast";

function Trigger() {
  const toast = useToast();
  return (
    <button onClick={() => toast.neutral({ title: "Bottom-right toast" })}>
      Show
    </button>
  );
}

export default function BottomRight() {
  return (
    <ToastProvider position="bottom" align="right" maxWidth={440}>
      <Trigger />
    </ToastProvider>
  );
}
```

## Platform support

| Aspect         | Web                                        | React Native                      |
| -------------- | ------------------------------------------ | --------------------------------- |
| Rendering      | `ReactDOM.createPortal` to `document.body` | Absolute-positioned `Box` in tree |
| Event handlers | `onClick`                                  | `onPress`                         |
| Root container | Any element                                | Must have `flex: 1`               |

## Accessibility

- Neutral and success toasts render with `role="status"` and polite announcements.
- Warning and alert toasts render with `role="alert"` and assertive announcements.
- The close button has `aria-label="Dismiss notification"`.
- Do not rely on color alone; the icon and text also communicate the type.

## Troubleshooting

- **"useToast must be used within a ToastProvider"** - ensure the calling component is a descendant of `ToastProvider`.
- **Description text is cut off with an ellipsis** - a `descriptionLines` value is clamping it. Remove the prop to let the toast grow to fit, or raise the limit.
- **Toasts hidden on React Native** - wrap content in `<View style={{ flex: 1 }}>` inside `ToastProvider`.
- **Toasts appear behind a modal (web)** - `ToastGroup` uses `z-index: 9999`. Place `ToastProvider` above the modal in the tree, or adjust z-index.
- **Auto-dismiss not working** - `duration: 0` disables auto-dismiss; check both per-toast and provider defaults.
