component

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 accent surface with high-contrast text; secondary (default) uses a feedback-tinted soft surface; tertiary uses a neutral surface with bold feedback-colored text; adaptive inherits ambient text color and tints from the surrounding context.

Primary Secondary Tertiary Adaptive
---
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>
  <Badge emphasis="tertiary">Tertiary</Badge>
  <Badge emphasis="adaptive">Adaptive</Badge>
</div>

Feedback Colors

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

Primary Success Warning Danger Neutral
Primary Success Warning Danger Neutral
Primary Success Warning Danger Neutral
---
import Badge from "../Badge.astro";
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 feedback="primary" emphasis="primary">Primary</Badge>
    <Badge feedback="success" emphasis="primary">Success</Badge>
    <Badge feedback="warning" emphasis="primary">Warning</Badge>
    <Badge feedback="danger" emphasis="primary">Danger</Badge>
    <Badge feedback="neutral" emphasis="primary">Neutral</Badge>
  </div>

  <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>

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

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

soft step.1 step.2 step.3 deep transparent
soft step.1 step.2 step.3 deep transparent
soft step.1 step.2 step.3 deep transparent
---
import Badge from "../Badge.astro";
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 feedback="primary" emphasis="primary" background="surface.soft">
      soft
    </Badge>
    <Badge feedback="primary" emphasis="primary" background="surface.step.1">
      step.1
    </Badge>
    <Badge feedback="primary" emphasis="primary" background="surface.step.2">
      step.2
    </Badge>
    <Badge feedback="primary" emphasis="primary" background="surface.step.3">
      step.3
    </Badge>
    <Badge feedback="primary" emphasis="primary" background="surface.deep">
      deep
    </Badge>
    <Badge feedback="primary" emphasis="primary" background="transparent">
      transparent
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge feedback="primary" emphasis="secondary" background="surface.soft">
      soft
    </Badge>
    <Badge feedback="primary" emphasis="secondary" background="surface.step.1">
      step.1
    </Badge>
    <Badge feedback="primary" emphasis="secondary" background="surface.step.2">
      step.2
    </Badge>
    <Badge feedback="primary" emphasis="secondary" background="surface.step.3">
      step.3
    </Badge>
    <Badge feedback="primary" emphasis="secondary" background="surface.deep">
      deep
    </Badge>
    <Badge feedback="primary" emphasis="secondary" background="transparent">
      transparent
    </Badge>
  </div>

  <div
    class={stack({
      gap: "md",
      direction: "row",
      align: "center",
      flexWrap: "wrap",
    })}
  >
    <Badge feedback="primary" emphasis="tertiary" background="surface.soft">
      soft
    </Badge>
    <Badge feedback="primary" emphasis="tertiary" background="surface.step.1">
      step.1
    </Badge>
    <Badge feedback="primary" emphasis="tertiary" background="surface.step.2">
      step.2
    </Badge>
    <Badge feedback="primary" emphasis="tertiary" background="surface.step.3">
      step.3
    </Badge>
    <Badge feedback="primary" emphasis="tertiary" background="surface.deep">
      deep
    </Badge>
    <Badge feedback="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. 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

props · 11 total
prop type default req description
feedback "primary""success""warning""danger""neutral" "primary" Semantic feedback color scheme. primary: primary color palette. success: green. warning: orange. danger: red. neutral: gray.
emphasis "primary""secondary""tertiary""adaptive" "secondary" Visual emphasis level. primary: solid accent surface with high-contrast text. secondary: feedback-tinted soft surface. tertiary: neutral surface with bold feedback text. adaptive: inherit ambient text…

emphasis

Visual emphasis level. primary: solid accent surface with high-contrast text. secondary: feedback-tinted soft surface. tertiary: neutral surface with bold feedback text. adaptive: inherit ambient text and tint.

Type "primary" | "secondary" | "tertiary" | "adaptive"
Default "secondary"
Required No
size "xs""sm""md""lg" "md" Size variant for the badge. xs: extra small. sm: small. md: default. lg: large.
background "surface.soft""surface.step.1""surface.step.2""surface.step.3""surface.deep""transparent" "surface.soft" (or "transparent" when emphasis="adaptive") Surface fill. Forwarded to panel and remapped per emphasis (accent ramp under primary, palette-tinted under secondary, neutral under tertiary). transparent for no fill.
border "none""default""bold""muted""accent" "accent" | "default" | "muted" Border ring strength. Forwarded to panel. Defaults to "accent" for primary, "default" for adaptive, "muted" otherwise.
translucent boolean false Applies a frosted-glass backdrop blur to the surface.
buttonFit boolean true 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.
label stringSnippetslot undefined Optional label displayed as a separate section within the badge. In Svelte, accepts a string or Snippet; in Astro, use slot="label".
passThrough { root?: { style?: SystemStyleObject; props?: HTMLAttributes<HTMLSpanElement> }; label?: { style?: SystemStyleObject; props?: HTMLAttributes<HTMLDivElement> }; content?: { style?: SystemStyleObject; props?: HTMLAttributes<HTMLDivElement> } } undefined Custom styling and props for badge slots. Each slot accepts style (SystemStyleObject) and props (HTML attributes).
children Snippetslot undefined Badge content rendered inside the content slot.
...rest HTMLAttributes<HTMLSpanElement> - Standard HTML span attributes.