component

Badge

A versatile badge component for displaying status, categories, or small pieces of information. Supports multiple tone states and can be styled with different emphasis levels for various use cases. Compose with Group to build multi-section chips out of two or more badges (and optionally a trailing button) without any badge-specific plumbing.

Emphasis

The emphasis prop controls the visual style of the badge. primary uses a solid accent surface with high-contrast text; secondary (default) uses a tone-tinted soft surface; tertiary uses a neutral surface with bold tone-colored text; adaptive inherits ambient text color and tints from the surrounding context.

PrimarySecondaryTertiaryAdaptive
---
// GENERATED from packages/demos/badge/emphasis.demo.tsx — edit the source, not this file.
import Badge from "@pindoba/astro-badge";
import { stack } from "@pindoba/styled-system/patterns";
---

<div
  class={stack({
    gap: "md",
    direction: "row",
    align: "center",
    flexWrap: "wrap",
  })}
>
  <Badge emphasis="primary">Primary</Badge>
  <Badge emphasis="secondary">Secondary</Badge>
  <Badge emphasis="tertiary">Tertiary</Badge>
  <Badge emphasis="adaptive">Adaptive</Badge>
</div>

Tone Colors

Badges support semantic tone colors for communicating status at a glance. A color family (teal, cyan, violet, pink and the tinted neutrals slate, stone, sage, mauve) is accepted too: it colors without implying a status — use it for labels, people and categories, and keep the semantic tones for status.

Primary Success Warning Danger Neutral
PrimarySuccessWarningDangerNeutral
Primary Success Warning Danger Neutral
TealCyanVioletPinkSlateStoneSageMauve
---
// GENERATED from packages/demos/badge/tone.demo.tsx — edit the source, not this file.
import Badge from "@pindoba/astro-badge";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "md", direction: "column" })}>
  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="primary" emphasis="primary">
      Primary
    </Badge>
    <Badge tone="success" emphasis="primary">
      Success
    </Badge>
    <Badge tone="warning" emphasis="primary">
      Warning
    </Badge>
    <Badge tone="danger" emphasis="primary">
      Danger
    </Badge>
    <Badge tone="neutral" emphasis="primary">
      Neutral
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="primary">Primary</Badge>
    <Badge tone="success">Success</Badge>
    <Badge tone="warning">Warning</Badge>
    <Badge tone="danger">Danger</Badge>
    <Badge tone="neutral">Neutral</Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="primary" emphasis="tertiary">
      Primary
    </Badge>
    <Badge tone="success" emphasis="tertiary">
      Success
    </Badge>
    <Badge tone="warning" emphasis="tertiary">
      Warning
    </Badge>
    <Badge tone="danger" emphasis="tertiary">
      Danger
    </Badge>
    <Badge tone="neutral" emphasis="tertiary">
      Neutral
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="teal">Teal</Badge>
    <Badge tone="cyan">Cyan</Badge>
    <Badge tone="violet">Violet</Badge>
    <Badge tone="pink">Pink</Badge>
    <Badge tone="slate">Slate</Badge>
    <Badge tone="stone">Stone</Badge>
    <Badge tone="sage">Sage</Badge>
    <Badge tone="mauve">Mauve</Badge>
  </div>
</div>

Size

Four size variants are available to fit different layout contexts.

SmallMedium
---
// GENERATED from packages/demos/badge/size.demo.tsx — edit the source, not this file.
import Badge from "@pindoba/astro-badge";
import { stack } from "@pindoba/styled-system/patterns";
---

<div
  class={stack({
    gap: "md",
    direction: "row",
    align: "center",
    flexWrap: "wrap",
  })}
>
  <Badge size="sm">Small</Badge>
  <Badge size="md">Medium</Badge>
</div>

Background

Badge inherits panel’s full surface model. background, border, and translucent work across every emphasis × tone combination — panel’s compound variants remap each surface.* value to the correct ramp (accent under primary, palette-tinted under secondary, neutral under tertiary).

