# Badge

A small rounded pill of inline text for statuses, labels, and counts.

## Installation

```tsx
import { Badge } from "@makeplane/propel/components/badge";
```
## Usage

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

export default function BasicDemo() {
  return <Badge label="Badge" variant="neutral" size="xs" />;
}
```
## Examples

### Variants

The `variant` axis carries both semantic color (`neutral`, `brand`, `info`, `success`, `warning`, `danger`) and
decorative label hues (`grey`, `purple`, `indigo`, `emerald`, `yellow`, `crimson`, `orange`). A
semantic name says why the badge is coloured; a hue name says only what colour it is, for a
badge that carries no meaning ("Live" is `emerald`, not `success`).

```tsx
import { Badge, type BadgeVariant } from "@makeplane/propel/components/badge";

const VARIANTS: BadgeVariant[] = [
  // Semantic — the color carries meaning.
  "neutral",
  "brand",
  "info",
  "success",
  "warning",
  "danger",
  // Label hues — the color is decoration.
  "grey",
  "purple",
  "indigo",
  "emerald",
  "yellow",
  "crimson",
  "orange",
];

// Badge labels read as words: "Neutral", not "neutral".
const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);

export default function VariantsDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      {VARIANTS.map((variant) => (
        <Badge key={variant} variant={variant} size="xs" label={capitalize(variant)} />
      ))}
    </div>
  );
}
```

### Sizes

`size` has three steps — `xs` 20 px with 12px text, `sm` 24 px with 13px and `md` 28 px with
14px — following the Figma badges set: 6 / 6 / 8px sides, a 4 / 4 / 6px icon gap, a 14 / 14 / 16px
glyph and a 4 / 6 / 6px corner. Height, gap, glyph and corner are the compact ladder's rungs; the
sides are a badge's own, a step tighter than a button's from `sm` up.

```tsx
import { Badge, type BadgeSize } from "@makeplane/propel/components/badge";

const SIZES: BadgeSize[] = ["xs", "sm", "md"];

export default function SizesDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      {SIZES.map((size) => (
        <Badge key={size} variant="brand" size={size} label={size.toUpperCase()} />
      ))}
    </div>
  );
}
```

### With icon

```tsx
import { Badge } from "@makeplane/propel/components/badge";
import { Icon } from "@makeplane/propel/components/icon";
import { TickOutline, AiStar1Outline } from "@makeplane/propel/icons";

export default function WithIconDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Badge variant="success" size="xs" startIcon={<Icon icon={TickOutline} />} label="Done" />
      <Badge variant="brand" size="xs" endIcon={<Icon icon={AiStar1Outline} />} label="Pro" />
    </div>
  );
}
```

### Icon only

Omit `label` for a compact icon-only badge: the label part is skipped so the icon sits centered, and the pill renders `role="img"`. Because the icon is decorative, `aria-label` is required in this state — the type makes omitting both `label` and `aria-label` an error.

```tsx
import { Badge, type BadgeSize } from "@makeplane/propel/components/badge";
import { Icon } from "@makeplane/propel/components/icon";
import { TickOutline } from "@makeplane/propel/icons";

const SIZES: BadgeSize[] = ["xs", "sm", "md"];

export default function IconOnlyDemo() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      {SIZES.map((size) => (
        <Badge
          key={size}
          variant="success"
          size={size}
          startIcon={<Icon icon={TickOutline} />}
          aria-label="Completed"
        />
      ))}
    </div>
  );
}
```
## API Reference

### Badge

The ready-made badge: composes the atomic `Badge` pill with the `BadgeLabel` and optional leading
(`startIcon`) and trailing (`endIcon`) icon slots. With no `label` the pill is icon-only — the
label part is skipped entirely (an empty flex child would still consume the pill's `gap` and
render it lopsided), and the pill defaults to `role="img"` so an author-supplied `aria-label` is
valid ARIA (a bare `<span>`'s `generic` role doesn't support naming).

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` (required) | `"xs" \| "sm" \| "md"` | — |  |
| `variant` (required) | `"neutral" \| "grey" \| "brand" \| "info" \| "purple" \| "indigo" \| "success" \| "emerald" \| "warning" \| "yellow" \| "danger" \| "crimson" \| "orange"` | — |  |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
| `startIcon` | `ReactNode` | — | Element rendered before the label (inline-start), e.g. `<Icon icon={TickOutline} />`. |
| `endIcon` | `ReactNode` | — | Element rendered after the label (inline-end), e.g. `<Icon icon={Sparkles} />`. |
| `label` | `string` | — |  |
| `aria-label` | `string` | — |  |
