Fix tabindex values greater than zero for better accessibility

Fix tabindex values greater than zero in Lighthouse accessibility audits
Harlan WiltonHarlan Wilton4 min read Published

Positive tabindex values break keyboard navigation. They frustrate screen reader users and destroy the natural page flow.

What's happening

Elements have tabindex values greater than 0, which forces them into an explicit navigation order. This overrides the natural DOM order and confuses users. Users tab to high-tabindex elements first, then jump back to tabindex="0" elements. This makes navigation feel random and broken.

Diagnose

  1. Open DevTools (F12 or Cmd+Shift+I).
  2. Run this in the Console to find offending elements:
document.querySelectorAll('[tabindex]').forEach((el) => {
  if (Number.parseInt(el.getAttribute('tabindex')) > 0) {
    console.log(el, 'tabindex:', el.getAttribute('tabindex'))
  }
})
  1. Use Tab to navigate the page and check for unexpected focus jumps.

Fix

1. Remove positive tabindex values

Never use positive tabindex. Replace them with 0 or remove the attribute entirely.

<!-- Before -->
<button tabindex="5">Submit</button>
<input tabindex="1" type="text">
<a tabindex="3" href="/about">About</a>

<!-- After -->
<button>Submit</button>
<input type="text">
<a href="/about">About</a>

2. Reorder your DOM instead

Change their position in the HTML if elements need a different tab order.

<!-- Before: Using tabindex to force order -->
<div>
  <button tabindex="2">Second</button>
  <button tabindex="1">First</button>
</div>

<!-- After: Correct DOM order, no tabindex needed -->
<div>
  <button>First</button>
  <button>Second</button>
</div>

Use CSS (flexbox order or grid placement) to achieve visual order while keeping logical DOM order.

Framework examples

In Vue/Nuxt, audit your components for hardcoded tabindex values:
<template>
  <!-- Avoid this -->
  <button :tabindex="priority">
    Click me
  </button>

  <!-- Prefer this -->
  <button>Click me</button>
</template>
Stick to 0, -1, or remove the attribute for focus management.

For React, search your codebase for tabIndex props:

export function TabIndexExample() {
  return (
    <>
      {/* Avoid */}
      <input tabIndex={2} />

      {/* Prefer */}
      <input />
      {/* or for programmatic focus management */}
      <input tabIndex={0} />
    </>
  )
}

Verify the fix

  1. Run Lighthouse again and confirm the tabindex audit passes.
  2. Tab through your page from start to finish.
  3. Verify focus moves in a logical, predictable order matching the visual layout.

Common mistakes

  • Using tabindex to "prioritize" important elements: This breaks navigation for everyone. Fix the DOM order instead. Important elements must appear early in the DOM.
  • Setting tabindex="1" to make an element focusable: Use tabindex="0" to add an element to the natural tab order. tabindex="1" forces it to receive focus before everything else.
  • Third-party libraries injecting positive tabindex: Check modal dialogs and date pickers. Override or configure them to use tabindex="0" or -1.
  • Confusing tabindex="-1" with positive values: Use tabindex="-1" to remove elements from the tab order while allowing programmatic focus. Modals and skip links require this.

Tabindex issues often appear alongside:

Test your entire site

Tabindex issues often hide in shared components. Unlighthouse runs Lighthouse on every page of your site to catch accessibility problems wherever they appear.