On each web page, is the tab order consistent?
A keyboard user navigating follows the tab order like a metro line: if it skips stations or backtracks without reason, they get lost. An incoherent order is outright exclusion for people who cannot use a mouse. They have no way to recover a focus that flies away.
This criterion verifies that the Tab key moves focus in a logical and predictable order. "Logical" does not necessarily mean left to right and top to bottom: a column-by-column order can be perfectly coherent in a form table. What matters is that the user understands where they are going and why. A focus that jumps from the form to the footer and then back to the main menu is never defensible.
The second part concerns dynamically inserted content: modals, dropdowns, AJAX panels. When a button triggers a modal window to display, focus must enter that modal. When the modal closes, focus must return to the triggering element. If focus stays behind the modal while it is open, the keyboard user ends up interacting with content they cannot see.
Positive tabindex attribute (value > 0) is the leading cause of failures on this criterion in audits. As soon as a developer places a tabindex="1" somewhere, they create a priority queue that short-circuits the natural DOM order.
2 tests to ensure that tab order is logical
Coherent tab order in page
- From the top of the page, press Tab to navigate through all interactive elements in order.
- Press Shift+Tab to navigate backward.
- Verify that the focus sequence is coherent with the content structure: a focus that crosses a form, then a menu, then returns to the form is incoherent.
- If the page contains an open modal window, verify that tabulation stays confined to elements within that modal (no access to elements behind it).
- The order does not need to follow visual left-to-right/top-to-bottom reading, but it must remain understandable and predictable.
- If the order is coherent: test validated. If focus jumps without logic between unrelated areas: test failed.
Correct focus repositioning after content insertion
- Identify all elements that trigger insertion or display of content by script: modal open buttons, accordions, AJAX loading, dropdown menus.
- Place keyboard focus on the triggering element (with Tab).
- Activate it (Enter or Space).
- Verify that focus moves to the newly displayed content (e.g., first interactive element of the modal, title of the loaded area).
- Close the inserted content (if applicable) and verify that focus returns to the original triggering element.
- If focus remains coherent at each step: test validated. If focus is lost, stays on the trigger while the modal is open, or lands in an unrelated area: test failed.
Examples
❌ Non-compliant : Positive tabindex values that break natural order
<nav>
<a href="/home" tabindex="3">Home</a>
<a href="/services" tabindex="1">Services</a>
<a href="/contact" tabindex="2">Contact</a>
</nav>
<main>
<h1>Welcome</h1>
<a href="/discover">Discover our offers</a>
</main>The actual tab order will be: Services → Contact → Home → Discover our offers. The keyboard user navigates in an order that matches neither the visual order nor the DOM order. If a developer adds a fourth link without tabindex, it will come last, after "Discover our offers", even if it is in the middle of the menu. This is scenario F44 of the WCAG.
❌ Non-compliant : Modal without focus management
<button id="open-modal">Open terms</button>
<div id="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<h2 id="modal-title">Terms of Use</h2>
<p>Terms content...</p>
<button id="accept">Accept</button>
<button id="close-modal">Close</button>
</div>
<script>
document.getElementById('open-modal').addEventListener('click', function() {
document.getElementById('modal').style.display = 'block';
// focus not moved to modal
});
</script>The modal displays but focus remains on the "Open terms" button behind it. The keyboard user continues navigating in the main page content, invisible behind the modal. They cannot reach "Accept" or "Close" without using a mouse.
✅ Compliant : Natural DOM order without disruptive tabindex
<nav>
<a href="/home">Home</a>
<a href="/services">Services</a>
<a href="/contact">Contact</a>
</nav>
<main>
<h1>Welcome</h1>
<a href="/discover">Discover our offers</a>
</main>Without tabindex, tabulation follows the natural DOM order: Home → Services → Contact → Discover our offers. This order matches the visual order and the page logic. Adding a fifth link to the menu will automatically place it in the correct position in the tab order, without any maintenance.
✅ Compliant : Modal with correct focus management
<button id="open-modal">Open terms</button>
<div id="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title" style="display:none">
<h2 id="modal-title" tabindex="-1">Terms of Use</h2>
<p>Terms content...</p>
<button id="accept">Accept</button>
<button id="close-modal">Close</button>
</div>
<script>
const modal = document.getElementById('modal');
const openBtn = document.getElementById('open-modal');
openBtn.addEventListener('click', function() {
modal.style.display = 'block';
modal.querySelector('h2').focus(); // focus moved to modal
});
document.getElementById('close-modal').addEventListener('click', function() {
modal.style.display = 'none';
openBtn.focus(); // focus returned to trigger
});
</script>When opened, focus immediately enters the modal (on the title, made focusable via tabindex="-1"). The keyboard user navigates only within the modal. When closed, focus returns to the trigger button: the user resumes navigation where they left off.
Tips and pitfalls
⚠️ A single positive tabindex is enough to break everything
Setting tabindex="1" on a single element places it ahead of all elements without positive tabindex, and ahead of all those with tabindex="0". In audits, it is enough to inspect the DOM with developer tools and filter on the tabindex attribute to uncover these time bombs. The basic rule: use tabindex only with values 0 (add to natural order) or -1 (remove from order but keep focusable by script).
⚠️ CSS order does not affect tab order
flexbox with order, grid with explicit placement, position: absolute: changing visual order via CSS does not change tab order, which follows the DOM. A menu displayed visually before main content but placed after in the HTML will be reached last by tabulation. This is the subject of WCAG technique C27: ensuring that DOM order and visual order are coherent.
⚠️ Rich components with internal focus (roving tabindex)
A carousel, a navigation tree, or a group of accessible radio buttons sometimes use the "roving tabindex" pattern: only one element of the component is in the tab order (tabindex="0"), others are at -1, and arrow keys manage internal navigation. This pattern is correct and coherent: test 12.8.1 is validated because the user enters the component with a single Tab, then navigates with arrows.
💡 Test with keyboard, not just by reading code
Actual tab order can differ from what the code suggests, especially due to dynamic JavaScript injections or frameworks that reorganize the DOM on render. The only reliable method: open the page, keep your hands off the mouse, and press Tab while observing focus movement. Enable outline in styles if needed (* { outline: 2px solid red !important } in debug CSS).
⚠️ RGAA note: order does not need to be linear
The RGAA explicitly states that a column-by-column tab order in a table is valid, even if it does not follow line-by-line reading order. The criterion does not impose a particular order, but an order coherent with content logic. A form organized in two columns can very well be tabbed column by column if that is the business logic (like the marriage register example in WCAG technique H4).
💡 SPA and client-side navigation pitfall
In single-page applications (React, Vue, Angular), each route change is a new render without page reload. If focus is not explicitly repositioned after navigation (generally on the <h1> title of the new view or on a page change notification element), the user loses their place. This is not strictly a tabulation bug, but a test 12.8.2 failure because content is inserted by script.
Frequently asked questions
Why can tabindex="0" create a non-compliant RGAA tab order?
No, tabindex="0" is safe. It places the element in the natural tab order, at its position in the DOM, without making it jump ahead of other elements. It is the correct value for making focusable an element that is not interactive by default (like a <div> with role="button"). It is positive values (tabindex="1", tabindex="2", etc.) that create a priority queue and break natural order.
How do you audit tab order on a complex page with dozens of interactive elements?
Two complementary approaches. First, DOM inspection: search for all tabindex attributes with a positive value (DevTools → Elements tab → Ctrl+F → tabindex). Then, direct keyboard testing: navigate Tab by Tab and note if focus crosses areas without apparent logic. For modals and dynamic components, activate each trigger and verify that focus enters the displayed area. A tool like taba11y (Chrome extension) visualizes tab order as an overlay.
How do you manage tab order when a modal is open in RGAA?
Yes. When a modal is open, focus must stay trapped inside (focus trap). The user must not be able to reach main page elements with Tab: they are visually hidden, they must be hidden from the keyboard too. WCAG technique SCR26 describes the implementation. In practice, a focus trap is managed with an event handler on keydown that intercepts Tab and Shift+Tab to keep them in the modal. Libraries like focus-trap-react or @zag-js/focus-trap handle this mechanism.
What relationship does RGAA impose between tab order and visual reading order?
None, and this is an important nuance of RGAA 12.8. The order must be coherent with content logic, not necessarily identical to visual reading order from left to right and top to bottom. A navigation menu placed visually to the right of a logo but first in the DOM is acceptable if this order is understandable. The problem arises when DOM order and visual order diverge to the point of disorienting the user: this is the case described by WCAG technique C27.
How do you fix tab order when a JavaScript framework reorganizes the DOM?
Test in real conditions, not by reading JSX or template source. Frameworks like React can reorder DOM nodes during reconciliation or via portals (React Portals), which teleport elements out of their logical position in the tree. If a modal is rendered in a portal attached to document.body, its tab order depends on its position in the actual DOM, not in the JSX. Verify order via DevTools on the rendered DOM, then test with the keyboard.