Keyboard-Only Access:

What it is and why your website fails at it

Hero Image
Keyboard-Only Access: What it is and why your website fails at it

Keyboard-Only Access: What it is and why your website fails at it

You press the Tab key and nothing happens. Or the focus jumps to something in the middle of the page, then disappears entirely. Or you finally get to a dropdown menu and can't open it.

This is what happens when websites ignore keyboard accessibility. It's also the experience of millions of users every day.

Keyboard accessibility is the single most important technical requirement for web accessibility. If a user can't navigate your site with a keyboard, they can't use your site at all. Screen readers require keyboard navigation. People with motor disabilities who can't use a mouse require keyboard navigation. Even power users who prefer keyboard shortcuts require it.

Here's what you need to know about making it work, what the WCAG guidelines actually require, and where most websites get it wrong.

The CDC estimates that 1 in 4 U.S. adults has a disability . Mobility disabilities are the most common type. For many of these individuals, using a mouse is physically impossible.

Screen reader users also rely entirely on keyboard commands. JAWS, NVDA, and VoiceOver all operate through keyboard input. If your site isn't keyboard accessible, it's not screen reader accessible either .

The WebAIM survey found that 68% of screen reader users encounter keyboard traps or inaccessible interfaces monthly . That's not a niche problem. That's a majority of users in this group regularly hitting barriers.

Temporary impairments matter too. Broken arm. Carpal tunnel flare-up. Cataract surgery recovery. All of these can make mouse use difficult or impossible for weeks or months .

What WCAG requires for keyboard access

The Web Content Accessibility Guidelines address keyboard access primarily through Guideline 2.1: Keyboard Accessible . The core principle is simple: make all functionality available from a keyboard.

Here are the specific success criteria you need to know.

2.1.1 Keyboard (Level A) : All functionality must be operable through a keyboard interface . This means every button, link, form field, and interactive component must be reachable and usable with the keyboard alone. No mouse required.

2.1.2 No Keyboard Trap (Level A) : Users must be able to navigate into and out of any component using only the keyboard . If focus gets stuck in a modal or widget, that's a violation.

2.1.4 Character Key Shortcuts (Level A) : If you use single-character keyboard shortcuts, users must be able to turn them off, remap them, or make them only active when the component has focus . This prevents accidental activation when typing.

2.4.3 Focus Order (Level A) : Focus must move in a logical, meaningful sequence that matches the visual layout . Tab order shouldn't jump around randomly.

2.4.7 Focus Visible (Level AA) : The keyboard focus indicator must be clearly visible at all times . Users need to know where they are on the page.

2.4.11 Focus Not Obscured (Level AA, WCAG 2.2) : The focused element must not be hidden behind fixed headers, footers, or other content . If your sticky header covers the focused button, that's a problem.

2.4.13 Focus Appearance (Level AA, WCAG 2.2) : Focus indicators must have sufficient contrast and size. The indicator needs at least a 2px thick perimeter and 3:1 contrast between focused and unfocused states .

The focus fundamentals most developers miss

Keyboard accessibility starts with understanding what receives focus and what doesn't.

Natively focusable elements include links with href attributes, buttons, form inputs, selects, textareas, and elements with tabindex attributes . These work automatically. They respond to Enter and Space keys without extra JavaScript.

Elements that are not focusable by default include divs, spans, paragraphs, images, headings, and static text . If you attach a click handler to a div, it still won't receive keyboard focus. Users can't tab to it, can't activate it, can't interact with it.

The Connecticut state accessibility portal puts it bluntly: using a div with a click handler is not an acceptable substitute for a button . Native HTML elements provide keyboard support for free. Custom elements require manual implementation of everything.

Here's what happens when you get this wrong:

text

<!-- Inaccessible: not focusable, not keyboard operable --> <div class="button" onclick="submitForm()">Submit</div> <!-- Accessible: native button behavior --> <button type="button" onclick="submitForm()">Submit</button>

 

Tabindex: the three values you need to know

The tabindex attribute controls keyboard focus behavior. There are only three values you should ever use .

tabindex="0" adds an element to the natural tab order. Use this when you must make a non-interactive element focusable, like a custom widget built from divs.

