Back to blog
accessibilityWCAGkeyboard

What is a positive tabindex (and why it breaks keyboard navigation)

Every developer eventually meets a broken tab order. Focus jumps from the header straight to the footer, skipping the sidebar. The close button on a modal is the last thing the keyboard reaches. So someone drops tabindex="3" on a couple of elements, tabs through once, sees it “work”, and ships it.

Six months later nobody can explain why keyboard navigation on that page is a mess for everyone else.

Positive tabindex is the classic fix that looks like a solution and is actually a slow-motion bug. Here’s what it really does, and what to reach for instead.

How tabindex works

The tabindex attribute controls whether an element is focusable and where it sits in the tab sequence. It has three meaningful states:

  • tabindex="-1": Focusable programmatically (via JavaScript .focus()), but removed from the natural tab sequence.
  • tabindex="0": Joins the natural tab sequence at its position in the DOM. Useful for making non-interactive elements (like a custom <div> widget) keyboard-focusable.
  • tabindex="N" where N > 0: A positive tabindex. This is where things go wrong.

Elements with a positive tabindex are focused first, ahead of everything in the normal flow, in ascending numerical order. So tabindex="1" comes before tabindex="2", which comes before tabindex="3". Only once that whole positive group is exhausted does the browser fall back to source order for the tabindex="0" and natively-focusable elements. (Elements sharing the same positive value are visited in DOM order among themselves.)

Why positive tabindex breaks navigation

Imagine a page with a navigation bar at the top, a main content area, and a footer. The DOM order is sensible — nav, then content, then footer. But someone added tabindex="2" to a “Subscribe” button in the footer and tabindex="1" to a sidebar link.

Here’s what happens when a keyboard user presses Tab on that page:

  1. Sidebar link (tabindex="1")
  2. Subscribe button in the footer (tabindex="2")
  3. Every other interactive element in the nav
  4. Every other interactive element in the content area
  5. Everything else in the footer

The user gets yanked to the bottom of the page, then back up to the top, before the layout they can see has any say. And here’s the part that surprises people: those two attributes didn’t just reorder two elements. They reordered the entire page. The moment one positive tabindex exists, every other focusable element gets pushed behind it, so the natural order is broken for the whole document, not the one button you touched.

That’s a direct hit on WCAG 2.1 Success Criterion 2.4.3 (Focus Order) at Level A, which requires the focus sequence to preserve meaning and operability. When the navigation order and the reading order drift apart, you can trip SC 1.3.2 (Meaningful Sequence) as well.

The real problem positive tabindex “solves”

Nine times out of ten, reaching for tabindex="2" means the DOM order and the visual order have come apart. A sidebar sits visually on the left but comes after the main content in the source. A sticky header is written at the bottom of the template. A Grid or Flexbox layout rearranges boxes on screen without moving a single node in the DOM.

That mismatch is the actual bug, and tabindex doesn’t fix it. It just hides it. The fix is to align the DOM order with the order you want people to move through.

<!-- Wrong: CSS paints the aside on the left, but it comes after main in the DOM -->
<main>...</main>
<aside>...</aside>

<!-- Right: DOM order matches the intended reading/navigation order -->
<aside>...</aside>
<main>...</main>

Reaching for the CSS order property (or flex-direction: row-reverse and friends) is tempting here, but it lands you in the same trap. MDN and the WCAG techniques both flag it: order repaints the visual sequence without touching focus order, so keyboard and screen reader users still travel through the DOM order while sighted mouse users see a different layout. Use it for purely cosmetic shuffling, never to “fix” navigation. Aligning the DOM is still the only real fix.

When tabindex=“0” is appropriate

tabindex="0" is the safe, standards-compliant way to add keyboard focus to a custom interactive widget that isn’t a native HTML element:

<!-- A custom toggle that needs to be keyboard focusable -->
<div
  role="switch"
  tabindex="0"
  aria-checked="false"
  aria-label="Enable notifications"
>
  <!-- visual content -->
</div>

Before you do, ask whether a native element would do the job. A <button>, <a href>, <input>, <select>, or <textarea> is focusable out of the box, ships with keyboard behavior you’d otherwise reimplement (Space and Enter activation, arrow-key handling), and needs no JavaScript to be operable. The custom role="switch" above also owes you a keydown handler to toggle it; a native <input type="checkbox"> wouldn’t. The best ARIA pattern is usually no ARIA at all.

When tabindex=“-1” is appropriate

tabindex="-1" is useful for managing focus programmatically — for example, moving focus to a modal dialog when it opens, or to an error message after form validation fails:

<div id="error-summary" tabindex="-1" role="alert">
  Please fix the following errors before submitting.
</div>
document.getElementById('error-summary').focus();

This makes the error visible to screen readers without putting the error summary in the natural tab sequence, where it would confuse users who tab through the form normally.

How to audit your tab order

Tab order bugs are sneaky because they don’t show up where you usually look. A screenshot won’t reveal them. A code review won’t either, unless the reviewer happens to spot a stray tabindex in the diff. The only honest way to know is to drive the page with a keyboard.

The manual method: load the page, click an empty spot so nothing’s focused, then press Tab over and over and write down the order. Compare it against the visual flow.

It works. It’s also slow, and you have to redo it every time the layout shifts.

PxGuard’s Tab Order tool visualizes the focus sequence directly on the page — numbered indicators on each focusable element in the order they’ll receive focus. You can spot a positive tabindex pulling things out of sequence at a glance, without pressing Tab forty times. It’s especially useful when auditing pages with complex layouts, modals, or dynamically added content.

For a broader accessibility pass, this pairs naturally with the accessible icon button checks and the 5-minute WCAG check workflow.

Check the docs for how to read the Tab Order overlay and filter by focusable-only elements.

The rule of thumb

When you catch yourself typing tabindex="1" (or any positive number), stop and ask the real question: why doesn’t the DOM order match the order people should move through? Fix the source order. If that’s genuinely off the table, say a third-party widget with a DOM you can’t touch, write down the exception and live with the constraint instead of smearing positive tabindices across the page for the next developer to untangle.

Practically speaking, tabindex has two values worth using: 0 and -1. Anything positive is almost always a sign that something else needs fixing.

Install PxGuard free →

See it on your own pages

PxGuard is a free Chrome extension. Inspect spacing, typography, and accessibility in seconds.

Install Free