Toast
Stack-managed notification toasts with auto-dismiss, variant-colored indicators, configurable position, and smooth enter/exit animations.
<arc-toast> Overview
>
Toast provides a stack-managed notification system that surfaces brief, non-blocking messages to the user. Unlike modals or alerts, toasts appear in a fixed corner of the viewport and dismiss themselves automatically, making them ideal for confirming background operations — file saved, record updated, network reconnected — without interrupting the user's workflow.
A single `<arc-toast>` element acts as the toaster: you place it once in your layout and call its `show()` method imperatively whenever a notification needs to appear. Each call pushes a new toast onto the stack. Multiple toasts stack vertically with consistent spacing, and each one exits with a scale-and-fade animation after the configured duration. This imperative API keeps your template clean — there is no need to manage an array of open notifications in your component state.
**Queueing is built in.** `max-visible` (default 3) caps how many toasts are on screen at once; the rest wait and appear as slots free up, with `queue-limit` bounding the backlog. Set `max-visible="0"` for unbounded stacking. `dedupe` collapses a repeat of a message already showing into a "(×N)" counter on the existing toast — updated in place, so nothing flickers — and restarts its timer, so a message that keeps repeating stays on screen while it does. `arc-queue-change` reports the visible and queued counts; `arc-queue-overflow` fires when the backlog is full and the oldest queued toast is dropped.
`show()` returns the id it assigned, and `dismiss(id)` removes that toast whether it is visible or still queued.
Toasts can also be raised from anywhere without a reference to the element: dispatch an `arc-toast` event on `document` with the same options `show()` takes.
Four built-in variants — info, success, warning, and error — apply a colored bottom-edge indicator and a matching icon so users can parse the severity at a glance. The six position options let you anchor the toast stack to any corner or center-edge of the viewport, and a responsive breakpoint ensures toasts span the full width on small screens. The container carries `role="status"` and `aria-live="polite"` so screen readers announce new messages without stealing focus.Guidelines
When to use
- Place a single <arc-toast> element at the root of your layout so all pages share one toaster
- Use the success variant to confirm completed actions like saves, uploads, and deletions
- Keep messages short — one sentence or less — so users can read them before auto-dismiss
- Use the error variant for failures that need acknowledgment but not a blocking dialog
- Set duration to 0 for critical messages that the user must dismiss manually
- Pair with form submissions and async operations to provide immediate feedback
When not to use
- Do not create multiple <arc-toast> elements on the same page — use one shared instance
- Do not use toasts for information that requires user decision or input; use a Modal instead
- Do not display sensitive data (passwords, tokens) in a toast — they are visible to anyone nearby
- Do not set very short durations (under 2 000 ms); users may not have time to read the message
- Do not rely solely on color to convey meaning — the icon and message text must stand on their own
- Do not fire toasts in rapid succession for batch operations; summarize into a single notification
Features
- Imperative show() API — call with message, variant, and optional duration; returns the toast id
- `max-visible` caps on-screen toasts (default 3) and queues the rest; `queue-limit` bounds the backlog
- `dedupe` collapses a repeated message into a "(×N)" counter, updated in place
- `dismiss(id)` removes a toast whether it is visible or still queued
- Document-level `arc-toast` event raises a toast without a reference to the element
- `arc-queue-change` and `arc-queue-overflow` report queue state
- Four variants (info, success, warning, error) with color-coded bottom indicators and icons
- Six position anchors: top-right, top-left, top-center, bottom-right, bottom-left, bottom-center
- Auto-dismiss after configurable duration (default 4 000 ms); pass 0 to persist
- Smooth enter/exit animations with scale and opacity transitions
- Manual dismiss via close button on each toast
- Vertical stacking with consistent gap for multiple simultaneous toasts
- aria-live="polite" container for screen-reader announcements
- Respects `prefers-reduced-motion` — disables animations when set
- Responsive full-width layout on viewports under 640 px
- `arc-close` event fires when a toast is removed
Preview
Usage
This component requires JavaScript. No pure HTML/CSS version is available — use the Web Component directly or a framework wrapper.
<script type="module" src="@arclux/arc-ui"></script>
<arc-toast id="toaster" position="top-right"></arc-toast>
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
<arc-button variant="primary"
onclick="document.getElementById('toaster').show({ message: 'Changes saved successfully.', variant: 'success' })">
Success
</arc-button>
<arc-button variant="secondary"
onclick="document.getElementById('toaster').show({ message: 'Something went wrong.', variant: 'error' })">
Error
</arc-button>
<arc-button variant="ghost"
onclick="document.getElementById('toaster').show({ message: 'Deployment in progress...', variant: 'warning', duration: 6000 })">
Warning (6 s)
</arc-button>
</div> import { Toast, Button } from '@arclux/arc-ui-react';
import { useRef } from 'react';
export function NotificationDemo() {
const toastRef = useRef<HTMLElement>(null);
const showSuccess = () =>
(toastRef.current as any)?.show({ message: 'Changes saved successfully.', variant: 'success' });
const showError = () =>
(toastRef.current as any)?.show({ message: 'Something went wrong.', variant: 'error' });
return (
<>
<Toast ref={toastRef} position="top-right" />
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<Button variant="primary" onClick={showSuccess}>Success</Button>
<Button variant="secondary" onClick={showError}>Error</Button>
</div>
</>
);
} <script setup>
import { ref } from 'vue';
import { Button, Toast } from '@arclux/arc-ui-vue';
const toaster = ref(null);
const showSuccess = () => toaster.value?.show({ message: 'Changes saved successfully.', variant: 'success' });
const showError = () => toaster.value?.show({ message: 'Something went wrong.', variant: 'error' });
</script>
<template>
<Toast ref="toaster" position="top-right" />
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
<Button variant="primary" @click="showSuccess">Success</Button>
<Button variant="secondary" @click="showError">Error</Button>
</div>
</template> <script>
import { Button, Toast } from '@arclux/arc-ui-svelte';
let toaster;
const showSuccess = () => toaster?.show({ message: 'Changes saved successfully.', variant: 'success' });
const showError = () => toaster?.show({ message: 'Something went wrong.', variant: 'error' });
</script>
<Toast bind:this={toaster} position="top-right" />
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
<Button variant="primary" on:click={showSuccess}>Success</Button>
<Button variant="secondary" on:click={showError}>Error</Button>
</div> import { Component, ViewChild, ElementRef } from '@angular/core';
import { Button, Toast } from '@arclux/arc-ui-angular';
@Component({
imports: [Button, Toast],
template: `
<arc-toast #toaster position="top-right"></arc-toast>
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
<arc-button variant="primary" (click)="showSuccess()">Success</arc-button>
<arc-button variant="secondary" (click)="showError()">Error</arc-button>
</div>
`,
})
export class NotificationDemoComponent {
@ViewChild('toaster') toaster!: ElementRef;
showSuccess() {
this.toaster.nativeElement.show({ message: 'Changes saved successfully.', variant: 'success' });
}
showError() {
this.toaster.nativeElement.show({ message: 'Something went wrong.', variant: 'error' });
}
} import { Button, Toast } from '@arclux/arc-ui-solid';
export function NotificationDemo() {
let toaster: HTMLElement | undefined;
return (
<>
<Toast ref={toaster} position="top-right" />
<div style={{ display: 'flex', gap: '8px', 'flex-wrap': 'wrap' }}>
<Button variant="primary"
onClick={() => (toaster as any)?.show({ message: 'Changes saved successfully.', variant: 'success' })}>
Success
</Button>
<Button variant="secondary"
onClick={() => (toaster as any)?.show({ message: 'Something went wrong.', variant: 'error' })}>
Error
</Button>
</div>
</>
);
} import { Button, Toast } from '@arclux/arc-ui-preact';
import { useRef } from 'preact/hooks';
export function NotificationDemo() {
const toastRef = useRef<HTMLElement>(null);
const showSuccess = () =>
(toastRef.current as any)?.show({ message: 'Changes saved successfully.', variant: 'success' });
const showError = () =>
(toastRef.current as any)?.show({ message: 'Something went wrong.', variant: 'error' });
return (
<>
<Toast ref={toastRef} position="top-right" />
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<Button variant="primary" onClick={showSuccess}>Success</Button>
<Button variant="secondary" onClick={showError}>Error</Button>
</div>
</>
);
} API
-
position'top-right' | 'top-left' | 'top-center' | 'bottom-right' | 'bottom-left' | 'bottom-center''top-right' - Anchors the toast stack to a fixed edge of the viewport. Top-right is the most conventional position for web applications. Bottom positions work well for media players or editors where the top area is occupied by toolbars.
-
durationnumber4000 - Time in milliseconds before a toast auto-dismisses. Applies as the default for every show() call but can be overridden per-toast via the duration option in the show() payload. Set to 0 to disable auto-dismiss entirely, requiring the user to click the close button.
-
max-visiblenumber3 - Maximum toasts on screen at once (attribute: max-visible). Further show() calls queue FIFO and release as visible toasts dismiss. Set to 0 for no cap.
-
dedupebooleantrue - When true, a show() whose message and variant match a visible or queued toast is coalesced: the existing toast gains a "(×N)" counter and a fresh timer instead of a second toast appearing. Set the property to false from JS to disable.
-
queue-limitnumber20 - Maximum queued (not visible) toasts (attribute: queue-limit). Beyond it the oldest queued entries are dropped and arc-queue-overflow fires with the drop count.
Events
-
arc-queue-overflow - Fired when the queue exceeds queueLimit and the oldest queued entries are dropped. detail: { dropped }.
-
arc-queue-change - Fired whenever the visible or queued count changes. detail: { visible, queued }.
-
arc-close - Fired when a toast notification is dismissed. detail: { id } — the id show() returned.