Badge
A small hand-drawn pill for status, counts, and short labels.
Overview
A Badge is a small, non-interactive pill for a short status, count, or category label — “3 unread”,
“Beta”, “Success”. It carries no behaviour of its own; it just labels the thing next to it.
Anatomy
A sketchy rectangle outline drawn behind a short text label, filled with a solid, token-driven
background. Six semantic variants — neutral (default), primary, success, warning, danger,
info — each pair a background with the matching on-colour text and a slightly darker hand-drawn
outline.
Variants & States
Badge is a static, presentational element — it has no interactive states.
| State | Status | Notes |
|---|---|---|
| Hover | Not implemented | Badge is not interactive. |
| Focus | Not implemented | Badge is not focusable. |
| Disabled | Not implemented | — |
| Loading | Not implemented | — |
| Error | Not implemented | Use the danger variant to convey an error state. |
The only variation is the semantic variant colour.
Usage
Do
- Keep the label short — a word or two, or a small count.
- Match the variant to meaning: success for done, danger for errors, warning for caution.
- Add role="status" (React) or a label (WC/RN) when the badge conveys live, changing state such as an unread count.
Don't
- Don't put interactive controls inside a Badge — it isn't focusable or clickable.
- Don't rely on colour alone to convey meaning; include text so the state is readable without colour.
- Don't use a Badge for long body copy — it is a label, not a container.
Accessibility
- React: renders a plain
<span>. It is presentational by default — passrole="status"and anaria-labelwhen the badge reflects a live value (e.g. a notification count) so assistive tech announces changes. - Web Components: when a
labelis supplied,gh-badgesetsinternals.role = 'status'andinternals.ariaLabel; otherwise it stays presentational. - React Native: pass
accessibilityLabelfor icon-only or numeric badges that need a spoken name. - Colour is never the only signal — the variant background always pairs with a WCAG-AA on-colour text token. See the Accessibility Guide.
Content
A short label: a single word, a category, or a small number. For counts over a threshold, cap the
text yourself (e.g. 99+) — Badge does not truncate.
Props API
React@ghds/react
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'neutral' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral' | Semantic colour role. |
...rest | HTMLAttributes<HTMLSpanElement> | — | Native <span> attributes (e.g. role, aria-label) pass through. |
Web Components@ghds/web-components
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'neutral' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral' | Semantic colour role (reflected attribute). |
label | string | — | When set, exposes the badge as an ARIA status region with this label. |
React Native@ghds/react-native
| Prop | Type | Default | Description |
|---|---|---|---|
variant | 'neutral' | 'primary' | 'success' | 'warning' | 'danger' | 'info' | 'neutral' | Semantic colour role. |
children | ReactNode | — | Badge label (rendered inside a themed Text). |
accessibilityLabel | string | — | Spoken name for icon-only or numeric badges. |
testID | string | — | Test handle for queries. |
Code examples
// React — @ghds/react
import { Badge } from '@ghds/react/badge';
<Badge variant="success">Done</Badge>;
<!-- Web Components — @ghds/web-components -->
<script type="module">
import '@ghds/web-components/badge';
</script>
<gh-badge variant="success">Done</gh-badge>
// React Native — @ghds/react-native
import { Badge } from '@ghds/react-native/badge';
<Badge variant="success">Done</Badge>;