A short-lived message that floats above the page: the alert's anatomy, raised, arriving in a corner and leaving on its own after a few seconds unless someone is reading it.
Changes saved
Website redesign was updated a moment ago.
Options
| Choice | Values | Default | What it does |
|---|---|---|---|
tonesmbk-toast--tone-<value> | defaultinfosuccesswarningdanger | default | Neutral, or one of the four feedback families on their subtle surface, each with its own icon. |
Examples
Usage
The alert's anatomy, raised to the highest elevation.
Changes saved
Website redesign was updated a moment ago.
Tones
Five tones, each with its own icon, as the alert.
Changes saved
Website redesign was updated.
Update available
A new version is ready to install.
Invite sent
Tom will get an email shortly.
Connection unstable
We keep retrying in the background.
Upload failed
report.pdf is larger than 20 MB.
With action
One small action, typically undo.
Saved
Your changes are stored.
Dismissible
A close control appears when a dismiss handler is given.
Link copied
Anyone with the link can view this project.
Toast region
The page shows toasts through one region: they appear in the corner, stay six seconds unless pointed at or focused, and wait for their action if they have one.
Guidelines
Purpose
A brief message that floats above the page to confirm something just happened — saved, sent, copied — and then leaves. It has the alert's anatomy and the highest elevation, because it is not part of the page.
When to use
- Confirming an action whose result is not otherwise visible.
- A brief notice that needs no reply.
When not to use
- Anything the person must read or act on. Use an alert, or a dialog.
- Errors that block a task. Those belong where the task is.
Usage rules
Do
- Keep it to one line where possible.
Don't
- Show a toast for something the person did not do, unless it is a status change they are waiting for.
Invite sent
Tom will get an email shortly.
Keep a toast to one short line, so it can be read before it goes.
Invite sent
Tom Reyes will get an email with a link to join Website redesign. The link works for seven days, after which you can send a new invite from the members page or ask an owner to add him.
A paragraph in a toast disappears before the reader gets to the end.
Anatomy
The same parts as the alert: icon, body with title and description, action, dismiss. Each tone has its own icon, as the alert's, and the cross sits in a box big enough to press. The block is raised and has a fixed width.
- The region part, where a page's toasts appear: the corner at the end of the viewport's foot, one under another, the newest last. On a phone it spans the viewport less its inset, and so do its toasts.
Behavior
A toast region shows, times and closes a page's toasts. Put ToastRegion round the page once, as it loads, and call useToasts().show() anywhere inside it with the toast's tone, title, description and action; it returns an id that dismiss() takes.
- A toast stays six seconds, then leaves. A danger toast, and a toast with an action, stay until they are closed. A toast can set its own time, or none.
- While the pointer is on the region or focus is in it, every timer stops, and each starts again with the time it had left once the pointer and focus have gone. A page in a tab nobody is looking at holds them too.
- Escape closes the toast focus is in, and focus moves on to the next toast, or back where it was before it went into the region.
- Three toasts show at once; the others wait their turn and show as earlier ones leave.
A toast fades in and rises a spacing step into place when it appears, and fades out dropping a smaller step as it leaves. Under reduced motion it fades in and out in place, without rising or dropping.
On its own, without a region, the component renders one toast that stays until the page removes it or hides it with the hidden attribute, which plays the same exit.
Accessibility
What it does for you
- Each toast's title and description are announced as it shows, without moving focus: politely, or at once for a danger toast. Two hidden announcers inside the region say them, so none is said twice. A toast in a region has no role of its own; the region is a named landmark.
- A toast on its own carries the status role itself, or the alert role when it is a danger toast.
- F6 moves focus to the newest toast while any shows, so a toast with an action can be reached without tabbing through the page, and F6 again goes back where focus was. The region names the key for screen readers.
- A toast with an action stays until it is closed, unless given a time. A danger toast stays until closed, so it cannot vanish unread.
- While the pointer is on the region or focus is in it, every timer stops, so a toast does not leave while it is being read or reached.
- Escape closes the toast focus is in, and focus moves on to the next toast, or back where it was before it went into the region.
What you must do
- Put the region in the page as it loads, never with its first toast: it must be there before the first toast arrives, or that toast is not announced.
- Never let a toast be the only place a piece of information appears, because it leaves on its own.
- Give a toast with a time one long enough to reach the action by keyboard.
- Put an error the reader must act on where the task is, not in a toast.
- In plain markup, give the region the status role,
aria-atomic="false", so each toast is announced alone, and a name, and give the toasts in it no role; put a danger toast's words in an element with the alert role that is in the page from the start. Timing, holding and Escape are then the page's own script, and must keep to the rules above. - Give the page's own words for everything it says by itself, such as the dismiss cross's name, which keeps its English until you do; a page in another language can change it.
How to test
- With a screen reader, show a toast: it is announced without focus moving. Show one more after the page has loaded and check it is announced too.
- Tab into the region: the timers stop, the dismiss cross and any action take focus with a focus ring, and Escape closes the toast.
- Show a toast and press F6: focus moves to it. Press F6 again: focus goes back. Show a danger toast: it is announced at once, interrupting.
- Show a danger toast and one with an action: both stay until closed.
- With reduced motion on, a toast fades in and out in place.
Keyboard
| Key | What it does |
|---|---|
| F6 | While a toast shows, moves focus to the newest toast's action or dismiss cross; from inside the region, back to where focus was. |
| Tab | Moves focus to a toast's action and its dismiss cross. |
| EnterorSpace | Presses the action or the dismiss cross. |
| Escape | Closes the toast focus is in and moves focus to the next toast, or back to where it was before it entered the region. |
Source
The files the toast is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.
Stylesheet
/* smbk-toast — Toast. Figma "Toast" 4209:20131. The alert, raised to float above the page.
* It rises into place as it appears and drops away as it is hidden (motion rules at the end). */
.smbk-toast {
/* The neutral tone; each feedback tone sets these again below (the tone recipe). */
--smbk-tone-surface: var(--smbk-color-surface-default);
--smbk-tone-border: var(--smbk-color-border-rim);
--smbk-tone-text: var(--smbk-color-text-default);
--smbk-tone-icon: var(--smbk-color-icon-subtle);
box-sizing: border-box;
display: flex;
align-items: flex-start;
gap: var(--smbk-space-inline-lg);
width: var(--smbk-size-content-sm);
max-width: 100%;
padding: var(--smbk-space-inset-lg) var(--smbk-space-inset-xl);
border: var(--smbk-border-width-hairline) solid var(--smbk-tone-border);
border-radius: var(--smbk-radius-container-sm);
background: var(--smbk-tone-surface);
color: var(--smbk-tone-text);
box-shadow: var(--smbk-elevation-overlay);
}
/* The icon's box is as tall as the title's line, so the icon sits on the first line's middle.
The title sits on its text with no gap: the line heights give the air. */
.smbk-toast__icon { display: inline-flex; flex-shrink: 0; align-items: center; height: calc(var(--smbk-typography-label-lg-font-size) * var(--smbk-typography-label-lg-line-height)); color: var(--smbk-tone-icon); font-size: var(--smbk-size-icon-md); }
.smbk-toast__body { display: flex; flex: 1 1 0; flex-direction: column; gap: var(--smbk-space-text-label); min-width: 0; }
.smbk-toast__title { margin: 0; font-family: var(--smbk-typography-label-lg-font-family); font-size: var(--smbk-typography-label-lg-font-size); font-weight: var(--smbk-typography-label-lg-font-weight); line-height: var(--smbk-typography-label-lg-line-height); letter-spacing: var(--smbk-typography-label-lg-letter-spacing); text-transform: var(--smbk-typography-label-lg-text-transform); }
.smbk-toast__description { margin: 0; color: var(--smbk-color-text-subtle); font-family: var(--smbk-typography-body-sm-font-family); font-size: var(--smbk-typography-body-sm-font-size); font-weight: var(--smbk-typography-body-sm-font-weight); line-height: var(--smbk-typography-body-sm-line-height); letter-spacing: var(--smbk-typography-body-sm-letter-spacing); }
.smbk-toast__action { display: flex; flex-shrink: 0; flex-wrap: wrap; gap: var(--smbk-space-inline-md); }
/* .smbk-toast__dismiss: the alert's cross, the small press box shared in press-box/press-box.css, with its focus ring
and its press area on a touch screen. */
/* ---- feedback tones: the tone recipe, each family's subtle surface, its border, its text and its icon ---- */
.smbk-toast--tone-info { --smbk-tone-surface: var(--smbk-color-feedback-info-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-info-border); --smbk-tone-text: var(--smbk-color-feedback-info-text-on-subtle); --smbk-tone-icon: var(--smbk-color-feedback-info-icon); }
.smbk-toast--tone-success { --smbk-tone-surface: var(--smbk-color-feedback-success-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-success-border); --smbk-tone-text: var(--smbk-color-feedback-success-text-on-subtle); --smbk-tone-icon: var(--smbk-color-feedback-success-icon); }
.smbk-toast--tone-warning { --smbk-tone-surface: var(--smbk-color-feedback-warning-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-warning-border); --smbk-tone-text: var(--smbk-color-feedback-warning-text-on-subtle); --smbk-tone-icon: var(--smbk-color-feedback-warning-icon); }
.smbk-toast--tone-danger { --smbk-tone-surface: var(--smbk-color-feedback-danger-surface-subtle); --smbk-tone-border: var(--smbk-color-feedback-danger-border); --smbk-tone-text: var(--smbk-color-feedback-danger-text-on-subtle); --smbk-tone-icon: var(--smbk-color-feedback-danger-icon); }
/* ---- the region: where the page's toasts appear, in the corner at the end of the viewport's
foot, one under another, the newest last. It is in the page before the first toast arrives,
with the hidden announcers that say each toast inside it, and takes no room or pointer while it is
empty. On a phone it spans the viewport less its inset, and so do its toasts. ---- */
.smbk-toast__region {
position: fixed;
inset-block-end: var(--smbk-space-inset-xl);
inset-inline-end: var(--smbk-space-inset-xl);
z-index: var(--smbk-layer-critical);
display: flex;
flex-direction: column;
align-items: flex-end;
gap: var(--smbk-space-stack-sm);
max-width: calc(100% - 2 * var(--smbk-space-inset-xl));
margin: 0;
padding: 0;
pointer-events: none;
}
.smbk-toast__region > * {
pointer-events: auto;
}
@container style(--smbk-layout: narrow) {
.smbk-toast__region {
inset-inline-start: var(--smbk-space-inset-xl);
align-items: stretch;
}
.smbk-toast__region > .smbk-toast {
width: auto;
}
}
/* ---- motion: a toast fades in and rises a spacing step into place with the enter purpose when
it appears, and fades out dropping a smaller step with the exit purpose when the page hides it
with the hidden attribute. Its display changes at the end of the exit, so it stays drawn while
it leaves; a toast removed from the page outright simply goes. Under reduced motion it fades in and out in
place, without rising or dropping. ---- */
.smbk-toast {
transition-property: opacity, translate, display;
transition-duration: var(--smbk-motion-enter-duration);
transition-timing-function: var(--smbk-motion-enter-easing);
transition-behavior: allow-discrete;
}
/* The toast's own display would otherwise outweigh the hidden attribute. */
.smbk-toast[hidden] {
display: none;
opacity: 0;
translate: 0 var(--smbk-space-8);
transition-duration: var(--smbk-motion-exit-duration);
transition-timing-function: var(--smbk-motion-exit-easing);
}
@starting-style {
.smbk-toast {
opacity: 0;
translate: 0 var(--smbk-space-16);
}
}
@media (prefers-reduced-motion: reduce) {
.smbk-toast {
transition-property: opacity, display;
}
.smbk-toast[hidden] {
translate: none;
}
}Tokens it reads
| Token and CSS | Value | Preview | Foundation |
|---|---|---|---|
| size.target.min--smbk-size-target-min | 24px | Size | |
| typography.label-lg--smbk-typography-label-lg-font-family--smbk-typography-label-lg-font-size--smbk-typography-label-lg-font-weight--smbk-typography-label-lg-line-height--smbk-typography-label-lg-letter-spacing--smbk-typography-label-lg-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 16pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px | The quick brown fox | Typography |
| size.icon.md--smbk-size-icon-md | 20px | Size | |
| radius.full--smbk-radius-full | 9999px | Radius | |
| focus.ring.width--smbk-focus-ring-width | 2px | Focus | |
| color.border.focus--smbk-color-border-focus | light: #cc0d0ddark: #f34336changes with theme | Color | |
| focus.ring.offset--smbk-focus-ring-offset | 2px | Focus | |
| size.target.comfortable--smbk-size-target-comfortable | 44px | Size | |
| color.surface.default--smbk-color-surface-default | light: #f3f5f8dark: #181a1cchanges with theme | Color | |
| color.border.rim--smbk-color-border-rim | light: #ffffffe6dark: #ffffff0dchanges with theme | Color | |
| color.text.default--smbk-color-text-default | light: #232527dark: #e7ebf0changes with theme | Color | |
| color.icon.subtle--smbk-color-icon-subtle | light: #4e555edark: #c4cdd8changes with theme | Color | |
| space.inline.lg--smbk-space-inline-lg | comfortable: 12pxcompact: 8pxchanges with density | Spacing | |
| size.content.sm--smbk-size-content-sm | 480px | Size | |
| space.inset.lg--smbk-space-inset-lg | comfortable: 12pxcompact: 8pxchanges with density | Spacing | |
| space.inset.xl--smbk-space-inset-xl | comfortable: 16pxcompact: 12pxchanges with density | Spacing | |
| border-width.hairline--smbk-border-width-hairline | 1px | Border width | |
| radius.container.sm--smbk-radius-container-sm | 8px | Radius | |
| elevation.overlay--smbk-elevation-overlay | light: 0px 12px 16px 2px #373d431a, 0px 2px 8px 0px #373d4326dark: 0px 12px 16px 2px #00000026, 0px 2px 8px 0px #00000033 | Elevation | |
| space.text.label--smbk-space-text-label | 0px | Spacing | |
| color.text.subtle--smbk-color-text-subtle | light: #4e555edark: #9ba5b1changes with theme | Color | |
| typography.body-sm--smbk-typography-body-sm-font-family--smbk-typography-body-sm-font-size--smbk-typography-body-sm-font-weight--smbk-typography-body-sm-line-height--smbk-typography-body-sm-letter-spacing--smbk-typography-body-sm-text-transform | fontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 14pxfontWeight: 400lineHeight: 1.5letterSpacing: 0px | The quick brown fox | Typography |
| space.inline.md--smbk-space-inline-md | comfortable: 8pxcompact: 6pxchanges with density | Spacing | |
| color.feedback.info.surface-subtle--smbk-color-feedback-info-surface-subtle | light: #e3ebfedark: #162352changes with theme | Color | |
| color.feedback.info.border--smbk-color-feedback-info-border | #557bff | Color | |
| color.feedback.info.text-on-subtle--smbk-color-feedback-info-text-on-subtle | light: #2f4ab8dark: #b8ccffchanges with theme | Color | |
| color.feedback.info.icon--smbk-color-feedback-info-icon | light: #3b5ce3dark: #7fa0ffchanges with theme | Color | |
| color.feedback.success.surface-subtle--smbk-color-feedback-success-surface-subtle | light: #dffddfdark: #0a2d0echanges with theme | Color | |
| color.feedback.success.border--smbk-color-feedback-success-border | #269936 | Color | |
| color.feedback.success.text-on-subtle--smbk-color-feedback-success-text-on-subtle | light: #016317dark: #8ddf90changes with theme | Color | |
| color.feedback.success.icon--smbk-color-feedback-success-icon | light: #037a1fdark: #54b85bchanges with theme | Color | |
| color.feedback.warning.surface-subtle--smbk-color-feedback-warning-surface-subtle | light: #ffe6dbdark: #431b07changes with theme | Color | |
| color.feedback.warning.border--smbk-color-feedback-warning-border | #da601a | Color | |
| color.feedback.warning.text-on-subtle--smbk-color-feedback-warning-text-on-subtle | light: #913900dark: #febea2changes with theme | Color | |
| color.feedback.warning.icon--smbk-color-feedback-warning-icon | light: #b14904dark: #fa8349changes with theme | Color | |
| color.feedback.danger.surface-subtle--smbk-color-feedback-danger-surface-subtle | light: #fee5e1dark: #4b130echanges with theme | Color | |
| color.feedback.danger.border--smbk-color-feedback-danger-border | #f34336 | Color | |
| color.feedback.danger.text-on-subtle--smbk-color-feedback-danger-text-on-subtle | light: #a51d17dark: #febdb2changes with theme | Color | |
| color.feedback.danger.icon--smbk-color-feedback-danger-icon | light: #cc0d0ddark: #fe7d6dchanges with theme | Color | |
| layer.critical--smbk-layer-critical | 1400 | Stacking order | |
| space.stack.sm--smbk-space-stack-sm | comfortable: 8pxcompact: 6pxchanges with density | Spacing | |
| motion.enter.duration--smbk-motion-enter-duration | 320ms | Motion | |
| motion.enter.easing--smbk-motion-enter-easing | cubic-bezier(0.16, 1, 0.3, 1) | Motion | |
| space.8--smbk-space-8 | 8px | Spacing | |
| motion.exit.duration--smbk-motion-exit-duration | 200ms | Motion | |
| motion.exit.easing--smbk-motion-exit-easing | cubic-bezier(0.7, 0, 0.84, 0) | Motion | |
| space.16--smbk-space-16 | 16px | Spacing |
Manifest
{
"name": "toast",
"block": "smbk-toast",
"summary": "A short-lived message that floats above the page: the alert's anatomy, raised, arriving in a corner and leaving on its own after a few seconds unless someone is reading it.",
"adapt": "css",
"axes": {
"tone": {
"values": [
"default",
"info",
"success",
"warning",
"danger"
],
"default": "default",
"description": "Neutral, or one of the four feedback families on their subtle surface, each with its own icon."
}
},
"flags": {},
"parts": [
"icon",
"body",
"title",
"description",
"action",
"dismiss",
"region"
],
"figmaNodeId": "4209:20131"
}