A lightweight, dependency-free toast notification library built with vanilla JS and modern CSS. Forked from Butterup and rebuilt for simplicity, accessibility, and modern browser standards.
- Zero dependencies β vanilla JS and modern CSS only
- Fully accessible β ARIA live regions, keyboard dismissal, screen reader announcements
- Dark mode support via
prefers-color-scheme - Reduced motion support via
prefers-reduced-motion - Windows High Contrast mode support via
forced-colors - Progress bar countdown with CSS custom properties
- Deduplication β repeated toasts update in place instead of stacking
- Global
onToastandonDespawncallbacks for analytics and logging - Multiple toast types:
success,error,warning,info - Multiple themes:
glass,brutalist - Promise-based toasts for async workflows
- Configurable position, icons, buttons, and callbacks
Manual Download chirp.js and chirp.css from this repo and reference them in your HTML.
<link
rel="stylesheet"
href="chirp.css" />
<script src="chirp.js"></script>chirp.toast({
title: "Success",
message: "Your changes have been saved.",
type: "success",
location: "top-right",
icon: true,
dismissable: true,
});| Option | Type | Default | Description |
|---|---|---|---|
title |
string |
null |
Bold heading text |
message |
string |
null |
Body text |
type |
string |
null |
success, error, warning, info |
location |
string |
top-right |
top-right, top-center, top-left, bottom-right, bottom-center, bottom-left |
icon |
boolean |
false |
Show a type-matched icon |
customIcon |
string |
null |
Raw HTML/SVG to use as the icon |
theme |
string |
null |
glass, brutalist |
dismissable |
boolean |
false |
Dismiss on click, Enter, or Escape |
progress |
boolean |
false |
Show a countdown progress bar |
dedupe |
boolean |
false |
Update existing toast instead of spawning a duplicate |
customHTML |
string |
null |
Inject custom HTML into the toast body |
onClick |
function |
null |
Fires when the toast is clicked |
onRender |
function |
null |
Fires immediately after the toast is added to the DOM |
onTimeout |
function |
null |
Fires just before the toast auto-dismisses |
primaryButton |
object |
null |
{ text: string, onClick: function } |
secondaryButton |
object |
null |
{ text: string, onClick: function } |
Returns the toastId string, which can be passed to chirp.despawnToast().
Programmatically dismiss a toast by its ID. Optionally fires onClosed after the exit animation completes.
const id = chirp.toast({ message: "Hello!" });
chirp.despawnToast(id, () => console.log("Toast gone"));Dismisses all active toasts, clears the dedup registry, and announces the action to screen readers.
chirp.clearAll();Shows a loading toast that updates automatically based on a promise outcome.
| Option | Type | Default | Description |
|---|---|---|---|
promise |
Promise |
required | The promise to track |
loadingMessage |
string |
'Loading...' |
Message shown while pending |
successMessage |
string |
'Operation successful' |
Message shown on resolve |
errorMessage |
string |
'An error occurred' |
Message shown on reject |
location |
string |
top-right |
Toast position |
theme |
string |
null |
glass, brutalist |
progress |
boolean |
false |
Show a countdown progress bar |
chirp.promise({
promise: fetch("/api/save"),
loadingMessage: "Saving...",
successMessage: "Saved successfully!",
errorMessage: "Something went wrong.",
location: "bottom-right",
});chirp.options.maxToasts = 5; // Max toasts visible at once (default: 5)
chirp.options.toastLife = 5000; // Auto-dismiss duration in ms (default: 5000)
chirp.options.onToast = null; // Fired when any toast is created
chirp.options.onDespawn = null; // Fired when any toast is removedProgress bar
chirp.toast({
title: "Uploading",
message: "Your file is being uploaded.",
type: "info",
icon: true,
progress: true,
dismissable: true,
});Deduplication
// Repeated calls update the existing toast instead of stacking
chirp.toast({
title: "Validation error",
message: "Please fill in all required fields.",
type: "error",
icon: true,
dedupe: true,
});Global callbacks
// Set once β fires for every toast in your app
chirp.options.onToast = (toast) => analytics.track("toast_shown", { id: toast.id });
chirp.options.onDespawn = (toast) => analytics.track("toast_dismissed", { id: toast.id });With buttons
chirp.toast({
title: "Delete item?",
message: "This action cannot be undone.",
type: "error",
icon: true,
primaryButton: {
text: "Delete",
onClick: () => deleteItem(),
},
secondaryButton: {
text: "Cancel",
onClick: () => console.log("Cancelled"),
},
});Custom icon and theme
chirp.toast({
message: "Copied to clipboard.",
theme: "glass",
icon: true,
customIcon: '<svg aria-hidden="true" focusable="false">...</svg>',
dismissable: true,
});Chirp is built with accessibility as a core requirement, not an afterthought.
- The toaster container has
role="region"andaria-label="Notifications"for screen reader landmark navigation - The toast rack uses
aria-live="polite"by default, switching toaria-live="assertive"forerrortype toasts - Each toast has
role="status"orrole="alert"depending on type - Dismissable toasts are keyboard focusable (
tabindex="0") and respond toEnterandEscape - All decorative icons include
aria-hidden="true"andfocusable="false" chirp.clearAll()announces dismissal to screen readers via a temporary live region- Focus styles use
:focus-visibleand match each toast type's colour - The progress bar is hidden from assistive technology via
aria-hidden="true"
Chirp targets modern browsers and uses the following features:
aria-live/ ARIA roles β universalprefers-reduced-motionβ all modern browsersprefers-color-schemeβ all modern browsersforced-colorsβ Chromium 89+, Firefox 89+:focus-visibleβ all modern browsers:is()selector β all modern browsers- CSS custom properties β all modern browsers
backdrop-filter(glass theme) β all modern browsers; no fallback in Firefox pre-103
Chirp is forked from Butterup by nlanger, distributed under the MIT License.
MIT β free to use in personal and commercial projects.
Fork the repo, make your changes, and open a pull request. Bug fixes and improvements are welcome.