Toast Cascade
A notification stack that tidies itself: each message enters from below with a slight bounce (0.4 s), the stack keeps only three — the fourth pushes the oldest out to the right (0.3 s) — and each one leaves on its own after 4 s. CSS and 2.5 KB of JavaScript, a 150 ms fade when the user reduces motion.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Transitions has 52 effects, including 9 free. Explore the category →
3 usage examples



How it works
The .tc-root block declares its background (#0a0a0f, white text) and centers the stack then the button in a column. The .tc-stack is 180 px tall, clips what overflows (overflow: hidden) and lays out its children in flex-direction: column-reverse: the first DOM child sits at the bottom, each new notification lands above the others. A .tc-item notification is 220 to 280 px wide, 10 px corners, a #1e1e2e → #2d2b55 gradient; on the left, a 24 px dot whose color tells the type: .tc-icon--success green #10b981, --error red #ef4444, --info blue #3b82f6.
The entrance is the tcEnter animation: 0.4 s, from translateY(30px) scale(.9) and opacity 0 to its place, on cubic-bezier(.34, 1.56, .64, 1) — the curve goes past 1, hence the small bounce at the end. Measured 60 ms after the click: scale 0.955, 13 px below its place, opacity 0.55 in Chromium 151 (same order in Firefox 153 and WebKit 26). The exit adds .tc-exit, which plays tcExit in 0.3 s: 100 px to the right, scale 0.8, opacity 0, while max-height and padding fold to zero; the script removes the node at 300 ms. The remaining notifications then slide 44 px down in one continuous movement, then the remaining 10 px at once when the script removes the node: the 8 px gap between two notifications and the 2 px border hold until then (measured frame by frame in Chromium and Firefox).
The script walks every .tc-root on the page: several stacks live side by side, each with its own counter, and the tc- prefix keeps them apart from the library's other notifications. addToast() counts the notifications that are not already leaving; from three on, it sends the oldest out (the first in the DOM, at the bottom), then creates the next one and gives it a 4,000 ms timer. Types rotate in the order success, error, info, with a message drawn at random in the block's language: data-lang if set, otherwise the page's lang, French by default. Two settings on the notification keep the stack calm when it overflows during an exit: max-height: 80px (from none, max-height does not interpolate) and flex-shrink: 0 (without it, the switch to overflow: hidden halfway through the exit lets flexbox crush the notification at once). On load, two demo notifications arrive at 300 and 700 ms: remove the two setTimeout calls at the end of the script for an empty stack at start.
Accessibility
- Announcement: the stack carries
aria-live="polite"; a screen reader reads each message as it arrives, without cutting off what it was saying. The dot isaria-hidden: the type must therefore read in the text ("Error: …") if the color carries information. For an alert that calls for immediate action, add a separaterole="alert"region. - Reading time: each notification leaves on its own after 4 s and the code does not pause the timer on hover. That is short for slow reading or a screen magnifier: put nothing in it that cannot be found elsewhere (history, tracking page), or lengthen the delay.
- Reduced motion: under
prefers-reduced-motion, entrance and exit become 150 ms fades (tcFadeIn,tcFadeOut), with no slide or bounce — measured in Chromium: 0.15 s animation, no transform. - Contrast: white text on the gradient, 16.4:1 on
#1e1e2eand 13.2:1 on#2d2b55. The white signs in the dots are weaker (2.5:1 on green, 3.8:1 on red): they back up the text without replacing it. The indigo#6366f1button gives 4.47:1, just below the 4.5:1 AA threshold:#4f46e5clears it easily (6.29:1).
Browser compatibility
CSS animations, flexbox in column-reverse and about fifty lines of JavaScript, no dependency. Measured in Chromium 151, Firefox 153 and WebKit 26: same bouncing entrance, same exit to the right, three notifications at most, empty stack 5 s after the last one was added, 150 ms fade under reduced motion. The script relies on NodeList.forEach and Element.closest, available since Chrome 51, Firefox 50 and Safari 10.
Without JavaScript, the stack stays empty and the button does nothing: show important messages in the page itself. With reduced motion, notifications appear and disappear with a 150 ms fade, at the same pace.
The code
HTML structure to paste into your page (the CSS + JS come with Premium):
<div class="tc-root">
<div class="tc-stack" aria-live="polite">
<!-- notifications created by the script: -->
<div class="tc-item">
<span class="tc-icon tc-icon--success" aria-hidden="true">✓</span><span>…</span>
</div>
</div>
<button class="tc-btn" type="button">Add Toast</button>
</div>
The full HTML + CSS + JS of Toast Cascade, ready to paste — plus all 840 effects. Or $299 once, lifetime access.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Maximum count (JS — toasts.length >= 3) |
3 notifications | Beyond it, the oldest leaves when a new one arrives. With 4 or 5, raise .tc-stack to 230 or 290 px: a one-line notification is 46 px tall, plus an 8 px gap. |
Display time (JS — 4000) |
4 s | Delay before each notification leaves on its own. 6,000 to 8,000 ms leave time to read a full sentence; for an alert that must stay, remove the timer and add a close button. |
Entrance (CSS — tcEnter, cubic-bezier(.34, 1.56, .64, 1)) |
0.4 s, 30 px rise, bounce | ease-out instead of the curve removes the bounce; translateY(60px) in the first step gives a longer rise. |
Exit (CSS — tcExit, translateX(100px)) |
0.3 s to the right | translateX(-100px) exits to the left. If you lengthen the animation, match the 300 in removeToast's setTimeout, or the node disappears before the end. |
Stack height (CSS — .tc-stack height) |
180px | Three one-line notifications fit (174 px with the inner padding). For a stack stuck to the bottom of the screen, set .tc-root to position: fixed; right: 16px; bottom: 16px; width: 320px. |
Messages and types (JS — TYPES object) |
3 types × 3 messages, in French and English | Replace the texts, add a language (de: […]) or a type; for a new type, also add its color .tc-icon--warning { background: #f59e0b; }. |
Colors (CSS — .tc-item, .tc-icon--*) |
#1e1e2e → #2d2b55 gradient |
On a light page: background: #fff; color: #111; border-color: #e5e7eb and a lighter shadow; keep at least 4.5:1 between text and background. |
FAQ
flex-direction: column-reverse: the first DOM child, the oldest, sits at the bottom, and it is the one that leaves when the stack is full. For the opposite order (newest at the bottom, against the screen edge), set .tc-stack to flex-direction: column; justify-content: flex-end: the oldest stays first in the DOM, so at the top, and the script still sends it out first.showToast(type, message) function. Toast Cascade lives in its own block (no position: fixed), keeps only three by pushing the oldest out, enters with a bounce and leaves after 4 s; nothing is exposed on window. Pick Toast Stack for page-wide action feedback, Toast Cascade for a stream of events in one area of a screen (supervision, live coverage, hall display).addToast(), which picks a type and a message in TYPES. To push a message from your application, give it two parameters — function addToast(type, text), then use type and text instead of the random draw — and make it reachable from the block, for instance root.addToast = addToast; right after its definition: document.querySelector('.tc-root').addToast('error', 'Dock 3 door open'). The text is set with textContent: a message typed by a user is shown as is, never parsed as HTML.