# axe-core focus-order-semantics error tab trap bug - how to fix it

## TL;DR

Put interactive elements in a DOM order that matches the visual order, and give focusable widgets roles that match what they do. If tabbing jumps around or gets stuck, reorder the DOM or fix the roles, then re-run axe. One line of why: keyboard users navigate in DOM order, so CSS that visually reorders content without moving the DOM creates a focus path that makes no sense.

## The error, verbatim

```text
{
  "id": "focus-order-semantics",
  "impact": "minor",
  "help": "Elements in the focus order should have an appropriate role",
  "nodes": [
    { "target": [".card[tabindex]"], "failureSummary": "Fix any of the following: Element has a tabindex greater than 0 or an inappropriate role for its position in the focus order" }
  ]
}
```

## Fix it step by step

### Step 1: Reproduce on one page

```bash
npx @axe-core/cli https://example.com --rules focus-order-semantics --save axe-focus.json
```

Expected: Violations array contains focus-order-semantics with the offending selectors.

### Step 2: Tab through the page manually

```bash
npx playwright test --headed || echo 'manual: press Tab from the address bar and note the jump order'
```

Expected: You observe focus jumping visually out of order or landing on elements with wrong roles.

### Step 3: Find positive tabindex and role mismatches

```bash
rg -n 'tabindex=.?[1-9]' src --glob '*.{jsx,tsx,vue,html}' | head -20
```

Expected: Lists elements with positive tabindex, the most common trigger.

### Step 4: Fix order and roles, then re-scan

```bash
npx @axe-core/cli https://example.com --rules focus-order-semantics
```

Expected: Exit code 0, 0 violations. A full keyboard pass now moves focus in visual order with no traps.

### Step 5: Gate the rule in CI

```bash
npx @axe-core/cli https://example.com --exit | tail -3
```

Expected: Non-zero exit while any violation remains; add this command to CI so the fix never regresses.

## When to use this skill

- Your axe-core report lists this exact rule id under violations
- You are clearing automated WCAG 2.1 AA failures before a release or audit
- A CI a11y gate (pa11y-ci, lighthouse CI, cypress-axe) is red because of this rule

## When NOT to use this skill

- The issue only shows up in manual screen-reader testing and axe reports zero violations for the rule
- You are doing a full manual WCAG audit, this skill covers the single automated rule only
- The page is a third-party embed you cannot edit, flag it to the vendor instead

## Compatibility

axe-core 4.8+ (rule focus-order-semantics, WCAG 2.4.3). Experimental rule, off in some configs, enable explicitly when testing tab order. Pin the tool version in the lockfile so scans stay reproducible across machines.

## Variant phrasings

### element has tabindex greater than 0 axe

The most common trigger phrasing, fix is tabindex 0 or -1 plus DOM order.

### keyboard tab order jumps around page

The user-visible symptom this rule tries to catch.

### axe DevTools flags the same rule

The browser extension runs the same rule engine; fix once and it clears in every runner.

## Why it happens

Positive tabindex values pull elements out of natural DOM order into an explicit sequence that rarely matches the visual layout, and generic divs with click handlers and tabindex 0 get focus without a role that tells the user what they are. Axe flags elements whose role does not fit their position in the focus order. CSS grid and flex visual reordering without DOM reordering is the other big source: sighted mouse users see one order, keyboard users get another. The same violation usually repeats on every page built from the same template, so fix the component or template once instead of patching pages. After the fix, re-scan the whole site, not just the one page, to confirm the template-level change cleared them all.

## Edge cases

- Never use positive tabindex to fix order, reorder the DOM instead, positive values are almost always the bug.
- Modal dialogs need a real focus trap (focus stays inside while open), which is correct behavior, not what this rule flags.
- This rule is marked experimental, pair it with a manual keyboard pass before calling the page fixed.
- Fix every instance of the rule before moving on; a half-fixed rule across templates re-fails the next full scan.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_iv_m-4krvyohZ9_VtoJmgQ
