Toast

Notification cards and a manager for stacked, queued notifications with independent lifetimes.

c2-toast displays a notification. c2-toast-region manages multiple notifications: three are visible by default, and additional notifications wait in a FIFO queue. Each toast gets its full duration once it becomes visible, so a burst of messages does not expire before the user can read it.

Installation

npm install @c2n/toast

Usage

Toast management

Three visible at a time. Hover or focus the stack to pause timers.

The shared toast manager creates a region in document.body on its first show() call. Importing the package is safe during server rendering; call the manager in browser event handlers. Managed toasts are dismissible by default and stay visible for 5,000 milliseconds.

import { toast } from '@c2n/toast'

toast.show({ heading: 'New message', message: 'Alex commented on your document.', variant: 'info' })
toast.show({ message: 'Your export is ready.', variant: 'success' })

// Reuse an ID to update a notification without adding another card.
const id = toast.show({ id: 'upload', message: 'Uploading…', duration: 0 })
toast.update(id, { message: 'Upload complete.', variant: 'success', duration: 5000 })

// Remove a visible or queued notification, or clear the whole queue.
toast.dismiss(id)
toast.clear()

duration: 0 keeps a notification until dismissed. Timers pause when the stack is hovered or contains keyboard focus, when the document is hidden, and when the region is disconnected. Resuming preserves the remaining time. Updating a toast resets its duration; queued updates retain their place. Identical messages are independent unless they share an explicit id.

Position and capacity

Use getToastRegion() to configure the shared stack, or place your own <c2-toast-region> in the application shell and call its methods. The region exposes show(options), updateToast(id, options), dismiss(id), clear(), pause(), resume(), count, and queuedCount. max-visible is clamped to at least one. Changing capacity preserves the remaining lifetime of toasts moved into the queue.

import { getToastRegion } from '@c2n/toast'

const region = getToastRegion()
region.position = 'top-right'
region.maxVisible = 4
region.show({ message: 'A new notification arrived.' })

Available positions are top-left, top-center, top-right, bottom-left, bottom-center, and bottom-right (default). The inline attribute embeds a stack in normal document flow. Set label to name the notification region. Width, spacing, viewport offset and stacking order use --c2-toast-region__container--width, --gap, --offset and --z-index with the same prefix.

Entrance and exit animations

Choose effects independently with enter-animation and exit-animation on the region, or the enterAnimation and exitAnimation properties. Both accept none, fade, slide-up, slide-down, slide-left, slide-right, and scale. The defaults are slide-up on entrance and fade on exit. animation-duration / animationDuration controls both effects in milliseconds (default 200).

Directions describe movement: slide-down enters from above and exits downward; slide-right enters from the left and exits toward the right. Try each combination with the controls in the management demo.

import { getToastRegion } from '@c2n/toast'

const region = getToastRegion()
region.enterAnimation = 'slide-down'
region.exitAnimation = 'slide-right'
region.animationDuration = 400
region.show({ message: 'A new notification arrived.' })

Timeouts, close buttons, actions, and clear() all use the exit effect. A closing toast keeps its slot until the animation completes; only then does the next queued toast appear and start its timeout. toast-dismiss fires after removal, and count includes closing toasts. Updating a closing toast cancels its removal and restarts its lifetime. Queued toasts are removed immediately when dismissed.

Animations are skipped for prefers-reduced-motion, zero duration, none, or when the Web Animations API is unavailable. Disconnecting the region cancels active effects and finishes pending removals. Standalone c2-toast cards remain immediate; these effects belong to the manager.

Actions and dismissal

import { getToastRegion } from '@c2n/toast'

const region = getToastRegion()
region.addEventListener('toast-action', event => {
  const { id } = (event as CustomEvent).detail
  if (id === 'archived') console.log('Restore the archived item')
})
region.addEventListener('toast-dismiss', event => {
  const { id, reason } = (event as CustomEvent).detail
  console.log(id, reason) // timeout, close, action, programmatic, or clear
})
region.show({ id: 'archived', message: 'Item archived.', actionLabel: 'Undo', duration: 0 })

Activating an action emits toast-action with { id, toast }, then dismisses the notification. The dismiss button and Escape while focused inside a dismissible toast close it. Notifications do not take focus when they arrive. Managed notifications are announced politely through the stack’s live region; hover and keyboard focus pause the entire stack. Use persistent notifications for actions users need time to consider.

Standalone cards have no timer. Add dismissible to allow closing, and listen for toast-close or toast-action. The icon, close-icon, default message, and action slots allow custom content. A custom action slot owns its own event handling.

Icons

Replace the leading icon with an inline SVG or a c2-feather-* component in slot="icon". Use --c2-toast__icon--size and --c2-toast__icon--color to style it (semantic variants use their own icon colour variables). The close-icon slot replaces the dismiss glyph. Icons are decorative; keep the meaning in the message.

Set no-icon on a card or pass noIcon: true to toast.show() to remove the leading icon and its spacing. The Gallery includes custom SVGs, Feather icons, custom dismiss icons, and several icon-free layouts.

Countdown bar and icon alignment

