v1.0.0

<bmx-toast>

A short message about something that has already happened: saved, sent, copied, or failed. It appears in a corner, says one sentence, and goes away on its own.

12 properties · 3 events · 2 methods · 7 parts

Example

Save Delete message Fail to save Send six at once
Draft saved. Message moved to Deleted. Undo We could not reach the server. Your changes are still here. accounts-q3.xlsx uploaded. payroll-august.csv uploaded. contract-northwind.pdf uploaded. site-photos.zip uploaded. handover-notes.docx uploaded. brand-assets.sketch uploaded.
The first is an ordinary confirmation and goes after five seconds. The second carries an action and stays twice as long, because it has to be read, decided on, and then reached — and a keyboard user has further to travel than a pointer user. The third is an alert: it interrupts a screen reader rather than waiting for a gap, and it stays until it is dismissed, because interrupting somebody and then taking the message away unread is the worst of both.
Press Send six at once and watch two things. New toasts are placed beyond the ones already on screen rather than at the corner, so nothing you are halfway through reading slides away underneath you. And only three are shown at a time — the rest wait, and take a place as one frees.
Rest the pointer anywhere in the stack, or Tab into one of the actions, and every clock in that corner stops until you leave. Switching to another tab does the same: a notification sent to a window nobody is looking at has not been seen. What is left of the time is kept rather than restarted, so a hover cannot pin a toast on screen for ever.
Show markup
<div class="row">
  <bmx-button id="ex-toast-save" variant="outline" tone="neutral">Save</bmx-button>
  <bmx-button id="ex-toast-delete" variant="outline" tone="neutral">Delete message</bmx-button>
  <bmx-button id="ex-toast-fail" variant="outline" tone="danger">Fail to save</bmx-button>
  <bmx-button id="ex-toast-burst" variant="outline" tone="neutral">Send six at once</bmx-button>
</div>

<bmx-toast id="ex-toast-saved" tone="success" position="bottom-end">Draft saved.</bmx-toast>

<bmx-toast id="ex-toast-undo" position="bottom-end">
  Message moved to Deleted.
  <bmx-button slot="action" size="sm" variant="ghost" id="ex-toast-undo-button">Undo</bmx-button>
</bmx-toast>

<bmx-toast id="ex-toast-error" tone="danger" priority="alert" heading="Could not save" position="bottom-end">
  We could not reach the server. Your changes are still here.
</bmx-toast>

<!--
  A fixed pool for the burst, reused rather than created on each press. The demo
  runtime counts every element the library registers and stops at a hundred, and
  the count never comes back down when one is removed - so a demonstration that
  made a toast per press would work for a minute and then start showing
  "Demo element limit reached". Opening the same elements again costs nothing.
-->
<bmx-toast class="ex-toast-file" tone="info" position="bottom-end">accounts-q3.xlsx uploaded.</bmx-toast>
<bmx-toast class="ex-toast-file" tone="info" position="bottom-end">payroll-august.csv uploaded.</bmx-toast>
<bmx-toast class="ex-toast-file" tone="info" position="bottom-end">contract-northwind.pdf uploaded.</bmx-toast>
<bmx-toast class="ex-toast-file" tone="info" position="bottom-end">site-photos.zip uploaded.</bmx-toast>
<bmx-toast class="ex-toast-file" tone="info" position="bottom-end">handover-notes.docx uploaded.</bmx-toast>
<bmx-toast class="ex-toast-file" tone="info" position="bottom-end">brand-assets.sketch uploaded.</bmx-toast>

<div class="row">
  <span class="note">
    The first is an ordinary confirmation and goes after five seconds. The second carries an action and stays twice as
    long, because it has to be read, decided on, and then reached &mdash; and a keyboard user has further to travel than
    a pointer user. The third is an <code>alert</code>: it interrupts a screen reader rather than waiting for a gap, and
    it stays until it is dismissed, because interrupting somebody and then taking the message away unread is the worst
    of both.
  </span>
</div>

<div class="row">
  <span class="note">
    Press <em>Send six at once</em> and watch two things. New toasts are placed <em>beyond</em> the ones already on
    screen rather than at the corner, so nothing you are halfway through reading slides away underneath you. And only
    three are shown at a time &mdash; the rest wait, and take a place as one frees.
  </span>
</div>

<div class="row">
  <span class="note">
    Rest the pointer anywhere in the stack, or Tab into one of the actions, and every clock in that corner stops until
    you leave. Switching to another tab does the same: a notification sent to a window nobody is looking at has not been
    seen. What is left of the time is kept rather than restarted, so a hover cannot pin a toast on screen for ever.
  </span>
</div>

<script>
  (function () {
    // Reopening one that is already on screen has to close it first: `open` is
    // already true, so setting it to true again is not a change and the
    // component rightly does nothing.
    const open = id => {
      const toast = document.getElementById(id);

      toast.open = false;
      requestAnimationFrame(() => (toast.open = true));
    };

    document.getElementById('ex-toast-save').addEventListener('bmxActivate', () => open('ex-toast-saved'));
    document.getElementById('ex-toast-delete').addEventListener('bmxActivate', () => open('ex-toast-undo'));
    document.getElementById('ex-toast-fail').addEventListener('bmxActivate', () => open('ex-toast-error'));

    document.getElementById('ex-toast-undo-button').addEventListener('bmxActivate', () => {
      // The toast closes itself once its action is pressed, so an Undo never
      // leaves its own message sitting there looking as though it did nothing.
      open('ex-toast-saved');
    });

    document.getElementById('ex-toast-burst').addEventListener('bmxActivate', () => {
      document.querySelectorAll('.ex-toast-file').forEach((toast, index) => {
        setTimeout(() => {
          toast.open = false;
          requestAnimationFrame(() => (toast.open = true));
        }, index * 200);
      });
    });
  })();
