Badge

A compact inline label for status, category or metadata, with an optional icon, avatar and close button.

Installation

bash
npx @smicolon/cli init
npx @smicolon/cli add badge

Preview

NewPaidFeatured

Variants

variant sets the visual weight and tone the meaning. Solid is the loudest; Soft lays a tint over the card surface, so it reads the same wherever it sits; Outline is the quietest.

NewDraftIn reviewPaidAwaiting paymentOverdue
NewDraftIn reviewPaidAwaiting paymentOverdue
NewDraftIn reviewPaidAwaiting paymentOverdue

radius="standard" swaps the pill for the design system’s small radius.

NewNewNewNew

Sizes

Heights are minimums: 20, 22 and 26px in English. Arabic’s taller line grows them to 24, 24 and 28px rather than squeezing the text.

FeaturedFeaturedFeatured

States

Pass onClose and the badge gets a real close button. Tab to one to see the focus ring, which wraps the whole badge 2px out.

DesignEngineeringResearchMarketing
FeaturedLaylaLaylaReviewer

Accessibility

  • The badge is a plain <span>. Only the close button is interactive, and it is a native <button>.
  • The close button is named after the label, "Remove Cairo", or "إزالة القاهرة" when the nearest lang is Arabic. It follows a language change without a reload. Pass closeLabel for more context, such as "Remove filter: Cairo".
  • The icon is decorative and hidden from assistive technology. The avatar is not: give the Avatar a name, or name="" when the label already names the person.
  • Don’t rely on tone alone to carry meaning — the label should say it.
  • Direction is inherited from dir, never a prop, so the badge can never disagree with the page.

Props

PropTypeDefaultDescription
childrenReactNode—The label.
variant"solid" | "soft" | "outline""solid"Visual weight.
tone"primary" | "secondary" | "info" | "success" | "warning" | "destructive""primary"Meaning, shown as colour.
size"sm" | "md" | "lg""sm"Badge size.
radius"rounded" | "standard""rounded"Pill or small radius.
iconReactNode—Icon before the label.
avatarReactNode—Avatar before the label.
onClose(event) => void—Adds a close button; called when pressed.
closeLabelstring—Close button’s accessible name, in the page language by default.
classNamestring—Extra classes for the component.

Usage

tsx
import { Badge } from "@/components/ui/badge"
import { Avatar } from "@/components/ui/avatar"

<Badge>New</Badge>
<Badge variant="soft" tone="success">Paid</Badge>
<Badge variant="outline" tone="destructive" size="md">Overdue</Badge>

// Icon and avatar — the badge sizes and colours them
<Badge icon={<Star />}>Featured</Badge>
<Badge avatar={<Avatar size="12" radius="rounded" src={user.photo} name="" />}>
  {user.name}
</Badge>

// Removable
<Badge variant="soft" onClose={() => remove(tag)} closeLabel={`Remove filter: ${tag}`}>
  {tag}
</Badge>