Kbd
The way the interface prints a key it wants pressed. It renders a real
<kbd> — the element HTML has for exactly this — and draws its fill and edge
out of currentColor, so the same cap sits in running text, on a tooltip's
inverted popup, inside a solid button and on a highlighted menu row without a
colour prop.
Import
import { Kbd, KbdGroup } from "@forte-ui/react";Examples
Groups and sequences
A chord can be one cap (⌘K) or one cap per key. For the second form, wrap
the caps in KbdGroup — it owns the spacing, and plain text written between
the caps (+, then) rides along at the group's gap. The wrapper is itself a
<kbd>, which is the HTML idiom for "these keys form one input".
Glyph chords read best bare; spelled-out keys read best with the separator written in. Nothing here maps keys per platform — write them the way your app prints them.
In running text
The cap's geometry is in em, not tokens, so it tracks whatever text it sits
in — help text, a table cell, a footnote — the way an inline code chip does.
Press ⌘K to open the palette, ↑ and ↓ to move through it, and Esc to put it away.
Inside other components
Because the chrome derives from currentColor, composing a cap into a
component costs nothing: on a solid button it draws itself out of the
button's own label colour.
Two components already do this for you.
Menu.Shortcut positions a cap at the end of a row and
hides it from assistive technology; Tooltip.Shortcut
is a Kbd, re-tuned through the tooltip's own knobs. Reach for those inside
their components, and for Kbd everywhere else.
Accessibility
A key sequence is literal — ⌘X is read left to right in every locale — so
the cap pins its own text direction: ltr with unicode-bidi: isolate, and
KbdGroup pins its layout the same way. In an RTL page the surrounding
sentence mirrors and the keys do not; flip the demo frame to RTL to see the
seam hold.
Under forced colors the translucent fill is replaced, and the cap survives on
its border, which is forced to full contrast. The cap also pins its
text to CanvasText there, so an inherited HighlightText cannot land on the
cap's forced Canvas fill and vanish.
Theming
Every visual decision is a --forte-kbd-* custom property declared on the
cap's own root, so a re-skin is a style object or one CSS rule. Scope a rule
with [data-forte="kbd"] to catch every cap — including the ones
Menu.Shortcut and Tooltip.Shortcut render — or with
[data-forte="menu-popup"] [data-forte="kbd"] to catch one component's.
| Property | Controls | Default |
|---|---|---|
--forte-kbd-bg | Cap fill. Translucent currentColor so it works on any surface. | color-mix(in oklab, currentColor 7%, transparent) |
--forte-kbd-border-color | Cap outline — the edge that survives forced colors. | color-mix(in oklab, currentColor 25%, transparent) |
--forte-kbd-radius | Corner radius. A step softer than the radius-1 hairline: at cap size, 2px corners read as a sharp box rather than a chip. | var(--forte-radius-2) |
--forte-kbd-padding-inline | Left and right padding inside the cap. | var(--forte-space-1) |
--forte-kbd-font-size | Key label size, relative to the surrounding text. | 0.85em |
--forte-kbd-min-inline-size | Smallest cap width — what squares up a lone letter instead of leaving a sliver the exact width of an I. | 1.6em |
--forte-kbd-group-gap | Gap between the caps of a sequence. | var(--forte-space-1) |
The two colour knobs default to color-mix() expressions over currentColor
rather than to tokens, and that is the load-bearing decision: a cap has no
surface of its own to be measured against, so a looked-up grey would be a
contrast guess everywhere but the page — deriving from the text it sits with
keeps the glyphs at whatever contrast that text already has, on any surface
and in any re-skin.
API reference
Kbd
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | The key, written the way it is printed — `⌘`, `Ctrl`, `Esc`, `F2` — or a whole chord as one cap, `⌘K`. For a chord drawn as separate caps, put several `Kbd`s in a `KbdGroup` instead. | |
className | string | Additional class name(s). Applied after the internal styles so consumer utilities (e.g. Tailwind) win without needing `!important`. | |
render | RenderProp<Record<string, unknown>> | Replaces the rendered `<kbd>` with another element or component. The default tag is already the semantically right one, so reach for this only when a host component needs the cap to be one of its own parts. |
KbdGroup
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | The caps of the sequence — `Kbd` children, with any separator text (`then`, `+`) written between them as plain text. | |
className | string | Additional class name(s). Applied after the internal styles so consumer utilities (e.g. Tailwind) win without needing `!important`. | |
render | RenderProp<Record<string, unknown>> | Replaces the rendered `<kbd>` wrapper with another element or component. |