Vanilla UI Kit Toast v1.0.0 · datepicker →

Toasts, one file.

Zero-dependency notifications that follow your page's theme on their own. Click anything below — every card is live.

Types

Info, success, error, warning — errors announce assertively.

Toast.success('Changes saved')
Toast.error('Could not reach the server',
            { title: 'Save failed' })

Action + undo

One action button; the toast dismisses after the click.

Toast.show('Message deleted', {
  action: { label: 'Undo',
            onClick: () => restore() }
})

Promise flow

Loading → success or error, in place.

Toast.promise(saveUser(), {
  loading: 'Saving…',
  success: u => `Saved ${u.name}`,
  error:   e => `Failed: ${e.message}`
})

Positions

Six stacks; newest sits nearest the edge. Hover a stack to pause it.

Toast.defaults.position = 'top-center'

Sticky + titles

duration: 0 stays until dismissed.

Toast.warning('Reconnecting…',
  { title: 'Connection lost', duration: 0 })

Family theming

With the VC core loaded, one call themes every component.

VC.config({ accent: '#b45309' })

API reference

Usage

Toast is a static API — there is no constructor. Every call returns a handle for the toast it created.

Toast.show(message, options?)          // → handle { el, dismiss(), update(message, options?) }
Toast.info(message, options?)          // type shorthands — same signature,
Toast.success(message, options?)      //   with `type` preset
Toast.error(message, options?)
Toast.warning(message, options?)
Toast.loading(message, options?)       // sticky (duration 0) unless you set one
Toast.promise(promise, msgs, options?) // loading → success | error; returns the promise
Toast.dismissAll()

Messages are text by default (rendered with textContent); pass html: true to opt in to trusted markup. Also works with CommonJS/AMD and is SSR-safe — without a DOM every call is a no-op returning a dummy handle.

Options

Second argument to Toast.show() and the shorthands. The first eight are keys of Toast.defaults — change one there to affect every toast, e.g. Toast.defaults.position = 'top-center'. The rest are per-call only.

NameTypeDefaultDescription
positionstring'bottom-right'top|bottom + -left|-center|-right — six stacks; unknown values fall back to 'bottom-right'.
durationnumber4000Milliseconds before auto-dismiss; 0 = sticky until dismissed.
typestring'info''info' | 'success' | 'error' | 'warning' | 'loading' — icon, color, and ARIA role.
dismissiblebooleantrueShow the ✕ button.
maxnumber5Per-stack cap; the oldest toast is evicted beyond it.
stylesbooleantruefalse = headless, no CSS ever injected (see Theming).
themestring'auto''auto' | 'light' | 'dark' — resolved page-wide from Toast.defaults.theme (see Theming).
labelsobject{ dismiss: 'Dismiss' }Accessible name of the ✕ button (set via Toast.defaults.labels).
titlestring—Optional bold title above the message.
actionobject—One action button: { label, onClick(handle) }. The toast dismisses after the click.
htmlbooleanfalseOpt in to render the message as trusted markup (titles are always text).
onDismissfunction—fn(handle) — see Events & callbacks.

Handle methods

Every Toast.show()/shorthand call returns a handle:

MemberReturnsDescription
elElementThe toast’s root .vt element (property, null in SSR).
dismiss()undefinedDismiss this toast (animates out, then fires onDismiss).
update(message, options?)handleReplace message and options in place — resets the timer; how Toast.promise turns a loading toast into success/error. No-op after dismissal.

Statics & helpers

