Keyboard Accessibility

Tab Order Best Practices

13 min read

Tab Order Best Practices

Tab order determines the sequence in which users navigate through interactive elements using the Tab key. A logical, predictable tab order is essential for keyboard users and assistive technology users. This guide provides comprehensive best practices for managing tab order effectively.

How Tab Order Works

By default, tab order follows the DOM order. Elements receive focus in the order they appear in your HTML:

html
<!-- Focus order: 1 → 2 → 3 -->
<button>1. First</button>
<button>2. Second</button>
<button>3. Third</button>

The Golden Rules

1. DOM Order = Visual Order

Your HTML structure should match the visual layout:

html
<!-- Good: DOM matches visual order -->
<header>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
  </nav>
</header>
<main>
  <h1>Welcome</h1>
  <button>Primary Action</button>
  <button>Secondary Action</button>
</main>

2. Never Use Positive tabindex

html
<!-- BAD: Positive tabindex creates confusion -->
<button tabindex="3">Third</button>
<button tabindex="1">First</button>
<button tabindex="2">Second</button>

<!-- Good: Natural order -->
<button>First</button>
<button>Second</button>
<button>Third</button>

Using positive tabindex values creates a separate "priority" focus order that precedes normal tab order, this quickly becomes unmanageable.

3. Only Use tabindex="0" and tabindex="-1"

html
<!-- tabindex="0": Make non-interactive element focusable -->
<div tabindex="0" role="button" onclick="action()">
  Custom widget
</div>

<!-- tabindex="-1": Programmatic focus only -->
<h2 tabindex="-1" id="section-header">
  Section Title
</h2>
<script>
  // Focus after user action
  document.getElementById('section-header').focus();
</script>

CSS Layout and Tab Order

Flexbox Direction Traps

css
/* Visual: C B A | Tab: A B C */
.container {
  display: flex;
  flex-direction: row-reverse;
}

Solution: Reorder DOM elements, or use order property with caution:

html
<!-- Reordered DOM for correct tab order -->
<div class="container">
  <button style="order: 3">A (Visual first)</button>
  <button style="order: 2">B (Visual second)</button>
  <button style="order: 1">C (Visual third)</button>
</div>

Grid Placement Issues

css
/* Visual order disrupted by grid placement */
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.item-c {
  grid-column: 1;  /* Moves C visually to the left */
}

Common Tab Order Patterns

Header Navigation

html
<header>
  <a href="/" class="logo">Logo</a>
  <nav>
    <ul>
      <li><a href="/">Home</a></li>
      <li><a href="/products">Products</a></li>
      <li><a href="/about">About</a></li>
    </ul>
  </nav>
  <button class="menu-toggle" aria-label="Menu">☰</button>
</header>

Tab order: Logo → Home → Products → About → Menu toggle

Form Layout

html
<form>
  <div class="form-row">
    <label for="first">First Name</label>
    <input id="first" type="text">
  </div>
  <div class="form-row">
    <label for="last">Last Name</label>
    <input id="last" type="text">
  </div>
  <div class="form-row">
    <label for="email">Email</label>
    <input id="email" type="email">
  </div>
  <button type="submit">Submit</button>
</form>

Tab order follows reading order: First Name → Last Name → Email → Submit

Multi-Column Layouts

For multi-column layouts, decide on the reading pattern:

html
<!-- Row-by-row reading (most common for Western audiences) -->
<div class="grid">
  <!-- Row 1 -->
  <article>Item 1</article>
  <article>Item 2</article>
  <article>Item 3</article>
  <!-- Row 2 -->
  <article>Item 4</article>
  <article>Item 5</article>
  <article>Item 6</article>
</div>

Removing from Tab Order

Sometimes elements shouldn't be in the tab order:

html
<!-- Hidden content -->
<div hidden>
  <button>Not focusable</button>
</div>

<!-- Off-screen but announced -->
<nav aria-hidden="true" tabindex="-1">
  <a href="/" tabindex="-1">Home</a>
</nav>

<!-- Disabled elements -->
<button disabled>Can't focus me</button>

<!-- inert attribute (when supported) -->
<div inert>
  <button>Not focusable</button>
</div>

Dynamic Content Tab Order

When adding content dynamically, maintain logical order:

javascript
// Adding a new item
function addItem(container, content) {
  const item = document.createElement('button');
  item.textContent = content;
  
  // Append at the end (natural tab order)
  container.appendChild(item);
  
  // Or insert at a specific position
  const referenceNode = container.children[2];
  container.insertBefore(item, referenceNode);
}

Testing Tab Order

  1. Tab through the entire page: Does the order make sense?
  2. Shift+Tab backwards: Reverse order logical?
  3. Compare visual to tab order: Any mismatches?
  4. Test all viewport sizes: Order consistent at all sizes?
  5. Check dynamic content: New content in logical position?

Tab Order Checklist

  • No positive tabindex values used
  • Tab order follows visual layout
  • CSS layout doesn't disrupt focus order
  • All interactive elements reachable via Tab
  • Hidden content removed from tab order
  • Dynamic content maintains logical order
  • Tested on mobile and desktop viewports

Was this article helpful?