HTML — One Tag Stands In For a Feature
Go All the Way Through With Tab Alone
In one line
Keyboard accessibility is not a feature but an order. What receives focus, in what sequence it cycles, and whether it is visible where it is now. These three cover most of it.
Why this was needed
If you put the mouse away and use your own screen with Tab alone for 5 minutes, almost always something turns up. Focus suddenly jumps to the very bottom of the page. You enter a hidden menu and pressing keys many times doesn't get you out. You can't see where focus is now.
These never turn up in QA, because QA uses a mouse too. And users don't report them either — a screen you can't use is just left, not something to report.
There are more people who use only a keyboard than you might think. People who hurt their wrists, screen reader users, people with tremors who find it hard to aim a mouse, and people for whom Tab is simply faster. If this screen is a payment screen, those people can't pay.
How it works — the sequential focus order
The rules set by the HTML Standard's tabindex section are simple.
1. tabindex 가 양수인 것들 — 값이 작은 것부터, 같으면 문서 순서
2. 그다음 tabindex="0" 과 기본으로 초점을 받는 요소들 — 문서 순서
a[href], button, input, select, textarea, and summary receive focus by default. tabindex="-1" can give focus only through script and isn't reached by Tab.
The thing people get hurt by most often here is a positive tabindex. tabindex="3" doesn't move that one element to third place; it splits the whole page into two groups. Everything with a positive value is cycled through first, and then the rest. So if you put tabindex="1" on one place, that element becomes the first stop on the page.
If you want to change the order, change the markup order. Don't move things with tabindex.
Changing only the visual order with CSS (order, grid-row) creates the same problem. The sequence you see and the sequence Tab cycles through diverge, and what WCAG's Focus Order calls "an order that preserves meaning" breaks.
Skip links
Screen reader users can jump over landmarks, but keyboard-only users can't. If the menu has twenty items, you press twenty times on every page before reaching the body.
So you put a link to the body as the first stop on the page. This is what WCAG's Bypass Blocks requires.
There are two traps here.
- It must be the first stop. If it is after the menu, you have already passed everything there was to skip
- The destination must be able to receive focus. With
<main id="main">alone, the browser sometimes changes only the address and leaves focus where it was. If you give ittabindex="-1", focus follows even without script
The convention is to keep it off-screen normally and show it only when focus arrives. Two lines do it: .skip { position: absolute; left: -9999px } and .skip:focus { left: 8px }.
Is what you hid really hidden
Collapsed menus, closed dialogs, panels pushed off-screen. These often remain in the tab order. Focus goes there but nothing is visible on screen, so the user feels that focus vanishes every time they press Tab.
- The
hiddenattribute ordisplay: none— drops out of the focus order entirely inert— everything inside drops out of focus, clicks, and readingaria-hidden="true"— disappears only from the assistive technology tree. Focus still goes there
The last is the worst combination. Focus goes there, but the screen reader reads nothing. MDN's aria-hidden documentation also states firmly not to use it on focusable elements. Use aria-hidden only on things that need no reading and receive no focus, like icons drawn with text characters.
Focus must be visible
outline: none is the first line that people learning CSS learn, and the line that damages accessibility the most. If you remove the focus ring, keyboard users press Tab without knowing where they are.
:focus-visible solves this. It doesn't apply when pressed with a mouse and applies only when reached by keyboard. You can show the ring only to those who need it without harming the design.
What it looks like in the field
Here are three things that actually happened in this service.
- When a modal was opened, focus didn't go to the modal, so the screen reader was still reading the body behind it
- A collapsed filter panel had a height of 0 rather than
visibility: hidden, so the buttons inside remained in the tab order - The design system's button component applied
outline: noneglobally and gave no replacement style, so focus was invisible across the whole site
All three come out on the spot with "put the mouse away and Tab alone for 5 minutes." Putting that one line in the PR checklist is better than ten automated tools.
What you will do in the next lab
You write a single console screen and use the calculator to check the sequence Tab actually goes through. You put a skip link in the first stop, remove positive tabindex, take collapsed areas out of the tab order, and bring back the focus indicator.