Badge
A small pill with a word in it — and no script behind it.
src/components/rahti_ui/badge.rs
The badge
Four of the five variants, which is what shadcn's first demo shows.
shadcn's own demo. A badge is a `<span>` with a `data-variant`, and that is the whole of it — no script, no state, nothing to mount. It is the second component here with nothing to improve after the runtime arrives, the first being the Alert.
The destructive pill is a tint with red text rather than a solid red one, which is the same choice the Alert's destructive variant makes.
Variants
Five, where the published registry lists six.
All five. Hover the ghost one: it fills in, and none of the others do — their hover styles are written `[a]:hover:`, which only applies to a badge that is a link. A ghost pill has nothing else to show it is there, so shadcn gives it an unconditional hover.
`outline` is the only variant that wants a real border and `destructive` is the only one that wants a red focus ring, so those two utilities live in the variants here rather than in the base string. shadcn puts them in the base and lets tailwind-merge take them out again; there is no merge on the server, so the layers are arranged not to argue. The rendered list is the same.
With an icon
And the padding trim that follows it, which is a selector rather than a prop.
The attribute goes on the **icon**, not on the badge: `data-icon="inline-start"` or `"inline-end"`. The badge finds it with `has-data-[icon=inline-start]:pl-1.5` and trims two pixels off that side, because a glyph needs less room beside it than a word does. `badge_icon(…)` is the sugar for writing it.
Compare the three pills in the second row: the one with no icon keeps `px-2` on both sides, and each of the others gives up a little on exactly the side its icon is on. No prop says so — the selector notices.
The icon needs no size either. `[&>svg]:size-3!` is important on purpose: an icon component that ships its own size class would otherwise win, and a badge is too small for a 16px glyph.
With a spinner
The same attribute, on something that moves.
shadcn's spinner demo. A spinner is just an icon that turns, so it takes the same `data-icon` attribute and gets the same padding trim — there is nothing badge-specific about it.
As a link
An `href` makes it an anchor, which is what the hover styles are written for.
Give a badge an `href` and it renders as `<a data-slot="badge">`. shadcn's newest spelling for this is Base UI's `render={<a href="…">…</a>}`, which replaced Radix's `asChild`; neither crosses into Rust, and this library's answer to both has always been to render the element itself.
Hover one: it darkens, and the plain badges above do not. Their hover styles are written `[a]:hover:`, and the missing `&` looks like it should matter — an arbitrary variant with no `&` is usually read as a descendant. Measured in this project's compiled stylesheet, it is not: Tailwind emits `.[a]:hover:bg-primary/80:is(a):hover`, so the new spelling means what the registry's older `[a&]:hover:` meant — this badge, when it is a link.
Colours
Five tones, no new variants.
shadcn's colours demo, and not one of these is a variant — every pill is the default with a `class` over it. That is the one thing that costs a badge anything: an override can conflict with what the component chose, so the class list becomes `{twMerge(…)}` and is resolved in the browser. With no override it is a finished string from the server.
The port
Where these strings came from, and the eight places the registry disagrees.
Two components in a row where the published registry no longer matches the published page. The rule the rest of this library follows — take the strings from `new-york-v4` — holds for components the registry still describes; where it does not, the page wins and the module docs say so.