Keyboard Accessibility Guide

All functionality must work for users who can't use a mouse. Learn how to build fully keyboard-accessible interfaces.

Reading time: 10 minutes

Why Keyboard Accessibility Matters

Many people don't use a mouse:

WCAG 2.1.1 (Keyboard):"All functionality of the content is operable through a keyboard interface."

Essential Keyboard Controls

Standard Keyboard Navigation Keys
Key Action
Tab Move to next interactive element
Shift+Tab Move to previous interactive element
Enter Activate links, buttons, submit forms
Space Activate buttons, check checkboxes, expand dropdowns
Arrow Keys Navigate within components (menus, tabs, radio groups)
Escape Close modals, menus, tooltips
Home/End Jump to first/last item in a list or field

Use Native HTML Elements

Native HTML elements have built-in keyboard support. Use them instead of custom widgets when possible.

Element Built-in Keyboard Behavior
<a href> Focusable, Enter activates
<button> Focusable, Enter and Space activate
<input>,<textarea> Focusable, full text editing support
<select> Focusable, arrow keys navigate options
<details> Focusable, Enter/Space toggles
<dialog> Focus trapping, Escape closes

Don't: Custom div "button"

<div class="btn" onclick="save()">
 Save
</div>
<!-- Not focusable, not keyboard operable -->

Do: Real button

<button type="button" onclick="save()">
 Save
</button>
<!-- Focusable, works with Enter/Space -->

Making Custom Widgets Keyboard Accessible

If you must create custom interactive elements, add keyboard support manually.

Step 1: Make It Focusable

<!-- Add tabindex="0" to include in tab order -->
<div role="button" tabindex="0">Custom Button</div>

Step 2: Handle Keyboard Events

element.addEventListener('keydown', (e) =>{
 if (e.key === 'Enter' || e.key === ' ') {
 e.preventDefault();
 activateButton();
 }
});

Step 3: Add ARIA Role

<div role="button" tabindex="0" aria-pressed="false">
 Toggle Setting
</div>

Prefer Native Elements

This is a lot of work to replicate what<button>gives you for free. Only create custom widgets when absolutely necessary.

Common Keyboard Patterns

Navigation Menus

Tab Panels

<div role="tablist">
 <button role="tab" aria-selected="true" aria-controls="panel1">Tab 1</button>
 <button role="tab" aria-selected="false" aria-controls="panel2">Tab 2</button>
</div>
<div role="tabpanel" id="panel1">Content 1</div>
<div role="tabpanel" id="panel2" hidden>Content 2</div>

Modal Dialogs

Accordions/Disclosures

<!-- Native HTML disclosure -->
<details>
 <summary>Section Title</summary>
 <p>Content that can be expanded/collapsed.</p>
</details>

Allow keyboard users to bypass repetitive navigation:

<!-- First element in body -->
<a href="#maincontent" class="skip-link">Skip to main content</a>

<!-- CSS: visible only on focus -->
<style>
.skip-link {
 position: absolute;
 top: -40px;
 left: 0;
 padding: 8px 16px;
 background: #000;
 color: #fff;
 z-index: 100;
}
.skip-link:focus {
 top: 0;
}
</style>

<!-- Target element -->
<main id="maincontent">...</main>

Common Keyboard Accessibility Failures

Problem Solution
Click-only interactions Add keyboard event handlers (keydown for Enter/Space)
Hover-only content Also show on focus; make content persistent
Can't reach element with Tab Use native interactive element or add tabindex="0"
Can't see where focus is Add visible :focus styles; never use outline:none alone
Focus gets trapped Ensure Escape closes overlays; check for infinite loops
Focus gets lost Manage focus when content changes (modals, deletions)
Wrong tab order Match DOM order to visual order; avoid positive tabindex
Can't operate carousel/slider Add keyboard controls (arrows, buttons)

Testing Keyboard Accessibility

  1. Put your mouse away— Literally move it out of reach
  2. Start at the browser address bar
  3. Tab through the entire page
    • Can you reach every interactive element?
    • Can you always see where focus is?
    • Does the tab order make sense?
  4. Test all interactions
    • Can you activate every button and link?
    • Can you open and close menus, modals, accordions?
    • Can you submit forms?
  5. Check for keyboard traps
    • Can you Tab away from every element?
    • Does Escape close overlays?

Quick Keyboard Test Script

// Paste in browser console to log focus changes
document.addEventListener('focusin', (e) =>{
 console.log('Focus:', e.target);
});

Keyboard Accessibility Checklist