Notification Badge
A red badge on a bell that pulses while something is unread, bounces with each arrival and shakes before vanishing when cleared.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Utilities has 43 effects, including 3 free. Explore the category →
3 usage examples



How it works
The .nbg-badge badge sits in the corner of a real .nbg-icon button (an SVG bell). The starting number is the one written in the badge (3 by default). While it is above zero, the badge breathes in a loop (nbg-pulse, 2 s, growing to 115 %).
The counter is driven by a small API set on the root: root.fx0744.add() adds 1 (or add(5)), set(12) sets a value, clear() resets to zero, pulse(false) stops the breathing, count() reads the value. A rise makes the badge bounce (0.5 s); a drop changes the figure without animation; zero makes it shake then disappear. It only comes back with a new add() or set().
The click on the bell is yours: hook it to opening your notification list, then call clear() once they have been read. Each root has its own counter: two bells on the page, or the code pasted twice, never get mixed up.
Accessibility
- A real, named button: the bell is a
<button>, reachable from the keyboard, with a visible focus ring. Its name is the hidden.nbg-srtext, kept up to date: "3 unread notifications", "No unread notifications". - Changes announced: each API call writes the new phrase into a
role="status"region, read politely. The starting state is not announced. - Wording in the HTML:
data-nbg-one,data-nbg-manyanddata-nbg-nonegive the singular, the plural and the zero case, in your language and vocabulary (messages, orders, parcels). - Reduced motion: under
prefers-reduced-motion: reduce, no breathing, bounce or shake; the figure changes and the badge disappears at zero, with no delay. - Not colour alone: red catches the eye but the written number and the button name carry the information; nothing relies on colour.
Browser compatibility
Dependency-free JavaScript, CSS animations (transform, box-shadow) and inline SVG. Hiding text for screen readers uses clip-path: inset() (Chrome 55, Firefox 54, Safari 9.1). Measured in Chromium 151, Firefox 153 and WebKit 26.5 (Playwright): two copies driven separately, code pasted twice, add, clear that stays at zero, set, pulse(false), keyboard, touch, 390 px, reduced motion, English page, no name added to window, 0 errors.
Without JavaScript, the bell and badge show with the number written in the HTML, pulsing; only the API is missing. In Safari on Mac, Tab only reaches buttons if the system keyboard option is turned on.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0744-root" data-nbg-one="unread notification" data-nbg-many="unread notifications" data-nbg-none="No unread notifications">
<button class="nbg-icon" type="button">
<svg class="nbg-bell" aria-hidden="true">…</svg>
<span class="nbg-badge nbg-pulse" aria-hidden="true">3</span>
<span class="nbg-sr">3 unread notifications</span>
</button>
<span class="nbg-sr nbg-live" role="status"></span>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Starting number (HTML — text of .nbg-badge) |
3 |
Write the real value on the server; 0 starts with the badge hidden. |
Drive the counter (JS — root.fx0744) |
— | add(k), set(n), clear(), pulse(true|false), count(). For instance add() on each message received over a WebSocket. |
Spoken wording (HTML — data-nbg-one, data-nbg-many, data-nbg-none) |
"unread notification", "unread notifications", "No unread notifications" | Your interface's vocabulary and language: "unread message", "order to prepare"… |
Breathing (HTML — nbg-pulse class) |
present | Remove the class for a still badge, or call pulse(false) once read. |
Icon (HTML — content of .nbg-icon) |
SVG bell | Swap the SVG for your icon, or for text (a channel name): the badge follows the button. |
Colours and size (CSS — .nbg-badge) |
gradient #ef4444 → #dc2626, 22 px |
Your brand's alert colour; enlarge min-width, height and font-size together. |
FAQ
clear() resets to zero, the badge shakes then disappears and stays hidden. It only comes back if your code calls add() or set() with a positive number.root.fx0744.set(n) on each answer from your server (polling, WebSocket, push notification). set only bounces when the number goes up.clear() once the user has seen them.