Badge

Inline status indicators, counts, labels, and tags

Overview

The s-badge attribute provides inline status indicators for counts, labels, and tags with automatic dark mode support and auto-contrast text colors.

Import

/* Full framework (includes all components) */
@import "@shift-css/core";

/* Or import just the badge component */
@import "@shift-css/core/components/badge";

Note: When importing individual components, you also need @shift-css/core/reset and @shift-css/core/tokens for the base styles and design tokens.

Color Variants

All color variants

Default Primary Secondary Accent Success Warning Danger
<div class="demo-row">
<span s-badge>Default</span>
<span s-badge="primary">Primary</span>
<span s-badge="secondary">Secondary</span>
<span s-badge="accent">Accent</span>
<span s-badge="success">Success</span>
<span s-badge="warning">Warning</span>
<span s-badge="danger">Danger</span>
</div>

Outline Variants

For a lighter, bordered style:

Outline variants

Outline Primary Success Warning Danger
<div class="demo-row">
<span s-badge="outline">Outline</span>
<span s-badge="outline-primary">Primary</span>
<span s-badge="outline-success">Success</span>
<span s-badge="outline-warning">Warning</span>
<span s-badge="outline-danger">Danger</span>
</div>

Sizes

Use the s-size attribute to change badge size:

Size variants

Small Default Large
<div class="demo-row">
<span s-badge="primary" s-size="sm">Small</span>
<span s-badge="primary">Default</span>
<span s-badge="primary" s-size="lg">Large</span>
</div>

Modifiers

Pill Style

Fully rounded badges for counts and tags:

Pill style badges

Pill Active 99+
<div class="demo-row">
<span s-badge="primary" s-pill>Pill</span>
<span s-badge="success" s-pill>Active</span>
<span s-badge="danger" s-pill>99+</span>
</div>

Dot Indicator

Small status dots without text. Size variants also apply to dots.

Dot indicators

<div class="demo-row">
<span s-badge="success" s-dot role="status" aria-label="Success status"></span>
<span s-badge="warning" s-dot role="status" aria-label="Warning status"></span>
<span s-badge="danger" s-dot role="status" aria-label="Danger status"></span>
</div>

Accessibility: Dot indicators without visible text should include role="status" and aria-label to convey meaning to screen reader users.

Usage in Context

With Buttons

Badges inside buttons

<div class="demo-row">
<button s-btn="secondary">
  Notifications <span s-badge="danger" s-pill>5</span>
</button>
<button s-btn="outline">
  Messages <span s-badge="primary">12</span>
</button>
</div>

Status Indicators

<div>
  <span s-badge="success" s-dot></span> Online
</div>
<div>
  <span s-badge="danger" s-dot></span> Offline
</div>

How It Works

Badges use the Layered Intent pattern with :where() for zero-specificity base styles:

/* Base styles - easily overridable */
:where([s-badge]) {
  --_badge-bg: var(--s-neutral-200);
  --_badge-text: var(--s-text-primary);

  display: inline-flex;
  align-items: center;
  padding: var(--s-space-1) var(--s-space-2);
  border-radius: var(--s-radius-sm);
  font-size: var(--s-text-xs);

  background-color: var(--_badge-bg);
  color: var(--_badge-text);

  /* Auto-contrast for custom backgrounds */
  @supports (color: oklch(from red l c h)) {
    color: oklch(
      from var(--_badge-bg) clamp(0.15, calc((0.55 - l) * 1000 + 0.15), 0.95) 0 0
    );
  }
}

/* Variant styles */
[s-badge="primary"] {
  --_badge-bg: var(--s-primary-700);
}

CSS Custom Properties

PropertyDescription
--_badge-bgBackground color (private)
--_badge-textText color (private, auto-contrast)

All Attributes

AttributePurposeValues
s-badgeBadge variantprimary, secondary, accent, success, warning, danger, outline, outline-primary, outline-success, outline-warning, outline-danger
s-sizeBadge sizesm, lg
s-pillFully roundedBoolean
s-dotDot indicatorBoolean

Customization Examples

Override Primary Color

Change the primary hue to affect all primary badges:

:root {
  --shift-hue-primary: 280; /* Purple */
}

Custom Badge Style

[s-badge="primary"] {
  --_badge-bg: var(--s-accent-500);
  border-radius: var(--s-radius-full);
}
Search