<!-- Generated by scripts/gen-docs.mts from the JSDoc in registry/. Do not edit; run `pnpm run docs`. -->

# Badge

A short label for a status, category or count: "Draft", "Vegan", "3". With `href` it becomes a link and with `onClick` a button, with press feedback and a 44px touch target on phones, the same way `Button` does. Without `onClick` or `href` a Badge is not a button: it is a static `<span>`, and `disabled` and `type` do nothing.

```tsx
import { Badge } from "@/components/ouro/badge/badge";
```

## Example

```tsx
<Badge>Draft</Badge>
<Badge tone="success" variant="soft">Published</Badge>
<Badge href="/tags/vegan">Vegan</Badge>
<Badge onClick={toggle} aria-pressed={active}>Quick</Badge>
```

## Use instead when

- Use `Button` instead for an action that needs a clear call to action, and `Alert` for a message the reader must notice.

## Props

The props the modes share. Type: `BadgeProps`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children` (required) | `ReactNode` |  | The label: a short word, or a count. |
| `className` | `string` |  | Layout only: margins, grid placement (`ms-2`). Appearance classes warn in development; use `tone` and `variant`. |
| `icon` | `BadgeIcon` |  | Icon before the label. Pass the component, not an element: `icon={CheckIcon}`. |
| `onClick` | `MouseEventHandler<HTMLElement>` |  | Runs when the badge is pressed. Without `href` it makes the badge a `<button>` (pass `aria-pressed` too when it toggles a filter); with `href` it runs before the link navigates. |
| `tone` | `"primary" \| "secondary" \| "destructive" \| "success" \| "warning"` | `"secondary"` | Color role from the theme, named after its token. `secondary` is a quiet neutral label; use `success`, `warning` and `destructive` for status. shadcn's `variant="destructive"` is `tone="destructive"`. |
| `variant` | `"solid" \| "soft" \| "outline"` | `"solid"` | Visual weight: `solid` fills the badge with the tone, `soft` tints it, `outline` draws only the border. shadcn's `default` is `tone="primary"`, its `secondary` and `destructive` are tones, and `ghost` is `soft`. |

## Modes

`BadgeProps` is a union: pass the props of exactly one mode. Mixing them is a type error.

### With `href`

| Prop | Type | Description |
| --- | --- | --- |
| `href` (required) | `string` | Where the link goes. Internal paths render through `OuroProvider`'s `linkComponent` (your router's link); absolute URLs render as a plain `<a>`, and with `target="_blank"` always get `rel="noopener noreferrer"`. |
| `ref` | `Ref<HTMLAnchorElement>` | Reaches the rendered element. |

Also accepts the attributes in React's `AnchorHTMLAttributes` and `HTMLAttributes` (`id`, `aria-*`, event handlers), passed to the rendered element, except those named on this page.

Not in this mode (typed `never`):

- `disabled`: A link cannot be disabled. Remove `href` and pass `onClick` for a button.
- `type`: Links have no `type`. Remove `href` and pass `onClick` for a button.

### With `type`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `ref` | `Ref<HTMLButtonElement & HTMLSpanElement>` |  | Reaches the rendered element. |
| `type` | `"button" \| "submit" \| "reset"` | `"button"` | With `onClick`, the button's role in a form. |

Also accepts the attributes in React's `ButtonHTMLAttributes` and `HTMLAttributes` (`id`, `aria-*`, event handlers), passed to the rendered element, except those named on this page.

Not in this mode (typed `never`):

- `href`: Not set: a static badge or, with `onClick`, a button. Pass `href` for a link.

## Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

- `asChild`: Not a prop. To render a link, pass `href`: `<Badge href="/tags/vegan">`.
- `color`: Not a prop. Ouro UI uses `tone`: `<Badge tone="destructive">`.

## `className` is for layout only

Pass margins, width and grid placement (`mt-4`, `w-full`, `col-span-2`). Your classes always win over Ouro UI's: Badge merges `className` last with `cn` (tailwind-merge), so a class that conflicts with its own replaces it.[^layout-classes]

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (`hover:`, `sm:`) and any `!` are removed, it starts with `bg-`, `text-`, `border-`, `rounded-`, `shadow-`, `font-`, `ring-`, `from-`, `via-`, `to-`, `fill-` or `stroke-`, or is `border`, `rounded`, `shadow` or `ring`, or has a `dark:` variant. Text wrapping, overflow and table layout classes (`text-wrap`, `text-ellipsis`, `border-collapse`) are layout and do not warn.

Alignment classes (`text-center`) warn too: set alignment on a parent element.

## Types

### `BadgeIcon`

An icon component, such as one from `lucide-react` or `@heroicons/react`.

```ts
type BadgeIcon = ComponentType<SVGProps<SVGSVGElement>>;
```

[^layout-classes]: test:registry/ouro/components/ouro/badge/badge.test.tsx::Badge > forwards a ref and lets app layout classes win verify:testExists(registry/ouro/components/ouro/badge/badge.test.tsx::Badge > forwards a ref and lets app layout classes win)