</script>
<bmx-toast id="saved" tone="success">Draft saved.</bmx-toast>
<script>
  document.getElementById('saved').open = true;
</script>

WHEN NOT TO REACH FOR ONE

If the user has to decide something, this is the wrong component: a message that leaves on a timer cannot be one somebody must act on, and a toast is easy to miss by design - it is in the corner, it is brief, and it is gone. Use bmx-dialog when an answer is required, and the page itself when the information is part of the task rather than about it. A toast may carry one action - Undo, View - as a convenience, but the sentence has to make sense to somebody who never reaches it.

WHAT MAKES THIS ONE DIFFERENT FROM A BOX WITH A TIMER

Everything that only happens when there is more than one of them, or when somebody is actually reading:

  • A stack that does not move. New toasts are placed beyond the ones already on screen rather than at the corner, so nothing you are halfway through reading slides away as another arrives.
  • A clock that stops. Hovering or focusing anywhere in the corner pauses every toast in it, and so does switching tabs - a notification sent to a window nobody is looking at has not been seen. What is left of the time is kept rather than restarted, so a hover cannot pin a toast on screen for ever.
  • A queue with an end. Three at a time, eight more waiting, and past that the oldest waiting message is dropped rather than the newest - see src/core/toast.ts for why round that way.

status OR alert

status is polite and waits for a gap in whatever a screen reader is saying. alert interrupts. Nearly everything is status; keep alert for the cases where being interrupted is better than not knowing, and note that an alert stays until it is dismissed, because interrupting somebody and then removing the message unread is the worst of both. The judgement is the same one that separates bmx-tooltip from bmx-popover, in different clothes.

Properties

PropertyAttributeTypeDefaultDescription
closeLabel close-label string 'Dismiss' The dismiss button's accessible name.
closeOnAction close-on-action boolean true Whether pressing the action closes the toast.
dismissible dismissible boolean true Whether to draw the dismiss button.
duration duration number How long it stays, in milliseconds. Zero or less keeps it until dismissed. Left unset it is five seconds, ten for a toast carrying an action - which has to be read, decided on and then reached - and sticky for an alert.
heading heading string A short bold line above the message.
hideIcon hide-icon boolean false Whether the tone's own glyph is drawn when nothing fills the icon slot.
limit limit number 3 How many toasts this corner shows at once. A property of the corner rather than of this toast, carried here because there is no region element for a consumer to configure. The value on whichever toast is opening is the one that applies, which is harmless in practice: an application sets these once, in the helper that creates its toasts, so every toast in a corner carries the same pair.
open open boolean false Whether the toast is asking to be on screen. Asking, rather than being: a corner already showing its limit holds this one back until a slot frees, and open stays true throughout - it is what the consumer asked for, and the component reports what actually happened through bmxOpenChange.
position position BmxToastPosition 'bottom-end' Which corner it appears in. start and end mirror under RTL.
priority priority BmxToastPriority 'status' How it is announced: politely, or by interrupting. alert is for the cases where not knowing is worse than being interrupted. It also makes the toast sticky by default.
queueLimit queue-limit number 8 How many may wait behind them before the oldest waiting one is dropped.
tone tone BmxTone 'neutral' The semantic colour. Chosen by meaning, not by colour.

Events

EventDetailDescription
bmxAction void Fired when the action was pressed, before the toast starts to leave.
bmxClose BmxToastCloseDetail Fired once it has finished leaving, with why it went.
bmxOpenChange boolean Fired when the toast reaches the screen, and when it has left it.

Methods

MethodSignatureDescription
hide hide() => Promise<void> Hide it, whatever it is currently doing.
show show() => Promise<void> Show it.

Slots

SlotDescription
(default) The message.
action One control: Undo, Retry, View.
heading A short bold line above it.
icon Replaces the tone's own glyph.

CSS shadow parts

PartDescription
action The wrapper around the action slot.
close The dismiss button.
content Heading and message together.
heading The bold line.
icon The glyph beside the message.
message The message itself.
surface The box, and the live region.

CSS custom properties

PropertyDescription
--bmx-toast-accent The bar down the leading edge, and the icon's colour. Follows the tone by default.
--bmx-toast-background The box's background.
--bmx-toast-border-color Its border. Carries the shape under forced colours.
--bmx-toast-color Its text colour.
--bmx-toast-font-size The message's text size.
--bmx-toast-gap Space between the icon, the message and the action.
--bmx-toast-inline-size The box's width. It is a fixed width rather than a maximum, so a stack of them has one edge.
--bmx-toast-inset How far the stack sits from the edges of the viewport.
--bmx-toast-padding-block Space above and below the content.
--bmx-toast-padding-inline Space either side of it.
--bmx-toast-radius The corner radius.
--bmx-toast-shadow The shadow under the box.
--bmx-toast-z-index Only used on a browser with no top layer, where the stack has to be given one by hand.