Badge
A small status indicator. One axis (variant: soft / solid / outline), none of which is colour. Tone arrives as a palette class — the same discipline as Clickable.
npx shadcn@latest add @fragiola/badgeShow code
"use client";
import { CheckIcon } from "lucide-react";
import type { ReactNode } from "react";
import { Badge } from "#/atoms/badge";
// The palette class goes on the badge itself, not on a wrapping context.
// The floor is palette-surface; each badge carries its own palette-* class
// and styles only itself. Six palettes, one subtree, side by side — the
// architecture's claim made visible.
//
// Axes shown, one per row, all aligned:
// variant soft / solid / outline
// with-icon soft + icon
const CHROMATIC = [
"blue",
"purple",
"green",
"orange",
"rose",
"danger",
] as const;
export default function BadgeDemo() {
return (
<div className="palette-surface flex flex-col gap-6 rounded-lg border border-palette-line bg-palette-base p-6">
{/* soft — one badge per chromatic palette */}
<Row label="soft">
{CHROMATIC.map((palette) => (
<Badge key={palette} className={`palette-${palette}`}>
{palette}
</Badge>
))}
</Row>
{/* solid — one badge per chromatic palette */}
<Row label="solid">
{CHROMATIC.map((palette) => (
<Badge
key={palette}
className={`palette-${palette}`}
variant="solid"
>
{palette}
</Badge>
))}
</Row>
{/* outline — one badge per chromatic palette */}
<Row label="outline">
{CHROMATIC.map((palette) => (
<Badge
key={palette}
className={`palette-${palette}`}
variant="outline"
>
{palette}
</Badge>
))}
</Row>
{/* with icon — soft variant, one badge per chromatic palette */}
<Row label="with icon">
{CHROMATIC.map((palette) => (
<Badge key={palette} className={`palette-${palette}`}>
<CheckIcon />
Verified
</Badge>
))}
</Row>
</div>
);
}
// Row — a labelled, aligned row of badges. The label is a <span>, not a
// heading, so it does not pollute the page's table of contents.
function Row({ label, children }: { label: string; children: ReactNode }) {
return (
<div className="flex flex-col gap-2">
<span className="text-xs font-mono text-palette-accent/85">
{label}
</span>
<div className="flex flex-wrap items-center gap-2">{children}</div>
</div>
);
}
No colour variant
A destructive badge is <Badge className="palette-danger"> — there is no variant="destructive" and no tone prop. The palette class overrides the six roles, so bg-palette-soft becomes the danger soft tint and text-palette-accent becomes the danger accent — soft and outline paint text with accent (content on soft or on a foreign background), solid paints with contrast (content on a base you painted). This is what stops destructive from coming back as a hundred copies.
Parts
| Part | Renders |
|---|---|
Badge | <span> with badge() variants |
Breadcrumb
A navigation trail. Uses a semantic `<nav>` with an ordered list. The last item is the current page (aria-current), not a link. Separators are chevrons on the inline axis, RTL-aware.
Table
Semantic HTML with palette roles. No primitive, no family. The grid is `border-palette-line`; the header and hover are `bg-palette-soft`; header text is `text-palette-contrast`; body text is `text-palette-accent/85`.