Data Display
Kbd
A key cap — the <kbd> a shortcut is drawn with.
import { Kbd } from 'omaris' Examples
Basic
keys="mod+k" draws the whole combo — ⌘ K on a Mac, Ctrl + K elsewhere — and each symbol is read by its name. Children draw a single cap.
Search anywhere with Control+ K.
Send with Control+ Enter, new line with Shift+ Enter.
Close with Esc.
<script lang="ts"> import { Kbd, Text } from 'omaris';</script><div class="flex flex-col items-start gap-3"> <Text variant="body-md">Search anywhere with <Kbd keys="mod+k" />.</Text> <Text variant="body-md"> Send with <Kbd keys="mod+enter" size="sm" />, new line with <Kbd keys="shift+enter" size="sm" />. </Text> <Text variant="body-md">Close with <Kbd>Esc</Kbd>.</Text></div> Shortcut sheet
A shortcut sheet. platform pins a keyboard for a comparison like this one; left on auto, the reader's own keyboard is the one drawn.
- Open the command palette
- Command K
- New issue
- C
- Toggle the sidebar
- Command B
- Go to settings
- Command ,
- Every shortcut
- Shift /
<script lang="ts"> import { Kbd, SegmentedButton, Text } from 'omaris'; const SHORTCUTS = [ { action: 'Open the command palette', keys: 'mod+k' }, { action: 'New issue', keys: 'c' }, { action: 'Toggle the sidebar', keys: 'mod+b' }, { action: 'Go to settings', keys: 'mod+,' }, { action: 'Every shortcut', keys: 'shift+/' } ]; let platform = $state('apple');</script><div class="flex w-full max-w-md flex-col gap-3"> <SegmentedButton bind:value={platform} label="Keyboard" items={[ { value: 'apple', label: 'Mac' }, { value: 'other', label: 'Windows' } ]} /> <dl class="divide-y divide-border rounded-shape-md border border-border"> {#each SHORTCUTS as shortcut (shortcut.keys)} <div class="flex items-center justify-between gap-4 px-4 py-2.5"> <Text as="dt" variant="body-md">{shortcut.action}</Text> <dd> <Kbd keys={shortcut.keys} platform={platform === 'apple' ? 'apple' : 'other'} size="sm" /> </dd> </div> {/each} </dl></div> Overridden
classes reaches the caps, the row and the separator; separator replaces the +; and a title or aria-* the caller sets lands on the root.
<script lang="ts"> import { Kbd, formatShortcut } from 'omaris';</script><div class="flex flex-wrap items-center gap-4"> <Kbd keys="mod+shift+p" platform="other" separator="·" classes={{ separator: 'px-0.5 text-primary' }} /> <Kbd keys="mod+k" platform="apple" title={formatShortcut('mod+k', { platform: 'apple' })} classes={{ root: 'gap-0', key: 'rounded-none border-primary bg-primary-container text-primary-container-foreground first:rounded-s-shape-sm last:rounded-e-shape-sm' }} /> <Kbd variant="flat" class="rounded-full px-3">Space</Kbd></div> When to use it
Use it for
- A shortcut beside the thing it triggers — a menu row, a Command Palette result, a button's tooltip:
<Kbd keys="mod+k" size="sm" />. - A shortcut sheet or a help page, one row per action, with
keysso every reader sees their own keyboard. - A key named in running text — "press <kbd>Esc</kbd> to close" — as a single cap with children.
- The string form,
formatShortcut('mod+k'), where a cap cannot go: atitle, a tooltip, anaria-keyshortcutsattribute ({ aria: true }).
Not for
- A status or a count → Badge. A cap reads as a key, and a key reads as something to press.
- A code identifier or a value to copy → Code Block, or
<Text font="mono">inline. - A button that performs the shortcut → Button with the
Kbdinside it; a cap is not pressable.
Do
- Write
modrather thancmdorctrlfor the primary modifier. It is ⌘ on Apple devices and Ctrl everywhere else, and it is whatmatchShortcutbinds. - Use
size="sm"inside a menu row, a list item or a chip, andvariant="flat"where the row already has its own surface. - Match the listener to the label: bind with
matchShortcut(event, 'mod+k')so the key that is drawn is the key that works, on every keyboard layout. - Pin
platformonly when comparing keyboards side by side; left onauto, the reader's own keyboard is the one drawn.
Don't
- Hand-write
⌘Kin a label. A Windows reader sees a key they do not have, and a screen reader skips the symbol entirely. - Put a shortcut on a phone-only screen. There is no keyboard; the cap is noise.
- Style the caps with
classwhen you meanclasses.key— on a combo,classis the row the caps sit in.
Quick reference
variant raised(default)flat
size smmd(default)lg
API
Kbd
A key cap — the <kbd> a shortcut is drawn with.
Give it keys and it draws the whole combo, one cap per key, in the platform's own spelling: keys="mod+k" is ⌘ K on a Mac and Ctrl + K everywhere else, with the modifiers in the order that keyboard writes them. Each symbol carries its name for a screen reader, so ⌘ is read as "Command" rather than skipped.
Give it children instead and it is one cap around whatever you wrote.
The server does not know the keyboard, so it renders Ctrl; the cap swaps to ⌘ on hydration. Pass platform to pin it.
import { Kbd } from 'omaris' <Kbd keys="mod+k" /><Kbd keys={['shift', 'enter']} size="sm" /><Kbd>Esc</Kbd> Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- The combo. Not a cap itself — the row the caps sit in.
key- One key cap.
separator- The
+between caps on a non-Apple keyboard.
Props
keys string | readonly string[] The combo to draw: 'mod+k', 'shift+enter', or the same as an array. mod is ⌘ on Apple devices and Ctrl elsewhere. Without it, the children are drawn as one cap.
variant Defaults to 'raised'
KbdVariant raised- A cap with depth: a heavier bottom edge, like the real key.
flat- Flat, for inside a menu row or a chip where depth is noise.
size Defaults to 'md'
KbdSize smmdlg
platform Defaults to 'auto'
ShortcutPlatform Which keyboard to draw for. auto asks the browser once it has hydrated, and draws Ctrl until then.
separator string | false Drawn between caps. Defaults to nothing on a Mac, where combos are written ⌘K, and + elsewhere, where they are written Ctrl+K. false for none anywhere.
class string classes KbdClasses Per-part Tailwind overrides. class still covers the root.
children Snippet One cap's content, when keys is not set.