tabindex="-1" makes an element programmatically focusable but removes it from the tab order. This is essential for focus management. You can use JavaScript to focus the element when needed, but users won't tab to it accidentally .

Positive values (tabindex="1", "2", etc.) should never be used. They create an explicit tab order that overrides the natural document flow, creates maintenance nightmares, and confuses users expecting logical navigation . There is no good reason to use positive tabindex values in modern web development.

Focus visibility: the outline you should not remove

The default browser outline isn't pretty. Developers have been removing it for years with CSS like outline: none; or *:focus { outline: 0; }.

This is an accessibility failure . If you remove the outline without providing a highly visible alternative, keyboard users have no way to track their position on the page.

The Connecticut portal states explicitly: never use outline: none; without providing a highly visible alternative style for the :focus state .

Here's what compliant focus styling looks like:

text

/* Basic compliant focus indicator */ :focus {  outline: 3px solid #005fcc;  outline-offset: 2px; } /* Use :focus-visible to show only for keyboard users */ :focus:not(:focus-visible) {  outline: none; } :focus-visible {  outline: 3px solid #005fcc;  outline-offset: 2px; }

 

The :focus-visible pseudo-class is useful because it shows the focus ring only when the user is navigating with keyboard, not when clicking with a mouse. This preserves design aesthetics while maintaining accessibility.

Focus order and why it breaks

Focus should follow the visual layout of the page. In Western languages, that means left to right, top to bottom . This happens automatically when your DOM order matches the visual order.

Problems arise when CSS reorders content without changing the DOM. Flexbox with flex-direction: row-reverse changes visual order but not tab order. Users tab through elements in the DOM sequence while seeing them in a different visual sequence. This is disorienting .

Positive tabindex values also break focus order. When you set tabindex="1" on one element and tabindex="2" on another, you're forcing a custom order that may not match what users expect. Remove all positive tabindex values and let natural order prevail .

Keyboard traps: when focus won't leave

A keyboard trap occurs when focus enters a component and cannot leave using standard keyboard commands . Modals are the most common source of this problem.

Proper modal behavior requires trapping focus within the modal while it's open, but providing a clear exit mechanism. Users must be able to close the modal with the Escape key . When the modal closes, focus should return to the element that opened it .

Here's the pattern:

text

function trapFocus(modal) {  const focusableElements = modal.querySelectorAll(    'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'  );  const firstElement = focusableElements[0];  const lastElement = focusableElements[focusableElements.length - 1];  modal.addEventListener('keydown', (e) => {    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();      }    }    if (e.key === 'Escape') {      closeModal();    }  }); }

 

Without this pattern, keyboard users can become trapped. They can tab into the modal but can't tab out. The only way to escape might be closing the browser tab entirely.

Skip links: letting users bypass your navigation

Skip links are the first focusable element on the page, usually hidden until they receive keyboard focus. They allow users to jump directly to the main content, bypassing repetitive navigation links .

Without a skip link, keyboard users must tab through every single navigation item on every page load. If your header has 20 links, that's 20 key presses before reaching any content.

Implementation requires a link, a target, and CSS:

text

<!-- First element in body --> <a href="#main-content" class="skip-link">Skip to main content</a> <header>  <nav><!-- many navigation items --></nav> </header> <main id="main-content" tabindex="-1">  <!-- main content --> </main> <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>

 

The target element needs tabindex="-1" so it can receive focus programmatically. Without this, the skip link would move the viewport to the main content but focus would remain elsewhere.

Focus management for dynamic content

Single-page applications and dynamic interfaces create special challenges. When content updates without a page reload, keyboard focus can become lost or disoriented .

The pattern is straightforward. When content changes, move focus to that content. If you load a new page view, move focus to the main content heading. If you open a modal, move focus inside it. If you display form errors, move focus to the first error .

Here's the pattern for route changes:

text

function handleRouteChange() {  requestAnimationFrame(() => {    const mainContent = document.getElementById('main-content');    mainContent.tabIndex = -1;    mainContent.focus();    mainContent.addEventListener('blur', () => {      mainContent.removeAttribute('tabindex');    }, { once: true });  }); }

 

