Fragiola UI
Display

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/badge
soft
bluepurplegreenorangerosedanger
solid
bluepurplegreenorangerosedanger
outline
bluepurplegreenorangerosedanger
with icon
VerifiedVerifiedVerifiedVerifiedVerifiedVerified
Show 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

PartRenders
Badge<span> with badge() variants

On this page