Eunomia v1.0.0-beta.2
AllAngularReactNext.jsJavaScriptVue
EunomiaSalmonForestVioletOceanGoldFireCustom…
🇬🇧 English🇫🇷 Français

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.

Overview API Examples Accessibility
5 9

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.

Alternatives

You want .. Prefers A status or category label that reads on its own eun-tag

Guidance

  • 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 label whenever 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

NameTypeDefaultDescription
variant'primary' | 'secondary' | 'critical' | 'success' | 'warning''critical'The color variant to apply
size's' | 'm' | 'l''m'The size of the badge
labelstringAn 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 Types.

Slots

NameDescription
(default)The badge's content, typically a short number. Left empty, the badge renders as a plain dot

CSS custom properties

NameDescription
--badge-backgroundSets the background color of the badge
--badge-colorSets the text color of the badge
--badge-sizeSets the diameter of the badge, driving both the empty circle and the content pill
--badge-padding-inlineSets the horizontal padding around the badge's content. Ignored while empty
--badge-font-sizeSets the font size of the badge's content

CSS shadow parts

NameDescription
badgeThe 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.

5 99
<eun-badge></eun-badge>
<eun-badge>5</eun-badge>
<eun-badge>99</eun-badge>

Variants

1 2 3 4 5
<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 Variants, alongside every other component that reuses it.

Sizes

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

Inbox 5 Sent 5 unread messages. No sent messages.
<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.

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

12
<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 whenever label is unset (the default)
  • role="status" + aria-label="<label>": set instead whenever label is provided, kept in sync on every subsequent label change

Reference links

WAI-ARIA: status role
WAI-ARIA Authoring Practices: Read Me First (live regions)