Data display
Badge
Badges are small labels for status, category and counts. Six tones in three variants cover everything from a quiet tag to a loud warning; add a dot or an icon so meaning never depends on color, or make them removable for filter chips. Corners follow the theme radius.
On this page
NeutralAccentSuccessWarningDangerInfo
NeutralAccentSuccessWarningDangerInfo
NeutralAccentSuccessWarningDangerInfo
const tones = ["neutral", "accent", "success", "warning", "danger", "info"] as const; {(["soft", "solid", "outline"] as const).map((variant) => ( <div key={variant} className="flex flex-wrap items-center gap-2"> {tones.map((tone) => ( <Badge key={tone} tone={tone} variant={variant}> {tone[0].toUpperCase() + tone.slice(1)} </Badge> ))} </div>))}Usage
components/ui/badge.tsx
import { Badge } from "@/components/ui/badge"; <Badge tone="success" dot>Paid</Badge>Examples
Tones and variants
NeutralAccentSuccessWarningDangerInfo
NeutralAccentSuccessWarningDangerInfo
NeutralAccentSuccessWarningDangerInfo
const tones = ["neutral", "accent", "success", "warning", "danger", "info"] as const; {(["soft", "solid", "outline"] as const).map((variant) => ( <div key={variant} className="flex flex-wrap items-center gap-2"> {tones.map((tone) => ( <Badge key={tone} tone={tone} variant={variant}> {tone[0].toUpperCase() + tone.slice(1)} </Badge> ))} </div>))}Status with dot
Pair the tone with a word and a dot. Soft is the default variant and the right choice for most status columns.
PaidPendingOverdueScheduledDraft
<Badge tone="success" dot>Paid</Badge><Badge tone="warning" dot>Pending</Badge><Badge tone="danger" dot>Overdue</Badge><Badge tone="info" dot>Scheduled</Badge><Badge tone="neutral" dot>Draft</Badge>Sizes and icons
sm (20 px) for tables and dense lists, md (24 px) next to headings. Icons are sized automatically.
NewNewPrivatePrivateBetaBeta
<Badge tone="accent" icon={<Sparkles strokeWidth={1.75} />}>New</Badge><Badge tone="accent" size="md" icon={<Sparkles strokeWidth={1.75} />}>New</Badge><Badge tone="neutral" variant="outline" icon={<Lock strokeWidth={1.75} />}>Private</Badge><Badge tone="neutral" variant="outline" size="md" icon={<Lock strokeWidth={1.75} />}>Private</Badge><Badge tone="warning" variant="solid" icon={<Zap strokeWidth={1.75} />}>Beta</Badge><Badge tone="warning" variant="solid" size="md" icon={<Zap strokeWidth={1.75} />}>Beta</Badge>Removable
Pass onRemove for filter chips and tags. The remove button is labelled "Remove <label>" automatically.
Status: OverdueOwner: Ada MorenoAmount > $1,000Region: EU
const [filters, setFilters] = useState(initialFilters); {filters.map((f) => ( <Badge key={f} size="md" variant="outline" onRemove={() => setFilters(filters.filter((x) => x !== f))}> {f} </Badge>))}In context
Status, priority and tags on an issue card, each in a different weight so the eye finds status first.
LED-482In reviewHigh priority
Reconcile March payouts for Fernhill Studio
billingpayoutsQ1 close
<div className="flex flex-col gap-3 rounded-ui-lg border border-line bg-surface p-5"> <div className="flex flex-wrap items-center gap-2"> <span className="font-mono text-[12px] text-subtle">LED-482</span> <Badge tone="info" dot>In review</Badge> <Badge tone="danger" variant="outline">High priority</Badge> </div> <p className="text-heading">Reconcile March payouts for Fernhill Studio</p> <div className="flex flex-wrap gap-1.5"> <Badge>billing</Badge> <Badge>payouts</Badge> <Badge tone="accent">Q1 close</Badge> </div></div>Props
PropTypeDefaultDescription
tone"neutral" | "accent" | "success" | "warning" | "danger" | "info"default: "neutral"Color role. Success, warning, danger and info are reserved for status.variant"soft" | "solid" | "outline"default: "soft"Soft tint, filled, or outlined.size"sm" | "md"default: "sm"20 px or 24 px tall.dotbooleandefault: falseLeading dot in the current color.iconReactNodedefault: —Leading lucide icon, sized to the badge.onRemove() => voiddefault: —Shows a remove button after the label.removeLabelstringdefault: —Accessible name for the remove button. Defaults to "Remove <label>".childrenrequiredReactNodedefault: —The label. Keep it to one or two words.
Accessibility
- A badge is plain inline text, read in place with its label. Always use a word; never rely on the tone color alone.
- Dots and icons are hidden from assistive tech because the label already says what they mean.
- The remove button is a real button with an accessible name such as "Remove Region: EU", reachable with Tab and activated with Enter or Space.
- After removing a chip, move focus somewhere sensible (the next chip or the filter input) if the list is long.
- Soft and outline tones keep at least 4.5:1 text contrast on the surface in both themes; solid badges use the canvas color on the tone.