Skip to content

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.

kbd/basic.tsx
⌘KTabEscF2

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".

kbd/group.tsx
PCtrl+Alt+DelG then D

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.

kbd/in-text.tsx

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.

kbd/composed.tsx

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.

Theming tokens for Kbd
PropertyControlsDefault
--forte-kbd-bgCap fill. Translucent currentColor so it works on any surface.color-mix(in oklab, currentColor 7%, transparent)
--forte-kbd-border-colorCap outline — the edge that survives forced colors.color-mix(in oklab, currentColor 25%, transparent)
--forte-kbd-radiusCorner 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-inlineLeft and right padding inside the cap.var(--forte-space-1)
--forte-kbd-font-sizeKey label size, relative to the surrounding text.0.85em
--forte-kbd-min-inline-sizeSmallest cap width — what squares up a lone letter instead of leaving a sliver the exact width of an I.1.6em
--forte-kbd-group-gapGap 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

Props for Kbd
PropTypeDefaultDescription
childrenReactNodeThe 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.
classNamestringAdditional class name(s). Applied after the internal styles so consumer utilities (e.g. Tailwind) win without needing `!important`.
renderRenderProp<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

Props for KbdGroup
PropTypeDefaultDescription
childrenReactNodeThe caps of the sequence — `Kbd` children, with any separator text (`then`, `+`) written between them as plain text.
classNamestringAdditional class name(s). Applied after the internal styles so consumer utilities (e.g. Tailwind) win without needing `!important`.
renderRenderProp<Record<string, unknown>>Replaces the rendered `<kbd>` wrapper with another element or component.