Focus order that jumps around the page
CSS reverses a row of buttons on screen while the markup keeps the old order, so Tab moves against the layout.
The problem
Keyboard users watch focus move against the layout, lose their place, and can press the wrong button.
A stationery shop's checkout ends with three actions at the right-hand end of a bar. On screen, from left to right, they read "Place order", "Save for later" and "Back to basket". Someone using a keyboard tabs into the bar. Focus lands on "Back to basket", the one on the far right. The next Tab moves left to "Save for later", and the next to "Place order". The ring travels against the direction the page reads, and the person watching it is never sure which button comes next.
The design wanted the main action first, so the stylesheet puts the row in flex-direction: row-reverse. The markup still lists the actions as back, save, place. The browser tabs through the markup, not through the pixels, so the order on screen and the order of focus are opposites. WCAG 2.4.3 asks for a focus order that preserves meaning and operability. Here the meaning is a row that reads one way, and focus travels the other.
Keyboard and switch users, who have only focus to tell them where they are. A screen magnifier user sees a small piece of the page, and a ring that leaps from the right edge to the left is easy to lose. Screen reader users hear the actions in markup order, which contradicts what a sighted colleague describes. Someone who expects focus to start on the first button they see presses Enter on "Back to basket" instead, and leaves the checkout.
Every control in the bar is focusable, named and reachable, so a scanner has nothing to flag. A check of the DOM sees a valid list. Finding the problem means comparing where each element is drawn with where it falls in the tab sequence, and few tools do that. Generated code makes it worse: it will happily add order or row-reverse to match a mockup and leave the markup alone.
Try it
Press Tab and watch which way focus moves.
Tab through the three buttons. Does focus move the way the row reads, left to right?
Focus last moved: (none yet)
A recreated demo. The broken version is intentionally inaccessible.
The fix
Reorder the markup to match the design, or drop the CSS that reverses it.
- 1Markup order is focus order: CSS may not reverse or reorder focusable items.
- 2Use only tabindex 0 or -1, never a positive number.
- 3Check each breakpoint, because layouts reorder at narrow widths.
html
<div class="actions"> <a href="/basket">Back to basket</a> <button type="button">Save for later</button> <button type="submit">Place order</button></div>css
.actions { display: flex; flex-direction: row-reverse; gap: 12px;}On screen the row reads place, save, back from left to right. Focus visits back, save, place. The CSS changed what people see and left the order that keyboards and screen readers use.
The markup now lists the actions in the order the design shows, and the CSS no longer reverses anything. Tab moves from "Place order" to "Save for later" to "Back to basket", left to right, as the page reads. The look is unchanged.
Each passes, because in both the order on screen and the order of focus are the same. Reordering the markup keeps the design exactly as approved, and costs an edit to the template, and to any tests or snapshots that rely on it. Removing the reversal costs nothing in code but changes how the bar looks, so it needs the designer's agreement. Choose by which order is right for the content: if the markup order is the natural sequence and the reversal was only styling, remove the reversal; if the design is fixed, change the markup.
row-reverse is only one of several ways to get here. The order property, column-reverse, grid placement with grid-area or grid-row, floats and absolute positioning can all move an element on screen without moving it in the markup. Search for them next to anything focusable.
Do not repair the order with tabindex="1", "2" and so on. Elements with a positive value are visited first, ahead of everything else on the page, so the numbers pull the buttons out of the flow of the page and every element added later has to be numbered too. Use tabindex="0" for a custom control that must be focusable and -1 for something focused by script, as in the skip link guide.
Layouts that change at a breakpoint are the usual source of a second bug. A sidebar that moves below the content on a phone, or a card whose image moves above its heading, needs the same check at that width. Right-to-left languages should be handled with dir="rtl", which reverses the layout and the focus order together, rather than with row-reverse.
Order has to preserve meaning, not match every pixel. A decorative control that sits slightly out of line does not fail. A step that falls before the one it depends on does. Content that appears on demand, such as a menu or an error message, should be inserted next to the control that opened it, or take focus when it opens; for dialogs see the modal guide. The newer reading-flow CSS property can align the two in browsers that support it, but support is not yet universal, so fix the markup.
Verify the fix
4 checks, no mouse.
Test with: Keyboard, Screen reader, Zoom
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat after layout or component styles change.
Limits
This is a starting pattern, not a guarantee for every application. It covers a static row of actions. Data grids, carousels and widgets that move focus with arrow keys follow their own patterns and need their own checks. Whether an order preserves meaning is a judgement about the content, so test the complete journey on your real pages.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.