webdevpuneet.com

Notification Badge — Free HTML CSS JS Snippet

Notification Badge · Buttons · Plain HTML, CSS & JS · Live preview

CategoryButtons

Copy & paste

Source Code

<div class="page">

  <!-- Icon button with count badge -->
  <div class="demo-row">
    <span class="demo-label">Count badge</span>
    <div class="demo-group">
      <div class="badge-wrap">
        <button class="icon-btn" aria-label="Notifications">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
        </button>
        <span class="badge count" id="notif-count">5</span>
      </div>

      <div class="badge-wrap">
        <button class="icon-btn" aria-label="Messages">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
        </button>
        <span class="badge count" style="background:#ec4899">12</span>
      </div>

      <div class="badge-wrap">
        <button class="icon-btn" aria-label="Cart">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"/></svg>
        </button>
        <span class="badge count" style="background:#10b981">3</span>
      </div>

      <div class="badge-wrap">
        <button class="icon-btn" aria-label="Alerts">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
        </button>
        <span class="badge dot pulse"></span>
      </div>
    </div>
  </div>

  <!-- Avatar with online status -->
  <div class="demo-row">
    <span class="demo-label">Status dot</span>
    <div class="demo-group">
      <div class="badge-wrap">
        <div class="avatar-btn" style="background:linear-gradient(135deg,#6366f1,#a78bfa)">AJ</div>
        <span class="badge status online"></span>
      </div>
      <div class="badge-wrap">
        <div class="avatar-btn" style="background:linear-gradient(135deg,#ec4899,#f97316)">SM</div>
        <span class="badge status busy"></span>
      </div>
      <div class="badge-wrap">
        <div class="avatar-btn" style="background:linear-gradient(135deg,#10b981,#0ea5e9)">RP</div>
        <span class="badge status away"></span>
      </div>
      <div class="badge-wrap">
        <div class="avatar-btn" style="background:linear-gradient(135deg,#64748b,#94a3b8)">MK</div>
        <span class="badge status offline"></span>
      </div>
    </div>
  </div>

  <!-- Nav item with badge -->
  <div class="demo-row">
    <span class="demo-label">Nav item</span>
    <div class="nav-list">
      <a href="#" class="nav-item active">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
        Dashboard
      </a>
      <a href="#" class="nav-item">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
        Messages
        <span class="nav-badge">8</span>
      </a>
      <a href="#" class="nav-item">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/></svg>
        Notifications
        <span class="nav-badge red">24</span>
      </a>
    </div>
  </div>

  <!-- Live controls -->
  <div class="demo-row">
    <span class="demo-label">Controls</span>
    <div class="controls">
      <button class="ctrl" onclick="change(-1)">− Remove</button>
      <button class="ctrl accent" onclick="change(1)">+ Add</button>
      <button class="ctrl" onclick="clear()">Clear</button>
    </div>
  </div>

</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Count badge: top:-5px right:-5px absolute, border:2px solid #fff separation trick
99+ overflow: textContent capped at "99+" for counts above 99
Pulsing dot: @keyframes scale(1→1.3) + opacity oscillation for live alert
Status dots: bottom:1px right:1px, green/red/amber/grey for online/busy/away/offline
Nav item badge: margin-left:auto pushes to right edge of nav link
Scale-pop on update: transform scale(1.3) → reset in 150ms setTimeout
Hide at zero: badge.style.display = "none" — never show "0" badge
All badge types: single position:relative .badge-wrap wraps any element

About this UI Snippet

Notification Badge — Count Badges, Status Dots, Pulse Animation & Nav Item Badges

Screenshot of the Notification Badge snippet rendered live

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.

Step by step

How to Use

  1. 1
    Click + 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.
  2. 2
    Apply 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.
  3. 3
    Use 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.
  4. 4
    Add 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.
  5. 5
    Hide 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.
  6. 6
    Export 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

App navigation and header notification counters
Count badges on header icons (bell, message, cart) communicate unread counts — wire one onto the notification bell. The white border separation makes badges readable on any background colour. Update counts via WebSocket or polling and apply the scale-pop animation on each update.
Team collaboration user presence and availability
Status dots on user avatars communicate real-time presence. Green = available for collaboration, red = do not disturb (in a meeting), amber = away, grey = offline. Update status via WebSocket events from your presence system.
Sidebar navigation unread count indicators
Nav item badges show unread messages, notifications, or items requiring attention inside a sidebar nav. The badge pushes to the far right of the nav item via margin-left: auto. Red badges communicate urgent items; blue for normal unread counts.
E-commerce shopping cart item count
Cart icon badges show the number of items in the mini cart. The scale-pop animation fires on each "Add to cart" action, giving clear visual feedback. Clear the badge when the cart is emptied. The 99+ overflow handles large carts gracefully.
Study the badge border-white separation trick
The border: 2px solid #fff on count badges creates a visual gap between the badge and the button. Without it, the badge blends into the button background. This trick works on any background colour because the white border matches the card or page background.
Gaming and social leaderboard achievement badges
Use count badges for achievement counts, streak indicators, or level badges. The pulsing dot variant communicates a newly earned achievement. Stack multiple badge types (count + status) on a profile avatar for a rich presence indicator.
Related: Vibration API Pattern Demo
See the Vibration API Pattern Demo for a related buttons pattern worth pairing with this one.

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}.