EmSnackbar
A toast in five severity variants. Toasts can stack in a queue that auto-dismisses, pauses on hover, and announces messages to screen readers.
Usage
The compound has five parts. EmSnackbarPortal is the fixed region toasts stack in — bottom-right, newest on top. EmSnackbarQueue connects that region to a useNotifications instance and hands you its tickets through v-slot="{ items }". EmSnackbar is one toast; EmSnackbarContent is its message and EmSnackbarClose its dismiss button.
The queue is optional. A static EmSnackbar under a v-if is a complete snackbar — show it when something happens, listen for dismiss to hide it. Reach for the queue when toasts arrive faster than they leave: it orders them, auto-dismisses them, and pauses the clock while the reader is hovering or focused.
Anatomy
<script setup lang="ts">
import { EmSnackbar } from '@paper/emerald'
</script>
<template>
<EmSnackbar.Portal>
<EmSnackbar.Queue>
<EmSnackbar>
<EmSnackbar.Content />
<EmSnackbar.Close />
</EmSnackbar>
</EmSnackbar.Queue>
</EmSnackbar.Portal>
</template>Composed on Vuetify0
Each part is a thin skin over the matching part of Vuetify0’s Snackbar compound — Snackbar.Portal, Snackbar.Queue, Snackbar.Root, Snackbar.Content, Snackbar.Close. Emerald adds no behavior of its own.
The split is clean. v0 owns everything that moves: the portal’s teleport and z-index coordination through the stack, the queue’s connection to useNotifications with its FIFO auto-dismiss and its hover/focus pause, the dismiss context that wires a Close button to its Root, and the live-region plumbing that makes a freshly mounted toast actually get announced. Emerald owns everything you see: the card — border, radius, shadow, status-token tint keyed off the data-variant attribute the root writes — the fixed bottom-right region capped at min(360px, 100vw - 32px), and the close button’s hit area and default glyph.
Inside a queue, EmSnackbarClose removes the ticket from the notifications instance. Outside a queue, Snackbar.Root emits dismiss, which is why the basic example hides its toast with @dismiss on EmSnackbar. Each Emerald part renders one Vuetify0 component at its root, so a prop the wrapper does not declare still reaches that component through Vue fallthrough. namespace on the portal is one of those — the portal only declares teleport. urgent is a declared EmSnackbar prop. The prop tables below are Emerald’s contract.
Examples
Props
Defaults marked v0 are inherited from the wrapped v0 part — Emerald passes the prop through without re-declaring the default.
EmSnackbar
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'success' | 'error' | 'info' | 'warning' | 'neutral' | 'neutral' | Status tint and accent border |
id | ID | auto-generated (v0) | Ticket identity. Pass the notification’s id inside a queue so Close dismisses the right ticket |
namespace | string | 'v0:notifications' (v0) | Which notifications instance to bind to |
urgent | boolean | true for variant="error", otherwise false | role="alert" when true, role="status" otherwise. Pass it to override the variant default |
Outside a queue, the underlying root emits dismiss with the snackbar’s id when its Close button is pressed — listen for it on EmSnackbar to hide a static toast. Inside a queue the dismissal goes to the queue instead and no event fires.
Parts
| Part | Props | Notes |
|---|---|---|
EmSnackbarPortal | teleport ('top-layer' | string | false, default 'top-layer' (v0)) | The fixed bottom-right region. 'top-layer' mounts into the topmost open modal, falling back to body; false renders inline |
EmSnackbarQueue | namespace (string, default 'v0:notifications' (v0)) | Exposes queue tickets via v-slot="{ items }", newest first. Pauses auto-dismiss on hover and focus |
EmSnackbarContent | — | The message body. Flexes to fill the row |
EmSnackbarClose | namespace (string, default 'v0:notifications' (v0)) | Dismiss button. Default slot replaces the built-in close glyph |
EmSnackbarContent and EmSnackbarPortal take no namespace of their own — Content needs no context, and the portal coordinates on the Vuetify0 default unless you pass one through.
Accessibility
The behavior below belongs to the wrapped v0 parts; Emerald only styles it.
Announcements
A snackbar appears without the reader asking for it, so the compound does the announcing. Each root renders role="alert" when it is urgent, which is the default for variant="error", and role="status" otherwise. status waits for a pause. alert interrupts. Because screen readers are unreliable about announcing live regions injected after page load, EmSnackbarPortal also renders a persistent, visually hidden announcer pair from mount, and every snackbar that appears under it mirrors its text there — which is what makes the first toast reliably heard. The announcer clears before it fills, so sending the same message twice announces twice.
urgent is an EmSnackbar prop. It defaults to true for variant="error" and false otherwise. Pass it to override that default. true switches the root to role="alert" and routes the mirror to the assertive region.
Timing
EmSnackbarQueue pauses auto-dismiss on mouseenter and focusin and resumes only when both hover and focus have left the stack — moving focus between a toast’s Close button and its neighbor does not restart the clock, and dismissing one toast while still hovering keeps the rest paused. This is the WCAG 2.2.1 (Timing Adjustable) accommodation: content that removes itself on a timer must give the reader a way to hold it.
Static snackbars outside a queue have no timer, and nothing in the compound handles Escape — a snackbar is not a dialog and owns no dismissal keys.
The close button
EmSnackbarClose renders a native <button type="button">, so Enter and Space activation, focusability and the implicit role all come from the platform. Its accessible name is the locale string Snackbar.close, falling back to “Dismiss”; the default glyph inside is aria-hidden and contributes nothing, so replacing it through the slot does not change the name. Keyboard focus gets Emerald’s standard focus ring via :focus-visible.
The close button is the compound’s only interactive element. Everything else — message text, the tinted card — is static content a screen reader encounters through the announcement, not the tab order.