Without this, keyboard users tab through the page and nothing happens because the interactive elements they expect are no longer there.

Common component patterns

Different components have standard keyboard interaction patterns. Users expect these patterns. Breaking them creates confusion.

Buttons: Activate with Enter or Space .

Links: Activate with Enter only. Space does nothing on links .

Checkboxes: Toggle with Space. Checked state changes without requiring Enter .

Radio buttons: Navigate between options with arrow keys. Space selects the current option. Once selected, arrow keys move focus and change selection .

Select dropdowns: Arrow keys navigate options. Enter or Space selects .

Menus: Arrow keys navigate items. Enter selects. Escape closes without selecting .

Tabs: Arrow keys switch between tabs. The selected tab's panel becomes visible .

Sliders: Arrow keys adjust value. Home and End keys jump to minimum and maximum .

Modals: Focus trapped inside. Escape closes. Focus returns to trigger on close .

The limitation of automated testing

Automated tools catch about 30 to 50 percent of accessibility issues . They can detect missing alt text, insufficient color contrast, and missing form labels. They cannot detect whether the focus order makes sense, whether skip links actually work, or whether a custom dropdown is usable with arrow keys.

The Connecticut portal notes that the axe DevTools extension is excellent for catching common issues and rarely has false positives. It specifically advises not to rely on WAVE, which often has false positives .

Manual keyboard testing is irreplaceable. Unplug your mouse. Use only the Tab key, Shift+Tab, Enter, Space, and arrow keys. Try to complete every task on your site .

If you can't do it, your users can't either.

How to test keyboard accessibility

Start at the browser address bar. Press Tab repeatedly until you return to the address bar. Watch what happens .

Check these specific things:

Does focus move in logical order that matches visual layout? Are any interactive elements skipped entirely? Can you reach everything ?

Is the focus indicator always visible? Can you see where you are on every element ?

Can you activate every control with Enter or Space as appropriate? Do dropdowns open? Do modals appear ?

If focus enters a modal or widget, can it leave? Does Escape close what should close ?

Test reverse navigation with Shift+Tab. Does it work correctly ?

Test forms completely. Can you fill every field, submit, and handle errors without a mouse ?

Test with high zoom. At 400 percent, does focus remain visible? Does content become unusable ?

Test with forced colors mode if you're on Windows. Does your focus indicator survive ?

The aria-hidden trap

There's a common mistake with aria-hidden that destroys keyboard accessibility. The Connecticut portal calls it "the danger zone" .

aria-hidden="true" hides an element from screen readers but leaves it visually visible and interactive. If you set aria-hidden on a focusable element, sighted keyboard users can still tab to it, but screen reader users cannot perceive it.

Never use aria-hidden="true" on a focusable, interactive element . The result is a completely broken experience where some users see and can interact with an element that others can't even detect.

The rule for hiding content is simple. To hide something completely from all users, use display: none;. To hide something only from screen readers because it's purely decorative, use aria-hidden="true" .

What keyboard accessibility costs

Fixing keyboard accessibility requires development time. Testing takes hours. Maintaining it requires ongoing attention.

The trade-off is that keyboard accessibility is foundational. Without it, nothing else matters. You can have perfect color contrast, perfect alt text, perfect ARIA labels. If a user can't reach your content with a keyboard, none of it is usable .

The numbers from WebAIM are stark. 95.9 percent of homepages have detectable WCAG failures . Keyboard issues represent 15 to 20 percent of those violations . Combined with focus management problems, keyboard issues affect a majority of accessibility-dependent users.

The bottom line

Keyboard accessibility is not complicated technically. Use native HTML elements. Keep focus visible. Manage focus when content changes. Test with your keyboard.

The surprising thing is how often these basics get missed . Development workflows prioritize mouse interactions. Designers remove focus outlines for aesthetic reasons. Custom components get built without keyboard handlers.

But for millions of users, keyboard access isn't a preference. It's the only way they can use your site. If it doesn't work, they leave. And sometimes they come back with a lawsuit.

Start testing today. Unplug your mouse. Tab through your site. See what breaks. Then fix it.