Tag.
A tag is a compact, non-interactive label that classifies the thing next to it — a status, a type, an owner, a count. It earns its place when the same information would cost a whole sentence in the row. Five colour variants carry meaning, so the label has to say the same thing the colour does: a reader who cannot separate the greens from the reds should lose nothing. Tags add clarity, not decoration; a row wearing four of them is telling you the table needs a column instead.
Variants
primaryThe default. A category or attribute that carries no status meaning of its own.#F4FFE0 / #447952 / #152F1AneutralPlain metadata — a type, an owner, a document number. Reach for this before Primary when nothing is being signalled.#F2F2F2 / #000000 / #0D0D0DsuccessCompleted, approved, active, paid.#C2F8DD / #119754 / #052D19warningNeeds attention but nothing is broken yet: expiring, pending, on hold.#FDEDAA / #BF9C0C / #2F2603dangerFailed, rejected, overdue, blocked.#FFE7E2 / #E1563B / #441A12All five label colours clear 4.5:1 against their own fill. What the palette cannot do is carry meaning on its own — under WCAG 1.4.1 the label text has to say what the colour says, so "Overdue" in Danger, never a red tag reading "Invoice 4417". Keep one variant meaning one thing across a product; a Warning tag that means "draft" in one screen and "expiring" in the next is worse than no colour at all.
Size
Match the tag to the type around it, not to the space available: Small inside dense table rows, Medium in cards and detail panels, Large only where the surrounding type is 16 px too. A Large tag next to 14 px body text reads as a button and invites a click that does nothing.
Shape & options
Removable turns a label into a control, and that changes what the component owes the user: a real button with its own accessible name, a keyboard route to it, a visible focus ring and a 24 × 24 px target. The Figma set provides none of those today — no hover, no focus, no disabled state. An interactive tag needs states; the demo above adds them rather than reproducing the gap.
Anatomy
Two things in the Figma component don't hold up under this anatomy: the icon-to-label gap silently drops from 12 px to 8 px the moment Rounded or Removable turns on, with no stated reason, and the declared 12 px vertical padding at Medium is mathematically impossible inside a fixed 38 px height holding a 19 px line box — the value sits in the panel but nothing reads it. Reproduced as specified above; flagged here rather than quietly fixed.
Do & don't
Accessibility
Props (React) — draft
// DRAFT. Derived from the Figma properties plus the MUI Chip accessibility notes.
// Anything marked (proposed) does not exist in the Figma component yet.
type TagProps = {
variant?: "primary" | "success" | "neutral" | "warning" | "danger"; // Figma: Variant
size?: "small" | "medium" | "large"; // Figma: Size
rounded?: boolean; // Figma: Rounded
removable?: boolean; // Figma: Removable
showIcon?: boolean; // Figma: Show icon
label: string; // Figma: Label
icon?: ReactNode; // (proposed) instance-swap slot in Figma
onRemove?: () => void; // (proposed)
removeLabel?: string; // (proposed) e.g. "Remove Frontend"
};
// (proposed) States the set does not carry at all: hover, focus-visible, disabled.
// They only matter once removable is true — and the answer is yes, an interactive
// tag needs states; a smaller prop surface is not a reason to skip them.
// Not a prop: showIcon and removable cannot both be on. The Figma set drops
// the leading icon when Removable = Yes, so the API should make that explicit.