Badge

A badge wraps something and pins a marker to its top corner: unread messages on an inbox button, an online dot on an avatar. It takes the thing it marks as children.

Basic badge

4AL12
stack({ direction: 'row', gap: 'xl', align: 'center' },
  badge({ content: 4 }, button({ variant: 'soft', color: 'neutral' }, 'Inbox')),
  badge({ content: 12 }, avatar({ name: 'Ada Lovelace' })),
)

Colors

33333
stack({ direction: 'row', gap: 'xl', align: 'center', wrap: true },
  badge({ content: 3, color: 'primary' }, button({ variant: 'soft', color: 'neutral' }, 'Primary')),
  badge({ content: 3, color: 'neutral' }, button({ variant: 'soft', color: 'neutral' }, 'Neutral')),
  badge({ content: 3, color: 'success' }, button({ variant: 'soft', color: 'neutral' }, 'Success')),
  badge({ content: 3, color: 'warning' }, button({ variant: 'soft', color: 'neutral' }, 'Warning')),
  badge({ content: 3, color: 'danger' }, button({ variant: 'soft', color: 'neutral' }, 'Danger')),
)

Maximum

A count above max renders as n+, so a badge never grows wide enough to unbalance the thing it sits on.

999+250
stack({ direction: 'row', gap: 'xl', align: 'center', wrap: true },
  badge({ content: 9 }, button({ variant: 'soft', color: 'neutral' }, 'Nine')),
  badge({ content: 250 }, button({ variant: 'soft', color: 'neutral' }, 'Capped at 99')),
  badge({ content: 250, max: 999 }, button({ variant: 'soft', color: 'neutral' }, 'max: 999')),
)

Dot

A dot says "something changed" without saying how much. Give it a label — a bare dot means nothing to a screen reader, so without one it is hidden from the accessibility tree entirely.

ALOnlineNeeds attention
stack({ direction: 'row', gap: 'xl', align: 'center' },
  badge({ dot: true, color: 'success', label: 'Online' }, avatar({ name: 'Ada Lovelace' })),
  badge({ dot: true, color: 'warning', label: 'Needs attention' },
    iconButton({
      label: 'Settings',
      variant: 'soft',
      color: 'neutral',
      icon: icon('settings'),
    }),
  ),
)

Labelling the count

A bare number is ambiguous out of context. label becomes the badge’s accessible name, so it reads as "4 unread messages" rather than "4".

4 unread messages
badge({ content: 4, label: '4 unread messages' },
  button({ variant: 'soft', color: 'neutral' }, 'Inbox'),
)

Props

PropTypeDefaultDescription
contentstring | numberWhat the badge shows. Ignored when dot is set.
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''danger'Badge colour.
dotbooleanfalseA small dot instead of a value.
maxnumber99Counts above this render as n+.
labelstringAccessible name for the badge itself.