Hud Chip
The interactive HUD label: nav items, row actions, a zoom cluster, retry, esc. HudLabel's instrument typography with a real button underneath.
ActionsServer safeclass-variance-authority@radix-ui/react-slot
Preview
viewport
Installation
The CLI writes the source into your project and pulls in whatever it depends on.
$
pnpm dlx shadcn@latest add @duck/hud-chipPulls in @duck/theme, @duck/hud-label. Already installed items are skipped.
Usage
import { HudChip } from "@/components/ui/hud-chip"The file lands in components/ui/hud-chip.tsx and belongs to you from that point on. Edit it in place rather than wrapping it.
Props
Everything not listed here is forwarded to the underlying element.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "outline" | "ghost" | "primary" | "outline" | outline is the default chrome read. ghost is for a row dense enough that borders would be noise. primary is the one chip in a group that commits something. |
| size | "sm" | "default" | "default" | 28px or 36px. sm drops the type to 10px and the icons to 12px with it. |
| active | boolean | false | Paints the current read and emits data-active. Visual only — pair it with aria-current, aria-pressed or aria-selected yourself. |
| asChild | boolean | false | Render the child element instead of a button: a next/link Link for a nav item. type and disabled are withheld, because neither applies to an anchor. |
| children | React.ReactNode | - | Two or three words, plus any lucide icon — icons size themselves. An icon-only chip needs an aria-label. |
Rules
What keeps this component from turning into noise.
- Typography comes from the .hud utility that @duck/hud-label ships, not from classes in this file. Install that item or the chip renders as plain sans — and nothing here redeclares .hud, so the chip and a HudLabel beside it cannot drift apart.
- active is a paint job. A nav chip wants aria-current="page", a filter wants aria-pressed, a chip in a tablist wants aria-selected and the roving keyboard behaviour that goes with it — the same highlight means all three, so the component does not guess. Without one of them the state is invisible to a screen reader.
- It is a real button: Enter and Space fire it, disabled takes it out of the tab order. Under asChild it cannot be disabled, because an anchor cannot — remove the href instead.
- Chrome, not a call to action. Machine output — zoom, retry, esc, a route name — is a chip; anything with a sentence for a label, or that is the point of the screen, is a QuackButton.
- For a set of chips where exactly one is chosen, StickerToggleGroup already has the radiogroup semantics and the arrow keys. Reach for a row of HudChips when the items navigate rather than select.
