component
A versatile badge component for displaying status, categories, or small pieces of information. Supports multiple feedback 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.
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.
---
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>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: "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>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>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).
---
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.peak">
soft
</Badge>
<Badge feedback="primary" emphasis="primary" background="surface.hill">
step.1
</Badge>
<Badge feedback="primary" emphasis="primary" background="surface.base">
step.2
</Badge>
<Badge feedback="primary" emphasis="primary" background="surface.valley">
step.3
</Badge>
<Badge feedback="primary" emphasis="primary" background="surface.ground">
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.peak">
soft
</Badge>
<Badge feedback="primary" emphasis="secondary" background="surface.hill">
step.1
</Badge>
<Badge feedback="primary" emphasis="secondary" background="surface.base">
step.2
</Badge>
<Badge feedback="primary" emphasis="secondary" background="surface.valley">
step.3
</Badge>
<Badge feedback="primary" emphasis="secondary" background="surface.ground">
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.peak">
soft
</Badge>
<Badge feedback="primary" emphasis="tertiary" background="surface.hill">
step.1
</Badge>
<Badge feedback="primary" emphasis="tertiary" background="surface.base">
step.2
</Badge>
<Badge feedback="primary" emphasis="tertiary" background="surface.valley">
step.3
</Badge>
<Badge feedback="primary" emphasis="tertiary" background="surface.ground">
deep
</Badge>
<Badge feedback="primary" emphasis="tertiary" background="transparent">
transparent
</Badge>
</div>
</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.
---
import Badge from "../Badge.astro";
import { CircleCheck, CircleX, Info, Lock, TriangleAlert } from "@lucide/astro";
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">
<CircleCheck class={css({ w: "1em", h: "1em" })} />
Deployed
</Badge>
<Badge feedback="warning">
<TriangleAlert class={css({ w: "1em", h: "1em" })} />
Pending
</Badge>
<Badge feedback="danger">
<CircleX class={css({ w: "1em", h: "1em" })} />
Failed
</Badge>
<Badge feedback="primary">
<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">
<CircleCheck class={css({ w: "1em", h: "1em" })} />
Active
</Badge>
<Badge feedback="warning" emphasis="secondary">
<TriangleAlert class={css({ w: "1em", h: "1em" })} />
Review
</Badge>
<Badge feedback="danger" emphasis="secondary">
<CircleX class={css({ w: "1em", h: "1em" })} />
Blocked
</Badge>
<Badge feedback="neutral" emphasis="secondary">
<Lock class={css({ w: "1em", h: "1em" })} />
Private
</Badge>
</div>
</div>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 } }).
---
import Badge from "../Badge.astro";
import Stamp from "@pindoba/astro-stamp";
import {
ArrowRight,
CircleCheck,
Sparkles,
TriangleAlert,
} from "@lucide/astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div class={stack({ gap: "xl", direction: "column" })}>
<!-- Leading stamps: the same Affix + Stamp composition buttons use -->
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Badge feedback="success">
<Stamp emphasis="ghost" slot="leading"><CircleCheck /></Stamp>
Deployed
</Badge>
<Badge feedback="warning" emphasis="secondary">
<Stamp emphasis="ghost" slot="leading"><TriangleAlert /></Stamp>
Review
</Badge>
<Badge feedback="primary" emphasis="primary">
<Stamp emphasis="ghost" slot="leading"><Sparkles /></Stamp>
New
</Badge>
</div>
<!-- Trailing stamps + accented fills, across sizes -->
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<Badge size="xs" feedback="neutral">
<Stamp emphasis="ghost" slot="trailing"><ArrowRight /></Stamp>
Details
</Badge>
<Badge size="sm" feedback="neutral">
<Stamp emphasis="ghost" slot="trailing"><ArrowRight /></Stamp>
Details
</Badge>
<Badge size="md" feedback="primary" emphasis="secondary">
<Stamp emphasis="secondary" slot="leading"><Sparkles /></Stamp>
Accented stamp
</Badge>
</div>
</div>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.
---
import Badge from "../Badge.astro";
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" feedback="neutral" emphasis="secondary">
sm badge
<Button shape="circle" emphasis="ghost"><X /></Button>
</Badge>
<Badge size="md" feedback="neutral" emphasis="secondary">
md badge
<Button shape="circle" emphasis="ghost"><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"><X /></Button>
</Badge>
<Badge feedback="success">
Deployed
<Button shape="circle" emphasis="ghost"><X /></Button>
</Badge>
<Badge feedback="warning">
Pending
<Button shape="circle" emphasis="ghost"><X /></Button>
</Badge>
<Badge feedback="danger">
Failed
<Button shape="circle" emphasis="ghost"><X /></Button>
</Badge>
<Badge feedback="neutral">
Archived
<Button shape="circle" emphasis="ghost"><X /></Button>
</Badge>
</div>
</div>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 feedback) — 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.
Use passThrough to apply custom Panda CSS styles or HTML attributes to any slot (root, content).
---
import Badge from "../Badge.astro";
import { stack } from "@pindoba/styled-system/patterns";
---
<div
class={stack({
gap: "md",
direction: "row",
align: "center",
flexWrap: "wrap",
})}
>
<!-- PassThrough custom root styles -->
<Badge
feedback="danger"
passThrough={{ root: { style: { letterSpacing: "wider" } } }}
>
Custom
</Badge>
<!-- PassThrough content styles -->
<Badge
feedback="primary"
passThrough={{
content: { style: { fontWeight: "bold", textTransform: "uppercase" } },
}}
>
Bold
</Badge>
<!-- PassThrough with ARIA props -->
<Badge
feedback="success"
passThrough={{
root: { props: { role: "status", "aria-label": "Success status" } },
}}
>
Accessible
</Badge>
</div>falseMark this as the chosen one in a set (a current nav item, a checked option, a selected card). Where `interactive` says it *can* be clicked, `active` says it *is* the current choice. Use `activeEmphasis` to control how loud the state is. Combined with `interactive`, hover and press restart their stepping from the active baseline rather than falling back to the unchosen ramp. The three values differ only in what drives the state: `true` by the prop (via `data-panel-active`), `"checked"` by a wrapped native input (`:has(input:checked)` — zero JS, nothing to re-render), and `"current"` by an existing `aria-current`.
the panel's `emphasis`How loud the `active` state is. Defaults to the panel's own `emphasis`, so the chosen state lands on the same ramp as the resting look — this prop is the override for when the active item should be louder (or quieter) than the panel itself. `"primary"` fills with the feedback accent ramp and flips text to the contrast scale (~150 RGB units of separation — the option that reads at a glance in a dense list). `"secondary"` tints the feedback surface ramp and `"tertiary"` the neutral ramp; both promote text to `bold` and lift the border to the default line, but move the surface only ~7–18 units.
Surface background from the Panel scale (`surface.peak` → `surface.ground`, or `transparent`).
Border style: `none`, `default`, `bold`, or `muted`.
No description yet.
Border style applied on hover / focus interaction.
trueWhen true (default), buttons placed inside the badge content are automatically resized to fit the badge's dimensions. Set to false to let buttons render at their own default size.
No description yet.
No description yet.
"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 and tint.
Semantic color tone: `neutral`, `primary`, `success`, `warning`, `danger`, or `inherit`.
Enable interactive (hover / focus / press) affordances and states.
No description yet.
Inner padding from the spacing scale.
Per-slot style and attribute override bag. Each slot accepts a Panda `style` object and a `props` bag of HTML attributes merged onto that slot.
Corner radius override from the spacing scale (or `full` for fully rounded).
Corner radius for the bottom-left and bottom-right corners.
Corner radius for the top-left and bottom-left corners.
Corner radius for the top-right and bottom-right corners.
Corner radius for the top-left and top-right corners.
Keep (or drop) the always-1px transparent border the panel reserves so a border appearing or changing never shifts layout. Computed automatically — it's reserved when a resting `border` is visible, or when `interactive` / `borderInteract` / `active` can change the border at runtime. Set `false` only on a panel that must not occupy that 1px (e.g. a borderless housing frame that would otherwise add 2px around a set).
Elevation shadow applied to the surface.
"md"Size variant for the badge. xs: extra small. sm: small. md: default. lg: large.
trueWhen true (default), SVGs placed inside the badge content are automatically resized to fit the badge. Set to false to let SVGs render at their own size.
No description yet.
Apply a frosted-glass effect with backdrop blur over a surface background.
Plus all standard <span> HTML
attributes.