Badge
A short label on something else: a booking's status, its code, a staff member's hours. A pill in five variants and two sizes, built on the vendored shadcn badge.
Playground
import { Badge } from "@/components/shared/badge";
<Badge>Confirmed</Badge>Variants
Each colour keeps one meaning. Primary, in the studio's green, is a good state: a booking that's confirmed. It follows the studio's colours (Meadow's leaf, Ink's sage). Secondary (ink) is emphasis without a verdict. Outline holds identifiers and plain facts. Muted is a quiet state. Destructive is for what went wrong or was called off. Butter is what waits on someone.
import { Badge } from "@/components/shared/badge";
<Badge>Confirmed</Badge>
<Badge variant="secondary">New</Badge>
<Badge variant="outline">OK-4F2K</Badge>
<Badge variant="muted">Inactive</Badge>
<Badge variant="destructive">Cancelled</Badge>
<Badge variant="butter">Invited</Badge>Sizes
sm is 20px, the default, and sits in a line of text. md is 24px, the height of an xs button, so the two share a row in a table without either one jumping.
import { Badge } from "@/components/shared/badge";
<Badge variant="muted">Inactive</Badge>
<Badge variant="muted" size="md">Inactive</Badge>
<Button variant="secondary" size="xs">Edit</Button>With an icon
A leading icon adds recognition, never meaning: the word still says it. It is drawn at 12px and hidden from assistive tech.
import { Badge } from "@/components/shared/badge";
import { CheckIcon, ClockIcon } from "@hugeicons/core-free-icons";
import { OkamiIcon } from "@/components/shared/dot-mark/okami-mark";
<Badge leadingIcon={CheckIcon}>Confirmed</Badge>
<Badge variant="outline" leadingIcon={ClockIcon}>45 min</Badge>
<Badge variant="muted" leadingIcon={OkamiIcon}>Booked by Okami</Badge>In a booking
The day view labels each booking with its code, and a staff card lists working hours as a row of outline badges that wraps.
10:00–10:45 · Haircut & beard
Martin Kováč · +421 905 123 456
import { Badge } from "@/components/shared/badge";
<p className="text-sm font-medium">10:00–10:45 · Haircut & beard</p>
<p className="text-sm text-muted-foreground">Martin Kováč · +421 905 123 456</p>
<div className="mt-1 flex items-center gap-2">
<Badge variant="outline">OK-4F2K</Badge>
<Badge variant="muted" leadingIcon={OkamiIcon}>Booked by Okami</Badge>
</div>
<div className="flex flex-wrap gap-1">
{hours.map(([day, time]) => (
<Badge key={day} variant="outline">{day} {time}</Badge>
))}
</div>API reference
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "primary" | "secondary" | "outline" | "muted" | "destructive" | "butter" | "primary" | Primary is the studio's green (it follows the studio's colours), a good state; secondary ink, emphasis; outline a hairline, for identifiers and facts; muted an ink tint, a quiet state; destructive the one red, for what failed or was cancelled; butter what waits on someone, like an invite not yet taken up. |
| size | "sm" | "md" | "sm" | 20 or 24px tall. md matches an xs button in a table row. |
| leadingIcon | Icon | — | An SVG icon component drawn before the label at 12px, hidden from assistive tech. |
| render | ReactElement | function | — | Render as another element, keeping the look (Base UI's useRender). |
| className | string | — | Layout only: margins, alignment. Never recolour it. |
Everything else passes through to the <span>.
Guidelines
- A badge labels; it never acts. For a pill someone taps, use a Chip or a Button.
- One or two words, in sentence case: “Confirmed”, not “CONFIRMED”. Codes and times are the exception, and are written as they are.
- Badge the exception, not the rule. When every booking in a list is confirmed, a Confirmed badge on each one is noise. Badge the one that was cancelled.
- Colour never carries the meaning alone. The word says it, and the colour only lets the eye find it faster.