Structure, keyboard, and focus

Protect focus order and visibility

Keep DOM order logical, focus indicators visible, and off-screen or hidden content out of the sequence.

Focus order is the path the Tab key takes through the page. It should follow the task in the same order a sighted user reads it, and at every stop the user should see exactly where they are. Two criteria cover this: WCAG 2.4.3 Focus Order (Level A) and 2.4.7 Focus Visible (Level AA).

Order comes from the DOM

The browser tabs through focusable elements in source order. CSS can move things visually without moving them in the source, and that’s where the mismatch begins.

On the flawed page the “Early bird” ticket card is pushed first with CSS:

.ticket-cards {
  display: flex;
}

.card--early-bird {
  order: -1;
}

Visually: Early bird, Regular, Supporter. Tab order: Regular, Supporter, Early bird. Focus jumps right, right, then all the way back left. The fix is not more CSS. Move the Early bird card first in the HTML and delete the order rule.

The other tempting patch is tabindex="3" on the Register button to “pull it forward”. Positive values create a second ordering that runs before everything else. I never use them. tabindex="0" makes something focusable in its natural position, tabindex="-1" makes it focusable only by script, and that’s it.

Make focus visible

The flawed page has this rule, added because someone disliked the blue ring:

*:focus {
  outline: none;
}

That removes the only indication of where keyboard input will go. Replace it with a style you like, shown only when the browser decides focus should be visible:

:focus-visible {
  outline: 3px solid #005a9c;
  outline-offset: 2px;
}

:focus-visible matches when you Tab to an element but not when you click it, so the mouse experience stays the same. Check the ring against its background: WCAG 1.4.11 Non-text Contrast asks for 3:1 on indicators like this.

Focus hidden behind sticky bars

Now zoom to 200 percent and Tab into the form. On the flawed page the sticky header is tall enough at that zoom to cover the focused email field completely. Focus is technically visible, but you can’t see it. WCAG 2.2 added 2.4.11 Focus Not Obscured (Minimum), Level AA, for this case.

One line usually solves it:

html {
  scroll-padding-top: 5rem;
}

The browser now scrolls focused elements into view below the header instead of under it.

Keep hidden things out of the sequence

The guest modal is hidden with opacity: 0 on the flawed page. It’s invisible and still in the Tab order, so users Tab through three invisible fields. Hide it with the hidden attribute or display: none, which also removes it from the accessibility tree. For a panel that must stay rendered but not interactive, use the inert attribute.

Try this on your page: Tab through it at 200 percent zoom and once more with your OS high contrast setting on. Write down every stop where focus disappeared or jumped somewhere unexpected.

Lesson completed