Badge
A compact marker for status, counts and categories.
Active
Installation
Terminal
pnpm dlx @dowel-ui/cli add badgenpm packages installed: class-variance-authority, radix-ui.
Accessibility
The badge label must convey the meaning on its own; variant colour is decoration. Use asChild to render an interactive badge as a link or button rather than adding handlers.
Props
Badge
| Prop | Type | Default |
|---|---|---|
size | "sm" | "md" | "md" |
variant | "default" | "secondary" | "outline" | "destructive" | "success" | "warning" | "info" | "default" |
asChild | boolean | false |
Plus every attribute of <span>.
Quality
7/7 checks, measured from the source and its tests
- Tested — passes
- axe assertion — passes
- Keyboard tested — does not apply
- Storybook examples — passes
- Accessibility documented — passes
- Semantic tokens only — passes
- Motion from tokens — does not apply
- className merged — passes
- Visible focus — does not apply
- No fixed widths — passes
Used in
Whole screens assembled from this component. Installing one brings this and everything else it needs with it.
- Admin — users
- Admin dashboard
- Billing
- Command center
- CRM
- Dashboard
- Features bento
- Hero product
- Onboarding
- Pricing
- Pricing Single Plan
- Pricing Three Tier
- Pricing Two Tier
- Stats trend cards
Source
This is exactly what dowel add badge writes into your project, with imports rewritten to your own path alias.
ui/badge.tsx
import { cva, type VariantProps } from "class-variance-authority";
import { Slot } from "radix-ui";
import type { ComponentPropsWithRef } from "react";
import { focusRing, iconSlot } from "@/lib/styles";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
cn(
"inline-flex w-fit shrink-0 items-center justify-center gap-1 rounded-full border font-medium whitespace-nowrap",
"[&_svg:not([class*='size-'])]:size-3",
focusRing,
iconSlot,
),
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground",
secondary: "border-transparent bg-secondary text-secondary-foreground",
outline: "border-border-strong bg-transparent text-foreground",
destructive: "border-transparent bg-destructive text-destructive-foreground",
success: "border-transparent bg-success text-success-foreground",
warning: "border-transparent bg-warning text-warning-foreground",
info: "border-transparent bg-info text-info-foreground",
},
size: {
sm: "h-5 px-1.5 text-2xs",
md: "h-6 px-2 text-xs",
},
},
defaultVariants: {
variant: "default",
size: "md",
},
},
);
export interface BadgeProps
extends ComponentPropsWithRef<"span">, VariantProps<typeof badgeVariants> {
asChild?: boolean;
}
/**
* Compact status or category marker.
*
* Colour alone never carries the meaning: the label text must say what the
* badge means, so the monochrome theme and colour-blind users lose nothing.
*/
export function Badge({ className, variant, size, asChild = false, ...props }: BadgeProps) {
const Comp = asChild ? Slot.Root : "span";
return <Comp className={cn(badgeVariants({ variant, size }), className)} {...props} />;
}
export { badgeVariants };