Keyboard accessibility: focus order and visible focus

Make every control reachable and usable from the keyboard, keep a sensible order, and always show where focus is.

  • Beginner
  • 6 min read
  • Updated 17 Oct 2026

What you will learn

  • Test a page with the keyboard only
  • Style :focus-visible indicators
  • Use tabindex correctly
  • Keep the visual and focus order in step

Before you start

Many people never use a mouse. They include blind people using screen readers, people with motor disabilities using keyboards or switches, and power users who are simply faster with keys. If something only works with a mouse, it does not work for them.

This lesson covers the keyboard rules in WCAG, how to test them, and how to style focus well.

The rules

Success criterionLevelIn short
2.1.1 KeyboardAAll functionality works from a keyboard, without needing specific timings
2.1.2 No Keyboard TrapAFocus can always be moved away from any component with the keyboard
2.4.3 Focus OrderAFocus moves in an order that preserves meaning and operability
2.4.7 Focus VisibleAAThe keyboard focus indicator is visible
2.4.11 Focus Not Obscured (Minimum)AAThe focused element is not entirely hidden by other content
3.2.1 On FocusAReceiving focus does not trigger a change of context, such as opening a new page

Test with the keyboard first

Keyboard testing needs no tools, and it finds serious problems fast. Put your mouse aside and use these keys:

KeyAction
Tab / Shift+TabMove to the next / previous focusable element
EnterFollow a link, press a button, submit a form
SpacePress a button, tick a checkbox, scroll the page
Arrow keysMove within radio groups, selects, tabs and menus
EscapeClose a dialog, menu or popup

Safari on macOS does not move focus to links with Tab by default. Turn on "Press Tab to highlight each item on a webpage" in Safari's Advanced settings, or use Option+Tab.

As you tab through, check:

  1. Can you reach every link, button and form control?
  2. Can you see where focus is at every step?
  3. Does the order follow the visual layout and reading order?
  4. Can you operate everything: menus, sliders, carousels, custom controls?
  5. Can you leave every component, including embedded videos and maps?
  6. Does focus ever disappear behind a sticky header or off-screen?

Native elements are keyboard accessible

Links with an href, buttons, and form controls are focusable and operable by default. Custom elements are not.

HTML
<!-- Not focusable, ignores Enter and Space -->
<div class="button" onclick="saveDraft()">Save draft</div>

<!-- Focusable, works with Enter and Space, announced as a button -->
<button type="button" onclick="saveDraft()">Save draft</button>

Recreating a button on a div needs tabindex, a role, key handlers for both Enter and Space, and disabled handling. Using button gives you all of that for free. Lesson 11 compares links and buttons in detail.

tabindex: three values that matter

ValueEffectWhen to use
tabindex="0"Adds the element to the tab order at its place in the sourceCustom widgets that must receive focus, such as a scrollable region
tabindex="-1"Focusable by script, but not by TabTargets you move focus to with element.focus(), such as headings after navigation
Positive values, such as tabindex="3"Jumps the element ahead of everything elseNever. It breaks the natural order and is very hard to maintain
HTML
<!-- A scrollable code sample that keyboard users can scroll with the arrow keys -->
<pre tabindex="0" aria-label="Example output"><code>...long output...</code></pre>

Focus order follows the DOM

Focus moves in source order. CSS that rearranges content visually, such as order in flexbox, grid-template-areas, flex-direction: row-reverse or absolute positioning, does not change the focus order. Users then see focus jump around unpredictably.

CSS
/* Visual order: Buy, Details. Focus order: Details, Buy. Confusing. */
.actions {
  display: flex;
  flex-direction: row-reverse;
}

Fix the HTML order instead of reordering with CSS. If a layout must change across breakpoints, check the tab order at each one.

Visible focus

Browsers draw a default focus ring, but many sites remove it with outline: none and never replace it. That fails 2.4.7.

Use :focus-visible, which matches when the browser decides a focus indicator should be shown: always for keyboard focus, and for text inputs even after a click. It avoids rings on mouse clicks on buttons, which was the usual reason people removed outlines.

CSS
:focus-visible {
  outline: 3px solid #1a5fb4;
  outline-offset: 2px;
}

/* On dark sections, switch the colour so it still contrasts */
.dark-band :focus-visible {
  outline-color: #ffdd00;
}

Good focus indicators:

  • Have at least 3:1 contrast against the surrounding background (1.4.11 Non-text Contrast).
  • Are thick enough to see: 2 pixels or more. 2.4.13 Focus Appearance (AAA) sets detailed size and contrast targets worth aiming for.
  • Use outline, not only box-shadow. Windows contrast themes remove box shadows but keep outlines.
  • Do not rely on a colour change alone, such as a slightly darker background.

Never write *:focus { outline: none; } without a replacement. If you must hide the default ring for design reasons, provide a clearer one with :focus-visible in the same rule set.

Do not hide focus behind sticky content

2.4.11 Focus Not Obscured (Minimum) fails when a sticky header, cookie banner or chat widget completely covers the focused element. The scroll-padding property keeps scrolled-to elements clear of a fixed header.

CSS
html {
  scroll-padding-top: 5rem; /* height of the sticky header plus a margin */
}

Avoid keyboard traps

A keyboard trap happens when focus enters a component and cannot leave, for example an embedded player that captures Tab. 2.1.2 requires that you can always move away. If a component needs non-standard keys to exit, tell users how.

Modal dialogs are a deliberate exception: focus should stay inside an open modal, but Escape and a close button must always let users out. Lesson 15 builds one.

Do not change context on focus

Under 3.2.1 On Focus, simply moving focus to an element must not submit a form, open a new window or move focus somewhere else. Act on a deliberate action such as a click or Enter, not on focus.

Summary

  • Every function must work with the keyboard alone (2.1.1), with no traps (2.1.2).
  • Test by tabbing through each page before using any tool.
  • Native links, buttons and form controls are keyboard accessible; prefer them to custom elements.
  • Use tabindex="0" and tabindex="-1" only when needed, and never positive values.
  • Keep DOM order and visual order the same.
  • Style :focus-visible with a clear outline of at least 3:1 contrast, and keep focus clear of sticky content.

Further reading