Default — neutral
Overview
Badges sit one tier below status pills. Where a StatusPillclaims a card or row's headline status, a Badge tags a piece of metadata: a count, a tag, a small qualifier next to a heading. The shape — full-rounded, tinted-on-tinted — is the same; the role is quieter.
Pick the right component
Badge for inline metadata (tags, counts, small qualifiers). StatusPill for the headline status of a row or card. RiskBadge for the three-level risk signal (low/medium/high) on the prototypes catalog. They share visual DNA but communicate different things.
Tones
Seven variants. Six are status-driven (neutral, danger, warning, success, info, discovery); the seventh is outline — a bordered transparent variant for surfaces where another tinted pill would fight the page.
- Step
neutralThe default. Tags, generic metadata, dense list-row labels.
- Step
dangerDestructive states — killed prototypes, declined transactions.
- Step
warningPending decisions, building states, near-threshold metrics.
- Step
successDemo-ready, paid, healthy services.
- Step
infoNeutral status, links, advisories.
- Step
discoveryBeta, what's new, content the user hasn't seen before.
- Step
outlineWhen the surface beneath shouldn't bear extra chroma — bordered, transparent fill.
Sizes
Three sizes. md is the default; sm for dense rows or count badges; lgwhen the badge is a card's headline qualifier.
Small
size="sm"Dense rows, table cells, beside small text.
Medium
size="md"The default. Catalog rows, prototype detail meta, status callouts.
Large
size="lg"Hero surfaces, marketing tiles, anywhere a single badge headlines a card.
Compositions
Three composition patterns cover almost every Badge moment. Pick the one the data calls for; don't mix two — a leading icon and a dot together is visual noise.
Leading icon
Pass leadingIconfor a glyph before the label — trend arrows, info pictograms, anything the variant's tone doesn't already carry.
Dot
Pass dotfor a small filled circle in the variant's foreground tone — used for status badges where the dot reinforces the colour for users with reduced colour perception.
Count badges
Use size="sm" with a short numeric label — notification counts, unread totals. Cap at 99+ so the pill stays one line.
Outline (tag pattern)
Bordered, transparent fill. Use on already-tinted surfaces (an accent section background) where another tinted pill would fight for attention.
Anatomy
Three named parts. Only the label is required — leading icon and dot are opt-in.
Container
Full-rounded pill (radius 9999). Carries the variant background and, on outline, a 1 px border.
Leading slot
Optional. Either an icon (
leadingIconprop) or a coloured dot (dotprop). Inherits the variant's text colour viacurrentColor.Label
Inter Tight Semibold. Sentence case. Single line — let it truncate at the parent if the layout is constrained.
Behavior
- Static — not a button. Badges don't take click handlers. They're labels. If the user needs to act on it, wrap in a Link or use a different component.
- One badge tells one thing. A pile of three badges next to a row title means three things compete for the eye — usually the row needs a structural redesign, not more badges.
- Icon or dot, not both. The leading slot fits one piece of furniture. Two reads as busy.
- Counts cap at 99+. Three-digit counts force the pill into a second line or break the layout. Display
99+when the underlying number exceeds 99 and link to the underlying list.
Accessibility
- Don't carry meaning in colour alone. Every badge is a small text label first. The variant tone is reinforcement, not the only signal — colour-blind users read the label.
- Leading icon is decorative. The component sets
aria-hidden="true"on the icon slot — assistive tech reads the label, not the glyph. If the icon carries unique meaning, encode it in the label too. - Counts use the underlying number in the accessible name when wrapping the badge in a control: e.g.
<Link aria-label="Inbox · 12 unread">so screen readers don't announce just “12”. - Contrast is calibrated.Every variant's text/background combination meets WCAG AA at the default badge size. Smaller sizes drop below the recommended density for body text but keep AA for “large text” under WCAG 2.2 — fine for short labels and counts, less suitable for full sentences.
Code
tsx
import { Badge } from "@flatpay-dk/ui";
// Default — neutral, md
<Badge>Step</Badge>
// Status-driven tones
<Badge variant="success">Demo-ready</Badge>
<Badge variant="warning">Building</Badge>
<Badge variant="danger">Killed</Badge>
<Badge variant="info">Note</Badge>
<Badge variant="discovery">Beta</Badge>
// Outline — for tinted surfaces
<Badge variant="outline">internal</Badge>
// Sizes
<Badge size="sm">1</Badge>
<Badge size="lg">Demo-ready</Badge>
// With a leading icon
<Badge variant="neutral" leadingIcon={<TrendUpIcon />}>10%</Badge>
// With a status dot
<Badge variant="success" dot>Live</Badge>
// Count badge
<Badge size="sm" variant="discovery">12</Badge>
<Badge size="sm" variant="danger">99+</Badge>Best practices
Do
Use one badge per data point. The dot reinforces the colour for accessibility.
Don't
Don't pile badges. Three or more next to a row title means the row needs structural design, not more pills.
Do
Cap counts at 99+. Two characters max keeps the pill on one line.
Don't
Don't combine an icon and a dot. Pick one piece of leading furniture.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "neutral" | "danger" | "warning" | "success" | "info" | "discovery" | "outline" | "neutral" | Surface tone. Six status-driven options plus an outline for tinted contexts. |
| size | "sm" | "md" | "lg" | "md" | Vertical density. sm for dense rows + count badges; lg for hero qualifiers. |
| leadingIcon | ReactNode | — | Optional icon rendered before the label. aria-hidden by default — encode meaning in the label. |
| dot | boolean | false | Render a 6 px filled dot in the variant's text colour before the label. Mutually exclusive with leadingIcon. |
| ...rest | HTMLAttributes<HTMLSpanElement> | — | Standard span attributes (className, id, data-*, title) pass through. |