---
title: Badge
description: A word wearing a fill, in the site's own tone pairs - a badge invents no colour of its own.
source: https://adamjurek.com/components/badge
---

## Home


Badge is a word wearing a fill, sized for a label rather than a sentence. Its
`tone` selects one of the site's own category color pairs, so a badge reading
"Motion" matches "Motion" everywhere else it appears - the nav, the archive
filters - rather than inventing its own color.

<!-- ::start:showcase demo="badge" height="380" -->
<!-- ::end:showcase -->

## Why it is built this way

The tone values are not badge-specific colors - they are the same category
pairs the nav and the archive already use, resolved by the stylesheet from a
`data-tone` attribute rather than redrawn per component. A badge invents no
color of its own, so "motion" on a badge and "motion" in the nav are visibly
the same claim, not two designers' guesses at the same idea.

## What it does not do

It has no dismiss button and no click handler - it is a `<span>` with a
fill, not a filter chip or a tag input. Reach for a different component if
the badge itself needs to do something when pressed.

> [!NOTE] Install commands are not written here
> Anything in `packages/ui/registry.ts` gets its TanStack and shadcn commands
> attached automatically, so there is nothing to keep in sync.


## Install

<!-- ::start:tabs -->

### TanStack

```shell
tanstack add https://adamjurek.com/r/tanstack/badge.json
```

### shadcn

```shell
pnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/badge.json
```

### pnpm

```shell
pnpm add @sushindustries/ui @sushindustries/atoms
```

<!-- ::end:tabs -->

## What you get

| | |
| --- | --- |
| Version | 0.1.0 |
| Category | content · Text |
| Files | `badge.tsx` |
| Dependencies | None |
| Tags | label, tone, no-deps |

> [!NOTE] No runtime dependencies
> It brings nothing with it beyond the stylesheet.

## Get Started


Install commands are on Home, attached from the registry - they are not written
here, because a second copy is a copy that goes stale. This tab starts after the
install worked.

## Use it

```tsx
import { Badge } from "@sushindustries/ui";

export function Example() {
	return <Badge tone="motion">Motion</Badge>;
}
```

## What you should see

A small pill of mono text, filled with the `motion` tone's pastel and a
matching ink colour, with no border. Drop `tone` and the same pill
renders instead with a neutral outline and dim text - the quiet default
for a label that is not a category.

## If nothing happens

A badge that renders as plain text with no pill shape usually means the
atoms stylesheet is not imported - `Badge` carries no styles of its own,
only the `badge` class name. An unrecognised `tone` value is not an
error; it simply matches no rule in the stylesheet and falls back to the
untoned look.


## Guides


## Variants

`tone` writes `data-tone`, and the stylesheet supplies five pairs -
`motion`, `layout`, `content`, `docs`, `3d`:

```tsx
<Badge tone="docs">Docs</Badge>
```

```css
.badge[data-tone="docs"] {
	background: var(--tone-docs);
	color: var(--tone-docs-ink);
}
```

These are the site's own category colours, shared with `Avatar`, `Card`
and the nav panel - inventing a sixth tone here means inventing a colour
nothing else agrees with, so a value outside the five just renders the
untoned default rather than a wrong colour.

## When not to use it

A badge is a label, not a control - it has no `href` or `onClick`, and
wrapping one in a link changes nothing about how it looks or announces
itself. For a filter chip that actually does something when clicked,
reach for the pattern `Archive` uses instead: an anchor styled as a chip,
not a badge wrapped inside one.


## API


<!-- generated:api -->

## Props

| Prop | Type | Default | Does |
| --- | --- | --- | --- |
| `children` | `ReactNode` | - |  |
| `tone?` | `string` | - | Colour family, resolved by the stylesheet. Absent is the quiet default. |

<!-- /generated:api -->

## Notes

`Badge` has no `href` or `onClick` - it is always a `<span>`. Wrapping it
in a link or a button is the caller's job, and doing so changes nothing
about the badge's own markup or styling.


## Examples


Examples are the tab where the component is shown doing a job, not
demonstrating a prop. The API tab already lists the props.

<!-- ::start:showcase demo="badge" height="420" -->
<!-- ::end:showcase -->

Press Compare. The frames are real viewports, so a layout that breaks at 320
breaks here too rather than in somebody's hands.

## In a page

```tsx
import { Badge, Card } from "@sushindustries/ui";

export function ComponentSummary({ name, tone }: { name: string; tone: string }) {
	return (
		<Card title={name}>
			<Badge tone={tone}>{tone}</Badge>
		</Card>
	);
}
```

## What this example is not

`tone` is passed straight through from data here, not chosen for
contrast against the card behind it - the badge always uses the same
five tones regardless of what card or background it sits on, so there is
no per-page palette decision to make.
