Done
Components
Badge.
A badge is a small label that marks the state of the thing beside it — a status on a row, a count on a tab, a category on a card. It is an indicator: contextual, conditional and passive. It reports a state; it never asks anyone to act on it.
Colour
Primaryprimary
Draftneutral
Openinformation
Acceptedsuccess
Expiringwarning
Rejecteddanger
ColourWhat it meansContrast
primaryThe product’s own accent. A “this is ours” marker, not a status.
5.12:1neutralNo state at all — a category, a tag, or a count with nothing attached.
21:1informationIn progress or waiting on someone: Open, In review, Sent.
3.75:1successA settled positive outcome: Accepted, Paid, Approved.
3.76:1warningNeeds attention before a deadline: Expiring, Overdue soon.
2.63:1dangerA failed or blocked state: Rejected, Overdue, Cancelled.
3.75:1Colour repeats what the label already says — it never carries the meaning on its own. Four of the six colours currently fall below the 4.5:1 floor for 12px white text: information, success, danger and warning. Suggested replacements are in the review notes.
Shape
AcceptedDefault — 2px radius. Status beside a row or a title.
AcceptedPill — full radius. The same label, softer register.
6Number — 22 × 22. A count only; show 99+ above ninety-nine.
Anatomy
Accepted
Height22px, fixed. It never scales — the badge lives inside dense rows and toolbars.
Padding2px top, 4px bottom, 7px each side. The number shape is a fixed 22 × 22 square.
LabelManrope Regular 12px, sentence case, one or two words. Never a sentence.
Radius2px for default; full radius for pill and number.
Border1px white, so the badge stays separate from a coloured row behind it.
ColourThe fill carries the colour and the label is always white — which is where the contrast problem sits.
Do & don't
OpenAcceptedRejected
Do — keep labels to one or two words. The badge is a marker, not a sentence.
This order is open and awaiting approval
Don't — flood the badge with text. If it needs a sentence, it belongs in the row, not in a badge.
AcceptedExpiringRejected
Do — match colour to meaning: green settled, amber needs attention, red failed or blocked.
AbsentApproved
Don't — contradict the label with the colour. Green “Absent” and red “Approved” make people read twice and trust neither.
Accessibility
ContrastWhite 12px text needs 4.5:1. Four of the six colours fall short today — see the Colour section.
Never colour aloneThe label says the state. Colour repeats it. A colour-blind user reading only the label must lose nothing.
Not a controlA badge is passive. If it can be clicked or removed, it is a Tag or a Button — use those instead.
Number badgesA bare count needs context in text: aria-label="6 unread messages", not just "6".
Live updatesA badge that changes while the page is open belongs in an aria-live="polite" region.
PlacementAnnounce the badge with the thing it marks, so the row reads as one statement, not two.
Props (React)
type BadgeProps = {
color?: "primary" | "neutral" | "information" | "success" | "warning" | "danger";
shape?: "default" | "pill" | "number"; // default 2px radius, pill full, number 22x22
children: ReactNode; // one or two words, or a count
max?: number; // number shape: renders 99+ above this
label?: string; // aria-label when the visible text is a bare count
};