Toast notifications, the Shadix take on shadcn's sonner integration.
shadcn delegates entirely to the sonner npm library (a React component with
its own imperative toast() API). There is no equivalent runtime in Phoenix,
so this module is a Phoenix.LiveComponent that owns a :toasts stream and
renders an accessible region: a visually-hidden polite region, a
visually-hidden assertive region, and the visible stream <ol> of toast/1s.
Usage
Mount once in your layout (or root LiveView):
<.live_component module={Shadix.Components.Sonner} id="shadix-toaster" flash={@flash} />Trigger a toast from a LiveView:
def handle_event("save", _p, socket) do
Shadix.Components.Sonner.send_toast(title: "Saved", message: "Done", variant: "success")
{:noreply, socket}
endExisting flashes also appear automatically and are cleared so they won't replay:
{:noreply, put_flash(socket, :error, "Something went wrong")}Variants
Supported variant values are default | success | info | warning | error.
The error and warning variants are considered high-priority and use
role="alertdialog" with an aria-live="assertive" announcement channel.
All other variants use role="dialog" with an aria-live="polite" channel.
Accessibility
Each toast is role="dialog" or role="alertdialog", aria-modal="false",
focusable (tabindex="0"), and links its title/description via
aria-labelledby/aria-describedby. Two sr-only live regions (polite and
assertive) announce new toasts to screen readers. The ShadixSonner JS hook
handles: auto-dismiss after ~4 s, pause-on-hover/focus, Escape to dismiss
the focused toast, and F6 to move focus to the toast stack. Entry/exit
animation is pure-CSS via data-state; prefers-reduced-motion disables
transitions.
Simplifications vs. sonner
This v1 deliberately scopes out a lot of the sonner feature set:
- No swipe-to-dismiss, action buttons, promise toasts, programmatic update/dismiss-by-id, stacking-collapse, or position configuration (fixed bottom-right). The duration is a fixed ~4 s.
Summary
Functions
Builds a normalized toast map from user-supplied options. Pure; used by
send_toast/2 and the flash bridge to feed the :toasts stream.
Sends a toast to a mounted Sonner live component from anywhere with access to
the component (typically a LiveView handle_event/handle_info). opts are
passed to build_toast/1 (:title, :message, :variant, optional :id).
A single toast notification. Rendered by the Sonner live component's stream
loop, but kept as a standalone function component so its accessibility markup
is testable in isolation.
Functions
Builds a normalized toast map from user-supplied options. Pure; used by
send_toast/2 and the flash bridge to feed the :toasts stream.
Sends a toast to a mounted Sonner live component from anywhere with access to
the component (typically a LiveView handle_event/handle_info). opts are
passed to build_toast/1 (:title, :message, :variant, optional :id).
A single toast notification. Rendered by the Sonner live component's stream
loop, but kept as a standalone function component so its accessibility markup
is testable in isolation.
:variant drives both styling and a11y priority: error/warning render as
role="alertdialog" (high priority), the rest as role="dialog". The toast is
focusable (tabindex="0") and links its title/description via
aria-labelledby/aria-describedby only when those elements exist. Dismissal
is delegated to the ShadixSonner hook (the close button carries no
phx-click), and enter/exit is pure-CSS via data-state.
Attributes
id(:string) (required)variant(:string) - Defaults to"default". Must be one of"default","success","info","warning", or"error".title(:string) - Defaults tonil.message(:string) - Defaults tonil.class(:string) - Defaults tonil.- Global attributes are accepted.
Slots
inner_block