Out of Order

GitHub
Playground

Every way focus goes wrong

Focus can fail a keyboard or screen-reader user in plenty of ways: an order that jumps around, a control that can't be reached, one that's invisible or never announced. Each entry below trips one of the rules @out-of-order/core checks today, with the overlay tracing the real tab path. For the same overlay on a page that behaves, see What's tabbable.

Two fields fight for autofocus

duplicate-autofocus

Both inputs carry autofocus, but only the first in the DOM actually receives focus on load. The second is dead intent. See the autofocus attribute.

autofocus stranded on an unfocusable box

autofocus-not-focusable

This banner carries autofocus to grab focus on load, but a bare <div> isn't focusable (no tabindex, not a form control), so the attribute does nothing. See the autofocus attribute.

Settings saved

Positive tabindex scrambles a tidy form

no-positive-tabindex visual-order-mismatch

Visually this reads top-to-bottom. But the tabindex values force the order 4 → 2 → 5 → 1 → 3, so focus hops around unpredictably. Fails WCAG 2.4.3 Focus Order.

DOM order fights visual order

visual-order-mismatch

There's no positive tabindex here, but the visually-first button is last in the DOM (stacked via absolute positioning), so the natural order still runs bottom-to-top. Fails WCAG 2.4.3 Focus Order.

Toolbar painted right-to-left

visual-order-mismatch

Flexbox row-reverse paints these right-to-left, but tab order follows the DOM left-to-right, so focus moves opposite to the visual flow. Fails WCAG 2.4.3 Focus Order.

Tabbable inside aria-hidden

aria-hidden-focusable

The wrapper is aria-hidden="true", but the button stays in the tab order, so a screen-reader user lands on a control their SR refuses to announce. See WAI-ARIA: aria-hidden.

Invisible, yet tabbable

hidden-while-focusable

Each button below is reachable by Tab but can't be seen, so a keyboard user lands on a control that paints nothing. There's more than one way to end up here, and the package names the cause for each: opacity: 0, a zero-size box, or left: -9999px off-screen. Fails WCAG 2.4.7 Focus Visible.

Icon button with no accessible name

missing-accessible-name

A focusable button whose only content is a decorative aria-hidden icon, with no text, label, or title. Screen readers announce nothing. Fails WCAG 4.1.2 Name, Role, Value.

Clickable div the keyboard can't reach

clickable-not-focusable

A <div role="button"> with no tabindex is clickable with a mouse, but skipped entirely by Tab. Fails WCAG 2.1.1 Keyboard.

Run report

Toolbar with one tab stop per item

composite-roving-tabindex

Per the ARIA toolbar pattern, a role="toolbar" should be a single tab stop and move between items with the arrow keys (roving tabindex). These four are four separate tab stops.

tabindex="0" on a non-interactive box

tabindex-on-noninteractive

This box is plain decorative text with no role and no behaviour, but tabindex="0" adds a dead stop to the tab order. See the ARIA APG's Developing a Keyboard Interface.

Just a styled box, not a control, but it still catches Tab.

Redundant tabindex on already-focusable controls

redundant-tabindex

Every control below is focusable on its own, so the tabindex="0" on it does nothing but add noise (and invites a stray positive value to creep in later). One per kind of natively-focusable element the rule knows about: a link, a <button>, the form controls, a <summary>, an <iframe>, and a media player. See the HTML spec's tabindex attribute.

<a href> Home
<button>
<input>
<select>
<textarea>
<summary>
More options

Extra settings.

<iframe>
<audio controls>

Generic elements that reimplement native controls

prefer-native-element

Each control below is a <div>/<span> carrying an interactive role + tabindex, reinventing the focus, keyboard activation, and screen-reader semantics a native element gives for free. One per native replacement the rule knows about. See the first rule of ARIA.

A button nested inside a link

nested-interactive

This card is a link with a <button> sitting inside it. Both are focusable, so they stack two tab stops on one spot, and a screen reader may merge or drop the inner control's role and name. Fails WCAG 4.1.2 Name, Role, Value.

View pricing

The same bugs, hidden inside shadow DOM

aria-hidden-focusable clickable-not-focusable autofocus-not-focusable nested-interactive

Every box below is a web component with an open shadow root. The analyzer walks the same composed tree the Tab key does, so rules cross the boundary in both directions.

aria-hidden host
mouse-only control
stranded autofocus
focusable host

An all-caps button label

no-shouting

Not a built-in. This is a custom rule passed to trace({ audit: { customRules: [...] } }), so it runs on the overlay next to the packaged ones. It flags any tab stop whose label is ALL CAPS. Write your own the same way, see adding a custom rule.

A finding you've approved with data-ooo-ignore

data-ooo-ignore

Both boxes are the same custom <div role="button">, which normally nudges you toward a native <button> (prefer-native-element). The right one carries data-ooo-ignore="prefer-native-element", your note that you have weighed it and it stays. See approving a finding.

flagged
Save
approved
Save