Badge

A versatile badge component for displaying status, categories, or small pieces of information. Supports an optional label section, multiple feedback states, and can be styled with different emphasis levels for various use cases.

Emphasis

The emphasis prop controls the visual style of the badge. primary uses a solid background with high-contrast text; secondary uses an outlined appearance with a subtle background.

Primary Secondary
---
import Badge from "../Badge.astro";
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>
</div>

Feedback Colors

Badges support semantic feedback colors for communicating status at a glance.

Primary Success Warning Danger Neutral
---
import Badge from "../Badge.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

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

Size

Four size variants are available to fit different layout contexts.

Small Medium
---
import Badge from "../Badge.astro";
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

When feedback="neutral", the badge uses panel surface props — background, border, and translucent — mirroring the button’s secondary emphasis behavior. This lets a neutral badge blend into different surface contexts.

Surface Sunken Elevated Transparent
---
import Badge from "../Badge.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div
  class={stack({
    gap: "md",
    direction: "row",
    align: "center",
    flexWrap: "wrap",
  })}
>
  <Badge feedback="neutral" emphasis="secondary" background="surface"
    >Surface</Badge
  >
  <Badge feedback="neutral" emphasis="secondary" background="sunken"
    >Sunken</Badge
  >
  <Badge feedback="neutral" emphasis="secondary" background="elevated"
    >Elevated</Badge
  >
  <Badge feedback="neutral" emphasis="secondary" background="transparent"
    >Transparent</Badge
  >
</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. The label prop (or slot="label" in Astro) adds a secondary section on the left — useful for pairing a version number or category with an icon-annotated value on the right.

Deployed Pending Failed Info
Active Review Blocked Private
v2.1 Featured Prod Live Auth Required
---
import Badge from "../Badge.astro";
import { Icon } from "astro-icon/components";
import { stack } from "@pindoba/styled-system/patterns";
import { css } from "@pindoba/styled-system/css";
---

<div class={stack({ gap: "xl", direction: "column" })}>
  <!-- Solid badges with status icons -->
  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge feedback="success">
      <Icon name="lucide:circle-check" class={css({ w: "1em", h: "1em" })} />
      Deployed
    </Badge>
    <Badge feedback="warning">
      <Icon name="lucide:triangle-alert" class={css({ w: "1em", h: "1em" })} />
      Pending
    </Badge>
    <Badge feedback="danger">
      <Icon name="lucide:circle-x" class={css({ w: "1em", h: "1em" })} />
      Failed
    </Badge>
    <Badge feedback="primary">
      <Icon name="lucide:info" class={css({ w: "1em", h: "1em" })} />
      Info
    </Badge>
  </div>

  <!-- Outlined badges with icons -->
  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge feedback="success" emphasis="secondary">
      <Icon name="lucide:circle-check" class={css({ w: "1em", h: "1em" })} />
      Active
    </Badge>
    <Badge feedback="warning" emphasis="secondary">
      <Icon name="lucide:triangle-alert" class={css({ w: "1em", h: "1em" })} />
      Review
    </Badge>
    <Badge feedback="danger" emphasis="secondary">
      <Icon name="lucide:circle-x" class={css({ w: "1em", h: "1em" })} />
      Blocked
    </Badge>
    <Badge feedback="neutral" emphasis="secondary">
      <Icon name="lucide:lock" class={css({ w: "1em", h: "1em" })} />
      Private
    </Badge>
  </div>

  <!-- Named label slot with icon in content -->
  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge feedback="primary" emphasis="primary">
      <Fragment slot="label">v2.1</Fragment>
      <Icon name="lucide:star" class={css({ w: "1em", h: "1em" })} />
      Featured
    </Badge>
    <Badge feedback="success" emphasis="primary">
      <Fragment slot="label">Prod</Fragment>
      <Icon name="lucide:circle-check" class={css({ w: "1em", h: "1em" })} />
      Live
    </Badge>
    <Badge feedback="neutral" emphasis="secondary">
      <Fragment slot="label">Auth</Fragment>
      <Icon name="lucide:lock" class={css({ w: "1em", h: "1em" })} />
      Required
    </Badge>
  </div>
</div>

With Buttons

