All articles
Accessibility

Keyboard Navigation Testing Checklist for Web Apps

Throw away your mouse. Learn how to test web apps for keyboard accessibility, focus order, visible focus states, skip links, and modal containment.

Kuda Zafevere·Full-Stack Engineer
·15 min read
Share:

The app looks polished. The components are well-styled, automated tests are green, and the design review went well. A keyboard-only user opens the same page, presses Tab, and within five keystrokes gets trapped behind a modal, loses track of which element has focus, or hits an invisible clickable region that is not focusable at all.

Keyboard accessibility is not a subjective polish item. It is a baseline operability requirement. If a web app fails keyboard navigation, it also fails users on switches, voice control, screen readers, refreshable braille displays, and ergonomic setups where a mouse is unavailable. The fastest way to discover these failures is the one that is still the hardest to automate: put the mouse away and use the product.

This is a manual QA checklist for doing that systematically. The checks are explicit, pass/fail, and scoped to the patterns that actually break most often in production web apps.

TL;DR

  • Every interactive element should be reachable by keyboard.
  • Focus must always be visible while tabbing through the page.
  • Focus order must match the logical reading and task order, not just the DOM’s accidental order.
  • Custom widgets need the correct arrow-key and activation behaviour, not just Tab.
  • Modals must move focus in, trap focus correctly, and return focus on close.
  • Automated checks help. Manual keyboard testing on real templates is still required.

Run a quick structural audit first to surface obvious accessibility defects, then put the mouse away and use the checklist below to test the experience the way real keyboard users do. The CodeAva Website Audit can highlight structural issues on a live URL, which makes starting a manual pass faster and more focused.

Why keyboard testing still matters

A mouse is not a fallback for users who cannot use one. Keyboard operability is a foundational requirement that overlaps with, but is not limited to, screen reader users. People use keyboards because of motor disabilities, temporary injuries, power-user efficiency, corporate desktop constraints, or assistive technologies that route all input through keyboard events.

Keyboard testing also surfaces a class of bugs automated tools cannot fully resolve: whether the focus order makes sense in context, whether the activation pattern matches user expectations for a specific widget, and whether overlays behave correctly when opened and closed. You have to drive the UI to find these.

Keyboard testing vocabulary cheat sheet

Before the checklist, a quick reference. These are the keystrokes every test pass uses, and knowing what each one is expected to do is what separates “the page works” from “the page works for keyboard users.”

  • Tab— move to the next focusable element.
  • Shift + Tab— move to the previous focusable element.
  • Enter— activate links and buttons; submit forms in many field contexts.
  • Space— activate buttons, toggle checkboxes and radios, and scroll the page when focus is not on a control.
  • Arrow keys— navigate inside composite widgets (tabs, menus, radio groups, toolbars, listboxes).
  • Esc— close dialogs, menus, popovers, and similar dismissible UI where that convention applies.
  • Home / End— jump to the first or last item in composite widgets that support it.

The 5-point keyboard QA checklist

Each check below is binary: pass or fail. If any of them fail, the app has a real keyboard accessibility defect, not a stylistic concern.

Check 1: focus visibility

  • Action: press Tab repeatedly through the page.
  • Pass: focus is always clearly visible; the active element is obvious without guesswork, against every background it encounters.
  • Common failure: outline: none applied globally or per component with no accessible replacement. Focus “works” but is invisible.

Check 2: focus order logic

  • Action: Tab through the full page, including complex layouts.
  • Pass: focus moves in an order that matches the task flow and reading order a sighted user would follow.
  • Common failure: CSS layout changes (flexbox order, grid placement) produce a visual order that does not match DOM order. Focus jumps unpredictably.

Check 3: reachability of custom UI

  • Action: Tabto every custom control — dropdowns, accordions, cards, tabs, toggles, and pseudo-buttons.
  • Pass: all intended interactive elements are reachable and usable with the keyboard.
  • Common failure: clickable <div> and <span> elements with onClick handlers and no keyboard support. They are visible, clickable, and totally unreachable by Tab.

Check 4: skip link behaviour

  • Action: load the page and press Tab once.
  • Pass: a visible “Skip to main content” link appears and, when activated, moves focus into the main content region so the next Tab lands inside the main body rather than back at the navigation.
  • Common failure: skip link missing entirely, visually hidden in a way that never becomes visible on focus, or present but not actually moving focus on activation.

Check 5: modal containment and return

  • Action: open a modal and navigate with the keyboard only.
  • Pass: focus moves into the modal on open; Tab and Shift + Tab cycle only inside it; Esc closes it where appropriate; focus returns to the element that opened it (or a logical next target) on close.
  • Common failure: focus escapes behind the modal, disappears entirely on close, or the trap silently locks the user out of dismissing it.

