poptjs
v2.0.2
Published
Beautiful toast notifications with pill-to-card animations, spring physics, and zero dependencies.
Maintainers
Readme
popt
Beautiful toast notifications with pill-to-card animations, spring physics, and zero dependencies.
~11 KB min JS · ~8 KB min CSS · Zero dependencies · TypeScript ready · llms.txt
Install
npm install poptjs// ESM
import popt from 'poptjs';
import 'poptjs/dist/popt.css';
// UMD / CDN
<script src="https://unpkg.com/poptjs/dist/popt.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/poptjs/dist/popt.min.css">Quick Start
// Simple pill notification
popt.success('Event saved!');
// Expanded card with description
popt.error({
title: 'Payment declined',
description: 'Card ending in 4242 was declined.'
});API
Methods
| Method | Description |
|--------|-------------|
| popt.success(opts) | Green checkmark. Confirmations, saves. |
| popt.error(opts) | Red X. Failures, declined payments. |
| popt.warning(opts) | Orange alert. Low inventory, expiring sessions. |
| popt.info(opts) | Blue info. Updates, announcements. |
| popt.action(opts) | Purple arrow. Includes action button. |
| popt.show(opts) | Gray default. Custom icon optional. |
| popt.confirm(opts) | Two-button confirmation. Returns Promise<boolean>. |
| popt.prompt(opts) | Input field with submit/cancel. Returns Promise<string\|null>. |
| popt.promise(promise, handlers) | Loading → success/error transition. |
| popt.dismiss() | Dismiss the most recent toast. |
| popt.dismissAll() | Dismiss all toasts. |
| popt.isActive() | Returns true if any toast is visible. |
| popt.getStackSize() | Returns the number of active toasts. |
| popt.init(config) | Set global defaults. |
Every method accepts a string (title-only pill) or an options object.
Toast Options
| Property | Type | Description |
|----------|------|-------------|
| title | string | Text shown in the pill header |
| description | string \| HTMLElement | Expanded card body content |
| duration | number \| null | Auto-dismiss ms. null = persistent |
| button | { title, onClick } | Action button inside card |
| cancel | { title, onClick } | Cancel button (shown alongside action) |
| input | { type, placeholder, value } | Input field (used with prompt) |
| icon | string | Custom SVG string for badge |
| fill | string | Custom badge/progress color |
| position | string | Override position for this toast |
| expandDelay | number | Pill → card delay in ms |
| onDismiss | () => void | Callback when toast is dismissed |
Init Options
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| position | string | 'top-center' | Default position |
| duration | number | 6000 | Default auto-dismiss ms |
| expandDelay | number | 1000 | Pill → card delay |
| theme | 'dark' \| 'light' \| 'auto' | 'dark' | Color theme |
| maxToasts | number | 3 | Max visible toasts |
Positions
top-left · top-center (default) · top-right · bottom-left · bottom-center · bottom-right
Examples
Action Button
popt.action({
title: 'Booking saved as draft',
description: 'Resume checkout anytime.',
button: {
title: 'Resume checkout',
onClick: () => window.location = '/checkout'
}
});Confirm Dialog
const accepted = await popt.confirm({
title: 'Delete item?',
description: 'This action cannot be undone.',
confirmText: 'Delete',
cancelText: 'Keep it'
});
if (accepted) {
// user confirmed
}Returns false if cancelled, dismissed, or closed.
Prompt
const name = await popt.prompt({
title: 'Rename event',
description: 'Enter a new name for this event.',
placeholder: 'Event name...',
submitText: 'Rename'
});
if (name !== null) {
// user submitted a value
}Returns null if cancelled or dismissed.
Async Promise
popt.promise(fetch('/api/booking'), {
loading: 'Processing booking...',
success: 'Booking confirmed!',
error: 'Something went wrong'
});
// Dynamic messages
popt.promise(fetch('/api/tickets'), {
loading: 'Loading...',
success: (data) => ({
title: 'Loaded!',
description: `${data.count} tickets found`
}),
error: (err) => ({
title: 'Failed',
description: err.message
})
});Themes
// Dark (default)
popt.init({ theme: 'dark' });
// Light
popt.init({ theme: 'light' });
// Auto (follows system preference)
popt.init({ theme: 'auto' });Toast Stacking
Multiple toasts stack visually (up to maxToasts). Oldest are dismissed when the limit is reached.
popt.init({ maxToasts: 5 });
popt.success('First');
popt.info('Second');
popt.warning('Third');Custom Configuration
popt.init({
position: 'bottom-right',
duration: 4000,
expandDelay: 800,
theme: 'light',
maxToasts: 5
});Custom Color
popt.show({
title: 'Custom notification',
description: 'With a custom color.',
fill: '#e040fb'
});Features
- Pill → Card animation — Starts as a compact pill, morphs into an expanded card
- Spring physics — Natural-feeling
linear()easing - Toast stacking — Multiple toasts with configurable limit
- Themes — Dark, light, and auto (system preference)
- Swipe to dismiss — Touch gesture support on mobile
- Pause on hover — Timer pauses when user interacts
- Progress bar — Visual countdown synced with dismiss timer
- Promise support — Loading → success/error transitions
- Confirm dialogs — Two-button accept/cancel with async/await
- Prompt input — Text input with submit/cancel, returns Promise
- Action buttons — Interactive CTAs inside cards
- 6 positions — Any corner or center, top/bottom
- Safe area support — Respects mobile notch
- Reduced motion — Respects
prefers-reduced-motion - Accessible —
role="status",aria-live="polite" - Zero dependencies — Vanilla JS, no framework needed
- ESM + UMD — Works with bundlers and CDN
- TypeScript definitions — Full IntelliSense support
Browser Support
All modern browsers (Chrome, Firefox, Safari, Edge). The linear() easing function requires recent browser versions.
License
MIT