peak hill base valley ground transparent
peak hill base valley ground transparent
peak hill base valley ground transparent
---
// GENERATED from packages/demos/badge/background.demo.tsx — edit the source, not this file.
import Badge from "@pindoba/astro-badge";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "md", direction: "column" })}>
  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="primary" emphasis="primary" background="surface.peak">
      peak
    </Badge>
    <Badge tone="primary" emphasis="primary" background="surface.hill">
      hill
    </Badge>
    <Badge tone="primary" emphasis="primary" background="surface.base">
      base
    </Badge>
    <Badge tone="primary" emphasis="primary" background="surface.valley">
      valley
    </Badge>
    <Badge tone="primary" emphasis="primary" background="surface.ground">
      ground
    </Badge>
    <Badge tone="primary" emphasis="primary" background="transparent">
      transparent
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="primary" emphasis="secondary" background="surface.peak">
      peak
    </Badge>
    <Badge tone="primary" emphasis="secondary" background="surface.hill">
      hill
    </Badge>
    <Badge tone="primary" emphasis="secondary" background="surface.base">
      base
    </Badge>
    <Badge tone="primary" emphasis="secondary" background="surface.valley">
      valley
    </Badge>
    <Badge tone="primary" emphasis="secondary" background="surface.ground">
      ground
    </Badge>
    <Badge tone="primary" emphasis="secondary" background="transparent">
      transparent
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="primary" emphasis="tertiary" background="surface.peak">
      peak
    </Badge>
    <Badge tone="primary" emphasis="tertiary" background="surface.hill">
      hill
    </Badge>
    <Badge tone="primary" emphasis="tertiary" background="surface.base">
      base
    </Badge>
    <Badge tone="primary" emphasis="tertiary" background="surface.valley">
      valley
    </Badge>
    <Badge tone="primary" emphasis="tertiary" background="surface.ground">
      ground
    </Badge>
    <Badge tone="primary" emphasis="tertiary" background="transparent">
      transparent
    </Badge>
  </div>
</div>

With Icons

Embed icons inside the badge content slot to reinforce meaning alongside the status label. Use 1em for icon sizes so they scale naturally with the badge’s size prop.

Deployed Pending Failed Info
Active Review Blocked Private
---
// GENERATED from packages/demos/badge/with-icons.demo.tsx — edit the source, not this file.
import Badge from "@pindoba/astro-badge";
import { CircleCheck, TriangleAlert, CircleX, Info, Lock } from "@lucide/astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="success">
      <CircleCheck /> Deployed
    </Badge>
    <Badge tone="warning">
      <TriangleAlert /> Pending
    </Badge>
    <Badge tone="danger">
      <CircleX /> Failed
    </Badge>
    <Badge tone="primary">
      <Info /> Info
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="success" emphasis="secondary">
      <CircleCheck /> Active
    </Badge>
    <Badge tone="warning" emphasis="secondary">
      <TriangleAlert /> Review
    </Badge>
    <Badge tone="danger" emphasis="secondary">
      <CircleX /> Blocked
    </Badge>
    <Badge tone="neutral" emphasis="secondary">
      <Lock /> Private
    </Badge>
  </div>
</div>

With Stamps

Badges host Stamp addons through the same leading / trailing slots buttons use. The slot renders an affix box and the badge’s content area publishes the Control Var Contract’s --affix-* vocabulary, so a <Stamp emphasis="ghost"> snaps to a deterministic square matched to the badge’s size — identical composition, one tier smaller. Customize the box via passThrough.leading / passThrough.trailing (e.g. { props: { decorative: true } }).

