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
| 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
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 |
|---|---|---|---|
| color | 'primary' | 'secondary' | 'critical' | 'success' | 'warning' | 'critical' | The color 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
Colors
<eun-badge color="primary">1</eun-badge>
<eun-badge color="secondary">2</eun-badge>
<eun-badge color="critical">3</eun-badge>
<eun-badge color="success">4</eun-badge>
<eun-badge color="warning">5</eun-badge>
Part of the
Sizes
<eun-badge size="s">1</eun-badge>
<eun-badge size="m">1</eun-badge>
<eun-badge size="l">1</eun-badge>
In context
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;
}
Server-side rendering
Before the badge upgrades, its content is plain inline text. Link
badge-ssr.css in the page's <head> to draw the final badge from the
first paint:
<link
rel="stylesheet"
href="node_modules/@eunomia/elements/dist/ssr/badge-ssr.css"
/>
A bundler that resolves package exports can reach it as
@eunomia/elements/badge-ssr.css.
It covers every size and color, the empty dot, and the --badge-*
properties.
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;
}
The custom properties that change colors, borders, shadows and corners, at a glance. The API tab lists every property of the component.
| Variable | Sets |
|---|---|
--badge-background |
Sets the background color of the badge |
--badge-color |
Sets the text color of the badge |
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