For the full implementation details behind this check, see Accessible React Modal Focus Management.

Focus order vs. visual order

Tab order follows focusable-element order in the DOM. CSS can rearrange the page visually — flexbox order, grid grid-row and grid-column placement, absolute positioning — without changing the DOM. Keyboard users get the DOM order. Sighted mouse users get the visual order. If the two disagree, the keyboard experience feels chaotic.

The correct fix is almost always to adjust the DOM so its order matches the intended reading and task order. Reach for tabindex values only when there is no sensible DOM rearrangement and the visual order is fixed for product reasons. Even then, tabindex values above 0tend to make things worse, not better — they remove the element from the natural tab sequence and rarely produce intuitive results.

Good defaults:

  • Write markup in the logical order you want users to experience it.
  • Use tabindex="0" only to make an otherwise non-focusable element focusable.
  • Use tabindex="-1" for elements that should be programmatically focusable but not in the tab order (for example, a section heading to which you move focus after a route change).
  • Avoid tabindex values greater than 0 as a design pattern.

Custom widgets and the roving tabindex pattern

Not every widget should require the user to Tab through every child. Composite widgets such as tablists, toolbars, menus, radio groups, and listboxes have their own conventional keyboard model: Tab once to enter the widget, use the arrow keys to move between its items, then Tab to leave to the next region of the page.

The mechanism that makes this work is roving tabindex. Only one child of the composite widget has tabindex="0" at any time; the rest have tabindex="-1". As the user presses arrow keys, the widget moves tabindex="0" to the current item and sets focus on it. The user experiences a single tab stop for the widget even though it contains many items.

This pattern is not a universal solution. Apply it only to widgets where it matches the expected keyboard model:

  • TablistsTab onto the active tab, left/right arrows switch tabs.
  • ToolbarsTab onto the toolbar, arrow keys move between tools.
  • Radio groupsTab onto the selected radio, arrow keys move the selection.
  • Menus and menu buttons— one tab stop for the trigger, arrow keys navigate opened menus.
  • ListboxesTab onto the listbox, arrow keys move the selection.

For the full interaction patterns behind tablists, menus, and accordions, see Accessible Tabs, Accordions, and Menus with ARIA.

When roving tabindex is the wrong tool

Forms, lists of cards, and arbitrary navigation do not use roving tabindex. Each item in those is its own focusable tab stop. Use roving tabindex only for widgets that semantically behave as a single control with internal navigation.

What to test in common UI patterns

Forms

  • Every field is reachable with Tab in the correct order.
  • Labels are associated with their controls; focusing a label does not steal focus from the field.
  • Submit buttons are reachable and activate on Enter.
  • Validation errors are announced and focusable; focus moves to the first invalid field on submit.

Dropdowns and disclosures

  • The trigger is a <button> (not a <div>).
  • Enter or Space opens and closes the disclosure.
  • For menu-style dropdowns, arrow keys move between items and Esc closes the menu.
  • Focus returns to the trigger on close.

Tabs

  • Tab list uses roving tabindex; only the active tab is in the tab order.
  • Left and right arrow keys move between tabs; Home and End jump to the first and last tabs.
  • Activating a tab (either automatically on arrow or manually on Enter/Space) shows its associated panel.

Modals and dialogs

  • Focus moves into the modal on open.
  • Focus is trapped inside the modal while it is open.
  • Esc closes the modal where appropriate.
  • Focus returns to the trigger (or a logical next target) on close.

Command menus and action menus

  • A keyboard shortcut opens the menu.
  • Focus moves into the search field or the first result.
  • Arrow keys move between results; Enter activates.
  • Esc dismisses; focus returns to the previous context.

Anti-patterns that fail keyboard users

  • Clickable <div> or <span> used as a button. Looks like a button, behaves like a button for mouse users, silently skipped by keyboard users.
  • Hidden focus styles. outline: none with no accessible replacement. A pointer-only product.
  • Focusable elements inside hidden panels. Collapsed accordions or closed dialogs whose inner controls remain in the tab order. The user tabs into invisible UI.
  • Broken skip links. Present in markup, invisible on focus, or pointing at a target that does not move focus.
  • Tabbing into off-screen UI unintentionally. Menus that are visually hidden with display: none (good) versus moved off-screen with their controls still focusable (bad).
  • Focus not returning after overlays. Modals and menus that close without sending focus back to a sensible location leave the user stranded at the top of the document.
  • Custom components that ignore the keyboard model. A toggle that only responds to click, a combobox that does not handle arrow keys, a menu that ignores Esc. Users expect the convention to work.
  • Positive tabindex values. tabindex="5" does not magically fix focus order. It usually makes it worse.

The bug that hides in plain sight

