Toast
A transient notification panel that floats above page content to report the outcome of an action, colored by one of five semantic tones and structured like an inline status panel.
View code
let toaster = new Toaster<Notice>({ defaultDuration: 6000 });
toaster.addEventListener("change", () => void handle.update());
<div mix={[hstack({ gap: 2, align: "center" })]}>
<Button mix={[on<HTMLButtonElement, "click">("click", () => void toaster.add(notices.saved))]}>
Publish
</Button>
<Button
color="danger"
variant="outline"
mix={[on<HTMLButtonElement, "click">("click", () => void toaster.add(notices.failed))]}
>
Upload a bad file
</Button>
<Button
variant="outline"
mix={[on<HTMLButtonElement, "click">("click", () => void toaster.add(notices.invited))]}
>
Invite a teammate
</Button>
</div>
<Toast.Region aria-label="Notifications" placement="bottom-end">
{toaster.toasts.map((toast) => (
<Toast
key={toast.id}
id={`preview-toast-${toast.id}`}
color={toast.data.color}
live={toast.data.color === "danger" ? "assertive" : "polite"}
mix={[
// The queue owns the countdown, so the mixin is here only to answer
// the close button's --ui-dismiss command.
dismiss({ duration: null }),
on<HTMLDivElement, "ui:dismiss">("ui:dismiss", () => void toaster.dismiss(toast.id)),
on<HTMLDivElement, "pointerenter">("pointerenter", () => toaster.pause(toast.id)),
on<HTMLDivElement, "pointerleave">("pointerleave", () => toaster.resume(toast.id)),
]}
>
<Toast.Icon>
{toast.data.color === "success" ? (
<CircleCheckIcon />
) : toast.data.color === "danger" ? (
<CircleAlertIcon />
) : (
<InfoIcon />
)}
</Toast.Icon>
<Toast.Content>
<Toast.Title>{toast.data.title}</Toast.Title>
<Toast.Description>{toast.data.description}</Toast.Description>
</Toast.Content>
{toast.data.action ? <Toast.Action>{toast.data.action}</Toast.Action> : null}
<Toast.Close
aria-label="Dismiss"
commandfor={`preview-toast-${toast.id}`}
command="--ui-dismiss"
/>
</Toast>
))}
</Toast.Region>Installation
An ordinary dependency: install it, import the theme once, and import the component where it is used.
npm add @sdxc/uiimport "@sdxc/ui/theme.css";Usage
import { Toast } from "@sdxc/ui";Renders a single toast panel, tinted, bordered, and shadowed by
Toast.Props.color, with role and aria-atomic defaulted
(overridable); pair a mix-driven enterExit(), since it holds still.
Composition
The parts the component publishes, each a static property of the host.
ToastToast.IconRenders Toast.IconProps.children as the toast's leading icon, hidden from assistive technology since the toast's color and text already carry its meaning; swap in Toast.Loader while pending.Toast.LoaderRenders Toast.LoaderProps.children as the toast's loading graphic, laid out identically to Toast.Icon so swapping between them keeps the layout stable; pair aspin()mixfactory for its rotating loop.Toast.ContentRenders Toast.ContentProps.children as the toast's body block: a column flex container that shrinks to fit alongside a leading icon or trailing action, holding Toast.Title and Toast.Description.Toast.TitleRenders Toast.TitleProps.children as the toast's heading, in a native<h3>sized and weighted as a small, single-line label above the toast's description.Toast.DescriptionRenders Toast.DescriptionProps.children as the toast's supporting message, in a native<p>set slightly translucent against the toast's foreground color so it reads as secondary to Toast.Title.Toast.ActionRenders Toast.ActionProps.children as the toast's primary trailing control;typedefaults to"button", ordered before the consumer's attributes socommand/commandforstill run; usetype="submit"for a form.Toast.CancelRenders Toast.CancelProps.children as a neutral-tinted, secondary trailing control;typedefaults to"button", ordered before the consumer's attributes socommand/commandforstill run.Toast.CloseRenders a dismiss control for the ancestor Toast: an icon-only<button>removable via a consumer's mixin or thecommandfor/commandpair;type="button"precedes those attributes so the pairing still runs.Toast.RegionRenders the fixed viewport stacking every queued Toast at a corner via Toast.RegionProps.placement;pointer-eventsstaysnoneso clicks pass through to content behind, restored per toast.
Examples
<Toast color="danger" live="assertive">
<Toast.Content>
<Toast.Title>{t("toasts.uploadFailed.title")}</Toast.Title>
</Toast.Content>
<Toast.Action>{t("actions.retry")}</Toast.Action>
<Toast.Cancel>{t("actions.dismiss")}</Toast.Cancel>
</Toast>Props
Read from the component's own types, so every prop, every default and every allowed value is listed.
| Prop | Type | Description |
|---|---|---|
color? | "brand" | "neutral" | "success" | "warning" | "danger" | Semantic tone of the toast. Default: "neutral". |
live? | "polite" | "assertive" | "off" | aria-live politeness for the toast's content. Default: "polite". |
children | RemixNode | The toast's compound parts: Toast.Icon, Toast.Content, Toast.Action, or any other content. |
Also accepts everything in TagProps<"div">.
Toast.Icon
| Prop | Type | Description |
|---|---|---|
children | RemixNode | The icon graphic, typically a single SVG icon. |
Also accepts everything in TagProps<"div">.
Toast.Loader
| Prop | Type | Description |
|---|---|---|
children | RemixNode | The loading graphic shown in place of Toast.Icon while the toast represents pending work. |
Also accepts everything in TagProps<"div">.
Toast.Content
| Prop | Type | Description |
|---|---|---|
children | RemixNode | The toast's title, description, or other body content. |
Also accepts everything in TagProps<"div">.
Toast.Title
| Prop | Type | Description |
|---|---|---|
children | RemixNode | The toast's heading text. |
Also accepts everything in TagProps<"h3">.
Toast.Description
| Prop | Type | Description |
|---|---|---|
children | RemixNode | The toast's supporting message text. |
Also accepts everything in TagProps<"p">.
Toast.Action
| Prop | Type | Description |
|---|---|---|
children | RemixNode | The action's label. |
Also accepts everything in TagProps<"button">.
Toast.Cancel
| Prop | Type | Description |
|---|---|---|
children | RemixNode | The cancel control's label. |
Also accepts everything in TagProps<"button">.
Toast.Close
| Prop | Type | Description |
|---|---|---|
"aria-label" | string | Accessible label for the icon-only control — required, since the button carries no visible text for assistive technology to read. |
Also accepts everything in TagProps<"button">.
Toast.Region
| Prop | Type | Description |
|---|---|---|
placement? | "top-start" | "top-center" | "top-end" | "bottom-start" | "bottom-center" | "bottom-end" | Corner of the viewport the stack renders in. Default: "bottom-end". |
children | RemixNode | Every currently queued Toast. |
Also accepts everything in TagProps<"div">.
Types
| Name | Values | What it decides |
|---|---|---|
Color | "brand" | "neutral" | "success" | "warning" | "danger" | Semantic tone driving the host element's border, tint, and foreground color through the --ui-* variables for that color. |
Live | "polite" | "assertive" | "off" | aria-live politeness applied to the host element. |
Placement | "top-start" | "top-center" | "top-end" | "bottom-start" | "bottom-center" | "bottom-end" | Corner of the viewport Toast.Region renders in. |