MemberReturnsDescription
Toast.show(message, options?)handleShow a toast.
Toast.info / .success / .error / .warning(message, options?)handleType shorthands for show().
Toast.loading(message, options?)handleSpinner toast; sticky (duration: 0) unless a duration is given.
Toast.promise(promise, msgs, options?)the promiseLoading toast that updates into success or error. msgs: { loading, success, error } — success/error may be strings or fn(result | reason) => string. Returns the original promise.
Toast.dismissAll()undefinedDismiss every toast in every stack.
Toast.defaultsobjectGlobal defaults — change any once, e.g. Toast.defaults.duration = 6000.
Toast.saltstring | falseCSS isolation namespace (default 'vc1'). Set your own token before the first toast, or false to disable.
Toast.cssstringThe full stylesheet, always rendered with the current salt — a starting point for headless styling.
Toast.versionstringLibrary version.

Events & callbacks

CallbackSignatureFires
onDismissfn(handle)When the toast is dismissed — timer, ✕ button, action click, eviction beyond max, or dismissAll().
action.onClickfn(handle)When the action button is clicked; the toast dismisses right after.

Toast dispatches no CustomEvents — the callbacks above are the whole event surface.

Declarative init

None — toasts are inherently imperative (they exist only when your code shows one), so Toast has no data-* auto-init. Set global behavior once via Toast.defaults instead.

Theming

Auto light/dark with the family’s resolution order: <html data-theme> / data-bs-theme / .dark class → prefers-color-scheme, re-resolved live. Pin it with Toast.defaults.theme = 'dark'. All colors are CSS custom properties on .vt (dark values are defined on .vt-stack[data-theme=dark] .vt):

PropertyRole
--vt-accentInfo icon, action button.
--vt-successSuccess icon.
--vt-errorError icon.
--vt-warningWarning icon.
--vt-bgToast background.
--vt-textText color.
--vt-mutedSecondary text (message under a title, ✕ button).
--vt-faintBorder and hover fills.
--vt-shadowToast shadow.
--vt-radiusCorner radius.
--vt-fontFont stack.

CSS isolation — stacks render as class="vt-stack vc1" and structural rules ship salted, so host-page design systems can’t override the toasts; the --vt-* variable overrides above keep working (var definitions are deliberately unsalted). Custom token: Toast.salt = 'acme' before the first toast; disable with Toast.salt = false.

Headless — Toast.defaults.styles = false injects no CSS, ever. You keep the full behavior (stacking, timers, pause-on-hover, ARIA) and the stable markup contract (.vt-stack → .vt.vt-success.vt-in → .vt-icon / .vt-body (.vt-title, .vt-msg) / .vt-action / .vt-close; .vt-out while leaving). Start from Toast.css (string) or dist/toast.css. With the VC core loaded, VC.config({ accent: … }) themes toasts and every other family component in one call.

Accessibility

KeyAction
Tab / Shift+TabMove focus to a toast’s action and ✕ buttons (native buttons in the document order).
Enter / SpaceActivate the focused action or dismiss button.

Errors and warnings render with role="alert" / aria-live="assertive"; info, success, and loading use role="status" / aria-live="polite". Hovering a stack pauses all of its timers; they resume with the time they had left (minimum 400 ms so nothing vanishes mid-read). :focus-visible outlines on buttons; all animation is disabled under prefers-reduced-motion: reduce.

Headless mode

Set Toast.defaults.styles = false and no CSS is ever injected — you keep the full behavior (stacking, timers, pause-on-hover, ARIA) and the stable .vt-* markup contract to style however you like. Both demos below run in an <iframe>: the styled examples above have already injected the stylesheet into this page, so a headless toast out here would still match those rules.

Unstyled (styles: false)

The raw browser rendering — a sticky toast is shown on load (unstyled stacks are not position: fixed, so it simply flows in the document), and the button adds more.

Toast.defaults.styles = false            // headless: zero CSS injected
Toast.info('A raw toast', { duration: 0 })
Toast.success('Changes saved', { title: 'All set' })

Bring your own design (Tailwind)

The same headless calls with the .vt-* hooks mapped to Tailwind utilities via @apply — including the .vt-in/.vt-out state classes, so the enter/leave transitions are yours too. The button fetches cdn.tailwindcss.com, so nothing loads until you ask.