Tab order is a promise the DOM makes
The Tab key follows the HTML, not the layout. CSS cannot change that order, a positive tabindex hijacks it, and aria-hidden does not remove a stop. One real walk found 162 stops where a list of the markup said 64. Here is why, and how to check.
Press Tab on a page and focus moves to the next thing. Which thing? Not the next thing on screen. The next focusable element in the HTML, in the order the elements were written. That is the promise. Everything that breaks keyboard navigation breaks it by making the screen and the HTML tell different stories.
The rule, with its one exception
The browser keeps one sequence. It is the DOM order of every focusable element: links with an href, buttons, form fields, and anything with tabindex="0". Elements that are display: none or visibility: hidden are left out. Disabled controls are left out.
The one exception is a positive tabindex. Any element with tabindex="1" or higher goes to the front of the sequence, lowest number first, before every element without one. So a single tabindex="5" on a button in the middle of a page makes it the first stop, ahead of the header. The fix is never a bigger number on something else. The ruleset calls this AW-KEY-002, and the Tab Walk lab has a checkbox that does exactly this to a Buy button so you can watch stop 1 move.
What CSS cannot do
CSS can put a card anywhere. order, flex-direction: row-reverse, grid placement, absolute positioning, floats. None of it changes the Tab sequence. So a row of three cards reordered with order: 3, 1, 2 still Tabs in the written order 1, 2, 3, which on screen is middle, right, left. Focus jumps backwards across the row. A sighted keyboard user sees it happen and is confused. A screen reader user hears the right order and never knows the screen disagrees.
The Reading Order note covers this from the screen reader's side. From the keyboard's side the lesson is the same. If a card should come first, put it first in the HTML. AW-ORDER-001 flags the CSS; AW-ORDER-002, added with this note, asks an agent to walk the page and compare the stops it visits with where they sit on the screen.
Three ways to hide, two of which work
| Hiding | On screen | In the Tab sequence | Announced |
|---|---|---|---|
display: none |
No | No | No |
visibility: hidden |
No, space kept | No | No |
aria-hidden="true" |
Yes | Yes | No |
The third row is the trap. aria-hidden hides an element from the accessibility tree and from nothing else. The link is still on the page and still a Tab stop. A screen reader user presses Tab, lands on it, and hears nothing, because the thing they are on has been declared not to exist. That is AW-ARIA-002, and the lab's hide control shows the stop staying in the sequence with a ghost badge.
Stops that do nothing
tabindex="0" on a container, a card, a list item, or a heading, makes it a Tab stop. Nothing happens when you reach it. Three cards with tabindex="0" are three extra presses between the hero and the form, each landing on a box that does not respond to Enter. The fix is to leave tabindex off anything that is not interactive. A container that needs to receive focus from a script gets tabindex="-1", which lets a script focus it without putting it in the sequence.
Count by walking, not by listing
Here is the number that changed how the lab works. One real run on a large public home page listed 64 focusable elements from the markup and then walked them with Tab. The walk visited 162 stops.
| Method | Stops | What it sees |
|---|---|---|
| List focusable elements in the markup | 64 | Elements present when the page is read |
| Walk the page with Tab | 162 | Everything focus actually lands on, including controls that appear only when something before them gets focus |
Menus open when their button is focused. Disclosures expand. Carousels reveal their dots. A list made from the markup sees the page at rest. A walk sees the page as a keyboard user meets it. When the two numbers differ, the walk is the one that happened.
The lab plants a small version of this: a "More ways to pay" button that opens on focus and adds three links. At rest the sample page has 13 stops. Walked, it has 16.
Checking a page
- Press Tab from the top and say each stop out loud. If you say something that is behind or to the left of the last thing you said, the order is broken.
- Search the CSS for
order:,row-reverse,column-reverse, and explicitgrid-roworgrid-column. Each one is a place where the screen may disagree with the HTML. - Search the HTML for
tabindex=with a value above 0. Remove it and fix the DOM instead. - Search for
aria-hidden="true"on anything that contains a link, button, or field. Either remove the focusable content or hide it for real. - If you can script a browser, walk Tab and count. Compare the count with your list. Trust the walk.
Try it
Open the Tab Walk lab, press the walk button, and watch the numbers. Then tick each checkbox in turn and walk again. The readout under the page says how many stops moved out of reading order and how many are hidden but still reachable.
Sources
- w3.org, Understanding Success Criterion 2.4.3: Focus Order
- w3.org, Understanding Success Criterion 2.4.7: Focus Visible
- html.spec.whatwg.org, the tabindex attribute and sequential focus navigation order