Deployed Review New
Details Details Accented stamp
---
// GENERATED from packages/demos/badge/with-stamps.demo.tsx — edit the source, not this file.
import Badge from "@pindoba/astro-badge";
import Stamp from "@pindoba/astro-stamp";
import {
  CircleCheck,
  TriangleAlert,
  Sparkles,
  ArrowRight,
} from "@lucide/astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="success">
      <Stamp slot="leading" emphasis="ghost">
        <CircleCheck />
      </Stamp>
      Deployed
    </Badge>
    <Badge tone="warning" emphasis="secondary">
      <Stamp slot="leading" emphasis="ghost">
        <TriangleAlert />
      </Stamp>
      Review
    </Badge>
    <Badge tone="primary" emphasis="primary">
      <Stamp slot="leading" emphasis="ghost">
        <Sparkles />
      </Stamp>
      New
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge size="xs" tone="neutral">
      <Stamp slot="trailing" emphasis="ghost">
        <ArrowRight />
      </Stamp>
      Details
    </Badge>
    <Badge size="sm" tone="neutral">
      <Stamp slot="trailing" emphasis="ghost">
        <ArrowRight />
      </Stamp>
      Details
    </Badge>
    <Badge size="md" tone="primary" emphasis="secondary">
      <Stamp slot="leading" emphasis="secondary">
        <Sparkles />
      </Stamp>
      Accented stamp
    </Badge>
  </div>
</div>

With Buttons

Buttons placed inside a badge are automatically resized to fit the badge’s dimensions. You don’t need to set a size prop on the button; it scales proportionally with the badge’s own size variant. Use shape="circle" emphasis="ghost" for a compact dismiss target that inherits the badge’s color context without adding visual noise.

To opt out of this behavior, set buttonFit={false} on the badge — buttons will then render at their own default size.

sm badge md badge
New feature Deployed Pending Failed Archived
---
// GENERATED from packages/demos/badge/with-buttons.demo.tsx — edit the source, not this file.
import Badge from "@pindoba/astro-badge";
import Button from "@pindoba/astro-button";
import { X } from "@lucide/astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge size="sm" tone="neutral" emphasis="secondary">
      sm badge{" "}
      <Button aria-label="Dismiss" shape="circle" emphasis="ghost">
        <X />
      </Button>
    </Badge>
    <Badge size="md" tone="neutral" emphasis="secondary">
      md badge{" "}
      <Button aria-label="Dismiss" shape="circle" emphasis="ghost">
        <X />
      </Button>
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge tone="primary">
      New feature{" "}
      <Button aria-label="Dismiss" shape="circle" emphasis="ghost">
        <X />
      </Button>
    </Badge>
    <Badge tone="success">
      Deployed{" "}
      <Button aria-label="Dismiss" shape="circle" emphasis="ghost">
        <X />
      </Button>
    </Badge>
    <Badge tone="warning">
      Pending{" "}
      <Button aria-label="Dismiss" shape="circle" emphasis="ghost">
        <X />
      </Button>
    </Badge>
    <Badge tone="danger">
      Failed{" "}
      <Button aria-label="Dismiss" shape="circle" emphasis="ghost">
        <X />
      </Button>
    </Badge>
    <Badge tone="neutral">
      Archived{" "}
      <Button aria-label="Dismiss" shape="circle" emphasis="ghost">
        <X />
      </Button>
    </Badge>
  </div>
</div>

Composing with Group

Wrap two or more badges in a Group to build multi-section chips. Group owns the outer outline and emits dividers between children, so badges (with different emphasis or tone) — and even a trailing Button — visually merge into a single unit. This replaces the previous label slot. See the Group page for the live demo.

Custom Styling

Use passThrough to apply custom Panda CSS styles or HTML attributes to any slot (root, content).

Custom Bold Accessible
---
// GENERATED from packages/demos/badge/custom.demo.tsx — edit the source, not this file.
import Badge from "@pindoba/astro-badge";
import { stack } from "@pindoba/styled-system/patterns";
---

<div
  class={stack({
    gap: "md",
    direction: "row",
    align: "center",
    flexWrap: "wrap",
  })}
>
  <Badge
    tone="danger"
    passThrough={{
      root: { style: { letterSpacing: "wider" } },
    }}
  >
    Custom
  </Badge>

  <Badge
    tone="primary"
    passThrough={{
      content: {
        style: { fontWeight: "bold", textTransform: "uppercase" },
      },
    }}
  >
    Bold
  </Badge>

  <Badge
    tone="success"
    passThrough={{
      root: {
        props: { role: "status", "aria-label": "Success status" },
      },
    }}
  >
    Accessible
  </Badge>
</div>
  • Components
  • Blocks