Snackbar
A snackbar tells people about something the app has done, at the bottom of the screen: a message archived, a photo saved. It doesn't take focus or ask for anything, and has one action at most, such as Undo. For a decision, use a dialog. See the M3 snackbar guidelines.
Usage
show() puts it in a queue shared by every snackbar on the page: one shows at a time, the
others wait their turn. Without an action it goes after 4 seconds.
import { createSnackbar } from 'mtrl';
const snackbar = createSnackbar({ message: 'Photo saved' });
document.body.append(snackbar.element);
function tell() {
snackbar.show();
}
<m-snackbar message="Photo saved"></m-snackbar>
<script type="module">
const snackbar = document.querySelector('m-snackbar');
function tell() {
snackbar.show();
}
</script>
import { useState } from 'react';
import { Snackbar } from 'mtrl/react';
export function Example() {
const [open, setOpen] = useState(false);
const tell = () => setOpen(true);
return (
<Snackbar open={open} onClose={() => setOpen(false)} message="Photo saved" />
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { MSnackbar } from 'mtrl/vue';
const open = ref(false);
function tell() {
open.value = true;
}
</script>
<template>
<MSnackbar :open="open" @close="open = false" message="Photo saved" />
</template>
<script lang="ts">
import { Snackbar } from 'mtrl/svelte';
let open = $state(false);
function tell() {
open = true;
}
</script>
<Snackbar open={open} onclose={() => (open = false)} message="Photo saved" />
import { createSignal } from 'solid-js';
import { Snackbar } from 'mtrl/solid';
export function Example() {
const [open, setOpen] = createSignal(false);
const tell = () => setOpen(true);
return (
<Snackbar open={open()} onClose={() => setOpen(false)} message="Photo saved" />
);
}
Examples
Undo
A snackbar with an action stays until it is acted on or dismissed, so there is time to reach it. The action closes it.
import { createSnackbar } from 'mtrl';
const snackbar = createSnackbar({ message: 'Message archived', action: 'Undo' });
snackbar.on('action', () => undoArchive());
document.body.append(snackbar.element);
function archive() {
snackbar.show();
}
<m-snackbar message="Message archived" action="Undo"></m-snackbar>
<script type="module">
const snackbar = document.querySelector('m-snackbar');
snackbar.addEventListener('action', () => undoArchive());
function archive() {
snackbar.show();
}
</script>
import { useState } from 'react';
import { Snackbar } from 'mtrl/react';
import { undoArchive } from './app';
export function Example() {
const [open, setOpen] = useState(false);
const archive = () => setOpen(true);
return (
<Snackbar onAction={() => undoArchive()} open={open} onClose={() => setOpen(false)} message="Message archived" action="Undo" />
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { MSnackbar } from 'mtrl/vue';
import { undoArchive } from './app';
const open = ref(false);
function archive() {
open.value = true;
}
</script>
<template>
<MSnackbar @action="undoArchive()" :open="open" @close="open = false" message="Message archived" action="Undo" />
</template>
<script lang="ts">
import { Snackbar } from 'mtrl/svelte';
import { undoArchive } from './app';
let open = $state(false);
function archive() {
open = true;
}
</script>
<Snackbar onaction={() => undoArchive()} open={open} onclose={() => (open = false)} message="Message archived" action="Undo" />
import { createSignal } from 'solid-js';
import { Snackbar } from 'mtrl/solid';
import { undoArchive } from './app';
export function Example() {
const [open, setOpen] = createSignal(false);
const archive = () => setOpen(true);
return (
<Snackbar onAction={() => undoArchive()} open={open()} onClose={() => setOpen(false)} message="Message archived" action="Undo" />
);
}
A close button, and how long it stays
dismissible adds a close button. duration is short (4s), long (10s), indefinite, or
milliseconds.
import { createSnackbar } from 'mtrl';
const snackbar = createSnackbar({ message: 'Update available', dismissible: true, duration: 'long' });
document.body.append(snackbar.element);
function tell() {
snackbar.show();
}
<m-snackbar message="Update available" dismissible duration="long"></m-snackbar>
<script type="module">
const snackbar = document.querySelector('m-snackbar');
function tell() {
snackbar.show();
}
</script>
import { useState } from 'react';
import { Snackbar } from 'mtrl/react';
export function Example() {
const [open, setOpen] = useState(false);
const tell = () => setOpen(true);
return (
<Snackbar open={open} onClose={() => setOpen(false)} message="Update available" dismissible duration="long" />
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { MSnackbar } from 'mtrl/vue';
const open = ref(false);
function tell() {
open.value = true;
}
</script>
<template>
<MSnackbar :open="open" @close="open = false" message="Update available" dismissible duration="long" />
</template>
<script lang="ts">
import { Snackbar } from 'mtrl/svelte';
let open = $state(false);
function tell() {
open = true;
}
</script>
<Snackbar open={open} onclose={() => (open = false)} message="Update available" dismissible duration="long" />
import { createSignal } from 'solid-js';
import { Snackbar } from 'mtrl/solid';
export function Example() {
const [open, setOpen] = createSignal(false);
const tell = () => setOpen(true);
return (
<Snackbar open={open()} onClose={() => setOpen(false)} message="Update available" dismissible duration="long" />
);
}
Replacing the one on screen
When only the newest message matters, queueBehavior: 'replace' closes the snackbar on screen
and drops the waiting ones.
import { createSnackbar } from 'mtrl';
const snackbar = createSnackbar({ message: 'Upload complete', queueBehavior: 'replace' });
document.body.append(snackbar.element);
function tell() {
snackbar.show();
}
<m-snackbar message="Upload complete" queue-behavior="replace"></m-snackbar>
<script type="module">
const snackbar = document.querySelector('m-snackbar');
function tell() {
snackbar.show();
}
</script>
import { useState } from 'react';
import { Snackbar } from 'mtrl/react';
export function Example() {
const [open, setOpen] = useState(false);
const tell = () => setOpen(true);
return (
<Snackbar open={open} onClose={() => setOpen(false)} message="Upload complete" queueBehavior="replace" />
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { MSnackbar } from 'mtrl/vue';
const open = ref(false);
function tell() {
open.value = true;
}
</script>
<template>
<MSnackbar :open="open" @close="open = false" message="Upload complete" queue-behavior="replace" />
</template>
<script lang="ts">
import { Snackbar } from 'mtrl/svelte';
let open = $state(false);
function tell() {
open = true;
}
</script>
<Snackbar open={open} onclose={() => (open = false)} message="Upload complete" queueBehavior="replace" />
import { createSignal } from 'solid-js';
import { Snackbar } from 'mtrl/solid';
export function Example() {
const [open, setOpen] = createSignal(false);
const tell = () => setOpen(true);
return (
<Snackbar open={open()} onClose={() => setOpen(false)} message="Upload complete" queueBehavior="replace" />
);
}
position places it at the start, center or end of the bottom edge. layer: 'top'
shows it in the browser's top layer; while a modal dialog is open, it moves inside the dialog,
so it can still be read and used. The web component always is in the top layer.
clearSnackbars() closes the one on screen and drops the queue, for when what they were about
goes away.
API
Options
| Option | Type | Default | Description |
|---|---|---|---|
message |
string |
required | The text, up to two lines |
action |
string |
undefined |
The action's label |
dismissible |
boolean |
false |
A close button |
closeLabel |
string |
'Dismiss' |
The close button's accessible name |
duration |
'short' | 'long' | 'indefinite' | number |
'indefinite' with an action, else 'short' |
How long it stays: 4s, 10s, until closed, or milliseconds (0 is indefinite) |
position |
'center' | 'start' | 'end' |
'center' |
Where along the bottom edge |
queueBehavior |
'queue' | 'replace' |
'queue' |
Whether it waits its turn or replaces the queue |
layer |
'top' |
undefined |
Shows it in the top layer, inside the topmost modal dialog while one is open |
onAction / onOpen / onClose |
(event: SnackbarEvent) => void |
undefined |
Handlers for action, open and close |
on |
{ open?, close?, action?, dismiss? } |
undefined |
Event handlers registered at creation |
class |
string |
undefined |
Additional CSS classes |
prefix |
string |
'mtrl' |
Prefix for CSS class names |
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
show() / hide() |
none | SnackbarComponent |
Queues it to show, or closes it with reason api |
getMessage() / setMessage(message) |
message: string |
string / SnackbarComponent |
The text |
getAction() / setAction(text) |
text: string |
string / SnackbarComponent |
The action's label |
getDuration() / setDuration(duration) |
duration: SnackbarDuration |
number / SnackbarComponent |
How long it stays, read in milliseconds; a change on screen starts the count again |
getPosition() / setPosition(position) |
position: 'center' | 'start' | 'end' |
string / SnackbarComponent |
Where it sits |
on(event, handler) / off(event, handler) |
event: string, handler: Function |
SnackbarComponent |
Adds or removes a listener |
destroy() |
none | void |
Removes it |
| Property | Type | Description |
|---|---|---|
element |
HTMLElement |
The snackbar |
state |
'visible' | 'hidden' |
Whether it is on screen |
actionButton / closeButton |
HTMLElement | undefined |
Its buttons |
clearSnackbars(), from mtrl, closes the snackbar on screen and drops the waiting ones.
Events
| Event | Description | Data |
|---|---|---|
open |
It is on screen | { snackbar, originalEvent } |
action |
The action was used | { snackbar, originalEvent } |
close / dismiss |
It closed | { snackbar, reason, originalEvent } |
reason is timeout, action, close-button, escape, api or queue (replaced or
cleared). The web component dispatches open, action and close; its close carries
{ reason }, and its open property is true from show() on, while it waits too.
Accessibility
- A
statuslive region: its message is announced politely, and focus is not moved to it. Escapecloses it while focus is inside it. If focus was inside when it closed, it goes back where it came from.- Its countdown pauses while the pointer is over it or focus is inside it. With an action, it stays until it is used or dismissed.
- The close button is named by
closeLabel.
Styling
Its colors are the inverse roles, so it reads as a surface of the opposite theme.
.mtrl-snackbar, .mtrl-snackbar--visible { }
.mtrl-snackbar--center, .mtrl-snackbar--start, .mtrl-snackbar--end { }
.mtrl-snackbar--with-action, .mtrl-snackbar--dismissible { }
.mtrl-snackbar--action-below { } /* an action wider than 128dp, on its own line */
.mtrl-snackbar__text, .mtrl-snackbar__action, .mtrl-snackbar__close { }
Measurements
| Attribute | Value |
|---|---|
| Container | inverse-surface, 4dp corners, elevation 3 |
| Height | 48dp, 68dp for two lines |
| Text | Body Medium, inverse-on-surface, two lines at most |
| Action | inverse-primary; below the text when wider than 128dp |
| Padding | 16dp at the start, 8dp between the text and the action |
| Motion | Fades on the fast effects spring, and scales from 0.8 on the fast spatial one |