Badge
A badge is a small, glanceable indicator that highlights new or unread activity nearby. It shows as a plain dot when it has nothing to display, and turns into a rounded pill as soon as a short number or bit of text is placed inside it.
When to use
Reach for a badge when something needs a quick, glanceable nudge, such as an unread count on an inbox tab, a "new" dot on a menu item, or a notification number on an icon. It's meant to sit next to something else, not stand alone. See Alternatives below if what you're showing is a status or category someone should be able to read on its own.
Install & usage
Pick a framework in the toolbar above and these snippets adapt.
npm install @eunomia/elements
import "@eunomia/elements/badge.js";
<eun-badge>5</eun-badge>
Importing the file registers <eun-badge> as a custom element, with no
further setup needed. It works with any framework, or none, since it's a
standard web component.
npm install @eunomia/elements
<script type="module">
import "@eunomia/elements/badge.js";
</script>
<eun-badge>5</eun-badge>
npm install @eunomia/elements
import "@eunomia/elements/badge.js";
function UnreadBadge() {
return <eun-badge>5</eun-badge>;
}
npm install @eunomia/elements
"use client";
import "@eunomia/elements/badge.js";
export function UnreadBadge() {
return <eun-badge>5</eun-badge>;
}
npm install @eunomia/elements
<script setup>
import "@eunomia/elements/badge.js";
</script>
<template>
<eun-badge>5</eun-badge>
</template>
npm install @eunomia/elements
import { CUSTOM_ELEMENTS_SCHEMA, Component } from "@angular/core";
import "@eunomia/elements/badge.js";
@Component({
selector: "app-unread-badge",
template: `<eun-badge>5</eun-badge>`,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class UnreadBadgeComponent {}
Alternatives
eun-tagGuidance
- Use an empty badge (no slotted content) to signal "something is new/unread" without a precise count
- Keep the slotted content short, a one- or two-digit number, since long text breaks the "always round" pill shape
- Set
labelwhenever the badge is the only thing conveying its information (e.g. decorating an icon-only trigger)
- Using a badge for a status/category label read on its own, see Alternatives above
- Relying on the badge alone to convey information with no visible or accessible text nearby (see the Accessibility tab)
- Overriding colors with inline styles instead of the
--badge-*CSS variables
Live testing
Properties
Badge <eun-badge>
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
| variant | 'primary' | 'secondary' | 'critical' | 'success' | 'warning' | 'critical' | The color variant to apply |
| size | 's' | 'm' | 'l' | 'm' | The size of the badge |
| label | string | — | An optional accessible name. When set, the badge announces this text instead of staying hidden from assistive technology |
Import the exact TypeScript type behind any property above, see
Slots
| Name | Description |
|---|---|
| (default) | The badge's content, typically a short number. Left empty, the badge renders as a plain dot |
CSS custom properties
| Name | Description |
|---|---|
| --badge-background | Sets the background color of the badge |
| --badge-color | Sets the text color of the badge |
| --badge-size | Sets the diameter of the badge, driving both the empty circle and the content pill |
| --badge-padding-inline | Sets the horizontal padding around the badge's content. Ignored while empty |
| --badge-font-size | Sets the font size of the badge's content |
CSS shadow parts
| Name | Description |
|---|---|
| badge | The badge's own box, carrying its size, padding, and background. Exposed for effects the badge's CSS custom properties don't cover |
Basic
An empty <eun-badge> renders as a plain dot, and any slotted content turns
it into a pill.
<eun-badge></eun-badge>
<eun-badge>5</eun-badge>
<eun-badge>99</eun-badge>
Variants
<eun-badge variant="primary">1</eun-badge>
<eun-badge variant="secondary">2</eun-badge>
<eun-badge variant="critical">3</eun-badge>
<eun-badge variant="success">4</eun-badge>
<eun-badge variant="warning">5</eun-badge>
Part of the shared vocabulary covered in
Sizes
<eun-badge size="s">1</eun-badge>
<eun-badge size="m">1</eun-badge>
<eun-badge size="l">1</eun-badge>
In a tab
eun-tab reserves a dedicated badge slot for exactly this purpose.
Dropping a <eun-badge> into it positions the badge the same way
eun-tab's own built-in dot indicator (its boolean badge attribute)
already does, without losing the ability to show a real count.
<eun-tab-group>
<eun-tab slot="navigation" panel="inbox">
Inbox
<eun-badge slot="badge">5</eun-badge>
</eun-tab>
<eun-tab slot="navigation" panel="sent">Sent</eun-tab>
<eun-tab-panel name="inbox">5 unread messages.</eun-tab-panel>
<eun-tab-panel name="sent">No sent messages.</eun-tab-panel>
</eun-tab-group>
See Tabs for the rest of the eun-tab/eun-tab-group API.
Overlaying an icon
There's no dedicated "overlay" API. Position the badge with plain CSS
against a position: relative wrapper, the same technique any icon library
composition would use.
<span style="position: relative; display: inline-flex;">
<eun-icon name="notifications" size="24"></eun-icon>
<eun-badge label="3 unread notifications" class="badge-overlay">3</eun-badge>
</span>
.badge-overlay {
position: absolute;
top: -4px;
right: -4px;
}
Custom
Override the --badge-* CSS variables, listed in full in the API tab. The
shape itself (always fully rounded) isn't one of them.
<eun-badge class="custom-badge">12</eun-badge>
.custom-badge {
--badge-background: #7c3aed;
--badge-color: #fff;
--badge-size: 24px;
--badge-font-size: 13px;
}
Decorative by default
With no label set, eun-badge is aria-hidden="true". It carries no ARIA
role and contributes nothing to the accessibility tree. This matches the
posture of eun-tag's own icon and eun-tab's pre-existing boolean dot
indicator, both purely visual accents rather than independent pieces of
content. This is the right default whenever the badge sits next to text
that already states its meaning. Take a tab labeled "Inbox" next to a 5
badge. A screen reader announcing just "Inbox" isn't losing essential
information that a sighted user has and a non-sighted one doesn't, as long
as the surrounding UI (or the tab panel's own content) states the actual
count somewhere perceivable.
Making it announced with label
Set label whenever the badge is the sole carrier of information, most
commonly an icon-only trigger with no other visible or accessible text
nearby (see the "Overlaying an icon" example). With label set, the badge
switches to role="status" and aria-label="<label>", and drops
aria-hidden. role="status" is a polite live region, appropriate for a
count that can change without an explicit user action forcing a re-read,
unlike role="alert", which interrupts.
Aria attributes
aria-hidden="true": set wheneverlabelis unset (the default)role="status"+aria-label="<label>": set instead wheneverlabelis provided, kept in sync on every subsequentlabelchange
Reference links