Web Accessibility Checklist

Chapter 2 of 8 · 6 checks · 3 min

Keyboard and focus

Everything a mouse can do must work from the keyboard, and people must always see where focus is. These checks cover the keyboard pass every release should get.

Show
  1. Make every function work with the keyboard alone (SC 2.1.1)Must

    Why it matters and how to check

    People who cannot use a mouse, including many screen reader users and people with tremors, rely on the keyboard. Menus, dialogs, carousels, date pickers, drag and drop and custom selects are where keyboard support usually breaks.

    To verify, put the mouse away and complete your three most important tasks, such as sign up, search and checkout, using only Tab, Shift+Tab, Enter, Space, Escape and the arrow keys. Note every step where you get stuck or have to guess.

  2. Let focus leave every component with the keyboard (SC 2.1.2)Must

    Why it matters and how to check

    WCAG 2.2 requires that if focus can move into a component, the user can move it away with the keyboard alone, and must be told how if it takes more than Tab or the arrow keys. Embedded players, code editors and third party widgets are common traps.

    To verify, tab into every embedded editor, map, video player and iframe on the page, then try to tab out of it in both directions. If you cannot leave without the mouse, it is a trap. A modal dialog that keeps focus inside on purpose is fine, as long as Escape or a close button lets you out.

  3. Show a clearly visible focus indicator on every focusable element (SC 2.4.7)Must

    Why it matters and how to check

    A global outline: none in a CSS reset leaves keyboard users unable to see where they are. Keep a strong focus style, using :focus-visible so mouse users do not see it on click. WCAG 2.2 adds Focus Appearance (2.4.13, level AAA), which describes a good target: an indicator at least 2 CSS pixels thick with at least 3:1 contrast against the unfocused state.

    To verify, tab through the whole page and confirm you can always see which element has focus, including on dark sections, images and buttons that change colour on hover. Search your CSS for outline: none and outline: 0 and check each has a visible replacement.

    Example

    CSS
    :focus-visible {
      outline: 2px solid var(--focus-ring);
      outline-offset: 2px;
    }
  4. Keep focused elements from being hidden behind sticky headers and banners (SC 2.4.11)Must

    Why it matters and how to check

    New in WCAG 2.2: when an element receives keyboard focus, it must not be entirely hidden by content you added, such as a sticky header, a cookie banner or a chat bubble. This happens on long pages when the browser scrolls the focused element under a fixed header.

    To verify, open the page with the cookie banner still showing and tab down through a long page, then Shift+Tab back up. The focused element should always be at least partly visible. A CSS fix is scroll-padding-top on the scrolling element, equal to the sticky header height.

    Example

    CSS
    html {
      scroll-padding-top: 5rem; /* height of the sticky header */
    }
  5. Keep the focus order the same as the reading order (SC 2.4.3)Should

    Why it matters and how to check

    Focus should move through the page in the order people read it. Positive tabindex values and CSS that visually reorders content (such as order in flexbox or grid placement) break this, and focus jumps around the screen.

    To verify, tab through each page and watch the focus move. It should go top to bottom and left to right in the order of the layout, with no jumps. Search the codebase for tabindex values above 0 and remove them.

    Avoid

    • tabindex="1" or any positive tabindex
    • Reordering content visually with CSS while the DOM order stays different
  6. Move focus into dialogs, return it on close and reset it on route changesShould

    Why it matters and how to check

    When a dialog opens, focus should move into it; when it closes, focus should return to the button that opened it. The native dialog element opened with showModal() handles much of this for you. In a single page app, a route change does not reset focus the way a full page load does, so move focus to the new page heading or main region after navigation.

    To verify, open every dialog with the keyboard and confirm focus lands inside it, Tab stays inside it, Escape closes it and focus returns to the trigger. Then navigate between two routes with the keyboard and check that a screen reader announces the new page.