Skip to content
Monogem

Component · Core

Badge

Overview

A static status, category, or count label — "Active," "Beta," "3 new." Not an interactive control: no click handler, no hover state, no keyboard focus.

Anatomy

Container → optional leading icon → label.

  • Container — the pill that carries the variant styling.
  • Leading icon — optional; sits before the label.
  • Label — the short status, category, or count text.

No dismiss button, no trailing content. A badge that needs to be removable is a different component (a future Tag), not a variant of this one.

Live Example

Variants

PrimarySecondaryDestructiveOutline

Leading icon

VerifiedFeatured

Code Example

badge-showcase.tsx
import { Check, Star } from "lucide-react";

import { Badge } from "@/components/ui/badge";

const VARIANTS = ["primary", "secondary", "destructive", "outline"] as const;

/**
* Live Badge example — the four documented variants and the optional leading
* icon slot, rendered with the real component so the page doubles as a visual
* check. Badge is static: nothing here is clickable or focusable.
*/
export function BadgeShowcase() {
return (
<div className="flex flex-col gap-8">
<section className="flex flex-col gap-3">
<h3 className="text-sm font-medium leading-snug text-muted-foreground">
Variants
</h3>
<div className="flex flex-wrap items-center gap-3">
{VARIANTS.map((variant) => (
<Badge key={variant} variant={variant}>
{variant[0].toUpperCase() + variant.slice(1)}
</Badge>
))}
</div>
</section>

<section className="flex flex-col gap-3">
<h3 className="text-sm font-medium leading-snug text-muted-foreground">
Leading icon
</h3>
<div className="flex flex-wrap items-center gap-3">
<Badge leadingIcon={<Check />}>Verified</Badge>
<Badge variant="outline" leadingIcon={<Star />}>
Featured
</Badge>
</div>
</section>
</div>
);
}

Variants

Four variants, reusing Button's non-navigational set. Every value below is an existing semantic token — see Color.

VariantBackgroundTextBorder
Primary--primary--primary-foregroundnone
Secondary--secondary--secondary-foregroundnone
Destructive--destructive--destructive-foregroundnone
Outline--background--foreground--border

Ghost and Link are dropped — both exist for interactive affordances (hover states, link-like behavior) that a static chip doesn't have.

Sizes

One size — badges are small status labels used at a consistent size across a UI, unlike Button's range of contexts.

PropertyTokenValue
Padding-x--scale-1-56px
Padding-y--scale-0-52px
Radius--radius-fullpill — see Radius
Icon size--scale-416px — Icons' "inline with text" recipe
Icon-to-label gap--scale-14px — same recipe Button uses
Texttext-xs font-medium leading-snug12px / 500 / 1.375 — Typography's "Captions, metadata" size

States

StateLook
DefaultVariant colors as above

Badge is static — nothing to hover, focus, activate, or disable.

Usage Guidance

Tokens

Badge uses existing Monogem semantic and primitive tokens to stay consistent with the design system; it introduces no component-specific styling values.

TokenWhere used
--primary / --primary-foregroundPrimary variant
--secondary / --secondary-foregroundSecondary variant
--destructive / --destructive-foregroundDestructive variant
--background / --foregroundOutline variant
--borderOutline variant border
--radius-fullCorner radius
--scale-1-5Padding-x
--scale-0-5Padding-y
--scale-4Icon size
--scale-1Icon-to-label gap

Do / Don’t

Do

  • Use Badge for status, category, or count labels that don't need interaction.
  • Default to Secondary or Outline for neutral labels.
  • Reserve Primary for a badge that needs to stand out, and Destructive for error/failure states.

Don’t

  • Make a Badge clickable or give it its own click handler — if it needs to be dismissible or actionable, that's a different (future) component.
  • Put more than a couple words in a Badge — it's a label, not a sentence.

Accessibility

  • Color alone shouldn't carry the only meaning — pair a status badge with text ("Active," not just a colored dot) so the information survives for colorblind users and screen readers.
  • No focus ring or tabindex — Badge is not in the tab order, since it isn't interactive.
  • If a badge sits next to other text as a count or status (e.g. inside a button or nav item), make sure its text is included in the accessible name, not lost as decoration.