Badge#

A small standing label: a count, a status, a tag. One part on a <span>, with no state and no behavior. It exists for the same reason Button does — the variant axes have to land on something a design system can select, and at badge scale the fill is the component.

Import#

TSX
import { Badge } from '@sigx/zero/badge';

Badge is a compound with a single member, Badge.Root; <Badge> is also callable directly. It is also re-exported from the @sigx/zero root, together with badgeAnatomy.

Usage#

TSX
import { component } from 'sigx';
import { Badge } from '@sigx/zero/badge';

const Status = component(({ signal }) => {
    const state = signal({ active: true });

    return () => (
        <Badge.Root color={state.active ? 'success' : 'neutral'} variant="soft">
            {state.active ? 'Active' : 'Paused'}
        </Badge.Root>
    );
});

There is no model: a badge shows what you put in it. The colour, size and variant come from the variant axes, and the text is the children.

A count#

TSX
<Badge.Root color="error" size="xs">{unread}</Badge.Root>
TSX
<Badge.Root color="primary" asChild>
    {(p) => <a href="/issues?label=bug" {...p}>bug</a>}
</Badge.Root>

<Badge.Root variant="outline" asChild>
    {(p) => <button type="button" onClick={() => remove(tag)} {...p}>{tag} ×</button>}
</Badge.Root>

A badge is so often already something else — an <a> to the filtered list, a <button> that removes the tag. With asChild the default slot receives the part's attribute bag; spread it so the anatomy lands on your element. A link inside a badge is not the same box as a badge that is a link, which is what asChild gives you.

A skin-specific axis#

TSX
<Badge.Root color="warning" axes={{ density: 'compact' }}>Beta</Badge.Root>

axes passes any extra axis a design system declares through as data-<axis>, and mods renders presence-only data-mod-<name> attributes. Neither shipped design system declares extra axes or modifiers on badge, so under their /register imports these are absent; a design system of your own can add them. See Variant axes.

Anatomy#

PartElementStatesFlagsNotes
rootspanCarries the variant axes and renders the text. asChild.

One element, and that is the whole point. There is no chrome to separate from content, so root both carries the axes and renders the text — the carrier is the text-bearing part. That makes badge the one content-tier scope whose colour contrast a one-element probe can measure directly, which is why it is the scope that wires its own variant vocabulary in @sigx/zero-basic (see below). See The anatomy contract.

Props#

Badge.Root#

PropTypeDefaultDescription
color / size / variant / axes / modsdesign-system vocabularyThe variant axes, rendered as data-* on root.
asChildbooleanfalseRender through the default slot, which receives the part bag.
classstringExtra classes.

An asChild badge that renders a <button> or an <a> gets that element's own interactivity; Badge adds no handlers, no disabled and no press feedback of its own.

In the shipped design systems#

colorsizevariantmods
@sigx/zero-basicthe eight recommended rolesxsxlsoft (default) · solid · outline
@sigx/zero-daisyuithe eight recommended rolesxsxl

@sigx/zero-basic narrows badge's variant to solid | soft | outline for the scope, with soft as the default — the design-system-wide set also has ghost, and a ghost badge is a word with no box. @sigx/zero-daisyui wires no variant on badge, so under its /register import the prop is absent; <Badge.Root color="success"> is styled in both. See Typed vocabulary.

The root's tokens are color, radius-field, size and text: a badge rounds like a field control rather than a box, and its text size comes off the --text-* ramp.

Button is the same one-part shape with press feedback; Kbd and Status are the other single-part content labels.