Level A

Single-character shortcuts bound on document fire whenever any element has focus -- speech dictation, fat-fingered keys, and open microphones all trigger unintended actions unless the shortcut can be disabled, remapped to a modifier, or scoped to a focused component.

2.1.4 Character Key Shortcuts

In Plain Language

2.1.4 Character Key Shortcuts (Level A, new in WCAG 2.1) applies when a keyboard shortcut is implemented using only printable character keys -- a letter, number, punctuation mark, or symbol -- with no modifier key. When a shortcut fits that definition, at least one of the following must be true[1]:

  • Turn off: the shortcut can be disabled.
  • Remap: the shortcut can be reconfigured to pair the character with a modifier (Ctrl, Alt, Cmd, Shift).
  • Active only on focus: the shortcut fires only while the relevant UI component has focus.

Shortcuts that already require a modifier (Ctrl+S, Alt+Shift+A) are out of scope. Shortcuts bound to native form controls -- type-ahead on a <select>, access key handling by the UA -- are also out of scope.

Why It Matters

  • A keydown listener attached to document fires for every keystroke the page receives, regardless of which element has focus. Bind r to "reply" and the webmail app sends users into a reply composer any time an r reaches the document -- including while a microphone is open and a coworker says "are you there".
  • Speech-recognition engines (Dragon, Windows Voice Access, macOS Voice Control) dictate into focused text fields by synthesising keystrokes. A user dictating the word "south" into a compose box emits s, o, u, t, h in sequence; if those characters are also bound as global shortcuts, the page archives, stars, unsubscribes, and deletes the draft mid-sentence. W3C Understanding 2.1.4 gives a worked example of a speech user whose coworker saying "Hey Kim" triggers a barrage of single-key commands[1].
  • Users with motor disabilities who fat-finger the keyboard, tremor into adjacent keys, or rest fingers on home row trigger destructive actions (delete, archive, send) with no confirmation step and no undo affordance.
  • Non-disabled keyboard users also trip these shortcuts. Sighted touch-typists in open offices, users on laptops with sticky keys, or anyone with a cat on the desk can fire d on a message list and lose the message. Accidental activation is not a niche concern -- it is the mechanism.

Examples

Do: Shortcut settings with disable and remap

Keyboard Shortcuts settings panel:

S = Star   [Remap] [Disable]

A = Archive   [Remap] [Disable]

✔ Users can disable or remap each single-character shortcut

<!-- Settings panel lets users manage shortcuts -->
<table aria-label="Keyboard shortcut settings">
  <thead>
    <tr>
      <th scope="col">Action</th>
      <th scope="col">Shortcut</th>
      <th scope="col">Options</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Star message</td>
      <td><kbd>S</kbd></td>
      <td>
        <button>Remap</button>
        <button>Disable</button>
      </td>
    </tr>
  </tbody>
</table>
Don't: Hardcoded single-key shortcuts with no user control

document.addEventListener('keydown', e => {

  if (e.key === 'd') deleteItem();

});

✘ Pressing 'd' anywhere on the page deletes an item -- no way to disable, remap, or scope to a component

<!-- FAILS: global single-key shortcut, no user control -->
<script>
document.addEventListener('keydown', (e) => {
  if (e.key === 'd') deleteItem();
  if (e.key === 's') starItem();
  if (e.key === 'a') archiveItem();
});
</script>

<!-- No settings to disable or remap these.
     Speech input saying "dash" fires 'd'.
     No modifier key required. -->
Do: Shortcuts active only when component has focus

<div role='grid' tabindex='0'> with keydown listener

✔ Single-key shortcuts (j/k to navigate rows) only fire when the grid has focus -- typing in a search field is unaffected

<!-- Shortcuts scoped to the focused component -->
<div role="grid" tabindex="0"
     aria-label="Message list"
     id="message-grid">
  <!-- rows... -->
</div>

<script>
const grid = document.getElementById('message-grid');
grid.addEventListener('keydown', (e) => {
  // Only fires when grid has focus
  if (e.key === 'j') selectNextRow();
  if (e.key === 'k') selectPrevRow();
});
</script>
Don't: Single-key shortcuts that fire while typing in an input

User types 'desk' in search field:

'd' -- deletes item, 'e' -- edits item, 's' -- stars item, 'k' -- moves up

✘ Global shortcuts intercept keystrokes while user is typing in a text field

How to Fix It

  1. Inventory every single-character shortcut. Grep the codebase for keydown, keyup, and keypress handlers bound to document, window, or body. Any branch that matches a printable character without also checking event.ctrlKey, event.altKey, event.metaKey, or event.shiftKey is in scope for 2.1.4.
  2. Pair the character with a modifier. The cleanest fix is to change S to Ctrl+S (or Cmd+S on macOS). Shortcuts that require a non-printable modifier key are exempt from 2.1.4 entirely[1], so this removes the failure at the source.
  3. Scope the handler to a focused component. Instead of document.addEventListener('keydown', ...), attach the listener to the specific widget element (a grid, an editor, a list). The shortcut only fires when that element is the activation target, which is what the "active only on focus" option in the SC definition requires[1]. Use tabindex="0" so the component is focusable and keyboard users can reach it.
  4. Expose a shortcuts settings surface. Give users a page that lists every character-key shortcut with a "disable" toggle and a "remap" control that requires at least one modifier in the new binding. Persist the user's choices; do not reset them on sign-out or version upgrade.
  5. If you keep global listeners, bail out inside editable contexts. At minimum, at the top of the handler check document.activeElement. If it is an <input>, <textarea>, or element with contenteditable, return before matching any character. This does not satisfy 2.1.4 on its own -- speech input also types into non-editable focus contexts -- but it stops the most obvious breakage.
  6. Verify with speech input. Dictate a sentence containing each bound character into a focused compose field using a speech-recognition tool. If any character fires a shortcut instead of appearing in the field, the handler is still global and still failing.

References

  1. [1] W3C (2023). Understanding Success Criterion 2.1.4: Character Key Shortcuts. W3C, Accessed 2026-04-07. https://www.w3.org/WAI/WCAG22/Understanding/character-key-shortcuts.html