Figma UI kit
Data display

Badges

v1.2.1

A count attached to a label: how many.

Quick usage

Import the shared component; never copy its markup into a feature. The Web TSX tab beside the live stage has a complete, working example.

import Chip from '@/shared/components/Chip';

Live stage and code

The live component and its controls on the left, the code on the right; change a control and the code follows: Web TSX (the component in a working example), Tailwind and HTML (the markup it renders, with its data attributes), React Native, SwiftUI and Jetpack Compose. The header theme switch drives every stage.

Props

Read from the component's source when the docs build: names and defaults from its signature, types and descriptions from its JSDoc. A blank type means the prop has no JSDoc yet.

PropTypeDefaultDescription
tone'neutral'|'success'|'warning'|'error'|'info'|'violet'|'primary'|'outline'|'ai''neutral'The CONDITION being reported, not a colour choice (sec.13). `outline` is for a tag that names a thing rather than a state, so it spends no status hue.
size'sm'|'md'|'lg''sm'The DENSITY of the surface, not the importance of the text: `sm` (20px) in table rows and grids, `md` (24px) on cards and panel headers, `lg` (32px) in inline banners only. The label is 12px at all three.
variant'chip'|'badge''chip'`badge` is the counter (Inter, tabular figures), one rung tighter than a chip at every tier (18 / 22 / 30).
dotbooleanfalseThe 5px leading dot, in the chip's own colour.
iconReact.ReactNodenullA leading glyph instead of the dot. Sized by the caller, because Phosphor takes size as a prop; 12px is the tier for a 20px chip.
trailingIconReact.ReactNodenullA glyph after the label, for a pressable chip that goes somewhere (an arrow to an external file). Same sizing as `icon`.
countnumber|stringnullA tally after the label ("Fasteners 4"). Rendered as a sibling so the chip's own gap spaces it, in the data face at the chip's own size. Do NOT nest a <Chip variant="badge"> inside a chip to get this.
selectedbooleanfalseThe chosen one in a row of filter chips. Steps up the surface ladder rather than taking a status hue: a filter you picked is not a condition the thing is in.
onClick() => void-Renders a <button> and adds the mandatory 44x44 boundary. The chip does NOT grow to 28px - see the ds-chip note in the CSS.
hrefstring-Renders an <a> instead, with the same boundary and hover. For a chip that GOES somewhere - a mailto, an external file - where a <button> with a programmatic navigation would lose the middle-click, the copy-link and the status bar a real link gives for free.
classNamestring--
childrenReact.ReactNode--
...rest--Any other prop is passed through to the root element.

Source: frontend/src/shared/components/Chip/Chip.js

DOM contract

What the rendered element carries: the data attributes its styles key on, its roles and ARIA, its focus style, and whether its stylesheet draws the 44px hit boundary. Also read from the source.

Data attributesdata-selecteddata-sizedata-tonedata-variant
RolesNative element semantics only
ARIANone set by the component
FocusStyles :focus-visible in its own stylesheet
44px hit boundaryYes: a ::after in Chip.module.css

Accessibility and touch

Passive; it is never the target.

Badges is passive: it reports a condition and is not pressed, so it has no hit boundary. If a design makes it pressable, it gains the 44 by 44 boundary and is marked /* ds-chip: why */.

Rules and constraints

Use it for

  • Tab counts, filter counts, the selected count in the bulk bar.

Avoid

  • Never a hand-rolled 18px pill: <Chip variant="badge"> is the only one.
  • Never nested inside a chip: pass count to the chip instead.

Constraints

  • One rung tighter than a chip: 18 / 22 / 30px. Inter tabular figures, never mono.

More in Data display