Stackable JavaScript Toast Notifications with Sound and Actions – chyrp

Category: Javascript , Notification | October 21, 2026
Authornatereprogle
Last UpdateOctober 21, 2026
LicenseMIT
Views0 views
Stackable JavaScript Toast Notifications with Sound and Actions – chyrp

chyrp is a Vanilla JavaScript toast notification library that enables a notification to change from a loading message to a completion alert in place and supports action buttons for user responses.

Features

  • Info, warning, error, and loading notification styles.
  • Automatic stacking with an overflow counter for additional messages.
  • 6 position options and bottom-centered placement on small screens.
  • Timed dismissal with pause on hover or keyboard focus.
  • Pointer-based swipe dismissal and click-to-dismiss behavior.
  • Configurable suppression of duplicate notifications.
  • Promise tracking with loading, completion, and error states.
  • Built-in sound presets and custom audio options.
  • Automatic light and dark colors with CSS variable overrides.
  • Reduced-motion preferences and TypeScript definitions.

How To Use It

Basic Usage

Place the stylesheet in the document head and the script at the end of the body. chyrp needs document.body to exist when it creates a notification.

<!-- In <head> -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/chyrp/dist/style.css">
<!-- Before </body> -->
<script src="https://unpkg.com/chyrp/dist/index.iife.js"></script>
<script>
  Chyrp.chyrp.info('Your settings were saved.');
</script>

Or install the package and import the required CSS once in your app entry file.

npm install chyrp
import { chyrp } from 'chyrp';
import 'chyrp/style.css';
chyrp.info('Your settings were saved.');
chyrp.warning('Storage is running low.');
chyrp.error('Unable to upload the file.');

Notifications with Actions

Action buttons can handle an Undo request or another explicit operation. The example expects an application-defined restoreFile() function. The toast closes after a button click unless its onClick handler returns false.

chyrp does not wait for a promise returned from this handler, so use return false and dismiss or update the toast yourself after asynchronous work.

chyrp({
  title: 'File deleted',
  body: 'report.pdf was moved to the trash.',
  persistent: true,
  actions: [
    {
      label: 'Undo',
      style: 'primary',
      onClick: () => restoreFile()
    },
    { label: 'Dismiss' }
  ]
});

Track an Asynchronous Task

Pass an existing promise to chyrp.promise() to show loading feedback and update the notification after resolution or rejection.

The example expects an application-defined saveSettings() function that returns a promise. The success handler receives the resolved value, and error receives the rejection reason.

chyrp.promise(saveSettings(), {
  loading: 'Saving settings...',
  success: () => 'Settings saved.',
  error: (error) => ({
    title: 'Save failed',
    body: error instanceof Error ? error.message : String(error)
  })
});

A loading toast displays a spinner by default. A positive max changes that indicator to a progress donut.

const progressToast = chyrp.loading('Preparing your export...', {
  max: 100,
  value: 0
});
progressToast.update({ value: 65, body: 'Exporting: 65%' });
progressToast.update({
  style: 'info',
  body: 'Your export is ready.',
  timeout: 2500
});

Configure Position, Timing, and Sound

Sound is enabled by default. configure() can change the global position, hover behavior, sound, and replay settings.

import { chyrp, configure } from 'chyrp';
configure({
  position: 'bottom-right',
  pauseOnHover: true,
  sound: false,
  resound: false
});
chyrp.info('Preferences updated.', {
  position: 'top-center',
  timeout: 6000,
  sound: 'success'
});

Update and Dismiss Notifications

Each call that creates a toast returns a ToastHandle with update() and dismiss(). You can change the message, notification style, action buttons, and timer through that handle.

const statusToast = chyrp.loading('Synchronizing data...', {
  sound: false
});
setTimeout(() => {
  statusToast.update({
    style: 'info',
    body: 'Synchronization complete.',
    timeout: 2500
  });
}, 1200);

channel groups notifications for dismissal. dismissChannel(name) clears that group, and dismissAll() clears every active toast.

