Quick Fix · Forms · Keyboard

Multi-Step Form Wizards: Where Keyboard Focus Goes to Get Lost

Illustration of a three-step progress bar with a footprint icon wandering off the path between steps two and three, in an oxblood and cream editorial style
  • Quick Fix
  • Forms
  • Keyboard

Try this right now: pull up your own checkout or signup wizard, put your mouse away, and tab your way to the "Next" button using only your keyboard. Hit Enter. Now, without touching your mouse, hit Tab again and see where you land.

If you have no idea where your cursor just went — or if it's still sitting on the ghost of the old "Next" button, which has now conveniently vanished from the DOM — you've just found the bug that quietly breaks multi-step forms for keyboard and screen reader users everywhere. It's an easy miss during development, because a mouse user never notices it. Their eyes just jump to the new content. A keyboard user's focus doesn't jump anywhere on its own. It has to be told.

The good news: this is a small, well-understood fix, not a redesign. Let's get into it.

The Stat: WCAG Success Criterion 2.4.3 Focus Order (Level A) requires that when content can be navigated sequentially, focusable components receive focus in an order that preserves meaning and operability — a requirement that applies directly to what happens to keyboard focus after each "Next" step in a wizard. (Source: W3C WCAG 2.1)

Focus management across a three-step form wizard Three numbered step boxes, Step 1 through Step 3, connected by arrows showing forward progress. Between Step 1 and Step 2, an annotation reads "focus should land here, not stay on the old Next button." A dashed focus-outline icon moves from the old Next button position onto the top of the Step 2 box. Step 1 Shipping info Step 2 Payment Step 3 Review & submit old focus position

focus should land here, not stay on the old Next button

Each step still needs visible field labels Heading announces the new step Progress state updates for AT

Why focus goes missing in the first place

Most wizards are built as a single page that swaps visible content in and out — a step counter increments, one <div> hides, another shows. Visually, this reads as "moving forward." But the DOM doesn't know that, and neither does the browser's focus system. When the "Next" button's containing block gets hidden or its content is replaced, focus can:

  • Stay attached to a button that no longer visually exists, going nowhere on the next Tab press.
  • Reset all the way to the top of the <body>, forcing a screen reader user back through your header and nav on every single step.
  • Just vanish, landing on nothing at all — which some browsers report as focus silently returning to the document body.

None of these are "broken" in a way that throws a console error. They're broken in a way that only shows up when a real person tries to get through your form without a mouse — which is exactly why they slip through so often. QA scripts click through happy paths with a mouse or a testing framework that simulates clicks, not real Tab-key navigation. Automated accessibility scanners can flag missing labels or bad contrast, but "where does focus go after this JavaScript state change" is a behavioral question that's much harder to catch without someone actually driving the interface by keyboard, step by step.

It gets worse on single-page applications built with React, Vue, or similar frameworks, where "navigating" to a new step often means nothing more than swapping which component is mounted. There's no real page load, no browser-native navigation event, nothing that would normally reset or redirect focus. The framework re-renders a chunk of the DOM, the old button's node might get destroyed entirely, and focus has nowhere defined to go unless a developer explicitly told it where to land. This is the exact scenario 2.4.3 was written to prevent, decades before SPAs existed, but it applies with even more force now that so much of the web behaves this way.

The fix: WCAG 2.4.3 Focus Order, applied to wizards

WCAG 2.4.3 Focus Order requires that focusable content receive focus in an order that preserves meaning and operability. For a static page, that mostly means "don't scramble your tab order with weird tabindex values." For a dynamic multi-step wizard, it means something more specific: when the visible content changes, focus has to move along with it, deliberately.

The standard pattern, drawn from the ARIA Authoring Practices Guide's patterns for dialogs and dynamic content regions, looks like this:

  1. When "Next" is clicked and step 2 becomes visible, programmatically move focus to a logical anchor inside step 2 — typically its heading or its first form field.
  2. Make that anchor focusable if it isn't already. A heading like <h2 tabindex="-1">Payment details</h2> can receive programmatic focus without being in the normal tab order.
  3. Fire the focus move with JavaScript (element.focus()) right after the new step renders — don't rely on the browser to figure it out.
  4. Keep the step heading's text descriptive enough that a screen reader announcing it ("Payment details, heading level 2") tells the user exactly where they landed.

Do the same thing in reverse for "Back." Users move both directions through a wizard, and focus needs to follow them both ways.

A few implementation notes that trip people up:

  • Don't focus the <h2> permanently into the tab order. Using tabindex="-1" makes an element programmatically focusable — reachable via .focus() in code — without adding it to the natural Tab sequence. That's exactly what you want: a one-time landing spot, not an extra stop every time someone tabs through the page later.
  • Time the focus call correctly. If your framework animates the step transition, don't fire .focus() before the new content has actually mounted and become visible — some screen readers won't announce a focus change on an element that's still display: none or mid-transition. Fire it after the new step is fully in the DOM and visible.
  • Announce the step count too, not just the heading. If your progress indicator says "Step 2 of 3," that text should be part of what gets read out, either by including it near the heading you're focusing or by using an aria-live region for the step counter itself. A sighted user sees the progress bar update instantly; a screen reader user needs to be told the same thing in words.
  • Test with a real screen reader, not just Tab key visual inspection. Watching the blue focus ring move to the right place tells you focus order is technically correct. It doesn't tell you whether what gets announced actually makes sense to someone who can't see the screen at all.

Don't forget labels on every step

It's common to see teams polish the first step of a wizard — clean labels, clear instructions, solid error messages — and then get sloppy by step three, because "the user's already committed, they'll figure it out." They won't, and they don't have to. WCAG SC 3.3.2 Labels or Instructions (Level A) applies to every field on every step, not just the first screen a user sees. A payment form on step 2 needs the same visible, programmatically associated labels as the shipping form on step 1.

WebAIM's guide to form accessibility is worth bookmarking here — it covers label association, error identification, and validation timing in more depth than any single blog post can, and it applies just as much to step 3 of your wizard as to a one-page contact form.

A quick self-audit checklist

Run through this on your own wizard before you ship the next update:

  • Tabbing past "Next" after a step change lands focus somewhere specific and sensible — not nowhere, not back at the top of the page.
  • The new step's heading or first field receives focus programmatically (via .focus() in JavaScript), not left to browser default behavior.
  • Every field on every step has a visible, programmatically associated <label> — not just the first step.
  • "Back" moves focus just as deliberately as "Next" does.
  • The step heading text is descriptive enough to orient a screen reader user immediately ("Payment details" rather than just "Step 2").
  • Progress indicators (like "Step 2 of 3") update in a way assistive technology can perceive, not just visually.
Wizard moment What should happen to focus
Click "Next" Focus moves to the new step's heading or first field
Click "Back" Focus moves to the previous step's heading or first field
Validation error on submit Focus moves to the first invalid field's error message
Final submission Focus moves to a confirmation heading or status message

None of this requires a framework migration or a redesign — it's a handful of .focus() calls placed at the right moments. But it's exactly the kind of thing that's invisible until you test with a keyboard, which means it's also exactly the kind of thing that ships broken by default.

If you'd rather not hand-test every step of every flow on your site, a scan can catch these focus-order gaps automatically. Get a free scan that flags focus-order problems in your checkout flow before your next customer runs into them — start with a free audit.