Accessibility failures are design decisions. Here is the proof. covered keyboard navigation in three paragraphs: who relies on it, the focus-indicator stat, and the skip link YouTube still gets wrong. That was the shape of the problem. This article is the practical guide: how tab order actually gets decided, why "just remove the ugly focus ring" breaks the product for a specific group of users every time, how to specify focus behavior for a modal so a developer implements it once instead of guessing, how to build a skip link correctly, and a two-minute test you can run on anything before it ships.
Who actually needs this
WebAIM's Screen Reader User Survey puts keyboard reliance among screen reader users at 97.6%. That number alone undersells the audience. A mouse asks for something specific: enough fine control in the hand to land a cursor on a small target and click it. Tremor, limited or no use of one or more fingers, paralysis, arthritis, and missing digits all remove that ability without removing the ability to press a key, use a switch device, or use a switch-adapted input that the browser treats exactly like a keyboard. This is a different axis than the mobility disabilities the CDC tracks, walking and climbing stairs, which mostly have no bearing on whether someone can use a mouse at all.
None of that requires a screen reader to be running. It requires the product to work when the pointer never moves. That is a much larger and much easier group to test for than "does it work with a screen reader," and it is the one this article is about.
Tab order is decided by the DOM, not the layout
2.1 What actually determines the sequence
Pressing Tab moves focus to the next focusable element in DOM order, the order elements appear in the underlying HTML, not the order they appear on screen. Most of the time those two orders match, because most layouts are built the same way they read. They stop matching the moment a layout is reordered visually without anyone touching the markup underneath it, and CSS makes that trivially easy to do by accident.
2.2 How a design decision breaks it without a single line of custom script
A CSS grid or flexbox `order` property, used to reflow a card layout for a narrower breakpoint, changes what the user sees without changing what the DOM contains. Tab order still follows the DOM. The result is a keyboard user tabbing from what looks like card one, to what looks like card three, because that is what the markup says is next, regardless of where the card was repainted on screen.
The live row below has exactly that bug, and it is safe to try. Click into it and press Tab.
Visual order 1, 2, 3, 4. Tab through it.
Same markup order, no CSS reorder. Tab through this one too.
Identical labels, identical visual position. The first row's Tab sequence is 2, 4, 1, 3. The second row's is 1, 2, 3, 4. Nothing about how either one looks tells you which is which.
2.3 The other way it breaks: tabindex
A positive tabindex value (`tabindex="1"`, `tabindex="2"`) forces an element to the front of the tab sequence, ahead of everything using the default order, and creates a second, competing order that has to be maintained by hand forever as the page changes. WCAG's own techniques documentation names this pattern as something to avoid. This project's own build rules ban positive tabindex outright for the same reason. `tabindex="0"` adds a naturally-unfocusable element into the default DOM-order sequence, which is safe. `tabindex="-1"` removes an element from the Tab sequence while still allowing it to receive focus programmatically, which is what makes it possible to move focus into a modal heading in section 4. Those are the only two values that belong in production markup.
Focus indicators are not optional styling
3.1 Where the 78% comes from
WebAIM's Million report found detectable focus-indicator issues on 78% of the home pages it analyzed. The most common root cause is `outline: none`, added to strip the browser's default focus ring off a button or link because it clashed with the visual design, with nothing added back in its place. Visually the fix looks clean. For a keyboard user, the interface stops telling them where they are.
3.2 :focus-visible fixes the actual complaint
The complaint behind most `outline: none` decisions was never "focus indicators are bad." It was "the focus ring showing up after a mouse click looks broken." `:focus-visible` solves that directly: it lets the browser show the indicator when focus arrives from the keyboard and suppress it when focus arrives from a pointer click, without a single line of JavaScript. There is no remaining reason to reach for `outline: none` in 2026.
Tab to each button. Same component, three implementations, three completely different experiences for a keyboard user.
3.3 WCAG 2.2 raised the bar on what "visible" means
SC 2.4.7 Focus Visible (AA) only requires that a keyboard focus indicator exist. WCAG 2.2 added two success criteria that go further: SC 2.4.13 Focus Appearance (AAA) sets a minimum size and contrast for the indicator itself, so a 1px low-contrast outline can technically satisfy 2.4.7 while still failing 2.4.13. SC 2.4.11 Focus Not Obscured (Minimum) (AA) requires that the focused element not be entirely hidden by other content, the sticky header or cookie banner covering it up. Both point at the same failure mode: an indicator that is present but useless because it is too faint, too small, or physically hidden behind something else on the page.
Focus management inside modals and dropdowns
4.1 Same modal, the keyboard side of the spec
What is ARIA and why product designers need to understand it covers the delete-project modal from the ARIA side: the role, the label, the behavior a designer has to write down for a developer to implement correctly. This is the same modal, tested. A designer can write the correct behavior notes and still ship a modal that fails, because ARIA attributes and actual keyboard behavior are two separate implementations of the same intent, built at different points in the code. The only way to know the second one exists is to run it with the keyboard yourself.
- Click triggerModal opens. Expected: focus moves to the dialog heading, not the first button inside it.
- TabFocus moves through the modal's own controls only. Expected: it never reaches the page behind it.
- Shift+Tabfrom the first control inside the modal. Expected: focus wraps to the last control inside the modal, not out to the page.
- EscapeModal closes. Expected: focus returns to the exact element that opened it, not the page body.
Four keys, four expected results. Any modal that fails one of these rows fails for every keyboard-only user, regardless of what the ARIA markup says.
4.2 The failure in the opposite direction: a keyboard trap
SC 2.1.2 No Keyboard Trap (A) exists because the opposite failure is just as disqualifying: focus enters a widget, usually a third-party embed, a rich text editor, or a custom date picker, and Tab or Escape no longer moves it back out. A modal that traps focus on purpose, inside itself, while it is open, is correct and required. A component that traps focus by accident, with no way to leave, fails outright. Both are tested with the same four keys above.
The skip link
5.1 What it is for
The previous article in this series pointed out that YouTube, built by the organization that wrote much of the standard, still does not implement its skip link correctly. Without one, a keyboard user arriving at any page with a repeated navigation has to tab through every item in it, every time, before reaching the content the page actually exists for.
5.2 How to build one
A skip link is the first focusable element in the page, visually hidden until it receives focus, pointing at an anchor on the main content region. On focus, it becomes visible and sits above everything else on the page. Activating it, by pressing Enter, moves focus directly to the target, skipping every navigation item in between. Every page on this site opens with exactly this pattern: a `` as the first element inside ``, and a matching `
No skip link
- Tab 1: Logo
- Tab 2–9: 8 nav links
- Tab 10: Language switch
- Tab 11: Search
- Tab 12: Contact CTA
- Tab 13: Main content, finally
13 tab presses before the content
With skip link
- Tab 1: "Skip to main content"
- Enter
1 tab press, 1 key press
Same page, same navigation. The only difference is one anchor link at the very top of the document.
The two-minute test any designer can run
No screen reader, no plugin, no developer required. Put the mouse out of reach, actually, not figuratively, and use only Tab, Shift+Tab, Enter, Space, the arrow keys, and Escape to complete the product's core flow. Four things to watch while you do it: can you reach every interactive element on the page, can you always see which one currently has focus, does the order you move through them match the order you'd read them in, and can you open and fully close every modal or dropdown without the pointer touching anything. A failure on any of the four is section 2, 3, or 4 above, not a new problem, the same six sources this series keeps coming back to, decided before a developer wrote a line of code.
Sources
- WebAIM. Screen Reader User Survey #10 Results. webaim.org/projects/screenreadersurvey10
- WebAIM. The WebAIM Million: The 2025 report on the accessibility of the top 1,000,000 home pages. webaim.org/projects/million
- W3C Web Accessibility Initiative. WCAG 2.2, SC 2.1.1 Keyboard, 2.1.2 No Keyboard Trap, 2.4.1 Bypass Blocks, 2.4.3 Focus Order, 2.4.7 Focus Visible. w3.org/TR/WCAG22
- W3C Web Accessibility Initiative. Understanding SC 2.4.11 Focus Not Obscured (Minimum) and SC 2.4.13 Focus Appearance. w3.org/WAI/WCAG22/Understanding/focus-appearance
- MDN Web Docs. :focus-visible. developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible
- W3C Web Accessibility Initiative. ARIA Authoring Practices Guide (APG), Dialog (Modal) Pattern. w3.org/WAI/ARIA/apg/patterns/dialog-modal