Notification Bell
A notification bell that rings and shakes on click, pops a counter badge, then slides in a confirmation toast — three CSS animations chained in sequence from a single interaction.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →
3 usage examples



How it works
The .fx-0366-root root (250 px tall, rgb(26, 26, 36) background) centers a group made up of the bell button and the toast. The .mi-bell-btn button holds a bell SVG and a span.mi-bell-badge (small red round badge, initially hidden). The .mi-bell-toast is a notification div positioned just below the button, initially invisible (opacity: 0; transform: translateY(10px)). The notification area has role="status", aria-live="polite", and aria-atomic="true".
On click, the script increments an internal counter and updates the button’s aria-label with the new count (e.g. Notifications, 4 unread). In CSS, adding .ringing to the button fires @keyframes miBellShake (±15° rotation, 0.5 s) on the SVG, and adding .show-badge reveals the badge with @keyframes miPopIn (scale 0 → 1.2 → 1 in 0.3 s). The toast appears with opacity: 1; translateY(0) in a 0.3 s transition. After 2 s, the classes are removed and the toast fades out.
The bell shake, badge appearance, and toast are independent in CSS — their animations are chained via transition-delay rather than nested setTimeout. Only the automatic class removal after 2 s uses setTimeout. The script also updates the badge text value and keeps a consistent counter across clicks.
Accessibility
- Keyboard: the bell button is a real
<button>; Tab reaches it, Space or Enter fires it. The focus outline (:focus-visible) appears on keyboard navigation only. - Toast announced: the toast has
role="status"andaria-live="polite": its text content is announced by screen readers when it appears, without interrupting the current reading (polite). - Counter in label: the button’s
aria-labelis updated on click with the unread notification count. The visual badge hasaria-hidden="true"to prevent double announcements. - Reduced motion: under
prefers-reduced-motion: reduce, the bell shake and toast translation are removed. The badge appears and the toast shows by opacity transition only, with no movement. - Touch: the button responds normally to touch events. Its 48×48 px target is comfortable.
Browser compatibility
CSS (@keyframes animations, transitions, aria-live) + 0.5 KB of JavaScript (classList, textContent, setAttribute, setTimeout), no dependency. Verified in Chromium 151: full sequence, aria-live announced, counter updated, two independent copies, reduced-motion without shake or translation, 0 errors.
Without JavaScript, the bell is displayed at rest; the badge and toast do not appear on click. Without CSS animations, state changes are instant with no shake or slide animation.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0366-root">
<div class="mi-bell-scene">
<button class="mi-bell-btn" aria-label="Notifications">
<svg viewBox="0 0 24 24">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 0 1-3.46 0"></path>
</svg>
<span class="mi-bell-badge" aria-hidden="true">3</span>
</button>
<div class="mi-bell-toast" role="status" aria-live="polite" aria-atomic="true">New notification!</div>
</div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Toast content (HTML — text in .mi-bell-toast) |
New notification! | Replace with your app’s message. For a dynamic message, update toast.textContent in the script before adding the display class. |
Toast display duration (JS — setTimeout) |
2 000 ms | Extend to 4 000 ms for a longer message; reduce to 1 200 ms for a discreet toast. |
Badge color (CSS — background on .mi-bell-badge) |
Red #ef4444 |
Switch to orange (#f97316) for a non-critical alert, or green (#10b981) for a positive notification. |
Shake intensity (CSS — @keyframes miBellShake) |
±15° over 0.5 s | Reduce to ±8° for subtle shaking; raise to ±25° for more dramatic ringing. Reduce the duration to 0.3 s for a crisp ring. |
FAQ
setTimeout. The counter continues to increment correctly..fx-0366-root if multiple instances coexist..mi-bell-toast is an ordinary div; add an SVG icon or image before the text. Keep the text as the first content for screen readers (aria-live reads the div’s text content).