Kbd
kbdDisplays keyboard keys or shortcuts with inline keycap styling.
Usage
Single button
Wraps a single key name and renders it as a bordered keycap style.
<Kbd>Esc</Kbd>Key combination
KbdGroup owns the gap and the separator, joining keys with + by default.
<KbdGroup keys={["⌘", "K"]} />Custom separator, or none
separator accepts any node; pass null to keep the spacing without a symbol.
<KbdGroup keys={["⌘", "⇧", "P"]} separator="·" />
<KbdGroup keys={["G", "T"]} separator={null} />Accessible name
label gives the whole combination one accessible name, and the separator itself stays out of the accessibility tree.
<KbdGroup keys={["⌘", "K"]} label="Open the command panel" />Lay out the keycaps yourself
Switch to children when one key needs its own styling or content; the separators are still inserted.
<KbdGroup label="Save">
<Kbd className="min-w-8">⌘</Kbd>
<Kbd>S</Kbd>
</KbdGroup>Embed text
The shortcut keys are shown in the text, and the keycaps are aligned with the text baseline.
<span className="text-sm text-muted-foreground">
Press <KbdGroup keys={["⌘", "S"]} label="Save" /> to save
</span>When to use
Use Kbd to label an individual key such as Esc, ⌘, or K. Wrap key combinations such as ⌘ + K in KbdGroup from the same directory: it owns the gap, draws the separator, and gives the whole combination one accessible name. Both work in server components. Use CodeBlock or Snippet for code instead.
Import
import { Kbd, KbdGroup } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| ...HTMLAttributes | HTMLAttributes<HTMLElement> | - | supports <kbd> native attributes (className, style, etc.) |
KbdGroup Props
| Name | Type | Default | Description |
|---|---|---|---|
| keys | ReactNode[] | - | Key names, each wrapped in a Kbd. The usual way to write a combination |
| separator | ReactNode | "+" | Separator between keys. Decorative only (aria-hidden, never announced). Pass null to keep the spacing without drawing a symbol |
| label | string | - | Accessible name for the whole combination, such as "Open command palette". role="group" is added only when it is present |
| ...HTMLAttributes | HTMLAttributes<HTMLSpanElement> | - | supports native attributes of the wrapping <span>. The rest spread comes first, so the role and aria-label the component computes win (see Usage guidelines) |
Slots
| Slot | Type | Description |
|---|---|---|
| children | ReactNode | Key content for Kbd. On KbdGroup it means you lay out the keycaps yourself, and keys is ignored |
Usage guidelines
- One Kbd renders one keycap and never adds a
+. Reach forKbdGroupinstead of hand-rollinginline-flex + gap + separatorat every call site, otherwise the spacing and separator styling drift apart across the app. - Without
label, KbdGroup adds norole="group"and a screen reader announces the keycaps as unrelated fragments. Nothing about the rendered output looks different, which is exactly why this step gets skipped: whenever the combination stands for a concrete action, put that action name inlabel. - When both
childrenandkeysare present onlychildrenis rendered;keysis ignored, because honouring both has no sensible meaning. - A `role` you pass cannot override the component's own `role="group"`. The rest spread comes first on the root node, see consuming.md §7. Without
labelthe component claims no role, so whatever you pass is what you get. - No symbol mapping is built in (
Meta → ⌘,Ctrl → ⌃, and so on). How a key should read depends on the platform detection and wording rules of the consuming app, so a built-in table would guess wrong in any cross-platform product.