Skip to content

ExpressiveCSS

Browse documentation

Snackbar

Material Design 3 snackbars, from the HTML.

View as Markdown

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.

Photo saved to album

Item archived

Can't send photo. Retry in 5 seconds.

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.

Single-line snackbar with action

<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.

<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();

No matching pages

  • Source color

    The seed every generated ramp derives from. Pick one and browse the docs — the whole theme follows. Error does not: it is a fixed hue.

    Default source: Mist Iris.

    ExpressiveCSS presets: Lagoon, Harbor, Breeze, Cobalt, Iris.

    Bold

    Soft

    Mist

    Glow