GrKbd
Displays a keyboard key or shortcut in a `<kbd>` element.
Overview
When to take it
- a key combination is shown — in a hint, in the help, in a menu item;
- the combination depends on the platform —
platformprints⌘on macOS andCtrlon the rest; - there are several keys —
keysassembles the combination with a separator, with no markup by hand; - native semantics is needed — it renders as a
<kbd>tag rather than as a styled<span>.
When to take something else
| Need | Take |
|---|---|
| A status or a tag is shown | GrBadge |
| A hint about the purpose is needed | GrTooltip |
| A fragment of code is shown | the markup of the application: there are no code blocks in the package |
| The combination opens the command palette | GrCommandPalette |
Variants every tone and view, rendered by the real component
variant
splitmergedsequence
Sizes
xssmmdlg
Playground controls come from the API, not from hand
Loading…
0 changed· state is in the URL
<GrKbd :keys="keys" />Examples 3 each with a live demo and its code
A keyboard shortcuts sheet
Each shortcut is written once, the way it is pronounced —
mod+K— and renders as ⌘K on a Mac and Ctrl+K elsewhere.mergedwrites a combination the way systems do,sequencesays «G then I» for a chord, andsplitdraws the arrows as separate keys pressed one after another.AnywhereSearch or run a commandCtrlKCreate an issueCShow this sheetShift/Go toInboxGIMy issuesGMSettingsGSIn a listPrevious / next issueArrow UpArrow DownOpen the issueEnterAssign to meCtrlShiftMA support article for two systems
An article about a specific system names the platform on purpose:
platform="apple"keeps ⌥⌘J in the macOS column even on a Windows laptop, andplatform="other"keeps Ctrl+Shift+J in the other. Everywhere else the automatic choice is the honest one.Send us the browser consoleSupport article · 2 minutes macOS1. Open the console: OptionCommandJ2. Select all messages: CommandA3. Copy them for the ticket: CommandCWindows and Linux1. Open the console: CtrlShiftJ2. Select all messages: CtrlA3. Copy them for the ticket: CtrlC
API generated from the package · cannot drift
Props
| Prop | Type | default | Description |
|---|---|---|---|
keys | string | string[] | undefined | undefined | The combination: as a string (`"mod+shift+K"`) or as a set of tokens (`['mod', 'K']`). The `mod` token is Cmd on macOS, Ctrl on the rest. A space in the string splits it into steps: `"g i"` is drawn as "G then I", and `"mod+k p"` as "⌘K then P". The array form stays a flat chord: the array does not tell a step from a key. |
variant | "split" | "merged" | "sequence" | undefined | "merged" | `merged` (the default) — the combination as a single plate, the way the systems themselves write it: `⌘K` on macOS, `Ctrl+K` on the rest. `split` — a plate per key. `sequence` — the chord "G then I": the keys are pressed one after another. |
size | "xs" | "sm" | "md" | "lg" | undefined | undefined | The height of the plate and the type size — to match the neighbouring text or button. Unset — from `GrConfigProvider`, otherwise `md`. |
separator | string | undefined | undefined | The separator between the keys. Unset — auto: in a merged plate the symbols are glued (`⌘K`) and the words are separated with a plus (`Ctrl+K`); in `split` it is a plus, in `sequence` a word from the locale. An empty string leaves only a gap. |
hotkey | string | undefined | undefined | The identifier of an entry in the registry (`granularityHotkeysPlugin`). The combination is taken from there, so the hint cannot diverge from the binding. Stronger than `keys`: if that one is set too, the registry wins — otherwise two sources would argue silently. |
platform | GrKbdPlatform | undefined | "auto" | How to draw `mod` and the modifiers: `apple` — `⌘`, `other` — `Ctrl`. `auto` by default: the platform is detected after mounting, and the server renders the non-Apple look. |
Slots
| Slot | Type | Description |
|---|---|---|
default | any | A key or a combination instead of the `keys` prop. |
Types
Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.
import type { GrKbdProps, GrKbdSize, GrKbdPlatform } from '@feugene/granularity/components/GrKbd'Filedist/types/components/GrKbd/GrKbd.vue.d.ts
interface GrKbdProps {
size?: GrKbdSize;
variant?: GrKbdVariant;
keys?: string | string[];
hotkey?: string;
separator?: string;
platform?: GrKbdPlatform;
}type GrKbdSize = GrComponentSize;type GrKbdPlatform = 'auto' | 'apple' | 'other';Filedist/types/components/GrKbd/config.d.ts
const grKbdConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrKbd">;Filedist/types/components/GrKbd/defaults.d.ts
interface GrKbdConfigurableProps {
size: GrKbdSize;
}Filedist/types/components/GrKbd/safelist.d.ts
const grKbdSafelist: string[];Filedist/types/components/shared/hotkeyTokens.d.ts
type GrKbdKeyName = 'command' | 'option' | 'shift' | 'control' | 'enter' | 'escape' | 'tab' | 'backspace' | 'delete' | 'pageUp' | 'pageDown' | 'arrowUp' | 'arrowDown' | 'arrowLeft' | 'arrowRight';type GrKbdTokenGroup = 'modifier' | 'editing' | 'navigation';interface GrKbdTokenSpec {
token: string;
aliases: readonly string[];
group: GrKbdTokenGroup;
apple: HotkeyKeyView;
other: HotkeyKeyView;
}const GR_KBD_TOKENS: readonly GrKbdTokenSpec[];function findKbdToken(token: string): GrKbdTokenSpec | undefined;Usage from the library prose
A single key and a combination
<GrKbd>
Esc
</GrKbd>
<GrKbd keys="mod+K" />
<GrKbd :keys="['mod', 'shift', 'P']" />The slot remains usable for a single key. The keys prop accepts both a string
("mod+shift+K") and an array of tokens — assembling ⌘ + K from two GrKbd and a
<span>+</span> is no longer necessary.
A combination is marked up with nested <kbd> — a device from the HTML specification:
that way it stays keyboard input as a whole rather than a set of neighbouring elements. Only
which of them carries the border depends on the variant.
The variants
variant | The look | When |
|---|---|---|
merged (the default) | a single plate: ⌘K, Ctrl+K | a hint next to a button or in a field — that is how the systems themselves write a combination |
split | a plate per key: ⌘ K | when it is exactly the set of keys that has to be shown, in a keyboard-shortcuts reference for instance |
sequence | G then I | a chord: the keys are pressed one after another rather than together |
<GrKbd keys="mod+K" /> <!-- ⌘K -->
<GrKbd keys="mod+K" variant="split" /> <!-- ⌘ K -->
<GrKbd :keys="['G', 'I']" variant="sequence" />The separator is computed by itself
separator is not set by default — that is auto:
- in
mergedthe separator is put only after a word:⌘Kand⌘⇧Kon macOS,Ctrl+KandCtrl+Shift+Kon the rest. Within one platform the set is uniform (macOS gives symbols, the rest give words), so the rule “a symbol on the left — glue them” means “write it the way the system does”; - in
splitit is+, insequencea word from the locale (gr.kbd.then).
An explicit separator is stronger than the auto one: separator="" will glue anything,
separator="+" will put a plus even between symbols. The separator is decorative
(aria-hidden).
A sequence is written in the string itself
A space divides keys into steps, a plus — the keys within a step:
<GrKbd keys="g i" /> <!-- G then I -->
<GrKbd keys="mod+k p" /> <!-- ⌘K then P -->That way the hint and the binding are described with one string: the same 'g i' is
understood by v-hotkey (keyboard.md). Before that the string 'g+i' meant
a chord for the directive and a chain for the hint, and what told them apart was variant, which
the directive does not see.
The boundary of a step is stronger than variant: “then” appears between steps regardless of it.
variant="sequence" remains working for a string without spaces — the old notation
keys="g+i" variant="sequence" gives the previous result.
The array form of keys remains a flat chord: which is a step and which is a key, an array does
not distinguish.
A combination from the registry
hotkey takes a combination by id from the registry of the application
(granularityHotkeysPlugin) — then the hint cannot diverge from the binding:
<GrKbd hotkey="search.open" />The prop is stronger than keys: when both are set, the registry wins, otherwise two sources would
argue silently. An unknown id in a dev build gives a warning rather than an empty hint.
The dictionary of keys
Tokens set more than the modifiers:
| Token (and aliases) | macOS | The rest |
|---|---|---|
mod | ⌘ | Ctrl |
ctrl, alt, shift | ⌃, ⌥, ⇧ | Ctrl, Alt, Shift |
enter / return | ↩ | Enter |
esc, space, home, end | as a word | as a word |
tab | ⇥ | Tab |
backspace | ⌫ | Backspace |
delete / del | ⌦ | Del |
pageup / pgup, pagedown / pgdn | ⇞, ⇟ | PgUp, PgDn |
up, down, left, right | ↑ ↓ ← → | ↑ ↓ ← → |
The case does not matter, and a glyph is accepted as a token too (↑, ⌘). Writing an arrow
as a literal in the markup is neither necessary nor advisable: a token brings a readable name
with it, while a bare glyph is pronounced by a screen reader as a sign.
The catalogue is not a table in the docs but data: GR_KBD_TOKENS is exported by the
package, the showcase page is built from it, and it feeds the formatter as well. A copy of
the list would diverge from the behaviour at the very first new key.
import { GR_KBD_TOKENS, findKbdToken } from '@feugene/granularity'
const navigation = GR_KBD_TOKENS.filter(spec => spec.group === 'navigation')
const command = findKbdToken('⌘') // the same token as `meta`The platform
The mod token is Cmd on macOS and Ctrl on the other platforms; ctrl, alt and shift
are shown as symbols on Apple too (⌃, ⌥, ⇧) and as words on the rest.
<GrKbd keys="mod+K" /> <!-- detected automatically -->
<GrKbd keys="mod+K" platform="apple" /> <!-- always ⌘ -->
<GrKbd keys="mod+K" platform="other" /> <!-- always Ctrl -->platform="auto" refines the platform after mounting: navigator in the first render
would diverge from the server HTML and break the hydration. The non-Apple variant therefore
always comes from the server, and the ⌘ appears on the client. If the audience is known in
advance, platform removes even that repaint.
The same mod token is understood by the v-hotkey directive, so the hint and the binding
are written the same way and do not diverge:
<div v-hotkey="{ 'mod+K': openSearch }">
<GrKbd keys="mod+K" />
</div>The parsing and the normalisation live in components/shared/hotkey.ts — the same module
GrCommandPalette uses to match its own opening combination. A shared module rather than an
import from the directory of the palette: otherwise a GrKbd → GrCommandPalette edge would
appear in the build, and granular doctor would count it as an undeclared dependency.
The screen reader
Symbol keys get a readable name beside the symbol:
<kbd><span aria-hidden="true">⌘</span><span class="sr-only">Command</span></kbd>Without that a screen reader pronounces ⌘ as “place of interest sign”. Words (Ctrl,
Shift, Enter) are read on their own — no name is added to them, otherwise it would come
out as “Ctrl Control”. The texts of the names are the gr.kbd.* keys.
The sizes
The full scale of the package: xs, sm, md (the default), lg — the height, the minimum
width, the padding, the type size and the gap between the keys change. size is read from
GrConfigProvider.
min-w keeps a single symbol square: without it “K” would be narrower than “Esc”, and a row
of shortcuts would jump.
Props in this example