Transitions✨ Premium

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.

JSAnimationStack

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

Cold room alerts — supervision of a logistics warehouse — Toast Cascade example 1

① Cold room alerts — supervision of a logistics warehouse

WhenThe night shift's screen is quiet; dock 3's door stays open, cold room B climbs to 6 °C, then the cooling unit restarts: the alerts stack up in the bottom right corner.
WhyThe alerts pile up without hiding the floor plan or the readings, and leave on their own once read; the limit of three keeps the latest in view even when several arrive at once.
Settings300 × 200 px stack in the bottom right, notifications widened to 270-290 px; a red "!" dot for alerts, blue for information, green for back to normal; "Test an alert" button in the header, dock plan and temperature curve on the left.
Checkpoint passages — live tracking of a trail race — Toast Cascade example 2

② Checkpoint passages — live tracking of a trail race

WhenRunners cross km 23 at the Croix pass: "Bib 412 at km 23 · 2 h 41", then 87, then 1033, a few seconds apart.
WhyThe stack limited to three keeps the page readable when a wave of runners passes within the same minute: the oldest passage gives way without the course map moving.
SettingsForest-green background, course drawn in yellow dashes on the left map, "212 / 480" counter; blue "i" notifications for passages, green for a finish; same 300 × 200 px stack on the right.
Pledges — the village hall screen during a local telethon — Toast Cascade example 3

③ Pledges — the village hall screen during a local telethon

WhenThe "€12,480" total shows in large type; each pledge taken at the front desk ("€20 pledge · Leroux family") arrives at the bottom of the screen, then leaves.
WhyEach pledge gets its moment on screen then makes room: the hall sees the momentum without an endless list covering the total and the afternoon program.
SettingsNight-violet background, 40 px total, pink-yellow gauge at 83%, afternoon program in the top right; green dots for pledges, blue for a challenge; "Add a pledge" button in the bottom left.

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 is aria-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 separate role="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 #1e1e2e and 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 #6366f1 button gives 4.47:1, just below the 4.5:1 AA threshold: #4f46e5 clears 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.

Chrome 51+✓ Full
Firefox 50+✓ Full
Safari 10+✓ Full
Edge 79+✓ Full
Mobile iOS 10+✓ Full
Android Chrome 51+✓ Full

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):

index.html — structure
<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>
Unlock the full code — $9.90/mo incl. tax

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 / propertyDefaultEffect
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

Because the stack uses 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.
Toast Stack piles its toasts in a container fixed to the top right of the window, with no count limit, leaving after 3 s, and is called through a global 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).
The button calls 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.