Icons are vertically centered by default. Set --c2-toast__icon--align-self: flex-start for top alignment.

Timed managed toasts show a thin countdown bar along the bottom by default. It shrinks from full to empty as the dismissal time runs out. Set region.showProgress = false to hide it for a region. Each toast can override the setting with showProgress: true or false:

import { toast } from '@c2n/toast'

toast.show({ message: 'Changes saved.', duration: 6000 })
toast.show({ message: 'Quiet notification.', showProgress: false })

The bar uses the same clock as dismissal: it pauses on hover, focus, hidden pages and disconnection, resets on updates, and starts only when a queued toast becomes visible. Persistent toasts (duration: 0) have no countdown. The management demo includes a toggle to try it.

Style the bar with --c2-toast__progress--height, --c2-toast__progress--color, and --c2-toast__progress--background. The countdown is decorative and does not repeatedly announce time changes. A standalone card can display a static preview with show-progress and progress="0.65" (remaining fraction from 0 to 1); it does not start a timer.

escAccordionConnected, animated panels with shared borders and single or multiple expansion.LayoutAvatarImage, initials or icon for a person, with status dot and badge.Data displayBadgeTinted pill for status text, counts and dots, optionally pinned to a corner of another element.Data displayBreadcrumbNavigation trail of link buttons with separators, a current page and optional collapsing.NavigationButtonThemeable button with slots for text, prefix, suffix and running icons.ButtonsButton GroupAttached buttons with shared borders, optionally a segmented control with single or multiple selection.ButtonsCardGroups related content and actions on a single bordered surface.LayoutChat InputAuto-growing message box with a send button for chat interfaces.ChatChat MessageOne chat bubble with avatar, title, timestamp and message body.ChatCheckboxNative checkbox behaviour in a quiet, themeable box with an opt-in hover layer.InputsCode ViewerSyntax-highlighted code with line numbers, copy button and dark mode, powered by shiki.Data displayColor AreaTwo-dimensional area for picking saturation and value of a colour.InputsColor SelectColour swatch that opens a full picker built from area and slider.InputsColor SliderHorizontal slider for choosing a hue from 0 to 360.InputsCopy ButtonButton that copies text to the clipboard — the element it sits in, another element by id, or a literal string.ButtonsDetailsCollapsible disclosure built on native details and summary.LayoutIcon ButtonRound, hoverable button wrapping a slotted SVG icon.ButtonsKbdKeyboard key label for shortcuts and command hints, with the semantics of the native kbd element.Data displayLabelCaption that names and activates the control referenced by its for attribute, with a required marker.InputsLink ButtonText-styled control for link and navigation actions, rendered as a real anchor or a button.NavigationListVertical list container with single or multiple selection.Data displayList ItemSelectable row with icon slots, used on its own or as the option of list and select.Data displayMenuCommands, links, checkboxes and submenus in a popover anchored to a trigger.NavigationModalDialog built on the native dialog element: focus trap, backdrop, Escape, title, body and footer.FeedbackNavigation MenuSite navigation bar whose triggers open panels of links below the header.NavigationOverlayAnchored popup built on the browser Popover API, positioned with floating-ui.FeedbackPaginationPage navigation in three layouts: numbered pages, a simple page status, or a table-footer row with rows-per-page.NavigationProgressLinear progress bar, indeterminate or filling to a value, with an optional label and count.FeedbackRadioRadio options built on native inputs, grouped into one value with keyboard navigation.InputsSelectDropdown that pairs a themeable trigger with an anchored list of c2-list-item options.InputsSeperatorHorizontal or vertical rule with an optional label, for dividing content and toolbars.LayoutSheetDialog pinned to an edge of the screen, for content that complements the page rather than interrupting it.FeedbackSide NavResponsive navigation drawer beside the page: pushes the content on large screens, slides over it with a backdrop on small ones.NavigationSkeletonPlaceholder block standing in for content that has not arrived, in three shapes and three animations.FeedbackSliderRange input with a themeable track, thumb, step ticks and value bubble.InputsSpinnerCircular progress indicator, indeterminate or showing a value, with optional text.FeedbackSwitchOn/off toggle on a native switch input, with label, description and thumb icons.InputsTableVirtualized data grid with declarative columns, sorting, selection, pinning and resizing.Data displayTabsTab strip that shows one content panel at a time.NavigationText FieldSingle-line input with icon slots, clear button, helper and error text, and a character counter.InputsTextareaMultiline text input with resizing, helper and error text, and a character counter.InputsToastNotification cards and a manager for stacked, queued notifications with independent lifetimes.FeedbackTooltipContextual hint shown when its target is hovered or focused, rendered in the top layer.FeedbackFeather Icons287 open-source Feather icons, one web component each.IconsMat Icon2,234 Material Icons ligatures rendered through a single element.IconsPhosphor Icons1,512 flexible icons in six weights, one web component each.IconsThemingTheme every c2n component from a handful of design tokens with @c2n/theme, or reach for any component variable directly.GuideUsing c2n in an applicationThe workflow behind every c2n app: load the theme once, use c2-* tags directly, and turn every repeated pattern into a small variant or composed component.Guide