Skip to content

Badge

A small marker overlaid on the corner of another element. Use it to report an unread count or a current status without covering what it sits on.

tsx
import { Badge, Button } from 'neba';

<Badge content={4} label="4 unread notifications">
  <Button startIcon={<BellIcon />} />
</Badge>

<Badge dot color="success" overlap="circle">
  <Avatar />
</Badge>;

Props

PropTypeDefaultDescription
variantshared'solid' | 'outline' | 'text''solid'Weight of the surface: filled, hairline, or none
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'The size of the mark, on a ladder of its own: a badge hangs off a corner, so it lines up with nothing
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'Semantic colour role. Arbitrary colour values are not accepted
densityshared'default' | 'compact''default'The room around the digits, and nothing else
elevationshared0 | 1 | 2 | 30Drop shadow depth. 0 means no shadow at all
contentReactNodeWhat the badge says: usually a count. Omit it and the badge draws a dot
maxnumber99Caps a numeric content and adds a plus. Text is never truncated
dotbooleanfalseDraws a dot even with content, keeping the count for screen readers only
showZerobooleanfalseShows a count of zero. Off by default: zero unread messages is not news
invisiblebooleanfalseHides the marker without giving up its box, so nothing moves when it returns
placementshared'top-start' | 'top-end' | 'bottom-start' | 'bottom-end''top-end'Which corner it sits on. start/end, so the corner flips with the writing direction
overlap'square' | 'circle''square'The shape underneath, which decides how far the mark tucks in: a circle's corner is further out than a square's
labelstringWhat a screen reader hears instead of the raw count: "3" beside a bell means nothing
childrenReactNodeWhat the badge is pinned to. Without it, the badge is a standalone marker that lays out inline

Given children, the wrapping <span> becomes the positioning context and the marker pins to its corner. Without children the marker lays out inline on its own, which is what a status marker in a table cell is.

The shared axes (variant size color density elevation) are defined in prop conventions.

Examples

variant and color

solid is a filled marker, outline a border over a faint panel, text a tinted mark with no edge. color picks one of the six role colours.

content and max

content is what the marker says. A number past max (default 99) renders as 99+; a string is never truncated.

A content of 0 draws nothing by default: turn it on with showZero. When there is nothing to count but something to report, use dot; invisible hides the marker while keeping the layout intact.

placement and overlap

placement is the corner the marker pins to: one of four values built from top/bottom and start/end. Because it uses start/end, the corner flips automatically in RTL.

overlap is the shape of the element underneath. circle tucks the marker further in, by the amount a circle's corner sits inside its bounding box, so the marker does not float off an avatar.

size

Badge has its own size steps rather than a step off the control heights. md is 18px, the smallest at which a two-digit number stays legible.

Accessibility

  • content={3} on its own is just "3" to a screen reader. A sentence in label becomes the marker's accessible name instead.
tsx
<Badge content={3} label="3 unread notifications">
  <Button startIcon={<BellIcon />} aria-label="Notifications" />
</Badge>
  • Under dot the content stays in the DOM, clipped rather than removed, so what the dot means is still readable.
  • When the badge is invisible or has nothing to show, it leaves the DOM entirely, so find-on-page does not turn up text that is not on screen.

Released under the MIT License