Rule
Notification badges must represent only unread, actionable items and clear when the content is viewed.
Why
Stale or inflated counts train users to ignore the badge. Incorrect counts erode trust in the interface.
Must
- Show a count of unread or unseen items only.
- Clear the badge when the user views the associated content.
- Cap display at a maximum such as 99+ to prevent layout overflow.
- Include an accessible label for screen readers such as "12 unread notifications".
Should
- Update badge count in real time via WebSocket or polling.
- Persist read state server-side so it survives page reload.
Anti-patterns
- Counting all items including already-read ones.
- Badge that never clears even after the user visits the inbox.
- Showing a badge of 0 as a visible number.
Test Cases
- Badge clears after navigating to the notification list.
- Badge persists across refresh if items remain unread.
Telemetry
- badge_count_rendered
- notifications_marked_read
- badge_overflow_displayed
Code Examples
Svelte
<script lang="ts">
let unread = 12;
function markInboxRead() {
unread = 0;
}
function receiveNotification() {
unread += 1;
}
$: badgeValue = unread > 99 ? '99+' : String(unread);
$: badgeLabel = unread > 0 ? badgeValue + ' unread notifications' : 'No unread notifications';
</script>
<button type="button" aria-label={badgeLabel}>
Inbox
{#if unread > 0}
<span aria-hidden="true">{badgeValue}</span>
{/if}
</button>
<button type="button" on:click={markInboxRead}>Mark all read</button>
<button type="button" on:click={receiveNotification}>Simulate new alert</button>Count unread items only, clear the badge when the user views the related content, and cap the display before it breaks the layout.