Snackbars with an action are persistent and dismissible by default. Escape dismisses a focused snackbar. Closing it restores focus only when focus is still inside the bar. If you set a finite timer, it still pauses during focus or hover.
Snackbars show short updates about app processes at the bottom
of the screen. They should not interrupt browsing. People can
keep using the page without interacting with the bar. Write the
bar as a .snackbar with a <p> for
the supporting text. A trailing <button> is
the optional action, and a .circle button is the
optional close.
Tokens follow the
M3 snackbar spec.
The container is inverse-surface with 4dp corners,
elevation 3, and a 48dp minimum height. Supporting text is
body-medium in inverse-on-surface,
up to two lines. The action is a label-large text button in
inverse-primary. Close is a 24dp
inverse-on-surface icon. On compact viewports the
bar is inset 8dp from the edges. From the Medium breakpoint up it
fits its content, between 344dp and 672dp wide, and sits centered 24dp from the bottom.
A text-only snackbar stays for 4 seconds by default. Setting action
changes the defaults to displayLength: Infinity and
dismissible: true. Explicit options override these defaults. Only one
snackbar shows at a time.
Snackbar is not in AutoInit(). Construct one
when you need it, or put a static .snackbar in
the page and add .active to pin it.
new Expressive.Snackbar({ text: 'Photo saved to album' });
new Expressive.Snackbar({
text: 'Item archived',
action: 'Undo',
onAction: function() { /* restore */ }
});
new Expressive.Snackbar({
text: "Can't send photo. Retry in 5 seconds.",
action: 'Retry',
dismissible: true
});
The constructor wraps text in a
<p> and appends the action and close when
those options are set. The live region is
role="status" with aria-live="polite",
so it announces without stealing focus.
Initialization
Actions and Dismiss use ordinary Tab navigation. When reaching the end of the page would be cumbersome, put a visible notification-actions button near the task trigger. Its click handler can call instance.el.querySelector('button, a[href]')?.focus(). Do not call it automatically when showing a snackbar.
The snackbar handles Escape only inside itself. Escape there does not also dismiss an enclosing dialog. If an action moves focus elsewhere, closing leaves focus there. For timed feedback, keep an equivalent persistent message.
The IIFE bundle exposes Expressive.Snackbar. Snackbar is
not in AutoInit(), so construct one when you need it.
new Expressive.Snackbar({
text: 'I am a snackbar!'
});
One way to hook that up is a click handler on a button:
<button type="button" id="snackbar-basic">Show</button>
document.getElementById('snackbar-basic').addEventListener('click', function() {
new Expressive.Snackbar({ text: 'I am a snackbar!' });
});
Markup
The same anatomy works as static HTML. Without
.active the bar stays in the document flow, which is useful for previews.
With .active it pins to the bottom of the
viewport, centered from the Medium breakpoint. Add
.top only if you must move it off the bottom.
<div class="snackbar">
<p>Photo saved to album</p>
</div>
<div class="snackbar">
<p>Item archived</p>
<button type="button">Undo</button>
</div>
<div class="snackbar active">
<p>Single-line snackbar with action</p>
<button type="button">Action</button>
</div>
A bar with .active does not dismiss itself. Add and remove the
class, or use the constructor if you want the 4-second timer and
swipe-to-dismiss.
Options
You can customize each snackbar with these options.
| Name | Type | Default | Description |
|---|---|---|---|
text |
String | '' |
Plain-text supporting text, wrapped in a <p>. If set, it replaces any HTML from snackbarId. |
action |
String | '' |
Optional action label, shown as a trailing text button. |
onAction |
Function | null |
Called when the action button is pressed. The snackbar still dismisses. |
dismissible |
Boolean | false |
Show a trailing close icon button. Defaults to true when action is set or displayLength is Infinity, so a swipe is never the only way to dismiss. An explicit false still applies. |
snackbarId |
String | — | Id of a <template> (or another element) used as the snackbar body. The snackbar clones a template. It moves any other element inside itself and puts it back on dismissal or replacement, so you can reuse the same ID. |
displayLength |
Number | 4000 |
How long the snackbar stays before it dismisses, in milliseconds. Default 4s without an action, or Infinity with an action. If you set a finite timeout, give equivalent persistent feedback elsewhere in the application. |
inDuration |
Number | 300 |
Enter transition duration, in milliseconds. |
outDuration |
Number | 375 |
Exit transition duration, in milliseconds. |
classes |
String | '' |
Space-separated classes added to the snackbar. rounded is a stadium. top moves the bar off the bottom. |
completeCallback |
Function | null |
Called when the snackbar is dismissed. |
activationPercent |
Number | 0.8 |
Fraction of the snackbar's width a drag must travel to dismiss it. |
root |
Element | null |
Any element in the tree the snackbar should render into. Pass one
when the page lives in a shadow root, so the snackbar appends its
container there instead of to document.body.
|
replace |
Boolean | false |
Show at once, cutting the snackbar on screen short, instead of waiting behind it. Copy buttons use this so a second copy answers right away. |
One snackbar shows at a time. A new one waits until the one on screen is
dismissed, then shows, so updates appear in order. A queued snackbar
captures focus and fires expressive:open when it appears.
If you dismiss it while it is still waiting, it runs
completeCallback and never shows. A snackbar built from
snackbarId takes its element when it shows, so its
el is unset while it waits.
Methods
Instance methods are called on the snackbar. You can get the instance like this:
const instance = Expressive.Snackbar.getInstance(elem);
.dismiss();
Dismiss this snackbar with its exit animation. Runs completeCallback when the animation finishes.
instance.dismiss();
Snackbar.dismissAll();
Dismiss the snackbar on screen and every one waiting behind it.
Expressive.Snackbar.dismissAll();
Properties
| Name | Type | Description |
|---|---|---|
el |
Element | The snackbar element. |
options |
Object | The options the instance was initialized with. |
panning |
Boolean | Whether the snackbar is being dragged. |
timeRemaining |
Number | Milliseconds left before the snackbar dismisses. |
Custom HTML
Pass snackbarId pointing at a
<template>. The snackbar clones the template's
first child. Leave text empty to keep the HTML. Use the
same anatomy as a static snackbar. For custom links or controls, set displayLength: Infinity explicitly and add a close control with dismissible: true.
This is snackbar nº1 with a link
This is snackbar nº2
<button type="button" class="tonal" id="snackbar-html-1">Show Snackbar 1</button>
<template id="my-snackbar-1">
<div>
<p>This is snackbar nº1 with a <a href="https://github.com">link</a></p>
</div>
</template>
new Expressive.Snackbar({ snackbarId: 'my-snackbar-1', displayLength: Infinity, dismissible: true });
Callback
Run a function when the snackbar is dismissed.
new Expressive.Snackbar({
text: 'I will call back when dismissed',
completeCallback: function() {
new Expressive.Snackbar({ text: 'Your snackbar was dismissed' });
}
});
Styling
Pass classes in the classes option.
rounded is a 24dp stadium. The M3 default is 4dp corners.
Snackbars sit at the bottom unless you add top.
new Expressive.Snackbar({
text: 'I am a snackbar!',
classes: 'rounded'
});
new Expressive.Snackbar({
text: 'Posted from the top',
classes: 'top'
});
Dismiss a snackbar programmatically
To remove a specific snackbar, get the instance from the snackbar
element and call dismiss(). Dragging it past 80% of its width,
or flicking it, also dismisses it. After a short drag, pausing for at
least 100ms before release lets the snackbar snap back. Cancelling a swipe leaves the snackbar
open and does not affect the next gesture. The action and close
buttons are not swipe handles.
const snackbarElement = document.querySelector('#snackbar-container .snackbar');
Expressive.Snackbar.getInstance(snackbarElement)?.dismiss();
Dismiss all snackbars
Expressive.Snackbar.dismissAll();