🔥 Oxide UI v0.1.0 — Dark mode only, copy-paste ready. Get started →
Skip to content

Notification Dot

<span> + ring-2 ring-[#09090b]

A small dot or count badge overlaid on an icon, avatar, or nav item. Used to signal unread notifications, online status, or pending actions.

Overview

Preview
3
AM
Notifications12

Count badge on icon

Preview
html
<div class="relative inline-flex">
  <button aria-label="Notifications (3 unread)" class="...">
    <!-- bell icon -->
  </button>
  <!-- Badge: aria-hidden because the count is in aria-label above -->
  <span aria-hidden="true"
    class="absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center
           rounded-full bg-[#D40C37] text-[9px] font-bold text-white
           ring-2 ring-[#09090b]"
  >3</span>
</div>

Pulse dot (no count)

Preview
html
<!-- Double-span creates the ping animation -->
<span aria-hidden="true" class="absolute -top-1 -right-1 flex h-3 w-3">
  <span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-[#D40C37] opacity-75"></span>
  <span class="relative inline-flex h-3 w-3 rounded-full bg-[#D40C37] ring-2 ring-[#09090b]"></span>
</span>

On an avatar

html
<div class="relative inline-flex">
  <div class="flex h-10 w-10 items-center justify-center rounded-full bg-[#D40C37]/20 text-sm font-semibold text-[#D40C37]">
    AM
  </div>
  <span aria-hidden="true" class="absolute -top-0.5 -right-0.5 h-3.5 w-3.5 rounded-full bg-[#D40C37] ring-2 ring-[#09090b]"></span>
</div>

In a nav item

html
<a href="/notifications" class="flex items-center gap-2 rounded-md px-3 py-2 text-sm text-zinc-300 hover:bg-zinc-800">
  <!-- icon -->
  <span>Notifications</span>
  <span aria-hidden="true" class="ml-auto inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-[#D40C37] px-1 text-[9px] font-bold text-white">
    12
  </span>
</a>

Status dots (online / away / offline)

html
<!-- Online -->
<span class="inline-flex h-2 w-2 rounded-full bg-emerald-400"></span>

<!-- Away -->
<span class="inline-flex h-2 w-2 rounded-full bg-amber-400"></span>

<!-- Busy / do not disturb -->
<span class="inline-flex h-2 w-2 rounded-full bg-[#D40C37]"></span>

<!-- Offline -->
<span class="inline-flex h-2 w-2 rounded-full bg-zinc-500"></span>

Dynamic count with Vue

vue
<script setup>
import { computed } from 'vue'
const props = defineProps({ count: { type: Number, default: 0 } })

const label = computed(() => props.count > 99 ? '99+' : String(props.count))
</script>

<template>
  <div class="relative inline-flex">
    <slot />
    <span v-if="count > 0"
      aria-hidden="true"
      class="absolute -top-1 -right-1 flex min-w-4 h-4 items-center justify-center rounded-full
             bg-[#D40C37] px-1 text-[9px] font-bold text-white ring-2 ring-[#09090b]"
    >{{ label }}</span>
  </div>
</template>

Accessibility notes

  • The count badge should be aria-hidden="true" when the count is already communicated in the trigger's aria-label (e.g. aria-label="Notifications (3 unread)")
  • Never rely solely on the dot to convey meaning — ensure the parent element has an accessible name that includes the notification state
  • For live updates (new messages arriving), wrap the count in aria-live="polite" so screen readers announce changes

Released under the MIT License.