# `SaladUI.Toast`
[🔗](https://github.com/bluzky/salad_ui/blob/main/lib/salad_ui/toast.ex#L1)

Toast notifications, parity target [Sonner](https://sonner.emilkowal.ski/).

Unlike every other SaladUI component, `toaster/1` does not manage a single
open/closed state machine — it mounts once per page and manages an unbounded,
dynamically created stack of independent toast cards entirely in JavaScript
(`assets/salad_ui/components/toast.js`).

## Mounting

    <.toaster id="toaster" position="bottom-right" />

## Triggering toasts

Use the helpers below from any `handle_event/3`:

    def handle_event("save", _params, socket) do
      {:noreply, SaladUI.Toast.toast_success(socket, "Saved!")}
    end

## Rich-content toasts

Declare a `:template` next to the toaster and reference it by name:

    <.toaster id="toaster">
      <:template name="new-follower">
        <img src={@avatar_url} class="size-8 rounded-full" />
        <p><%= @name %> followed you</p>
      </:template>
    </.toaster>

    SaladUI.Toast.toast(socket, template: "new-follower")

## Customization

SaladUI ships no predefined per-variant palette — every toast renders in the
card's neutral default (`border-border bg-background text-foreground`)
unless you opt in, either toaster-wide or per call.

Toaster-wide, applied to every card of that variant:

    <.toaster
      id="toaster"
      colors={%{success: "border-emerald-500/30 bg-emerald-50 text-emerald-900"}}
    />

Per-toast, overriding the toaster-wide default for just this call:

    SaladUI.Toast.toast_success(socket, "Saved!",
      color: "border-emerald-500/30 bg-emerald-50 text-emerald-900"
    )

`color`/`colors` replace the card's default border/background/text as one
unit — the icon and title inherit the resulting text color, while the
description keeps its own muted color. The toaster's `class` attr and a
per-toast `class` opt instead *append* to the default classes, so reach for
`!`-prefixed utilities there to reliably win the cascade:

    SaladUI.Toast.toast(socket, "Something broke",
      class: "!border-fuchsia-500/30 !bg-fuchsia-50 !text-fuchsia-900"
    )

`unstyled: true` drops every built-in class, leaving bare `data-part`
elements for `class` to style from scratch:

    SaladUI.Toast.toast(socket, "Bare toast", unstyled: true, class: "my-toast")

`action`/`cancel` accept an optional `value` map, merged into the event
payload pushed back to the LiveView (alongside `id`):

    SaladUI.Toast.toast(socket, "File deleted",
      action: %{label: "Undo", event: "undo_delete", value: %{file_id: file.id}},
      cancel: %{label: "Dismiss"}
    )

    def handle_event("undo_delete", %{"id" => _id, "file_id" => file_id}, socket) do
      # restore file_id
      {:noreply, socket}
    end

## Phoenix flash integration

Pass `flash` to render flash messages as toasts instead of a second
notification system:

    <.toaster id="toaster" flash={@flash} />

# `put_toast`

Drop-in-shaped alongside `Phoenix.LiveView.put_flash/3` /
`Phoenix.Controller.put_flash/3` — same arity, same call sites, works on both.

On a `%Socket{}` this behaves like `toast/3` with `variant: kind`. On a
`%Plug.Conn{}` (no toaster hook exists to push a command to — the page
hasn't rendered yet) it falls back to `Phoenix.Controller.put_flash/3`, so a
controller action followed by a redirect still shows *something* instead of
silently dropping the message; once the LiveView the redirect lands on
renders `<.toaster flash={@flash} />`, the flash bridge (`toast_flash/1`)
picks it up and converts it to a real toast on connect.

# `toast`

Push a toast to a mounted `<.toaster>` (see `toaster/1`).

`message` is either a string, used as the toast's `title`, or (for
template-based toasts, see `toaster/1`'s `:template` slot) a keyword
list/map of opts with no title:

    socket = SaladUI.Toast.toast(socket, "Saved!")
    socket = SaladUI.Toast.toast(socket, "Saved!", description: "Changes are live")
    socket = SaladUI.Toast.toast(socket, template: "new-follower", variant: "default")

Call `toast/4` directly to target a `<.toaster>` other than the default `"toaster"` id.
See `toaster/1`'s moduledoc for the full opts table.

# `toast`

# `toast_dismiss`

Removes one toast, or every toast on the given toaster if `id` is omitted.

# `toast_error`

Same as `toast/3`, with `variant: "error"`.

# `toast_flash`

Bridges `@flash` into toasts on the given `toaster` without patching the
toaster's own DOM subtree (which would tear down its live stack/timers).

Usually rendered implicitly via `toaster/1`'s `:flash` attribute; call it
directly when the toaster and the LiveView holding `@flash` are mounted
separately.

## Attributes

* `id` (`:string`) - Defaults to `"toast-flash-bridge"`.
* `flash` (`:map`) (required)
* `toaster` (`:string`) - Defaults to `"toaster"`.

# `toast_info`

Same as `toast/3`, with `variant: "info"`.

# `toast_success`

Same as `toast/3`, with `variant: "success"`.

# `toast_update`

Transitions an existing toast (by `id`) to a new variant/content, patched in place.

# `toast_warning`

Same as `toast/3`, with `variant: "warning"`.

# `toaster`

Mounts the toast stack. Render exactly once per page (typically the root layout).

## Server commands

* `"add"` - Creates and mounts a new toast card, arms its timer.
* `"update"` - Patches an existing card in place, keyed by `id`.
* `"dismiss"` - Plays the exit animation and removes a card (or every card if `id` is omitted).

## Component events

* `:on-dismiss` - Fired whenever a toast is removed, with `%{id:, reason:}` in the payload.

## Attributes

* `id` (`:string`) - Defaults to `"toaster"`.
* `position` (`:string`) - Defaults to `"bottom-right"`. Must be one of `"top-left"`, `"top-center"`, `"top-right"`, `"bottom-left"`, `"bottom-center"`, or `"bottom-right"`.
* `expand` (`:boolean`) - Stack fully expanded instead of collapsed. Defaults to `false`.
* `visible-toasts` (`:integer`) - Max stacked cards before overflow is collapsed. Defaults to `3`.
* `gap` (`:integer`) - px gap between stacked toasts when expanded. Defaults to `14`.
* `swipe-direction` (`:string`) - Defaults to `nil`.Must be one of `"up"`, `"down"`, `"left"`, `"right"`, or `nil`.
* `width` (`:integer`) - Fixed card width in px. Defaults to `356`.
* `offset` (`:any`) - px gap from the viewport edge, or a %{top:, right:, bottom:, left:} map. Defaults to `24`.
* `mobile-offset` (`:any`) - Same as :offset, applied below the 600px breakpoint. Defaults to `16`.
* `hotkey` (`:string`) - Keyboard shortcut that moves focus into the front toast. Defaults to `"Alt+T"`.
* `icons` (`:map`) - toaster-wide icon overrides, e.g. %{success: "hero-sparkles"}. Defaults to `%{}`.
* `colors` (`:map`) - per-variant toast color override, e.g. %{success: "border-emerald-500/30 bg-emerald-50 text-emerald-900"} — replaces the card's default border/background/text (icon + title inherit the text color; description keeps its own muted color) for that variant. No variant ships with a predefined color. Defaults to `%{}`.
* `class` (`:string`) - extra classes merged onto every card — use `!`-prefixed utilities, e.g. "!bg-secondary", to reliably override a default color class. Defaults to `nil`.
* `flash` (`:map`) - when given, also renders the flash-to-toast bridge (see toast_flash/1). Defaults to `nil`.
* `on-dismiss` (`:any`) - Handler fired when any toast is removed. Payload includes `id` and `reason`. Defaults to `nil`.
## Slots

* `template` - named rich-content template, referenced from `toast(socket, template: name)`. Accepts attributes:

  * `name` (`:string`) (required)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
