myelin key names
Version: 0.1
Status: Released on 2026-10-17. Versions 0.x may change
incompatibly (§9).
Licence: Apache-2.0, as the rest of this repository, the conformance
vectors included.
Abstract
This document gives every keystroke one name, which programs, terminals, protocols and keymaps can share. A name is the key's modifiers and its value: a physical key, a character or a modifier key. Two names are the same keystroke exactly when they are equal strings. Physical keys are the W3C's code values, and Appendix C says where the other names come from.
It also says how a name written by a person is read, how a sequence of keys is written and how a key is displayed. It is written so that a protocol, such as HOTTY, can cite it instead of defining key names of its own. The Go package myelinkey is its reference implementation, and conformance/keys.json holds its test vectors.
Contents
- Conventions
- Key names
- Identity and matching
- Reading written keys
- Sequences
- Display
- Naming input (informative)
- Conformance
- Versioning
Appendices: A. Physical keys · B. HOTTY's names before this document (informative) · C. Prior art (informative)
1. Conventions
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as described in BCP 14 (RFC 2119, RFC 8174) when, and only when, they appear in bold capitals.
- Names are UTF-8 strings, and they are compared byte for byte.
- ASCII white space is U+0009, U+000A, U+000C, U+000D and U+0020.
- A source is whatever turns a press into a name: a terminal decoder, a GUI toolkit's adapter, a protocol's host.
- A keymap binds keys and sequences to actions.
2. Key names
2.1 Form
A key's name is its modifiers, each followed by +, then its value:
key = *(modifier "+") value
modifier = "super" / "ctrl" / "alt" / "shift"
value = physical / character / modifier
A name is canonical when:
- each modifier appears at most once, in the order
super,ctrl,alt,shift; - the value is a physical key's name from Appendix A.1 (§2.3), a character as §2.4 writes it, or the name of a modifier (§2.5);
- a modifier key's own modifier is not among the modifiers before it.
A key is a canonical name. Every other way of writing a key is a spelling, which a reading (§4) turns into the key or refuses.
To take a key apart, strip the leading modifier+ words while something
remains after them. What remains is the value. A value is a physical key
if Appendix A.1 lists it, a modifier key if it is one of the four modifier
names, and a character otherwise.
| Key | Modifiers | Value | Kind |
|---|---|---|---|
a |
a |
character | |
shift+g |
shift | g |
character |
ctrl+shift+a |
ctrl, shift | a |
character |
? |
? |
character | |
ctrl++ |
ctrl | + |
character |
page_down |
page_down |
physical | |
ctrl+key_w |
ctrl | key_w |
physical |
super+ctrl+alt+shift+enter |
super, ctrl, alt, shift | enter |
physical |
shift |
shift |
modifier | |
ctrl+shift |
ctrl | shift |
modifier |
2.2 Modifiers
superis the platform's command key: Command on macOS, the Windows key or Super elsewhere. The W3C calls it Meta.ctrlis Control.altis Alt, which is Option on macOS.shiftis Shift (§2.6).
Caps Lock, Num Lock, Scroll Lock, Fn, Hyper and AltGr are not modifiers. A
key pressed with them is named without them. What AltGr does shows in the
character it types: @ typed with AltGr on a German layout is @.
mod, the command modifier (§4.4), is no modifier of a key. A reading
turns it into super or ctrl.
2.3 Physical keys
A physical key is a place on the keyboard. Its name is its W3C UI Events
code value (UIEVENTS-CODE) in snake
case: PageDown is page_down and KeyW is key_w. Appendix A.1 lists
every one, and the list is normative.
- A physical key is the same whatever the layout types there.
key_zis the key where a US keyboard has Z, which types y on a German layout. - Some keys are only ever physical. These are the space bar, Enter,
Tab, Backspace and Escape (
space,enter,tab,backspace,escape), the keys that type no text (page_down,arrow_up,f1), and the keypad's keys (numpad_enter). - A modifier's keys have sides:
shift_left,shift_right,control_left,control_right,alt_left,alt_right,meta_leftandmeta_right. The command key ismeta_*, as the W3C names it.
2.4 Characters
A character is what a key types, as the layout makes it. It is one extended grapheme cluster (UAX #29) whose first code point is neither U+0020 SPACE nor a control (general category Cc).
- Letters are written small: each code point is replaced by its simple
lowercase mapping (UnicodeData.txt), as in
a,é,dž. - Everything else is written as it is:
?,+,<,中,👩💻, U+00A0 NO-BREAK SPACE. - Space and control characters are never characters. The keys that type
them are physical keys (
space,enter,tab,backspace,escape), and a control character that a terminal sends for Ctrl with a letter is that letter withctrl(§7).
2.5 Modifier keys
A modifier key is a modifier pressed and released with no other key pressed in between: a tap.
- Its name is the modifier's, whichever side it was on:
shift,ctrl,alt,super. - A source names a modifier key on release, because only then is it a tap.
- The modifiers held at the release come before it, in §2.1's order,
and the tapped one is last:
ctrl+shiftis Shift tapped while Ctrl is held, andshift+ctrlis Ctrl tapped while Shift is held. A modifier let go during the tap is not held: Ctrl down, Shift down, Ctrl up, Shift up isshift. - A source that knows the side has the sided physical key too
(
shift_left; see §3). - A modifier held for a chord is no key. Shift held while g is pressed
is
shift+g, notshiftfollowed byshift+g.
A keymap binds a double tap of Shift, on either side, as the sequence
shift shift.
2.6 Shift
- With a letter, Shift is written as a modifier:
shift+g,ctrl+shift+a. A letter is a character with case: its simple uppercase mapping differs from it. The letter is written small whatever case it was typed in. - Caps Lock changes nothing, since it isn't a modifier. A letter typed
with Caps Lock and no Shift is
a. - Any other character already includes Shift. Shift with / types
?on a US layout, so the key is?. A source names the character Shift typed, withoutshift. shiftwith a character that has no case (shift+1) is a different key from the character Shift types (!). A source that can't tell what Shift types names the key this way. Keymaps SHOULD NOT bind it, and an implementation MAY warn about one.- With a physical key or a modifier key, Shift is a modifier like any
other:
shift+tab,shift+key_a,shift+ctrl.
3. Identity and matching
- Two keys are the same keystroke exactly when their canonical names are equal.
- Keys of different kinds never match.
ctrl+wis Ctrl with the key that types w, andctrl+key_wis Ctrl with the key in the US layout's W position. Likewiseshiftis either Shift andshift_leftis the left one. - A source that delivers one key per press decides which kind to name.
- A source may have two names for one press. It may have a physical key and a character, or a sided modifier and a modifier key. If it looks them up in a keymap, it MUST try the physical key first and the other name second. The first one bound wins.
4. Reading written keys
People write keys in config files. A reading turns what they wrote into a key, or refuses it. An implementation MUST offer the default reading, and MAY offer strict reading, shifted capitals and the command modifier as options.
4.1 The default reading
-
Parts. The text is read from the left, one part at a time. A part runs to the next
+, or to the end, and that+is consumed.- An empty part is the character
+:+is one empty part, andctrl++isctrland an empty part. - Text that ends in
+after a non-empty part is refused:ctrl+,a+.
- An empty part is the character
-
Modifiers. A part that is a modifier's name or an alias of one, in any case, is a modifier:
super,ctrl,altandshift;cmdandcommandfor super,optandoptionfor alt, andcontrolfor ctrl.
A modifier written twice is refused. So is
meta, which some programs use for super and others (Emacs) for alt, and so ismod, unless the reading says what it stands for (§4.4). -
The value. Any other part is the value, and there is at most one. It is read as:
- one character (§2.4), written as §2.4 writes it;
- otherwise a physical key: a name from Appendix A.1 in any case;
one of the other names in Appendix A.2, in any case; or a W3C code
value, if it converts to a name in Appendix A.1. To convert, write it in lower case
and start a new word, after
_, at each capital letter or digit after the first character:PageDownispage_down,KeyAiskey_a, andDigit1isdigit_1.
Anything else is refused.
-
No value. If no part is a value, the last modifier written is the value, as a modifier key:
shift,ctrl+shift,shift+ctrl. -
The key is the modifiers and the value in canonical form (§2.1).
-
Aliases. Text of the form
<name>is an alias (§5), not a key, and is refused.
4.2 Strict reading
Strict reading accepts only a key's canonical name and refuses every other spelling.
- A refusal SHOULD give the canonical name: "
Ctrl+d: writectrl+d". - Where the text has a capital letter and shifted capitals are off, it
SHOULD give the key with Shift as well: "
G: writeg, orshift+gfor Shift with it".
4.3 Shifted capitals
Shifted capitals are for keymaps that write keys as vim does. A capital is one code point c whose simple lowercase mapping l differs from c, and whose simple uppercase mapping of l is c again.
- A capital reads as
shiftwith its small letter:Gisshift+g, andctrl+Aisctrl+shift+a. - A title-case letter is no capital: Dž is
dž. - With strict reading also on, both the canonical name and the spelling with the capital are accepted.
4.4 The command modifier
mod is the platform's modifier for commands. One keymap binds mod+s,
and it is Command+s on macOS and Ctrl+s elsewhere. A reading that offers
it is told which modifier mod stands for, super or ctrl. Without
that, mod is refused.
modreads as the modifier it stands for, in any case and in any place among the others. Where it stands forctrl,mod+sisctrl+s,Mod+Shift+ArrowLeftisctrl+shift+arrow_left, andmodalone is the modifier keyctrl(§4.1, step 4).modwithsuperorctrlis refused, or with an alias of either, whichever onemodstands for. So a keymap reads on every platform or on none.modwritten twice is refused, as any modifier is.- No key is named with
mod. The key has the modifiermodstands for, and a source never names a press with it. - With strict reading also on, the canonical name is accepted, and so
is the canonical name with
modin the place of the modifier it stands for:mod+shift+s,alt+mod. With shifted capitals on as well, so is the spelling with the capital, withmodin that place:mod+S. - What
modstands for is the program's to say, or a protocol's. It is usuallysuperon macOS andctrlelsewhere. A HOTTY host reports it asmod.
5. Sequences
A sequence is keys typed one after another.
- Its keys are separated by ASCII white space:
g g,ctrl+x d,shift shift. Other white space, such as U+00A0, is part of a key. - Its canonical form separates the keys with one U+0020.
Aliases. A word of the form <name> stands for an alias: a sequence
that a keymap names and puts in the word's place, as in <leader> f.
- The name is one or more ASCII letters, digits,
_and-. - An alias's keys name no alias.
- An alias is not a key. An implementation without aliases refuses one, and so does a reading of one key (§4.1).
- The characters
<and>on their own are keys.
6. Display
A menu or a hint SHOULD show a key by its canonical name, except for
the keys in Appendix A.3, which it shows by their symbols. Other modifiers
are written before them as usual: ctrl+←, ctrl+⇧⇥. A program MAY
show keys its own way, for example a capital for shift with a letter
where its keymap reads shifted capitals.
7. Naming input (informative)
How common sources name a press. A protocol that carries keys, such as HOTTY (its §10.4), defines how its own hosts name them.
-
A browser's
KeyboardEventgives both kinds of key.- Its
codeis the physical key: convert it as §4.1 converts a W3C code value. - Its
keyis the character, when that is one character (§2.4); akeyof" "isspace. - The modifiers are
metaKey(super),ctrlKey,altKeyandshiftKey. - A
keyupof a modifier with no other key since itskeydownis a modifier key. Itscodegives the side, and the other modifiers' flags give the ones held (§2.5).
- Its
-
A terminal's bytes carry characters, and the library or protocol that reads them decodes them, as HOTTY §10.4 does. The result follows this document:
- the space bar and the control keys are physical keys;
- Ctrl with a letter is the character with
ctrl(ctrl+w); - a capital letter that arrives with no modifier information is
shiftwith its letter.
A legacy terminal reports no release, and so no modifier keys. The kitty keyboard protocol can report them.
8. Conformance
conformance/keys.json holds the vectors. An implementation conforms when it gives every vector's result. Error messages are not part of conformance.
physical: Appendix A.1's names, in order.read: the default reading (§4.1).inis the text, andkeythe key, or null where it is refused.strict: strict reading (§4.2), asread.shifted_capitals: the default reading with shifted capitals (§4.3).strict_shifted_capitals: strict reading with shifted capitals.mod: a reading with the command modifier (§4.4).modis what it stands for,superorctrl.strictandshifted_capitals, where true, turn those options on.inandkeyare as inread.split: a key taken apart (§2.1):modsin order,value, andkind, which ischaracter,physicalormodifier.sequences: a sequence read with the default reading and no aliases (§5).keysis the keys, or null where it is refused.display: a key as a menu shows it (§6).
9. Versioning
This is version 0.1, released on 2026-10-17. Versions 0.x may change incompatibly. A change to any name or rule is a new version, and the vectors carry the version they test.
Appendix A. Physical keys
A.1 Names
The physical keys, by the sections of the W3C's code values. Every name also reads as its W3C code value (§4.1), shown beside it.
Writing system keys
| Key | W3C code value |
|---|---|
backquote |
Backquote |
backslash |
Backslash |
bracket_left |
BracketLeft |
bracket_right |
BracketRight |
comma |
Comma |
digit_0 |
Digit0 |
digit_1 |
Digit1 |
digit_2 |
Digit2 |
digit_3 |
Digit3 |
digit_4 |
Digit4 |
digit_5 |
Digit5 |
digit_6 |
Digit6 |
digit_7 |
Digit7 |
digit_8 |
Digit8 |
digit_9 |
Digit9 |
equal |
Equal |
intl_backslash |
IntlBackslash |
intl_ro |
IntlRo |
intl_yen |
IntlYen |
key_a |
KeyA |
key_b |
KeyB |
key_c |
KeyC |
key_d |
KeyD |
key_e |
KeyE |
key_f |
KeyF |
key_g |
KeyG |
key_h |
KeyH |
key_i |
KeyI |
key_j |
KeyJ |
key_k |
KeyK |
key_l |
KeyL |
key_m |
KeyM |
key_n |
KeyN |
key_o |
KeyO |
key_p |
KeyP |
key_q |
KeyQ |
key_r |
KeyR |
key_s |
KeyS |
key_t |
KeyT |
key_u |
KeyU |
key_v |
KeyV |
key_w |
KeyW |
key_x |
KeyX |
key_y |
KeyY |
key_z |
KeyZ |
minus |
Minus |
period |
Period |
quote |
Quote |
semicolon |
Semicolon |
slash |
Slash |
Functional keys
| Key | W3C code value |
|---|---|
alt_left |
AltLeft |
alt_right |
AltRight |
backspace |
Backspace |
caps_lock |
CapsLock |
context_menu |
ContextMenu |
control_left |
ControlLeft |
control_right |
ControlRight |
enter |
Enter |
meta_left |
MetaLeft |
meta_right |
MetaRight |
shift_left |
ShiftLeft |
shift_right |
ShiftRight |
space |
Space |
tab |
Tab |
convert |
Convert |
kana_mode |
KanaMode |
non_convert |
NonConvert |
The control pad
| Key | W3C code value |
|---|---|
delete |
Delete |
end |
End |
help |
Help |
home |
Home |
insert |
Insert |
page_down |
PageDown |
page_up |
PageUp |
The arrow pad
| Key | W3C code value |
|---|---|
arrow_down |
ArrowDown |
arrow_left |
ArrowLeft |
arrow_right |
ArrowRight |
arrow_up |
ArrowUp |
The numpad
| Key | W3C code value |
|---|---|
num_lock |
NumLock |
numpad_0 |
Numpad0 |
numpad_1 |
Numpad1 |
numpad_2 |
Numpad2 |
numpad_3 |
Numpad3 |
numpad_4 |
Numpad4 |
numpad_5 |
Numpad5 |
numpad_6 |
Numpad6 |
numpad_7 |
Numpad7 |
numpad_8 |
Numpad8 |
numpad_9 |
Numpad9 |
numpad_add |
NumpadAdd |
numpad_backspace |
NumpadBackspace |
numpad_clear |
NumpadClear |
numpad_clear_entry |
NumpadClearEntry |
numpad_comma |
NumpadComma |
numpad_decimal |
NumpadDecimal |
numpad_divide |
NumpadDivide |
numpad_enter |
NumpadEnter |
numpad_equal |
NumpadEqual |
numpad_memory_add |
NumpadMemoryAdd |
numpad_memory_clear |
NumpadMemoryClear |
numpad_memory_recall |
NumpadMemoryRecall |
numpad_memory_store |
NumpadMemoryStore |
numpad_memory_subtract |
NumpadMemorySubtract |
numpad_multiply |
NumpadMultiply |
numpad_paren_left |
NumpadParenLeft |
numpad_paren_right |
NumpadParenRight |
numpad_subtract |
NumpadSubtract |
numpad_separator |
NumpadSeparator |
numpad_up |
NumpadUp |
numpad_down |
NumpadDown |
numpad_right |
NumpadRight |
numpad_left |
NumpadLeft |
numpad_begin |
NumpadBegin |
numpad_home |
NumpadHome |
numpad_end |
NumpadEnd |
numpad_insert |
NumpadInsert |
numpad_delete |
NumpadDelete |
numpad_page_up |
NumpadPageUp |
numpad_page_down |
NumpadPageDown |
The function section
| Key | W3C code value |
|---|---|
escape |
Escape |
f1 |
F1 |
f2 |
F2 |
f3 |
F3 |
f4 |
F4 |
f5 |
F5 |
f6 |
F6 |
f7 |
F7 |
f8 |
F8 |
f9 |
F9 |
f10 |
F10 |
f11 |
F11 |
f12 |
F12 |
f13 |
F13 |
f14 |
F14 |
f15 |
F15 |
f16 |
F16 |
f17 |
F17 |
f18 |
F18 |
f19 |
F19 |
f20 |
F20 |
f21 |
F21 |
f22 |
F22 |
f23 |
F23 |
f24 |
F24 |
f25 |
F25 |
fn |
Fn |
fn_lock |
FnLock |
print_screen |
PrintScreen |
scroll_lock |
ScrollLock |
pause |
Pause |
Media keys
| Key | W3C code value |
|---|---|
browser_back |
BrowserBack |
browser_favorites |
BrowserFavorites |
browser_forward |
BrowserForward |
browser_home |
BrowserHome |
browser_refresh |
BrowserRefresh |
browser_search |
BrowserSearch |
browser_stop |
BrowserStop |
eject |
Eject |
launch_app_1 |
LaunchApp1 |
launch_app_2 |
LaunchApp2 |
launch_mail |
LaunchMail |
media_play_pause |
MediaPlayPause |
media_select |
MediaSelect |
media_stop |
MediaStop |
media_track_next |
MediaTrackNext |
media_track_previous |
MediaTrackPrevious |
power |
Power |
sleep |
Sleep |
audio_volume_down |
AudioVolumeDown |
audio_volume_mute |
AudioVolumeMute |
audio_volume_up |
AudioVolumeUp |
wake_up |
WakeUp |
Legacy keys
| Key | W3C code value |
|---|---|
copy |
Copy |
cut |
Cut |
paste |
Paste |
A.2 Other names
The default reading also reads these names as physical keys, in any case (§4.1).
| Other name | Key |
|---|---|
up, down, left, right |
arrow_up, arrow_down, arrow_left, arrow_right |
pageup, pgup |
page_up |
pagedown, pgdn |
page_down |
esc |
escape |
A.3 Display
A menu or a hint shows these keys by their symbols (§6).
| Key | Shown |
|---|---|
arrow_left, arrow_right, arrow_up, arrow_down |
←, →, ↑, ↓ |
tab |
⇥ |
shift+tab |
⇧⇥ |
Appendix B. HOTTY's names before this document (informative)
HOTTY 0.2 and 0.3 write keys in their own way (§10.4). myelinkey's
ParseHotty reads those names into keys while HOTTY moves to this
document. HOTTY 0.3 adds Mod to bindings, which this document reads as
mod (§4.4). No pressed key is named with it, so ParseHotty refuses it.
| HOTTY 0.2, 0.3 | Key |
|---|---|
a |
a |
A |
shift+a |
Control+a |
ctrl+a |
Control+A, Shift+Control+a |
ctrl+shift+a |
! |
! |
Shift+1 |
shift+1 |
Meta+Enter |
super+enter |
PageDown |
page_down |
ArrowLeft |
arrow_left |
F12 |
f12 |
ContextMenu |
context_menu |
Space, a space |
space |
Control+ |
ctrl+space |
Control++ |
ctrl++ |
Shift |
shift |
Control+Shift |
ctrl+shift |
Appendix C. Prior art (informative)
- Ghostty's key bindings
gave the vocabulary:
- the modifier names and their aliases;
- the W3C code values in snake case;
- one character as a key, with case folded;
- the order in which a press with several names is looked up.
- The W3C UI Events code values name the physical keys.
- VS Code, Zed and Helix gave the other names for physical keys (
esc,pgdn,up). - vim writes Shift with a letter as a capital, which shifted capitals read.
- IntelliJ's "Search Everywhere" is a double tap of Shift, which
modifier keys write as
shift shift. - HOTTY's
Mod, CodeMirror's and ProseMirror'sMod-and Electron'sCommandOrControlgave the command modifier,mod.