JM Family Design System

Badge

Badges label compact metadata such as status, category, priority, or count. They should help scanning without becoming actions or decorative noise.

Guidance for adoption

Start here before choosing examples or props. These notes explain where Badge helps, where another pattern is better, and what should stay true in product work.

When to use

  • Status labels
  • Counts
  • Priority cues
  • Compact metadata

When not to use

  • Primary actions
  • Filters that change results
  • Long messages
  • Decorative labels

Accessibility expectations

  • Make badge text meaningful without color.
  • Do not make non-interactive badges focusable.
  • Expose counts with nearby context so the number has meaning.

Content guidance

  • Keep labels short and meaningful, such as "Draft", "Active", "Blocked", or "Expiring soon".
  • Use words that make sense without color so the state is still clear in assistive technology and grayscale views.
  • Pair counts with nearby context or an aria-label so a number is not announced without meaning.
  • Avoid stacking several badges on every row; use them where compact metadata genuinely helps scanning.

Implementation notes

  • Badge is non-interactive. Use a different component for filters, chips, links, or removable tags.
  • Choose the variant semantically: success for complete, warning for attention, and error for blocked or failed.
  • Mark decorative badge icons with aria-hidden and keep the text label as the source of meaning.
  • There are no size or removable variants, so dense and dismissable patterns need another component.

Import

import { Badge } from '@/components/ui/Badge';

Renders a non-interactive labeled span. Color reinforces meaning; the label carries it.

Variants

Badge color should support the label, not carry the meaning by itself. Choose the variant by the state being labeled.

Neutral
Draft

Use for low-emphasis state or category metadata.

Info
In review

Use for neutral progress or informational states.

Success
Active

Use for approved, complete, or healthy states.

Warning
Expiring soon

Use for attention states that are not blocking yet.

Error
Blocked

Use for blocked, failed, or high-risk states.

Usage patterns

Use badges where compact labels help users scan dense interfaces. Keep them short and close to the content they describe.

Status
Active

Label the current state with a word users recognize.

Count
Approvals12

Pair counts with nearby context so the number has meaning.

Priority
High priority

Use priority labels sparingly and only when they affect work order.

Category
Procurement

Use neutral badges for compact classification metadata.

Anatomy

A badge is a compact label. Its text, color, and placement should make the state readable without adding another interaction target.

Vendor statusActive
  1. Short label
  2. Optional icon
  3. Semantic color
  4. Nearby context
  5. Non-interactive state
  6. Accessible meaning

Props

The component accepts every native HTMLSpanElement attribute on top of the design system props below.

PropTypeDefaultDescription
variant'neutral' | 'info' | 'success' | 'warning' | 'error''neutral'Semantic color. Match the meaning of the state being labeled.
iconReactNodeOptional leading icon. Pair with aria-hidden; never rely on the icon alone for meaning.

Usage rules

Use badges for metadata

Badges label status, priority, category, or count information in a compact form.

Do not make badges act like controls

If the user can click it to change data, navigate, or filter results, use the correct interactive component.

Use clear words

Write labels that make sense without color, such as Active, Draft, Blocked, or Expiring soon.

Do not over-label everything

Badges lose value when every row has several decorative pills competing for attention.

Known limitations

What Badge does not do yet, and what consumers need to compose themselves.

  • No size variants. Padding and font size are fixed by a single style class, so dense table rows and large hero contexts cannot resize the badge through props.
  • No removable or dismissable mode. There is no built-in close affordance or onRemove hook, so filter-chip patterns must be composed by wrapping the badge.

Related anti-patterns

Catalog entries to watch for when using Badge. Read these before shipping; they describe the failure modes the system has already documented.

Agent guidance

Agents should generate badges only for compact labels. If the pill changes something, navigates somewhere, or filters content, it is not a badge.

Generation rules

  • Use badges to label state or metadata, not to trigger actions.
  • Keep badge text short and scannable.
  • Do not rely on color alone; use clear words such as Active, Draft, or Blocked.
  • Use Button, Link, Checkbox, or Select when the user can interact with the item.
  • Always include readable status text; a status pill whose meaning depends on color matches the color-only-state anti-pattern.
  • For counts, prefix the number with context inside the surrounding label or pass aria-label such as "3 unread messages" on the badge. A naked "3" announces as a number with no meaning.
  • Do not wrap the badge in a button or anchor; non-interactive badges should not be focusable. Filter chips and dismissable tags require a different component.
  • Pair the variant with semantic word choice: success for completed states, warning for needs-attention, error for blocking. Do not pick a variant for visual contrast alone.

Baseline example

import { Badge } from '@/components/ui/Badge';
import { CheckCircle2 } from 'lucide-react';

<Badge variant="success" icon={<CheckCircle2 size={14} aria-hidden="true" />}>
  Active
</Badge>

<Badge variant="error" aria-label="3 blocked requests">
  3 blocked
</Badge>
If generated UI needs clickable pills, agents should use a segmented control, checkbox group, menu, or filter pattern instead of Badge.

Decision standard

Badges label state. Controls change state. Decide by behavior first, then choose the visual treatment.