A web app can look visually complete and still be functionally unusable if keyboard users cannot see focus, reach controls, or exit interactive overlays safely. Automated tests, visual polish, and successful mouse use cover none of this.

The manual test protocol

Run this protocol on every key template and high-value flow before release. Start every pass with the mouse physically moved out of the way or replaced with a keyboard-only input device.

  1. Start at the top of the page with no mouse. Load the URL fresh.
  2. Press Tab through the entire interface. Do not skip sections.
  3. Confirm focus visibility at every stop. If you ever have to guess where focus is, that is a failure.
  4. Confirm the order is logical. Does it match the way a sighted user would read and act on the page?
  5. Activate controls with Enter and Space where appropriate; check that each behaves per its widget convention.
  6. Test arrow-key navigation inside composite widgets (tabs, menus, toolbars, radio groups).
  7. Open overlays and test containment— modals, side panels, popovers. Tab must stay inside.
  8. Close overlays and verify focus return to a sensible location, usually the trigger.
  9. Test skip links on the first Tab of a fresh page load.
  10. Repeat on key templates and flows. Home, sign-in, main dashboard, checkout, settings, and any template that renders dynamic content.
  11. File defects with keystrokes, not screenshots. Describe the exact sequence of keys and what happened. This is the language engineers can act on.

Automated checks vs. manual checks

Automated accessibility tooling has real value. Linters and scanners can catch:

  • Interactive elements that are not keyboard-focusable.
  • Form controls without associated labels.
  • Missing landmark regions and heading structure.
  • Obviously inaccessible names on buttons and links.
  • Duplicate IDs, colour-contrast issues, and similar structural defects.

What they cannot fully decide:

  • Whether a particular focus order makes sense for the page and task.
  • Whether a custom widget’s keyboard model matches user expectations.
  • Whether focus return after an overlay closes lands somewhere sensible.
  • Whether a skip link actually shortcuts to useful content.

Treat automated tools as the first pass. Use them in CI, fix the structural defects they find, then perform the manual pass above. Neither alone is sufficient — together they catch far more than either does on its own.

Test area at a glance

A single reference for the core checks. Use it during reviews and sign-offs.

Test areaWhat passing looks likeCommon failureWhy it matters
Focus visibilityFocus is always obvious against every background.outline: none with no replacement.Users cannot operate what they cannot see.
Focus orderSequence matches reading and task flow.CSS visual order diverges from DOM order.Unpredictable focus breaks task completion.
Skip linkFirst Tab reveals a working skip link that moves focus to main content.Missing, invisible on focus, or non-functional.Keyboard users must otherwise tab past the whole nav on every page.
Custom widgetsReachable, activatable, and using the correct arrow- and Esc-key patterns.Clickable <div> elements and components that ignore keyboard conventions.Whole regions of the UI become invisible to keyboard users.
Modal / dialogFocus enters, is trapped, and returns on close.Focus escapes behind the modal or disappears on close.Users can be locked out or stranded.
Keyboard activationEnter and Space activate controls per widget convention.Controls that only respond to click.Visible controls become non-functional without a mouse.

Where structural checks fit in the workflow

Manual keyboard QA is the decisive test, but it is faster and more focused when obvious structural defects have already been cleared. The CodeAva Website Audit can fetch a live URL and surface structural issues on the page — missing landmarks, non-semantic interactive elements, and the kind of obvious accessibility defects that would otherwise consume the first twenty minutes of a manual pass.

It is not a replacement for keyboard QA. No automated scanner can tell you whether the focus order of a specific dashboard makes sense to a human completing a task, whether your command menu behaves correctly when closed with Esc, or whether your skip link lands somewhere useful. Use the audit to clean up the structural layer, then do the manual pass described above for the interaction layer.

Keyboard operability is product quality

Keyboard testing reveals whether the app is genuinely operable, not just visually polished. Semantic HTML makes keyboard accessibility the default path: a <button> handles Enter, Space, and focus by itself; a clickable <div> does not. Custom components worth building are the ones that respect the conventions keyboard users already rely on.

Make keyboard QA part of routine release testing, not a once-a-year audit. Twenty minutes of manual Tab-only testing on your highest-traffic flows catches the majority of regressions. The test protocol above is designed to fit into that budget.

When you are ready to pair this with structural checks, run the CodeAva Website Audit against a live URL to clear the easy issues first. For the deeper widget patterns referenced here, read Accessible Tabs, Accordions, and Menus with ARIA and Accessible React Modal Focus Management. They cover the interaction details this checklist validates from the outside.

#accessibility#keyboard-navigation#focus-management#WCAG#QA#manual-testing#a11y#modal#tablist#skip-link

Frequently asked questions

More from Kuda Zafevere

Found this useful?

Share:

Want to audit your own project?

These articles are written by the same engineers who built CodeAva\u2019s audit engine.