Skip to content

Badge

A short status or count, never interactive. Text is 7:1 on its tint; counts carry a hidden label.

  • Free core, MIT
  • Version 2.0.0-rc.1
  • APG pattern: Status label (static)

Examples

These are the real React components. Use them with a mouse, touch or keyboard, and switch the theme and density for every example at once.

Default

Active
Show the code

Default, code

<Badge>Active</Badge>

Tones

DraftNewPaidDue soonOverdueBetaSave 50%
Show the code

Tones, code

<div className="flex flex-wrap gap-gap-sm">
  <Badge>Draft</Badge><Badge tone="brand">New</Badge><Badge tone="success">Paid</Badge>
  <Badge tone="warning">Due soon</Badge><Badge tone="danger">Overdue</Badge><Badge tone="info">Beta</Badge><Badge tone="strong">Save 50%</Badge>
</div>

Strong on brand

AnnualSave 50%
Show the code

Strong on brand, code

<div className="flex items-center gap-gap-sm rounded-surface border-2 border-line-brand bg-surface-brand-subtle p-[var(--sd-spacing-inset-md)] text-body-md text-fg">
  <span className="font-semibold">Annual</span><Badge tone="strong">Save 50%</Badge>
</div>

Count

3 unread messages
Show the code

Count, code

<Badge tone="brand" countLabel="unread messages">3</Badge>

Kinds

Status3 unread messages99+ unread messagesneutral status
Status3 unread messages99+ unread messagesbrand status
Status3 unread messages99+ unread messagessuccess status
Status3 unread messages99+ unread messageswarning status
Status3 unread messages99+ unread messagesdanger status
Status3 unread messages99+ unread messagesinfo status
Status3 unread messages99+ unread messagesstrong status
Show the code

Kinds, code

<div className="flex flex-col gap-gap-md">
  {(['neutral', 'brand', 'success', 'warning', 'danger', 'info', 'strong'] as const).map((tone) => (
    <div key={tone} className="flex items-center gap-gap-sm">
      <Badge tone={tone}>Status</Badge>
      <Badge tone={tone} kind="count" countLabel="unread messages">{3}</Badge>
      <Badge tone={tone} kind="count" countLabel="unread messages">{120}</Badge>
      <Badge tone={tone} kind="dot">{`${tone} status`}</Badge>
    </div>
  ))}
</div>

Anchored

3 unread notifications99+ unread messagesNew activity
Show the code

Anchored, code

<div className="flex items-center gap-gap-lg p-[var(--sd-spacing-inset-md)]">
  <span className="relative inline-flex">
    <IconButton label="Notifications" icon={Bell} variant="ghost" />
    <Badge tone="danger" kind="count" anchored countLabel="unread notifications">{3}</Badge>
  </span>
  <span className="relative inline-flex">
    <IconButton label="Messages" icon={Bell} variant="ghost" />
    <Badge tone="danger" kind="count" anchored countLabel="unread messages">{240}</Badge>
  </span>
  <span className="relative inline-flex">
    <Avatar name="Priya Shah" />
    <Badge tone="success" kind="dot" anchored>New activity</Badge>
  </span>
</div>

Tokens

The 17 design tokens Badge reads, with the value each has in the theme and density chosen above. Change a token and the component follows, in Figma and in code.

Show all 17 tokens
Tokens Badge reads
TokenValue nowCSS variable
badge.brand.bg…--sd-badge-brand-bg
badge.brand.fg…--sd-badge-brand-fg
badge.danger.bg…--sd-badge-danger-bg
badge.danger.fg…--sd-badge-danger-fg
badge.info.bg…--sd-badge-info-bg
badge.info.fg…--sd-badge-info-fg
badge.neutral.bg…--sd-badge-neutral-bg
badge.neutral.fg…--sd-badge-neutral-fg
badge.radius…--sd-badge-radius
badge.strong.bg…--sd-badge-strong-bg
badge.strong.fg…--sd-badge-strong-fg
badge.success.bg…--sd-badge-success-bg
badge.success.fg…--sd-badge-success-fg
badge.warning.bg…--sd-badge-warning-bg
badge.warning.fg…--sd-badge-warning-fg
color.bg.canvas…--sd-color-bg-canvas
color.border.subtle…--sd-color-border-subtle

Install

The npm package isn’t published yet. When it is, this is the install line; until then, this page runs the same build of the package (from commit fa3abf8).

Install

npm i @superduperui/react @superduperui/tokens

Import

import { Badge } from "@superduperui/react"

Props

Badge also takes the props of the React Aria component it’s built on.

Badge props
PropTypeWhat it does
tone'neutral' | 'brand' | 'success' | 'warning' | 'danger' | 'info' | 'strong'Default 'neutral'Colour family. The text always says what the colour means (1.4.1). Default neutral. strong is emphasis, not a status: a solid primary fill for one callout on a selected or brand surface.
kind'label' | 'count' | 'dot'Default 'label'label (default): text on a tint. count: a number, at least a 24px circle, capped at `max` ("99+"); give it a countLabel. dot: an 8px mark in the tone colour; its children are read by screen readers only.
childrenrequiredReactNodeThe visible text; the number for `kind="count"`; the hidden name for `kind="dot"`.
countLabelstringFor count badges ("3"): what is being counted, read by screen readers after the number ("3 unread messages"). Required whenever the visible text is a bare number.
maxnumberDefault 99Count only: a larger number shows as "max+" ("99+"). Default 99.
anchoredbooleanDefault falsePositions the badge over the top-end corner of its `relative` parent (an icon button, an avatar) with a 2px canvas ring; it grows towards the end.

When to use it

Use it for

  • Statuses in tables and cards (Paid, Overdue)
  • Counts next to a nav item or tab (with countLabel)

Use something else for

  • Anything people press: use Button or Tag
  • Long text: use Alert

Accessibility

Screen readers announce "Paid"; a count reads "3 unread messages" ("99+ unread messages" over max); a dot reads its name ("Online").

WCAG 2.2 AAA criteria the component meets in Comfortable density: 1.4.6, 1.4.9, 2.3.2.

Not interactive: density changes spacing only.

What your product still has to do

  • Give bare numbers a countLabel
  • Give every dot a name (its children)
  • Put an anchored badge in a relative parent and name the count in the control's label too when it matters

This release’s test results

Test results for Badge in this release
axe-core (WCAG A, AA, AAA and best practice)140 runs across every story, theme, density and width: 0 violations
Rendered text contrast450 text runs: 0 below 7:1, lowest 7.79:1
Target size4 targets: 0 under 44px in Comfortable
Focus appearance (2.4.13)0 focus stops in 0 browser engines: all pass

Manual passes with VoiceOver and NVDA haven’t been done yet.

In Figma

The Figma component is Badge, with Tone (Neutral, Brand, Success, Warning, Danger, Info, Strong); Kind (Label, Count, Dot). The Figma library isn’t published yet.

Figma to code: Tone → tone (Neutral = "neutral", Brand = "brand", Success = "success", Warning = "warning", Danger = "danger", Info = "info", Strong = "strong"); Label → children; Kind → kind (Label = "label", Count = "count", Dot = "dot"); Count → children when Kind=Count; Count label → countLabel when Kind=Count; Anchored → anchored.