Sonner
An opinionated toast component for PRISM.
Sonner is the toast system. It has a single Toaster component, mounted once in the layout: an empty fixed container. Toasts are then triggered from any script with document.dispatchEvent(new CustomEvent("bext-toast", { detail })); the island builds the toast, stacks it in the Toaster and removes it after the duration. The event must be dispatched on document, not on window.
The event detail accepts title, description, variant ("default", "success" or "destructive") and duration in milliseconds (4000 by default). Each toast has a close button. This port has no swipe-to-dismiss and no promise or loading state.
Installation
$ bext ui add sonner
Copy and paste the following into src/components/ui/sonner.tsx.
This component is interactive. Make sure the shared island is loaded (see Islands). It reacts to data-ui="sonner" hooks.
Usage
Anatomy
<Toaster position="bottom-right" />
API Reference
Toaster
| Prop | Type | Default | Description |
|---|---|---|---|
| position | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" | "bottom-right" | Corner or edge of the viewport where the toasts stack. |
| ariaLabel | string | "Notifications" | Accessible name of the region landmark. |
| closeLabel | string | "Close" | Accessible name of the close button of each toast. Written to data-close-label and read by the island. |
| className | string | — | Extra classes on the fixed container. |
bext-toast event detail
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | — | First line, in semibold. |
| description | string | — | Second line. |
| variant | "default" | "success" | "destructive" | "default" | Neutral, green-tinted or destructive colors. "destructive" is announced as an alert, the others politely. |
| duration | number | 4000 | Milliseconds before the toast is removed. |
Data Attributes
Attributes you can target in custom CSS to override the style for each state.
| Attribute | On | Values | Description |
|---|---|---|---|
| data-ui="toaster" | Toaster root | — | The island appends toasts to the first element with this attribute. |
| data-position | Toaster root | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right" | Reflects the position prop. |
| data-close-label | Toaster root | string | Reflects the closeLabel prop. The island uses it as the aria-label of each close button. |
| data-ui="toast-close" | close button of each toast | — | Clicking removes the toast. |
Keyboard
| Key | Action |
|---|---|
| Tab | Reaches the close button of each visible toast. |
| Enter / Space | On a close button: removes that toast. |
Accessibility
The Toaster is a landmark and a live region: role="region", aria-label ("Notifications" by default, ariaLabel to change it) and aria-live="polite". Each toast is inserted with role="status", so screen readers announce it when they are idle. A toast with variant: "destructive" gets role="alert" and aria-live="assertive" and interrupts.
The close button of each toast is named "Close"; pass closeLabel to the Toaster to translate it. Toasts disappear on a timer and cannot be paused, and the focus is not moved to them. Do not use a toast as the only feedback for an important result: also update the page (inline message, focus move).
Edit this component live in the bext playground. The PRISM + signals version is the bext-native idiom: fine-grained reactivity, no virtual DOM.
On This Page