Skip to content
kerfjsGitHub

Badge

A small label for a status or a count.

SourcePackage @kerf-js/veneerTry it

Preview

tsx
import { Badge } from '@kerf-js/veneer'export function Dot() {  return (  <>    <Badge      tone="positive"      dot    >      Live    </Badge>    <Badge      tone="info"      dot    >      Building    </Badge>    <Badge      tone="critical"      dot    >      Failed    </Badge>    <Badge      tone="neutral"      dot    >      Paused    </Badge>  </>  )}

Installation

npm install @kerf-js/veneer

Try it

Change the controls: the preview and its code update together. The address keeps what you set, so you can link to it.

Live preview
Controls
Tone
Emphasis
Codeupdates as you change the controls
import { Badge } from '@kerf-js/veneer'<Badge tone="positive" dot>Live</Badge>

Usage

tsx
import { Badge } from '@kerf-js/veneer'<Badge>Live</Badge>

Variants

soft by default.

tsx
import { Badge } from '@kerf-js/veneer'export function Emphasis() {  return (  <>    <Badge tone="positive">Status</Badge>    <Badge      tone="positive"      emphasis="solid"    >      Status    </Badge>    <Badge      tone="positive"      emphasis="outline"    >      Status    </Badge>  </>  )}

Tones

Use tone to colour a badge by what it means.

tsx
import { Badge } from '@kerf-js/veneer'export function Tones() {  return (  <>    {TONES.map((tone) => (      <Badge        key={tone}        tone={tone}      >        Status      </Badge>    ))}  </>  )}type Status =  | 'Live'  | 'Building'  | 'Failed'  | 'Paused'const TONES = [  'neutral',  'accent',  'positive',  'caution',  'critical',  'info',] as const

Counts

count makes it round. srText says what is being counted.

tsx
import { Badge } from '@kerf-js/veneer'export function Count() {  return (  <>    <Badge      count      tone="accent"      srText="unread comments"    >      3    </Badge>    <Badge      count      tone="critical"      emphasis="solid"      srText="failed deploys"    >      12    </Badge>    <Badge      count      emphasis="outline"      srText="open reviews"    >      99+    </Badge>  </>  )}

Status in a list

Use dot with a tone to show each row's status at a glance.

tsx
import { Badge, Row, Stack, Text } from '@kerf-js/veneer'export function ProjectList() {  return (  <Stack    gap="related"    divided  >    {PROJECTS.slice(2, 7).map((p) => (      <Row        key={p.id}        justify="between"        gap="group"      >        <Stack gap="hair">          <Text>{p.name}</Text>          <Text            role="caption"            subtle          >            {p.owner}          </Text>        </Stack>        <Badge          tone={TONE_OF[p.status]}          dot        >          {p.status}        </Badge>      </Row>    ))}  </Stack>  )}type Status =  | 'Live'  | 'Building'  | 'Failed'  | 'Paused'const PROJECTS: {  id: string  name: string  owner: string  region: string  status: Status  deploys: number  uptime: number  latency: number  cost: number  updated: string}[] = [  {    id: 'storefront',    name: 'storefront',    owner: 'Priya Raman',    region: 'eu-west',    status: 'Live',    deploys: 412,    uptime: 99.98,    latency: 84,    cost: 1240,    updated: '2026-10-31',  },  {    id: 'checkout',    name: 'checkout',    owner: 'Tomás Oliveira',    region: 'eu-west',    status: 'Live',    deploys: 288,    uptime: 99.95,    latency: 112,    cost: 980,    updated: '2026-10-31',  },  {    id: 'search-api',    name: 'search-api',    owner: 'Lena Fischer',    region: 'us-east',    status: 'Building',    deploys: 196,    uptime: 99.91,    latency: 146,    cost: 1610,    updated: '2026-10-30',  },  {    id: 'billing',    name: 'billing',    owner: 'Mara Lindqvist',    region: 'us-east',    status: 'Live',    deploys: 154,    uptime: 99.99,    latency: 73,    cost: 720,    updated: '2026-10-29',  },  {    id: 'mobile-bff',    name: 'mobile-bff',    owner: 'Sam Okafor',    region: 'ap-south',    status: 'Failed',    deploys: 233,    uptime: 99.42,    latency: 208,    cost: 860,    updated: '2026-10-29',  },  {    id: 'notifier',    name: 'notifier',    owner: 'Jun Tanaka',    region: 'eu-north',    status: 'Live',    deploys: 97,    uptime: 99.97,    latency: 58,    cost: 310,    updated: '2026-10-27',  },  {    id: 'reports',    name: 'reports',    owner: 'Noor Haddad',    region: 'us-west',    status: 'Paused',    deploys: 41,    uptime: 99.8,    latency: 321,    cost: 190,    updated: '2026-10-22',  },  {    id: 'edge-cache',    name: 'edge-cache',    owner: 'Jun Tanaka',    region: 'global',    status: 'Live',    deploys: 318,    uptime: 100,    latency: 21,    cost: 1430,    updated: '2026-10-31',  },  {    id: 'auth',    name: 'auth',    owner: 'Mara Lindqvist',    region: 'eu-west',    status: 'Live',    deploys: 176,    uptime: 99.99,    latency: 66,    cost: 540,    updated: '2026-10-28',  },  {    id: 'ingest',    name: 'ingest',    owner: 'Lena Fischer',    region: 'us-east',    status: 'Live',    deploys: 264,    uptime: 99.93,    latency: 131,    cost: 2120,    updated: '2026-10-30',  },]const TONE_OF: Record<  Status,  'positive' | 'info' | 'critical' | 'neutral'> = {  Live: 'positive',  Building: 'info',  Failed: 'critical',  Paused: 'neutral',}

A count on a button

Use count to show a number badge inside a button.

tsx
import { Badge, Button } from '@kerf-js/veneer'export function OnAButton() {  return (  <Button    emphasis="outline"    tone="neutral"    icon="bell"  >    Alerts{' '}    <Badge      count      tone="critical"      emphasis="solid"      srText="new"    >      2    </Badge>  </Button>  )}

When to use

Use it for

  • Saying what state something is in
  • A count on a button or a heading

Reach for something else

  • Tag, when people wrote the label themselves
  • Chip, when pressing it should do something
  • LiveStatus, when it shows a live connection

Accessibility

  • The words carry the status, so it never depends on colour.
  • A count alone means little to a screen reader: use srText.

Theming

Restyle it app-wide from veneer() in your kerf config. Every key takes colour, spacing, size, radius, shadow, type and motion; its own keys are listed. A state nests inside: hover: { bg: 'ink' }. Text on a fill you set is contrast-checked at build. How theming works

KeyOwn keysStates
parts.badge—hover, focus, press, active, open, disabled
ts
veneer({  colors: { brand: '#e8590c', ink: '#1b1b1f' },  parts: {    badge: { radius: 'pill', case: 'uppercase', tracking: 0.5 },  },})

API Reference

PropTypeDefaultDescription
toneTone—What it means.
emphasis"solid" | "soft" | "outline"—soft, solid or outline.
countbooleanfalseRound, for a number.
dotbooleanfalseA status dot before the label.
srTextstring—Extra words for screen readers, like "unread comments".
childrenunknown—What it contains.

Parts

Every element carries a stable name, so you can style it from your own CSS: [data-scope="badge"] [data-part="sr-only"]

sr-only

RelatedTagChipLive status