Badge
Badge is a compact label component for displaying counts, statuses, and categorical metadata.
The visual structure is driven entirely by CSS — no JavaScript is needed for static badges.
An optional runtime helper handles dynamic count rendering, capping, and aria-label sync automatically.
Badge is suited for notification counts, status indicators, version labels, action links, floating icon overlays, and any interface that needs a small, high-contrast inline label.
Getting Started
Apply .badge to any inline element — typically a <span> — and add a color class.
No fa-component attribute is required for static badges.
<span class="badge is-primary">New</span>
<span class="badge is-success">Active</span>
<span class="badge is-danger">9</span>
After new FrontAlign() runs on the client, the runtime scans for every [fa-component="badge"][data-count]
element and renders count values automatically. A MutationObserver watches the DOM — badges inserted
after page load are processed without additional code.
Structure
The badge markup is intentionally minimal.
| Element | Class / Attribute | Purpose |
|---|---|---|
| Root | badge | Base class. Apply to any inline element. |
| Runtime marker | fa-component="badge" | Required only for dynamic count rendering via data-count. |
| Color modifier | is-primary / is-success / is-warning / is-danger | Sets background color. Text color is auto-calculated via oklch. |
| Shape modifier | is-pill | Rounds the badge into a capsule shape with wider horizontal padding. |
| Outline modifier | is-outline + is-[modifier]-outline | Removes background, applies a 1.5px border and matching text color. Both classes required. |
| Dot modifier | is-dot | Renders a 0.55rem circular status indicator with no text. Exempt from empty-hide. |
| Floating modifier | is-floating | Absolute top-right positioning. Parent must be position: relative. |
| Circle modifier | rounded-circle | Round bubble via aspect-ratio: 1/1. Only effective with is-floating. |
| Count attribute | data-count | Numeric value rendered by the runtime. Displays 99+ for values ≥ 100. |
Color Variants
Use semantic color classes directly on .badge.
Text color is automatically calculated from the background color using oklch contrast formula —
no manual text color class is needed.
| Class | CSS Variable | Description |
|---|---|---|
is-primary | var(--primary) | Primary brand color. |
is-success | var(--success) | Green — positive or active state. |
is-warning | var(--warning) | Yellow — caution state. |
is-danger | var(--danger) | Red — error or critical state. |
Pill Variant
Add is-pill to round the badge into a capsule shape.
The pill variant increases border-radius to 999px and adds slightly wider horizontal padding.
<!-- Basic pill badges -->
<span class="badge is-primary is-pill">New</span>
<span class="badge is-success is-pill">Active</span>
<span class="badge is-danger is-pill">99+</span>
<span class="badge is-warning is-pill">Sale</span>
<!-- Version labels -->
<span class="badge is-success is-pill">v2.4.1</span>
<span class="badge is-warning is-pill">Beta</span>
<span class="badge is-danger is-pill">Deprecated</span>
<!-- Counts in pill -->
<span class="badge is-primary is-pill">3</span>
<span class="badge is-danger is-pill">12</span>
Outline Variant
Use is-outline together with a dedicated outline color class for a transparent badge with a colored border and text.
is-outline sets background: transparent and adds a 1.5px solid currentColor border.
is-{color}-outline sets the color CSS property which drives both the border and the text via currentColor.
Both classes are required — neither works alone.
| Class combination | Border & text color |
|---|---|
is-outline is-primary-outline | Uses var(--primary) |
is-outline is-success-outline | Uses var(--success) |
is-outline is-warning-outline | Uses var(--warning) |
is-outline is-danger-outline | Uses var(--danger) |
<!-- Filled color classes -->
<span class="badge is-outline is-primary-outline">Primary</span>
<span class="badge is-outline is-success-outline">Success</span>
<span class="badge is-outline is-warning-outline">Warning</span>
<span class="badge is-outline is-danger-outline">Danger</span>
<!-- Outline with pill shape -->
<span class="badge is-outline is-primary-outline is-pill">New</span>
<span class="badge is-outline is-success-outline is-pill">Active</span>
<span class="badge is-outline is-danger-outline is-pill">Deprecated</span>
<!-- Outline with count -->
<span class="badge is-outline is-primary-outline">5</span>
<span class="badge is-outline is-danger-outline">99+</span>
Dot Variant
Add is-dot for a minimal 0.55rem circular status indicator with no text.
Dot badges are rendered as display: none exemption — the .badge:empty:not(.is-dot) rule skips them,
so they stay visible even without content.
<!-- Basic dot indicators -->
<span class="badge is-primary is-dot"></span>
<span class="badge is-success is-dot"></span>
<span class="badge is-warning is-dot"></span>
<span class="badge is-danger is-dot"></span>
<!-- Inline status next to text -->
<span>Online <span class="badge is-success is-dot"></span></span>
<span>Offline <span class="badge is-danger is-dot"></span></span>
<span>Away <span class="badge is-warning is-dot"></span></span>
<!-- In a table cell -->
<td><span class="badge is-success is-dot"></span> Active</td>
<td><span class="badge is-danger is-dot"></span> Suspended</td>
<td><span class="badge is-warning is-dot"></span> Pending</td>
Link and Button Badge
Apply .badge directly to an <a> or <button> element for an interactive badge.
The CSS :is(a, button) selector activates hover, active, and focus-visible states automatically —
no additional modifier class is needed.
Hover applies brightness(0.96) and a translateY(-1px) lift.
Active snaps back with brightness(0.92) and translateY(0).
Keyboard focus shows a 3px ring via focus-visible using oklch opacity on currentColor.
<!-- Anchor badges -->
<a href="/updates" class="badge is-primary">3 Updates</a>
<a href="/changelog" class="badge is-success is-pill">v2.4.1</a>
<a href="/deprecations" class="badge is-outline is-danger-outline">Deprecated</a>
<a href="/beta" class="badge is-warning is-pill">Beta</a>
<!-- Button badges -->
<button class="badge is-primary">New</button>
<button class="badge is-danger is-pill">12 Errors</button>
<button class="badge is-outline is-primary-outline">Filter</button>
<!-- Interactive badge in a nav -->
<nav>
<a href="/inbox" class="badge is-primary">Inbox <span class="badge is-danger is-pill">5</span></a>
</nav>
Floating Badge
Add is-floating to anchor the badge absolutely at the top-right corner of its parent.
The parent must have position: relative (or any non-static position) for the anchor to work correctly.
The floating badge is positioned at top: -0.5rem; right: -0.5rem with z-index: 10
and always includes a 2px solid var(--primary) border to visually separate it from the parent.
pointer-events: none prevents the badge from intercepting clicks.
<!-- Floating count on a text button -->
<div class="is-relative">
<button class="button is-primary">Notifications</button>
<span class="badge is-floating is-danger">12</span>
</div>
<!-- Floating pill count -->
<div class="is-relative">
<button class="button is-primary-outline">Messages</button>
<span class="badge is-floating is-pill is-primary">5</span>
</div>
<!-- Floating on a card -->
<div class="is-relative">>
<span class="badge is-floating is-success">New</span>
<div class="card-body">Card content</div>
</div>
<!-- Floating warning on a menu item -->
<div class="is-relative">
<a href="/billing" class="button is-ghost">Billing</a>
<span class="badge is-floating is-warning">!</span>
</div>
Circular Floating Badge
Combine is-floating with rounded-circle for a perfectly round notification bubble.
rounded-circle applies border-radius: 50% and aspect-ratio: 1 / 1 with min-width: 1.45rem,
keeping the shape circular regardless of digit count.
rounded-circle only takes effect when combined with is-floating — it has no visual effect as a standalone modifier.
Empty is-floating badges remain visible — this is intentional for online status dot use cases.
Only non-floating, non-dot empty badges are hidden via display: none.
Count Rendering
After new FrontAlign() is called, the runtime scans all [fa-component="badge"][data-count] elements
and renders count values automatically. A MutationObserver watches the DOM continuously —
any badge inserted after page load is processed without additional code.
<!-- Simple count -->
<span fa-component="badge" class="badge is-primary" data-count="7"></span>
<!-- Capped count — renders 99+ -->
<span fa-component="badge" class="badge is-danger" data-count="142"></span>
<!-- Zero count -->
<span fa-component="badge" class="badge is-warning" data-count="0"></span>
<!-- Floating circle count -->
<span fa-component="badge" class="badge is-floating is-circle is-danger" data-count="5"></span>
<!-- Floating pill count -->
<span fa-component="badge" class="badge is-floating is-pill is-primary" data-count="500"></span>
data-count | Displayed | aria-label | Note |
|---|---|---|---|
0 | 0 | 0 | Zero is rendered normally. |
7 | 7 | 7 | — |
99 | 99 | 99 | — |
100 | 99+ | 100 | Cap threshold. |
342 | 99+ | 342 | True value preserved in aria-label. |
missing / NaN | — (no change) | — | Skipped silently. |
Empty Badge Behavior
Empty badges are automatically hidden unless they are used as dots or managed through the runtime using the data-count attribute.
The CSS rule is:
.badge:empty:not(.is-dot):not([data-count]) {
display: none !important;
}
| Condition | Behavior | Why |
|---|---|---|
| Empty badge | display: none | Prevents empty labels from appearing in the interface. |
Empty + | Visible | Dot badges intentionally render without text content. |
Empty + | Visible | The runtime injects the count value from the attribute. |
Empty + | Visible | Both patterns are explicitly excluded from the hiding rule. |
Runtime Behavior
Badge count rendering is initialized through the FrontAlign runtime.
When new FrontAlign() runs, the runtime queries all [fa-component="badge"][data-count] elements,
renders their values, and activates a MutationObserver for badges added dynamically.
| Behavior | Description |
|---|---|
| Initial scan | Queries all [fa-component="badge"][data-count] elements on initialization. |
| MutationObserver | Watches for dynamically inserted badges and processes them without additional code. |
| Count cap | Values of 100 or above are displayed as 99+. |
| aria-label sync | Sets aria-label to the raw numeric value — screen readers always hear the true count. |
| Silent skip | Missing or NaN values are skipped silently without errors. |
| Empty hiding | Empty badges without is-dot are hidden via display: none !important. |
React Usage
For React and Next.js projects, use the React integration layer instead of initializing FrontAlign manually.
'use client';
import { useBadge } from 'frontalign/react';
export function BadgeExample({ count = 128 }) {
const badgeRef = useBadge(count);
return (
<span className="badge is-danger" data-count={count} ref={badgeRef}></span>
);
}
Continue with the React Integration guide for provider setup, hooks, runtime helpers, and Next.js usage.
Accessibility
Static text badges such as New or Active need no extra attributes — text content is read directly.
Count badges using data-count automatically receive aria-label set to the raw numeric value,
so screen readers announce the true count even when the visual display shows 99+.
Floating badges on icon buttons must combine with aria-label on the parent button
so full context is announced when the button receives focus.
<!-- Correct — context provided via aria-label on the button -->
<button class="button is-icon" aria-label="Cart, 5 items">
<!-- icon -->
<span fa-component="badge" class="badge is-floating is-circle is-danger" data-count="5"></span>
</button>
<!-- Correct — aria-label includes true count, not 99+ -->
<button class="button is-icon" aria-label="Notifications, 128 unread">
<!-- icon -->
<span fa-component="badge" class="badge is-floating is-circle is-primary" data-count="128"></span>
</button>
<!-- Static badge — no extra attributes needed -->
<span class="badge is-success">Active</span>
Notes
- Badge requires no JavaScript for static use — CSS-only for all non-count badges.
- Add
fa-component="badge"only when usingdata-countfor dynamic count rendering. - Initialize the runtime once with
new FrontAlign(). is-outlineandis-{color}-outlinemust be used together — neither works alone.is-circleonly takes effect when combined withis-floating.- Empty badges are hidden automatically unless
is-dotoris-floatingis present. aria-labelalways reflects the true numeric value — not the displayed99+.- The floating badge parent must have
position: relativeor any non-static position. - The
2pxborder onis-floatingalways usesvar(--primary)regardless of the badge color class. - Use the React hook only in React or Next.js applications.
- Use the React Integration guide when building with React.