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.

Primary Active Pending Error

Structure

The badge markup is intentionally minimal.

ElementClass / AttributePurpose
RootbadgeBase class. Apply to any inline element.
Runtime markerfa-component="badge"Required only for dynamic count rendering via data-count.
Color modifieris-primary / is-success / is-warning / is-dangerSets background color. Text color is auto-calculated via oklch.
Shape modifieris-pillRounds the badge into a capsule shape with wider horizontal padding.
Outline modifieris-outline + is-[modifier]-outlineRemoves background, applies a 1.5px border and matching text color. Both classes required.
Dot modifieris-dotRenders a 0.55rem circular status indicator with no text. Exempt from empty-hide.
Floating modifieris-floatingAbsolute top-right positioning. Parent must be position: relative.
Circle modifierrounded-circleRound bubble via aspect-ratio: 1/1. Only effective with is-floating.
Count attributedata-countNumeric 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.

ClassCSS VariableDescription
is-primaryvar(--primary)Primary brand color.
is-successvar(--success)Green — positive or active state.
is-warningvar(--warning)Yellow — caution state.
is-dangervar(--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>
New v2.4.1 Beta Deprecated

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 combinationBorder & text color
is-outline is-primary-outlineUses var(--primary)
is-outline is-success-outlineUses var(--success)
is-outline is-warning-outlineUses var(--warning)
is-outline is-danger-outlineUses 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>
Primary Success Warning Danger

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>
3 Updates v2.4.1 Deprecated Beta

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-countDisplayedaria-labelNote
000Zero is rendered normally.
777
999999
10099+100Cap threshold.
34299+342True 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;
}
ConditionBehaviorWhy
Empty badgedisplay: nonePrevents empty labels from appearing in the interface.

Empty + is-dot

Visible

Dot badges intentionally render without text content.

Empty + data-count

Visible

The runtime injects the count value from the attribute.

Empty + is-dot + data-count

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.

BehaviorDescription
Initial scanQueries all [fa-component="badge"][data-count] elements on initialization.
MutationObserverWatches for dynamically inserted badges and processes them without additional code.
Count capValues of 100 or above are displayed as 99+.
aria-label syncSets aria-label to the raw numeric value — screen readers always hear the true count.
Silent skipMissing or NaN values are skipped silently without errors.
Empty hidingEmpty 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 using data-count for dynamic count rendering.
  • Initialize the runtime once with new FrontAlign().
  • is-outline and is-{color}-outline must be used together — neither works alone.
  • is-circle only takes effect when combined with is-floating.
  • Empty badges are hidden automatically unless is-dot or is-floating is present.
  • aria-label always reflects the true numeric value — not the displayed 99+.
  • The floating badge parent must have position: relative or any non-static position.
  • The 2px border on is-floating always uses var(--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.

FrontAlign