More Buttons Snippets
Notification Badge — Free HTML CSS JS Snippet
Notification Badge · Buttons · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Notification Badge — Count Badges, Status Dots, Pulse Animation & Nav Item Badges

Notification badges are one of the most ubiquitous UI patterns — the red number on an app icon, the green dot on a user avatar, the unread count in a sidebar nav. This snippet provides all four badge variants in one component: count badges on icon buttons, a pulsing dot badge for live alerts, status indicator dots on user avatars, and unread count badges inside navigation items.
The count badge positioning
Badges use position: absolute on a relatively positioned .badge-wrap container. The count badge sits at top: -5px, right: -5px — overlapping the icon button corner. The border: 2px solid #fff creates a white gap between the badge and the underlying element, making the badge appear to float above the button even when backgrounds differ. This border-white separation trick is the standard count badge pattern used across all major platforms.
The 99+ overflow
When count exceeds 99, badge.textContent is set to '99+'. The min-width: 18px on the badge ensures it never collapses, and padding: 0 5px gives the '99+' text room. The border-radius: 999px (pill) adapts to any text length. This overflow pattern is used by iOS, Android, and all major social platforms.
The pulsing dot badge
The .dot.pulse class applies a CSS @keyframes animation that scales the dot from 1 to 1.3 and back with opacity oscillation. This creates a "heartbeat" effect that communicates live activity — new data arriving, a user typing, or a system alert. The pulse communicates urgency without a number.
Status indicator dots
Four colours communicate four presence states: green (online), red (busy/do not disturb), amber (away), grey (offline). Each uses position: absolute at bottom: 1px, right: 1px on the avatar — placed at the bottom-right corner, the universally recognised position for status dots in chat and collaboration apps.
The scale-pop animation on update
When the count changes, a brief transform: scale(1.3) is applied, then reset. The 150ms setTimeout removes the scale. This gives users clear visual feedback that the count has changed — a pattern used in WhatsApp and iOS notification counts.',
Animating the badge in when count goes from zero to one
When count changes from 0 to 1 (badge becomes visible), add an entrance animation: badge.style.animation = "none"; badge.offsetHeight; badge.style.animation = "badgeIn 0.3s cubic-bezier(0.34,1.56,0.64,1)"; and define @keyframes badgeIn { from { transform: scale(0); } to { transform: scale(1); } }. The offsetHeight forces a reflow between clearing and resetting the animation, ensuring it triggers correctly.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Rather than eyeballing the badge positioning offsets, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the count badge's white border is what makes it read as floating above the icon button regardless of the button's own background color, and how the update() function's scale-pop animation avoids fighting with the badge's own hide/show display toggle. The same assistant can help optimize it, for instance asking whether reusing one update() function across the count badge, the nav-item badges, and the status dots would reduce duplication, or whether the badgePulse keyframe's infinite animation should pause when the tab is backgrounded to save battery. It's also useful for extending the component: ask it to add the badge-entrance scale animation described in the FAQ for when a count goes from zero to one, wire the status dots to real presence data from a WebSocket, or add a maximum-badges-visible rule so a page with many icon buttons doesn't feel noisy. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a set of "notification badge" variants in plain HTML, CSS, and JavaScript — count badges, a pulsing alert dot, avatar status dots, and nav-item unread counts — no icon or badge library.
Requirements:
- A reusable wrapper pattern: any icon button or avatar sits inside a relatively positioned container, and a badge element is absolutely positioned inside it, so the same wrapper pattern works for every badge type.
- A numeric count badge pinned to the top-right corner of an icon button, pill-shaped (fully rounded regardless of digit count), with a solid two-pixel border in the surrounding page/card background color so it reads as separated from the button underneath rather than blended into it; when the count exceeds 99, display "99+" instead of the raw number, and when the count is exactly zero, hide the badge entirely rather than showing a "0".
- A small pulsing dot badge (no number) that continuously scales up and fades slightly using a CSS keyframe animation, to signal live/ongoing activity without a specific count.
- Avatar status dots pinned to the bottom-right corner of circular avatars, using distinct solid colors for online, busy, away, and offline states, again separated from the avatar by a border matching the background.
- An unread-count badge inside a horizontal navigation item that is pushed to the far right edge of the row (not absolutely positioned relative to an icon), with a visually distinct "urgent" color variant for high-priority counts.
- JavaScript controls (increment, decrement, clear) that update the count badge's displayed number, hide it at zero, and play a brief scale-up-then-reset "pop" animation on every change so the update is visually noticeable.Step by step
How to Use
- 1Click + Add and − Remove to change the notification countThe badge updates with a scale-pop animation. Click Clear to hide the badge entirely when count reaches zero. Count automatically shows "99+" when it exceeds 99.
- 2Apply count badges to any buttonWrap your button in a .badge-wrap div and add a <span class="badge count">N</span> inside it. The badge positions itself top-right via absolute positioning. Change the background colour via inline style or CSS.
- 3Use status dots on user avatarsWrap the avatar in .badge-wrap and add <span class="badge status online"> (or busy/away/offline). The dot positions at bottom-right. Change the state by swapping the class name.
- 4Add nav item badgesInside any nav link, add <span class="nav-badge">8</span> at the end. It pushes to the right via margin-left: auto. Add class="nav-badge red" for critical/alert counts.
- 5Hide badge when count is zeroThe update() function sets badge.style.display = "none" when count === 0. This is the correct pattern — do not show a "0" badge, hide it entirely. Restore display when count increases.
- 6Export in your formatClick "HTML" for a standalone file, "JSX" for a React component with count in useState, or "Tailwind" for a React + Tailwind CSS version.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
The count badge has border: 2px solid #fff. This white ring appears between the badge and the button underneath. When the button has a white or light background, the white border blends with the background and makes the badge appear to float above the button. The effect also works on coloured backgrounds — the white border creates separation that makes the badge stand out regardless of what is underneath it. This is the standard technique used by iOS, Android, and virtually every web notification badge implementation.
Connect a WebSocket: const ws = new WebSocket("wss://your-server.com/notifications"); ws.onmessage = event => { const data = JSON.parse(event.data); if (data.type === "notification_count") { count = data.count; update(); } }. Call update() whenever the count changes. The scale-pop animation provides visual feedback each time a new notification arrives. For a smoother experience, update() could also show a brief toast: "New message from Alex".
In the update() function, when count goes from 0 to 1 (badge changes from display:none to visible), add an entrance animation: badge.style.animation = "none"; badge.offsetHeight; badge.style.animation = "badgeIn 0.3s cubic-bezier(0.34,1.56,0.64,1)"; with @keyframes badgeIn { from { transform: scale(0); } to { transform: scale(1); } }. The offsetHeight forces a reflow between clearing and re-setting the animation, triggering it correctly.
Click "JSX" to download. Manage count with useState(5). The badge renders conditionally: {count > 0 && <span className="badge count">{count > 99 ? "99+" : count}</span>}. For the scale-pop on change: use a useEffect that watches count and applies a CSS class for 150ms. For status dots, derive the status class from a user.status prop: className={"badge status " + user.status}.