Components
Badge
A small pill label — shadcn's Badge with the base styling untouched, recolored to the Acrylic theme tokens (accent default, neutral-chip secondary).
The shadcn Badge, recolored to the Acrylic palette. The base styling (shape,
sizing, focus ring, the six variants) is copied verbatim from shadcn; only the
colors are swapped to the theme tokens, so default is the accent, secondary
the neutral chip, and everything flips light/dark with the theme.
Installation
npx shadcn add https://acrylic-ui.vercel.app/r/badge.jsonUsage
DefaultSecondaryDestructiveOutline
import { Badge } from "@/registry/acrylic/badge"export default function BadgeDemo() { return ( <div className="flex flex-wrap items-center gap-2 text-foreground"> <Badge>Default</Badge> <Badge variant="secondary">Secondary</Badge> <Badge variant="destructive">Destructive</Badge> <Badge variant="outline">Outline</Badge> </div> )}import { Badge } from "@/components/acrylic/badge"
<Badge>Default</Badge>
<Badge variant="secondary">Secondary</Badge>Variants
variant:
default— solid accent fill (--primary), white label.secondary— the neutral chip fill (--acr-chip), foreground label.destructive— the system red (--destructive), white label.outline— a hairline border (--acr-border), no fill.ghost— no fill or border; a hover tint only when used as a link.link— accent text with a hover underline.
Example — inside a selected Item
secondary and outline escalate automatically when nested inside a selected
Item row (compare each pair below — resting vs. selected, same Badge
markup, no extra className).
import { Bell } from "lucide-react"import { Badge } from "@/registry/acrylic/badge"import { Item, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemMeta, ItemRow, ItemTitle,} from "@/registry/acrylic/item"const ROWS = [ { variant: "secondary" as const, label: "Draft", selected: false }, { variant: "secondary" as const, label: "Draft", selected: true }, { variant: "outline" as const, label: "Draft", selected: false }, { variant: "outline" as const, label: "Draft", selected: true },]export default function BadgeSelectedItem() { return ( <ItemGroup className="w-full max-w-md"> {ROWS.map((row, i) => ( <Item key={i} selected={row.selected}> <ItemMedia variant="icon"> <Bell /> </ItemMedia> <ItemContent> <ItemRow className="items-center"> <ItemTitle className="min-w-0 flex-1"> {row.variant} · {row.selected ? "selected" : "resting"} </ItemTitle> <ItemMeta> <Badge variant={row.variant}>{row.label}</Badge> </ItemMeta> </ItemRow> <ItemDescription> The badge reads group-data-[selected=true]/item: on its own — no extra className on the Badge itself. </ItemDescription> </ItemContent> </Item> ))} </ItemGroup> )}Notes
- Render as a link with
asChild:<Badge asChild><a href="…">New</a></Badge>— the[a&]:hover:*styles activate for the anchor. - It's a pill (
rounded-full) at the kit's caption size; passclassNamefor layout only, not to override its colors. - Nested inside a selected
Itemrow,secondaryandoutlineescalate automatically:Item's root carriesgroup/item, and both variants readgroup-data-[selected=true]/item:to swap onto a legible on-accent treatment instead of their normal translucent-chip-over-panel fill. This is the same cascading mechanismItemMediaalready uses forgroup-data-[size=*]/item:— no prop, it just works when the Badge is a descendant of a selectedItem.defaultdoesn't need it (it's already an opaquebg-primary/text-primary-foregroundpairing); avoid pairing adefaultbadge with a selected row anyway, since two adjacent solid--primaryfills read as one shape.ghost/linkinherit color rather than set their own, so they already track a retargeted row.