Toast
A short message after an action, such as a save or a delete. It stacks in a corner, shows in the top layer above dialogs and closes on its own.
Basic
Call tsunami.toast() with a message. A plain string
becomes the title. It closes after 5 seconds.
<button type="button" class="btn" id="show">Show toast</button>
<script>
document.getElementById("show").addEventListener("click", () => {
tsunami.toast("Order archived");
});
</script>
Variants
variant adds an icon and colours it like the alert.
Danger toasts use role="alert" and stay until closed.
tsunami.toast({ variant: "ok", title: "Order saved" });
tsunami.toast({ variant: "info", title: "Rates updated" });
tsunami.toast({ variant: "warning", title: "Low stock" });
tsunami.toast({ variant: "danger", title: "Payment failed" }); // stays
tsunami.toast({ variant: "neutral", title: "Draft restored" });
Title and text
Add text for a second, quieter line.
tsunami.toast({
variant: "ok",
title: "Customer imported",
text: "128 rows added, 3 skipped.",
});
Action
action adds a button. Clicking it runs
onClick and closes the toast with reason
action.
tsunami.toast({
title: "Order deleted",
action: { label: "Undo", onClick: () => restoreOrder() },
});
Sticky
duration: 0 keeps the toast until the user closes it
or code dismisses it.
const t = tsunami.toast({ title: "Sync in progress", duration: 0 });
// later
t.dismiss();
// or remove every toast
tsunami.toast.clear();
Replace by id
A new toast with the same id replaces the old one, so
repeated actions do not pile up.
tsunami.toast({ id: "draft", title: "Draft saved at " + new Date().toLocaleTimeString() });
Positions
Set data-position on the
.toaster element: bottom-end (default),
top-end, bottom-center,
top-center. Narrow screens always use a full-width
strip at the bottom.
const toast = tsunami.toast("Moved to the top");
toast.parentElement.dataset.position = "top-end";
// or write the container yourself
// <section class="toaster" popover="manual" aria-live="polite" data-position="top-end"></section>
Declarative
Put data-toast on a button. Add
data-toast-variant and
data-toast-title as needed. With a title, the
attribute value is the text.
<button type="button" class="btn" data-toast="Link copied">Copy link</button>
<button
type="button"
class="btn"
data-toast="Changes were sent to 3 customers."
data-toast-title="Email sent"
data-toast-variant="ok"
>
Send email
</button>
After a save
Pair it with the save bar: save, mark the form clean, then confirm with a toast.
<form id="product-form" method="dialog">...</form>
<div class="save-bar" data-form="product-form" role="region" aria-label="Unsaved changes" hidden>
...
</div>
<script>
const form = document.getElementById("product-form");
form.addEventListener("submit", async () => {
await save(new FormData(form));
tsunami.saveBar.markClean(form);
tsunami.toast({ variant: "ok", title: "Product saved" });
});
</script>
Inside a dialog
The toaster sits in the top layer above open dialogs. While a modal dialog is open the script moves it into the dialog, so its close and action buttons are not blocked by the inert page.
<button type="button" class="btn" data-toast="Note saved" data-toast-variant="ok">Save note</button>
API
| Name | Values | Description |
|---|---|---|
tsunami.toast(options) |
{ title, text, variant, duration, action, id }
|
Shows a toast and returns its element, which has
.dismiss(). A string is shorthand for
{ title }.
|
title |
string | Bold first line. |
text |
string | Second, quieter line. |
variant |
ok, danger,
warning, info,
neutral
|
Icon and colour. Omit for no icon.
danger uses role="alert"; the
rest use role="status".
|
duration |
milliseconds |
Time before it closes. Default 5000. 0 stays.
Danger defaults to 0.
|
action |
{ label, onClick } |
Adds a small button. Clicking it calls
onClick(toast) and closes the toast.
|
id |
string | A toast with the same id is replaced. |
.dismiss() |
Closes that toast (reason api). |
|
tsunami.toast.clear() |
Closes every toast. |
Events
Listen on document or on the toast itself.
| Name | Values | Description |
|---|---|---|
tsunami:toast |
detail: {} |
Bubbles from the toast when it is shown. |
tsunami:toastclose |
detail.reason |
Bubbles from the toast when it is dismissed. Reason:
timeout, close (close button),
action (action button) or
api (dismiss(),
clear(), replaced by id, or pushed out by the
limit of 3).
|
Attributes
| Name | Values | Description |
|---|---|---|
.toaster |
Container:
<section popover="manual"
aria-live="polite">. The script creates it when missing and shows it with
showPopover().
|
|
data-position |
bottom-end (default), top-end,
bottom-center, top-center
|
Set on .toaster. Under 40rem wide the stack
is a full-width strip at the bottom.
|
.toast |
One message: icon, body, optional action, close button. A 1px border separates it; there is no shadow. | |
data-variant |
ok, danger,
warning, info,
neutral
|
Icon and its colour, as on .alert. |
data-toast |
message | On a button: shows a toast with this message on click. |
data-toast-title |
string |
Makes data-toast the text and this the title.
|
data-toast-variant |
as data-variant |
Variant of the declarative toast. |
data-leaving |
Set by the script while a toast fades out. |
Accessibility
-
Normal toasts are
role="status"inside a polite live region. Danger toasts arerole="alert"and stay until closed. - The timer pauses while the pointer is over a toast, while focus is inside it, and while the page is hidden, so nobody loses a message or an Undo button.
-
The close button has
aria-label="Close". Icons arearia-hidden. - Keep toasts short. Do not put the only copy of an important result in one: also update the page.
- At most 3 toasts show at once; the oldest leaves first.
-
Motion is a short fade and slide, and stops under
prefers-reduced-motion.