Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Enable keyboard navigation for all elements

All interactive elements are accessible via keyboard with logical focus order and hidden elements excluded from tab sequence.

Utilities
Quick take
Typical fix time 30 min
  • All interactive elements must be reachable via Tab key
  • Focus order should follow visual reading order (left-to-right, top-to-bottom)
  • Never use positive tabindex values—they break natural flow
  • Hidden content must be removed from tab sequence with tabindex='-1'
  • Keyboard traps are only acceptable inside active modals with a working Escape path
Why it matters: 15% of users rely on keyboard navigation—broken focus order or keyboard traps make your site completely unusable for them.

Rule Details

All interactive elements must be keyboard accessible with a logical focus order that matches the visual layout.

Code Example

<!-- ✅ Good: Natural tab order -->
<button>First</button>
<button>Second</button>
<button>Third</button>
 
<!-- ❌ Bad: Positive tabindex breaks flow -->
<button tabindex="3">First</button>
<button tabindex="1">Second</button>
<button tabindex="2">Third</button>
 
<!-- ✅ Remove from tab order (for hidden content) -->
<button tabindex="-1">Hidden from tab</button>

Why It Matters

15% of users rely on keyboard navigation—broken focus order or keyboard traps make your site completely unusable for them.

Keyboard Navigation Basics

KeyExpected Action
TabMove to next focusable element
Shift+TabMove to previous focusable element
EnterActivate buttons/links
SpaceActivate buttons, toggle checkboxes
Arrow keysNavigate within widgets (menus, tabs, sliders)
EscapeClose modals, menus, dropdowns

Custom Interactive Elements

<!-- ❌ Bad: div is not keyboard accessible -->
<div class="button" onclick="doSomething()">Click me</div>
 
<!-- ✅ Good: Native button is keyboard accessible -->
<button onclick="doSomething()">Click me</button>
 
<!-- ✅ Acceptable: Custom element with keyboard support -->
<div
  role="button"
  tabindex="0"
  onclick="doSomething()"
  onkeydown="if(event.key === 'Enter' || event.key === ' ') doSomething()"
>
  Click me
</div>

Hidden Content Management

function Modal({ isOpen, children }) {
  return (
    <div
      role="dialog"
      aria-modal="true"
      // Remove from tab order when closed
      tabIndex={isOpen ? 0 : -1}
      style={{ display: isOpen ? 'block' : 'none' }}
    >
      {children}
    </div>
  )
}

Focus Trapping for Modals

Focus trapping is valid only when the user is inside an active modal or popover that must temporarily contain interaction. Trapping focus in drawers, carousels, chat widgets, or sticky banners without a clear exit creates a keyboard trap.

import { useEffect, useRef } from 'react'
 
function Modal({ isOpen, onClose, children }) {
  const modalRef = useRef<HTMLDivElement>(null)
 
  useEffect(() => {
    if (!isOpen) return
 
    const focusableElements = modalRef.current?.querySelectorAll(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    )
    const firstElement = focusableElements?.[0] as HTMLElement
    const lastElement = focusableElements?.[focusableElements.length - 1] as HTMLElement
 
    firstElement?.focus()
 
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose()
      if (e.key === 'Tab') {
        if (e.shiftKey && document.activeElement === firstElement) {
          e.preventDefault()
          lastElement?.focus()
        } else if (!e.shiftKey && document.activeElement === lastElement) {
          e.preventDefault()
          firstElement?.focus()
        }
      }
    }
 
    document.addEventListener('keydown', handleKeyDown)
    return () => document.removeEventListener('keydown', handleKeyDown)
  }, [isOpen, onClose])
 
  return <div ref={modalRef} role="dialog" aria-modal="true">{children}</div>
}

Exceptions

  • Temporary or intentionally inert UI can be removed from the focus order, but only when the same state is also communicated clearly to assistive technology users.
  • A focus-management issue should be evaluated in the rendered interaction, not only from static markup, because route changes, overlays, and JS timing can change the real behavior.
  • If a component is both unlabeled and focus-broken, fix the stronger user-facing orientation problem first rather than reporting multiple secondary symptoms.

Standards

  • Align the implementation with W3C WAI: WCAG Overview and verify the rendered experience, not only the source code.
  • Align the implementation with MDN: Accessibility and verify the rendered experience, not only the source code.

Verification

Automated Checks

  • Use browser accessibility tooling, axe, Lighthouse, or equivalent automated checks against a representative rendered state.

Manual Checks

  • Unplug your mouse and navigate the entire page using only keyboard
  • Verify Tab moves through all interactive elements in visual order
  • Confirm Enter/Space activate buttons and links
  • Check that hidden elements cannot receive focus
  • Test modals trap focus and Escape closes them
  • Enter and exit every overlay, drawer, and widget to confirm focus is never trapped outside an active modal dialog

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Test that all interactive elements are keyboard accessible, focus order matches visual/reading order, hidden content is not focusable, and no keyboard traps exist except in modals with proper escape handling.

Fix

Auto-fix issues

Ensure tab order follows DOM order matching visual layout. Use tabindex='-1' to remove hidden elements from focus. Avoid positive tabindex values. Ensure off-screen or display:none content cannot receive focus.

Explain

Learn more

Explain how logical focus order helps keyboard users navigate efficiently, why hidden elements must be removed from the tab sequence to avoid confusion, and how focus management affects users of screen readers and switch devices.

Review

Code review

Review the rendered markup and interactive states that affect Enable keyboard navigation for all elements. Flag exact elements, roles, labels, focus behavior, or keyboard interactions that violate the rule, and note how to verify the fix with browser accessibility tooling or assistive tech.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

axe DevTools
deque.comTool

Rules that often go hand-in-hand with this one.

Ensure logical focus order

Tab focus order follows the visual layout and logical reading sequence of the page.

Accessibility
Manage focus during dynamic interactions

Focus is programmatically managed during dynamic interactions like modals, page transitions, and content updates.

Accessibility
Provide visible custom focus indicators

Ensure all interactive elements have a clearly visible focus indicator for keyboard navigation — never just remove the default outline without providing a better alternative.

CSS
Test with screen readers

Pages must be tested with actual screen reader software (NVDA, JAWS, VoiceOver, TalkBack) to verify announcements, focus order, and widget behavior beyond what automated tools can detect.

Accessibility

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385