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.
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.
---
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>Badges support semantic feedback colors for communicating status at a glance.
---
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>Four size variants are available to fit different layout contexts.
---
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>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.
---
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>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.
---
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>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.
---
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>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.
---
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>| Prop | |
|---|---|
feedback | feedback Semantic feedback color scheme. primary: primary color palette. success: green. warning: orange. danger: red. neutral: gray. Type Default Required |
emphasis | emphasis Visual emphasis level. primary: solid background with high-contrast text. secondary: outlined style with subtle background and border. Type Default Required |
size | size Size variant for the badge. xs: extra small. sm: small. md: default. lg: large. Type Default Required |
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 Default Required |
border | border Border ring strength. Only valid when feedback="neutral". Defaults to "muted" for secondary emphasis, "none" for primary. Type Default Required |
translucent | translucent Applies a frosted-glass backdrop blur to the surface. Only valid when feedback="neutral". Type Default Required |
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 Default Required |
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 Default Required |
passThrough | passThrough Custom styling and props for badge slots. Each slot accepts style (SystemStyleObject) and props (HTML attributes). Type Default Required |
children | children Badge content rendered inside the content slot. Type Default Required |
...rest | ...rest Standard HTML span attributes. Type Default Required |