Why This Matters
Millions of people use keyboards exclusively: people with motor disabilities, blind users with keyboard shortcuts, power users who prefer keyboards, and people on devices without mice. If your website doesn't support keyboard navigation—or worse, traps keyboard users—you're violating the ADA and excluding millions of users. Focus management is a foundational accessibility requirement with no workarounds.
What is Focus?
Focus indicates which element is currently active on the page. When you Tab through a page, the browser moves focus from one element to the next. When you press Enter on a button, that button has focus. Focus should always have a visible indicator—typically a colored outline or border. WCAG requires:
Focus: Which element is ready to receive input (keyboard, screen reader, etc.)
Selection: Which text is highlighted (for copy/paste)
Visible focus indicator (3px minimum outline)
High contrast (4.5:1 ratio)
Visible on all interactive elements
No removal of default focus styles
Tab Order: The Logical Flow
Tab order determines the sequence in which focus moves through a page. By default, the browser follows source code order (top to bottom, left to right). In most cases, the natural source code order works perfectly: <!-- Source order = Tab order --> <button>First</button> <!-- Tab 1 --> <button>Second</button> <!-- Tab 2 --> <button>Third</button> <!-- Tab 3 --> When to Use tabindex (Rarely) Only use tabindex when the visual layout doesn't match the logical flow: <!-- 3-column layout: left, center, right --> <aside> <nav tabindex="0">...</nav> <!-- Tab 1: Sidebar --> </aside> <main> <button tabindex="0">...</button> <!-- Tab 2: Main content --> </main> <article> <button tabindex="0">...</button> <!-- Tab 3: Secondary --> </article> NEVER Use tabindex > 0 (Litigation Risk) Setting tabindex="1", tabindex="2", etc. breaks the natural tab order and confuses keyboard users:
✅ Use natural source code order (no tabindex needed)
✅ Use tabindex="0" only to make static elements focusable
❌ Never use tabindex > 0
❌ Never create a custom tab order
Visible Focus Indicators
Every interactive element must have a clear, visible focus indicator. This is WCAG 2.1 Level AA requirement (2.4.7: Focus Visible).
Focus indicator: 3px solid color minimum
Contrast ratio: 3:1 between focus indicator and background
Visible on both light and dark backgrounds
Should not be removed or hidden
Focus Traps: Locking Users Out
A focus trap occurs when keyboard users cannot escape an element. This is a WCAG 2.1 Level A violation and immediate litigation risk. When a modal opens, focus should trap inside the modal, preventing users from accidentally tabbing to background content. But when the modal closes, focus should return to the triggering element. /* Trap focus inside modal */ const firstButton = modal.querySelector('button:first-of-type'); const lastButton = modal.querySelector('button:last-of-type'); modal.addEventListener('keydown', (e) => { if (e.key === 'Tab') { if (e.shiftKey && document.activeElement === firstButton) { e.preventDefault(); lastButton.focus(); } else if (!e.shiftKey && document.activeElement === lastButton) { e.preventDefault(); firstButton.focus(); } } }); 2. Infinite Scroll (Never Trap) If a page loads infinite content, keyboard users can get stuck tabbing through new content forever. Provide a way to stop tabbing or reach the footer: <!-- Good: Users can always reach the footer --> <main id="infinite-content">...</main> <footer>...</footer> <!-- Tab can reach here --> <!-- Bad: Focus trapped in infinite loop --> <main id="infinite-content">...</main> <!-- No footer = no escape --> 3. Autocomplete Dropdowns Search fields with autocomplete dropdowns can trap focus if not built properly:
Tab through the entire page
Try pressing Escape to close modals
Verify Tab eventually reaches the footer
Check that Tab never gets stuck in a loop
Ensure focus returns to triggering element after modal closes
Skip Links: Keyboard Shortcuts
Skip links allow keyboard users to jump over repetitive content (navigation, headers) and go directly to main content.
Keyboard users don't have to Tab 20+ times to reach content
Screen reader users can jump directly to main content
Saves time and reduces frustration
WCAG 2.1 Level AAA requirement
Focus Management in Dynamic Content
When page content changes via JavaScript, focus must be managed properly. When content loads (pagination, filtering), move focus to the new content: // After content loads const mainContent = document.getElementById('results'); mainContent.focus(); mainContent.scrollIntoView({ behavior: 'smooth', block: 'start' }); // Announce to screen readers mainContent.setAttribute('role', 'status'); mainContent.textContent = 'Results loaded'; Scenario 2: Form Validation Errors When a form fails validation, move focus to the first error: // Find first error const firstError = form.querySelector('[aria-invalid="true"]'); if (firstError) { firstError.focus(); firstError.scrollIntoView({ behavior: 'smooth', block: 'center' }); } Scenario 3: Search Results After search, focus on results count or first result:
Testing Tab Order & Focus
Click away from the page
Press Tab to focus the first element
Press Tab repeatedly through entire page
Verify focus order matches visual layout (top to bottom, left to right)
Press Shift+Tab to go backwards
Verify focus indicators are clearly visible
Check that Tab eventually reaches the footer
Try opening/closing modals and verify focus trap works
Press Escape on any modal to close it
NVDA (free, Windows)
JAWS (commercial)
VoiceOver (free, Mac/iOS)
axe DevTools
WAVE
Lighthouse (Chrome DevTools)
☐ Tab order matches visual layout
☐ All interactive elements are reachable via Tab
☐ Focus indicators are visible (3px, high contrast)
☐ No focus traps (except intentional modals)
☐ Focus returns after modal closes
☐ Skip link works and hides when not focused
☐ Can Tab past infinite scroll to reach footer
☐ No keyboard shortcuts conflict with browser/OS defaults
☐ Focus follows updates to dynamic content