Buttons placed inside a badge — in either the content or label slot — 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
React 18 issues Filter 12 results v2.1 Released
---
import Badge from "../Badge.astro";
import Button from "@pindoba/astro-button";
import { Icon } from "astro-icon/components";
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" feedback="neutral" emphasis="secondary">
      sm badge
      <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button>
    </Badge>
    <Badge size="md" feedback="neutral" emphasis="secondary">
      md badge
      <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button>
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge feedback="primary">
      New feature
      <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button>
    </Badge>
    <Badge feedback="success">
      Deployed
      <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button>
    </Badge>
    <Badge feedback="warning">
      Pending
      <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button>
    </Badge>
    <Badge feedback="danger">
      Failed
      <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button>
    </Badge>
    <Badge feedback="neutral">
      Archived
      <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button>
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge feedback="primary" emphasis="secondary">
      <Fragment slot="label">
        React
        <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button
        >
      </Fragment>
      18 issues
    </Badge>
    <Badge feedback="neutral" emphasis="secondary">
      <Fragment slot="label">
        Filter
        <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button
        >
      </Fragment>
      12 results
    </Badge>
    <Badge feedback="success">
      <Fragment slot="label">
        v2.1
        <Button shape="circle" emphasis="ghost"><Icon name="lucide:x" /></Button
        >
      </Fragment>
      Released
    </Badge>
  </div>
</div>

Custom Styling

The label prop (Svelte) or label slot (Astro) adds a secondary section to the badge. Use passThrough to apply custom Panda CSS styles or HTML attributes to any slot.

New Feature v2.0 Release Beta Preview Custom Accessible
---
import Badge from "../Badge.astro";
import { stack } from "@pindoba/styled-system/patterns";
---

<div
  class={stack({
    gap: "md",
    direction: "row",
    align: "center",
    flexWrap: "wrap",
  })}
>
  <!-- With label slot -->
  <Badge feedback="success">
    <Fragment slot="label">New</Fragment>
    Feature
  </Badge>

  <!-- Label as primary emphasis -->
  <Badge feedback="primary" emphasis="primary">
    <Fragment slot="label">v2.0</Fragment>
    Release
  </Badge>

  <!-- Secondary with label -->
  <Badge feedback="warning" emphasis="secondary">
    <Fragment slot="label">Beta</Fragment>
    Preview
  </Badge>

  <!-- PassThrough custom styles -->
  <Badge
    feedback="danger"
    passThrough={{ root: { style: { letterSpacing: "wider" } } }}
  >
    Custom
  </Badge>

  <!-- PassThrough with ARIA props -->
  <Badge
    feedback="success"
    passThrough={{
      root: { props: { role: "status", "aria-label": "Success status" } },
    }}
  >
    Accessible
  </Badge>
</div>

Props

Prop
feedback
feedback

Semantic feedback color scheme. primary: primary color palette. success: green. warning: orange. danger: red. neutral: gray.

Type "primary" | "success" | "warning" | "danger" | "neutral"
Default "primary"
Required No
emphasis
emphasis

Visual emphasis level. primary: solid background with high-contrast text. secondary: outlined style with subtle background and border.

Type "primary" | "secondary"
Default "primary"
Required No
size
size

Size variant for the badge. xs: extra small. sm: small. md: default. lg: large.

Type "xs" | "sm" | "md" | "lg"
Default "md"
Required No
background
background

Surface fill. Only valid when feedback="neutral". Defaults to "surface". surface: light neutral tint. sunken: recessed. elevated: raised with shadow. transparent: no fill.

Type "surface" | "sunken" | "elevated" | "transparent"
Default "surface"
Required No
border
border

Border ring strength. Only valid when feedback="neutral". Defaults to "muted" for secondary emphasis, "none" for primary.

Type "none" | "default" | "bold" | "muted"
Default "muted" | "none"
Required No
translucent
translucent

Applies a frosted-glass backdrop blur to the surface. Only valid when feedback="neutral".

Type boolean
Default false
Required No
buttonFit
buttonFit

When true (default), buttons placed inside the badge content or label slot are automatically resized to fit the badge's dimensions. Set to false to let buttons render at their own default size.

Type boolean
Default true
Required No
label
label

Optional label displayed as a separate section within the badge. In Svelte, accepts a string or Snippet; in Astro, use slot="label".

Type string | Snippet | slot
Default undefined
Required No
passThrough
passThrough

Custom styling and props for badge slots. Each slot accepts style (SystemStyleObject) and props (HTML attributes).

Type { root?: { style?: SystemStyleObject; props?: HTMLAttributes<HTMLSpanElement> }; label?: { style?: SystemStyleObject; props?: HTMLAttributes<HTMLDivElement> }; content?: { style?: SystemStyleObject; props?: HTMLAttributes<HTMLDivElement> } }
Default undefined
Required No
children
children

Badge content rendered inside the content slot.

Type Snippet | slot
Default undefined
Required No
...rest
...rest

Standard HTML span attributes.

Type HTMLAttributes<HTMLSpanElement>
Default -
Required No