import { chyrp, dismissChannel, dismissAll } from 'chyrp';
chyrp.warning('Connection interrupted.', {
  channel: 'network',
  persistent: true
});
// Later, after the connection recovers:
// dismissChannel('network');
// To clear all live notifications:
// dismissAll();

API Methods

APIDescription
chyrp(options)Creates a toast from ToastOptions and returns ToastHandle.
show(options)Standalone equivalent of chyrp(options).
info(body, options?) / chyrp.info(...)Creates an info toast and returns ToastHandle.
warning(body, options?) / chyrp.warning(...)Creates a warning toast and returns ToastHandle.
error(body, options?) / chyrp.error(...)Creates an error toast and returns ToastHandle.
loading(body, options?) / chyrp.loading(...)Creates a loading toast and returns ToastHandle.
promise(promise, options) / chyrp.promise(...)Tracks a Promise<T> with PromiseOptions<T> and returns ToastHandle.
configure(options) / chyrp.configure(...)Updates global ToastConfig defaults. Returns void.
dismissAll() / chyrp.dismissAll()Dismisses every active toast. Returns void.
dismissChannel(name) / chyrp.dismissChannel(name)Dismisses active toasts assigned to a string channel. Returns void.
interceptAlert()Overrides window.alert() with an info toast and returns a function that restores native behavior.

A ToastHandle returned by a creation method has two methods.

MethodDescription
dismiss(): voidDismisses the notification.
update(options: ToastOptions): ToastHandleChanges a live notification and returns the handle for chaining.

Full Toast Options

OptionType, default, and behavior
titlestring, default ''. Bold text above the body.
bodystring, default ''. Main notification text.
style'info' | 'warning' | 'error' | 'loading', default 'info'. Sets color and built-in icon.
timeoutnumber, default 4000 ms for ordinary styles and 0 for loading. Set 0 to disable the timer.
persistentboolean, default false. Sets the initial timeout to 0. An explicit positive timeout overrides it.
debouncenumber, default 100 ms. Suppresses repeated equivalent toasts during this window.
swipeboolean, default true. Enables swipe dismissal when Pointer Events are available.
pauseOnHoverboolean, default true. Pauses the timer on hover and keyboard focus when the device is not touch-primary.
positionToastPosition, default 'top-right'. Accepts top-left, top-center, top-right, bottom-left, bottom-center, or bottom-right. Viewports at or below 600px use bottom-center.
channelstring, no default. Displays a channel label and groups notifications for dismissal.
iconstring | HTMLElement | false, no default. Displays a text icon, a cloned DOM element, or no icon.
actionsToastAction[], no default. Buttons below the message.
soundSoundOption, default true. Accepts true, false, gentle, alert, success, error, an audio URL string, or ChimeNote[].
resoundboolean, default false. Replays sound when an update changes style.
maxnumber, no default. A positive maximum enables a determinate progress donut for loading toasts.
valuenumber, default 0. Current loading progress value. The donut uses value / max when max > 0.

Global Configuration

configure(options) accepts only the global fields below. Per-toast options have priority over their corresponding global settings.

OptionType, default, and behavior
positionToastPosition, default 'top-right'. Default desktop anchor position.
pauseOnHoverboolean, default true. Controls timer pausing on desktop hover.
soundSoundOption, default true. Default notification sound.
resoundboolean, default false. Controls sound replay when a toast changes style.
soundPresetsPartial mapping of success, error, alert, and gentle to ChimeNote[]. Overrides individual built-in chimes.

Action, Promise, and Sound Types

Each object in the actions array accepts three public fields. Action callbacks receive the toast handle as their argument.

ToastAction fieldType and behavior
labelstring. Text displayed on the button.
style'primary' | 'default', default 'default'. Button appearance.
onClick(handle: ToastHandle) => boolean | void. Return false to keep the toast open.

PromiseOptions<T, E> has three optional branches. Each accepts PromiseValueSpec, which is a string, a ToastOptions object, or a function returning either. If the relevant success or error branch is omitted, the notification dismisses when the promise settles.

