Toast

.toast-region[data-ui="toast-region"]

Toast — a short message about something that just happened.

Examples

Polite by default, assertive only when it must interrupt

View “Polite by default, assertive only when it must interrupt” as
<div class="toast-region" data-ui="toast-region"></div>

<button class="btn btn-outline" type="button"
        onclick="mostlycss.toast('Changes saved', { variant: 'success' })">
  Save
</button>
<button class="btn btn-outline" type="button"
        onclick="mostlycss.toast('Message moved to Archive', { action: { label: 'Undo', onClick: () => {} } })">
  Archive
</button>
<button class="btn btn-danger" type="button"
        onclick="mostlycss.toast('Could not reach the server', { variant: 'danger', urgent: true })">
  Fail to save
</button>

The region is markup you write, and it has to be in the DOM before a message arrives: a live region inserted and populated in the same task announces nothing, in every engine.

That is the most common reason a toast is silent, and it is invisible on screen. role="status" is the default and waits for a gap; urgent: true uses role="alert" and interrupts, which is for messages where acting on stale information causes harm — a "Saved" toast that cuts a screen reader off mid-sentence is a defect. Politeness is fixed per element, so data-ui="toast-region" generates the assertive twin beside yours. The Archive toast has an action and therefore never auto-dismisses: "Undo" that vanishes after four seconds is a control taken away from anyone who reads slowly, which is WCAG 2.2.1. Hovering or focusing any toast pauses its countdown. Politeness is fixed per element and two fixed boxes in one corner overlap rather than stack, so the module wraps your region and the assertive twin in a single positioned stack — fire all three buttons and they queue in one column. The variants are alert.css's: same tint, same accent edge, same glyph, painted from CSS so no icon can be pasted without aria-hidden or drift out of step with the variant.

Tokens 29

Level 2, declared on .toast itself. Set any of them on that selector to restyle this component without touching the skin.

Level 2 tokens declared by toast
TokenDefault
--toast-durationvar(--duration-normal)
--toast-iconvar(--toast-icon-info)
--toast-icon-colorvar(--toast-accent)
--toast-icon-size1.25rem
--toast-radius-inline-end0
--toast-region-bleedvar(--space-6)
--toast-text-colorvar(--color-on-surface)
--toast-text-font-sizevar(--font-size-sm)
--toast-title-font-sizevar(--font-size-sm)
--toast-title-font-weightvar(--font-weight-semibold)
--toast-bgvar(--color-surface-raised)
--toast-colorvar(--color-on-surface)
--toast-gapvar(--space-3)
--toast-padding-blockvar(--space-3)
--toast-padding-inlinevar(--space-4)
--toast-radiusvar(--radius-box)
--toast-region-gapvar(--space-2)
--toast-region-insetvar(--space-4)
--toast-shadowvar(--elevation-3)
--toast-accentvar(--color-border-strong)
--toast-accent-size3px
--toast-close-size2rem
--toast-icon-checkurl("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='m8.5 12.5 2.5 2.5 4.5-5'/%3E%3C/svg%3E")
--toast-icon-dangerurl("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='m15 9-6 6'/%3E%3Cpath d='m9 9 6 6'/%3E%3C/svg%3E")
--toast-icon-infourl("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 16v-4'/%3E%3Cpath d='M12 8h.01'/%3E%3C/svg%3E")
--toast-icon-warningurl("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3'/%3E%3Cpath d='M12 9v4'/%3E%3Cpath d='M12 17h.01'/%3E%3C/svg%3E")
--toast-region-inset-inline0px
--toast-region-max-inline-size24rem
--toast-region-z-index100

Variants and states

Variants

  • .toast-danger
  • .toast-info
  • .toast-success
  • .toast-warning

Inside it

  • .toast-region
  • .toast-stack
  • .toast-region-block-start
  • .toast-region-center
  • .toast-region-inline-start
  • .toast-actions
  • .toast-body
  • .toast-close
  • .toast-region-block-end
  • .toast-region-inline-end
  • .toast-text
  • .toast-title

State it reads

Read from the platform, never mirrored into a class that could disagree with it.

  • data-legacy
  • data-toast-leaving

Before you ship it

This one needs a script. Opt the markup in with data-ui="toast" and import import 'mostlycss/js/components/toast' — that specifier and not the package root, which is a bundle carrying a second copy of the registry, so a page holding both initialises every element twice. What you lose without the script is listed below — for some components that is only polish, for others it is the whole interaction.

What you have to do 3 requirements

  1. Write the region into your page once, before any message arrives. A live region inserted and populated in the same task announces nothing in any engine, and that silence is invisible on screen.
  2. Reserve urgent: true for messages where acting on stale information causes harm. A "Saved" toast that interrupts a screen reader mid-word is a defect, not a preference.
  3. Do not add a second region by hand. data-ui="toast-region" generates the assertive one and the stack that keeps both in chronological order; two hand-placed regions in one corner overlap rather than stack.

src/css/components/toast.css · npx mostlycss add toast