Badge · Chip
Badge — a small square label that names a state, a category or a count.
Beta · Feedback and status · packages/css/src/components/badge.css · React on Base UI: @pepperui/badge
.pui-badge (square, radius 4; --small --large) · .pui-chip (pill, chip/radius; --small) · tones --blue --red --yellow --green --dark · __icon. Labels are pills, actions are squares.
Sizes across, states down.
What it is for
Section titled “What it is for”- Tone: neutral, blue, red, yellow, green, dark — a tone is fixed; it does not follow the slide accent
- Size: small (the site tag), medium (the slide’s --xs and the deck tool’s app badge), large (the slide badge)
- Label is a text property; Has icon + Icon for a 16px leading glyph; the gap is space/4. THE GLYPH TAKES THE BADGE OWN INK — tint/{tone}/fg, text/on-inverse on dark — never text/default. In CSS that is currentColor and free; in Figma a swapped Icon arrives bound to text/default, so recolour the instance stroke
- No states: a badge is not interactive. If it needs hover or focus it is a Toggle, or a Chip with an action
- Square, radius 4 — the tag shape. The pill is Chip
- NO BORDER, on any tone, in any size. The one ring in the library is not the Badge’s: a KPI tile rings its STATUS DELTA — green up, red down — with a 2px kpi/delta/ring stroke, because that badge sits on the tile’s tint and is the one thing saying good or bad. A badge wearing a card’s own tint is a label and stays flush, and so does a badge anywhere else — a table row, a card, a topbar, a tag on a page
Use when a status word on a KPI tile or a table row, a category tag on a card, a small count.
Do not use when it can be clicked (Toggle, Button); it is a state the user changes (Switch, Select); the label runs past two words — that is a sentence.
Relates to Chip (the pill), KPI tile (its status badge), Table (pills in headers), Card.
Anti-patterns a border on a badge (the delta ring is the tile, not the badge) · a glyph in a different ink from its label · red for anything that is not a problem · yellow text at the main step · a badge styled as a button · more than two tones in one row of tags · ALL CAPS.
Keywords: badge, tag, label, status, category, count
Chip is the removable / selectable form; one item, two exports
Figma — page Badge & Chip: Badge (18 variants) · Chip (12 variants)
shadcn: badge
The markup — copy this
Section titled “The markup — copy this”The class, the parts, and nothing that is not in the package. The same block opens badge.css.
<span class="pui-badge pui-badge--green">Done</span><span class="pui-chip pui-chip--blue pui-chip--small"><span class="pui-icon pui-chip__icon">…</span>Beta</span>The modifiers
Section titled “The modifiers”| What it is | Note | |
|---|---|---|
| Tone | (none) neutral · --blue · --red · --yellow · --green · --dark | fixed; never follows the accent |
| Badge | --small 12 Regular 2 / 4 · (none) medium 14 Regular 4 / 8 · --large 18 Medium 8 / 12 | — |
| Chip | --small 12 Regular 2 / 8 · (none) medium 14 Medium 4 / 12 | — |
| Parts | __icon — a 16 px leading glyph at a space/4 gap. It takes the label’s ink, because it inherits currentColor and nothing here overrides it; in Figma the same rule costs a recolour on every swapped Icon, which is why it is written in both contracts. | — |
Rule — the pills-vs-squares test reads these two radii.
Rule — NO BORDER, on any tone, in any size — a label is flush. The library’s one ring is not the Badge’s: a KPI tile rings its DELTA with kpi/delta/ring because the delta sits on the tile’s tint (kpi-tile.css). css_rules_test.py fails on a border or a ring declared here.
Accessibility — measured, not assumed
Section titled “Accessibility — measured, not assumed”Every number here is measured on the rendered component: contrast (npm run contrast:render), the focus ring (npm run focus) and the keyboard (npm run keyboard). The floors are principle 12’s.
Contrast
Section titled “Contrast”| ink on ground | reads | floor | the matrix calls it | |
|---|---|---|---|---|
#ec6663 on #f9d3d2 |
2.29 | 4.5 | Red badge / chip (exempt) | axe |
#6a66e4 on #d4d3f7 |
3.12 | 4.5 | Blue badge / chip (exempt) | axe |
#1a6b36 on #d4f2de |
5.48 | 4.5 | Success badge (promised) | ok |
#634e0f on #fad567 |
5.62 | 4.5 | Yellow badge / chip (promised) | ok |
#544d4d on #dad9d9 |
5.85 | 4.5 | text.muted on ground.card.sunken (promised) | ok |
#544d4d on #ffffff |
8.24 | 4.5 | text.muted on surface.page (promised) | ok |
#ffffff on #231b1b |
16.88 | 4.5 | text.on-accent on surface.inverse (not-offered) | ok |
7 distinct pair(s) rendered, 2 that axe calls a violation — each one carries its reason in storybook/test/axe-allowlist.json.
The focus ring
Section titled “The focus ring”Nothing in this component takes focus.
Keyboard
Section titled “Keyboard”Nothing in this component claims an ARIA role with a keyboard contract; its controls are ordinary ones and the browser already gives them their keys.
Tab order across the whole library is document order over 344 stops, and Shift+Tab is its exact reverse — no keyboard trap anywhere (WCAG 2.1.2).
Pepper UI v1.5.0
The Mobupps design system. 386 tokens, 88 components, 65 pages in the workshop — every page of this site generated from the repository, so nothing here is typed twice.