Keyboard key
Kbd — a keyboard key, as it appears in prose and in menus.
Examples
Press Esc to close, or Ctrl + S to save.
<p>Press <kbd class="kbd">Esc</kbd> to close, or
<kbd class="kbd-chord">
<kbd class="kbd">Ctrl</kbd>
<span class="kbd-sep">+</span>
<kbd class="kbd">S</kbd>
</kbd> to save.</p>The + is a real element with real text, never ::before content. Measured: two adjacent <kbd>s with nothing between them give an accessible text of "CtrlS" — one contiguous run naming a key that does not exist.
<kbd class="kbd">Esc</kbd>
<kbd class="kbd">Tab</kbd>
<kbd class="kbd">A</kbd>
<kbd class="kbd">Enter</kbd>
<kbd class="kbd">Backspace</kbd>
<kbd class="kbd">⇧</kbd>
<kbd class="kbd">⌘</kbd>Every cap is the same height and no cap is narrower than --kbd-min-inline-size, so a single letter sits square beside a word.
The sizes are in em, so a key inside a heading grows with the heading and a key in a footnote shrinks with it — one declaration covers the whole site.
Boxed is the default, for prose: press Ctrl to continue.
<p>Boxed is the default, for prose:
press <kbd class="kbd">Ctrl</kbd> to continue.</p>
<div class="menu" role="presentation" style="max-inline-size: 18rem">
<button class="menu-item" type="button">
Save
<kbd class="kbd kbd-plain menu-shortcut">⌘S</kbd>
</button>
<button class="menu-item" type="button">
Find
<kbd class="kbd kbd-plain menu-shortcut">⌘F</kbd>
</button>
</div>
<div class="preview-inverse">
Press <kbd class="kbd kbd-inverse">Space</kbd> to play.
</div>.kbd-plain drops the cap and keeps the type, which is what a menu row wants: eight boxed keys down the inline end of a menu are louder than the menu.
.kbd-inverse is for a key drawn on an inverted band, where a raised surface reads as a hole punched in the panel rather than as something standing up from it.
Tokens 18
Level 2, declared on .kbd itself. Set any of them on that selector to restyle this component without touching the skin.
| Token | Default |
|---|---|
| --kbd-chord-gap | 0.3em |
| --kbd-face | linear-gradient( to bottom, var(--color-surface-raised), color-mix(in oklab, var(--color-surface-raised) 88%, var(--color-on-surface)) ) |
| --kbd-letter-spacing | 0.01em |
| --kbd-min-inline-size | 1.8em |
| --kbd-sep-color | var(--color-on-surface-muted) |
| --kbd-bg | var(--color-surface-raised) |
| --kbd-border-color | var(--color-border) |
| --kbd-color | var(--color-on-surface) |
| --kbd-shadow-color | var(--color-border-strong) |
| --kbd-padding-block | 0.3em |
| --kbd-padding-inline | 0.55em |
| --kbd-depth | 2px |
| --kbd-gap | 0.25em |
| --kbd-font-family | var(--font-secondary) |
| --kbd-font-size | 0.8125em |
| --kbd-font-weight | var(--font-weight-medium) |
| --kbd-highlight-color | color-mix(in oklab, var(--kbd-color) 22%, transparent) |
| --kbd-radius | var(--radius-sm) |
Variants and states
Variants
.kbd-inverse.kbd-plain
Inside it
.kbd-chord.kbd-sep
State it reads
None.
Before you ship it
What you have to do 2 requirements
- Separate the keys of a chord with a real element holding real text.
<kbd>Ctrl</kbd><kbd>K</kbd>with nothing between them is read as one contiguous run and names a key that does not exist. Never::before, neveraria-hiddenon.kbd-sep, never whitespace. - Do not let a platform symbol be a key's whole content.
⌘and its neighbours are announced inconsistently and often not at all. Either spell the key out, or keep the glyph for the eye witharia-hidden="true"and put the word beside it in an.sr-onlyspan.
src/css/components/kbd.css · npx mostlycss add kbd