PromiseOptions fieldType and behavior
loadingPromiseValueSpec<void>. Message or options shown during the pending state.
successPromiseValueSpec<T>. Value or function used when the promise resolves.
errorPromiseValueSpec<E>. Value or function used when the promise rejects.

SoundOption accepts a boolean, a built-in sound preset, an audio URL string, or ChimeNote[]. The presets gentle, alert, success, and error use the Web Audio API. configure({ soundPresets }) accepts a partial object with those four preset keys, each assigned an array of notes. Custom notes have the following fields.

ChimeNote fieldType and behavior
frequencynumber. Oscillator frequency in hertz.
startOffsetnumber. Time offset from the start of the chime, in seconds.
durationnumber. Note duration in seconds.
amplitudenumber. Peak amplitude from 0 to 1.
oscillatorTypeOptional oscillator waveform: 'sine', 'square', 'sawtooth', or 'triangle'. Defaults to 'sine'.

Styling and CSS Variables

chyrp follows prefers-color-scheme for its default light and dark colors. Apply class="dark" to <html> to force dark colors or class="light" to use the light palette.

Set the --tt-* custom properties in your CSS after the library stylesheet. For explicit dark colors, define the overrides in html.dark too, since the built-in dark-mode rules can supersede :root values.

-root {
  --tt-bg: #fff;
  --tt-title-color: #1e293b;
  --tt-body-color: #475569;
  --tt-info: #2563eb;
}
html.dark {
  --tt-bg: #111827;
  --tt-title-color: #f9fafb;
  --tt-body-color: #d1d5db;
}

The default stylesheet exposes 25 CSS custom properties. These are their light-mode values.

UI and Text

VariablePurpose and light-mode default
--tt-bgToast background, #fff.
--tt-shadowResting shadow, 0 4px 12px rgba(0, 0, 0, 0.15).
--tt-shadow-hoverHover shadow, 0 6px 16px rgba(0, 0, 0, 0.18).
--tt-title-colorHeading color, #111827.
--tt-body-colorMessage text color, #4b5563.
--tt-channel-colorChannel label color, #9ca3af.

Notification Accents and Buttons

VariablePurpose and light-mode default
--tt-infoInfo accent, #3b82f6.
--tt-warningWarning accent, #f59e0b.
--tt-errorError accent, #ef4444.
--tt-loadingLoading accent, #6b7280.
--tt-focus-ringKeyboard focus outline, rgba(59, 130, 246, 0.5).
--tt-action-bgDefault action background, #f3f4f6.
--tt-action-colorDefault action text, #374151.
--tt-action-hover-bgDefault action hover background, #e5e7eb.
--tt-action-primary-bgPrimary action background, #3b82f6.
--tt-action-primary-hover-bgPrimary action hover background, #2563eb.

Loading, Progress, and Overflow

VariablePurpose and light-mode default
--tt-spinner-trackSpinner track, #e5e7eb.
--tt-spinner-fillSpinner accent, #6b7280.
--tt-progress-bgTimer progress track, rgba(0, 0, 0, 0.06).
--tt-donut-trackDeterminate progress track, rgba(0, 0, 0, 0.12).
--tt-overflow-colorOverflow counter text, #4b5563.
--tt-overflow-hover-colorOverflow counter hover text, #111827.
--tt-overflow-bgOverflow counter background, #fff.
--tt-overflow-borderOverflow counter border, rgba(0, 0, 0, 0.06).
--tt-overflow-shadowOverflow counter shadow, 0 2px 6px rgba(0, 0, 0, 0.1).

Optional Native Alert Replacement

interceptAlert() replaces window.alert() with info-style toast messages until its returned restore function is called. The override is opt-in. Native window.confirm() and window.prompt() are unaffected.

import { interceptAlert } from 'chyrp';
const restoreAlert = interceptAlert();
window.alert('Operation completed.');
// Restore the native browser dialog.
restoreAlert();

Alternatives & Related Resources

You Might Be Interested In:


Leave a Reply