# Tab order is logical and visible focus is clear

- **Pattern:** `ab-000051` (`accessibility-basics.semantic-structure-aria.tab-order-focus`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000051
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000051)

## Why it matters

Removing the focus outline with `outline: none` — one of the most common CSS resets — makes it impossible for keyboard-only users to know where they are on the page. This affects sighted keyboard users (power users, users with motor disabilities who cannot use a mouse) as well as those navigating by switch access or sequential navigation. WCAG 2.2 SC 2.4.7 (Focus Visible) requires a visible focus indicator; SC 2.4.11 (Focus Appearance, AA in 2.2) adds a minimum area and contrast requirement of 3:1. SC 2.4.3 (Focus Order) is violated when positive `tabindex` values reorder focus in ways that conflict with visual layout. Section 508 2018 Refresh 503.4 requires keyboard navigation support. An invisible focus state means a keyboard user literally cannot use the application — they click Tab repeatedly with no visible response.

## Severity rationale

High because invisible or illogical focus indicators prevent keyboard-only users from knowing their position on the page, making navigation functionally impossible.

## Remediation

Audit your global CSS for `outline: none` or `outline: 0` — these are only acceptable when a visible replacement focus style is provided on the same selector. Add `:focus-visible` styles for keyboard users without affecting mouse users:

```css
/* globals.css or tailwind base layer */

/* NEVER use this without a replacement */
/* * { outline: none; } */

/* Provide a clear, high-contrast focus ring */
*:focus-visible {
  outline: 3px solid #0066cc;
  outline-offset: 2px;
}

/* Component-specific overrides */
.btn:focus-visible {
  outline: 3px solid #0066cc;
  outline-offset: 2px;
  border-radius: 4px;
}

/* Tailwind users: add to tailwind.config.js or use ring utilities */
/* className="focus-visible:ring-2 focus-visible:ring-blue-600 focus-visible:ring-offset-2" */
```

Do not use positive `tabindex` values to reorder elements — reorder the DOM instead. Use `tabindex="0"` to add non-semantic elements to tab order and `tabindex="-1"` for programmatic focus only.

## Detection

- **ID:** `tab-order-focus`
- **Severity:** `high`
- **What to look for:** Check the tab order by tabbing through interactive elements in the application. The order should follow logical reading order (left-to-right, top-to-bottom). Check that a focus outline is visible on all interactive elements. Look for `outline: none` in CSS without a replacement focus style, or very faint focus indicators.
- **Pass criteria:** Tab order follows logical reading order across all pages. Focus indicator is visible and has adequate contrast (at least 3:1 ratio). Positive `tabindex` is not used to reorder elements.
- **Fail criteria:** Tab order jumps unexpectedly, or focus outline is removed (`outline: none`) without a visible replacement, or focus indicator is too faint to see.
- **Skip (N/A) when:** Never — tab order applies to all interactive pages.
- **Detail on fail:** Describe the tab order issue. Example: `"Tab order jumps from top navigation to footer, skipping main content. Focus outline has outline: none with no replacement style — users cannot see which element has focus."`
- **Remediation:** Ensure logical tab order and visible focus indicators:

  ```css
  /* Provide a clear focus style */
  button:focus {
    outline: 2px solid #0066cc;
    outline-offset: 2px;
  }

  /* Never use outline: none without a replacement */
  button:focus-visible {
    background-color: #f0f0f0;
    border: 2px solid #0066cc;
  }
  ```

  Avoid positive `tabindex` values. Let the DOM order determine tab order:

  ```tsx
  {/* Good: DOM order is tab order */}
  <button>First</button>
  <button>Second</button>

  {/* Avoid: tabindex breaks natural order */}
  <button tabIndex={2}>First</button>
  <button tabIndex={1}>Second</button>
  ```

## External references

- wcag 2.4.3
- wcag 2.4.7
- wcag 2.4.11
- section-508 503.4

Taxons: accessibility

HTML version: https://auditbuffet.com/patterns/ab-000051
