Accessibility
Semantics and ARIA, keyboard and focus, colour contrast, and how to actually test with a screen reader.
Why This Is Not Optional
The standard is WCAG, organised under four principles: content must be perceivable, operable, understandable and robust. Level AA is what regulations generally require and what teams target.
Most of it is not extra work — it is the work done properly. Semantic markup, real buttons, labelled inputs and sufficient contrast are all things a careful developer does anyway. The retrofit is expensive precisely because it means undoing shortcuts.
Automated tools catch perhaps a third of issues. They will tell you a contrast ratio is 3:1; they cannot tell you the focus order makes no sense or the alt text describes the wrong thing.
Semantics & ARIA
Every element has an implicit role. A <button> is announced as a button, is focusable, and fires on Enter and Space — none of which you had to write.
ARIA adds roles, states and properties where HTML has no equivalent. It changes only how something is announced — never how it behaves. Adding role="button" to a div does not make it focusable or keyboard-operable; it only makes a screen reader announce a lie.
| Attribute | Says | Use on |
|---|---|---|
aria-label | The accessible name, when no visible text exists | An icon-only button |
aria-expanded | Whether the thing it controls is open | Disclosures, menus, accordions |
aria-controls | Which element it operates | Paired with aria-expanded |
aria-current | The current item in a set | The active nav link, current page |
aria-live | Announce changes here | Status messages, results counts |
aria-hidden | Ignore this entirely | Decorative icons beside real text |
Keyboard & Focus Management
Everything usable with a mouse must be usable without one. That covers screen-reader users, people with motor impairments, and anyone whose trackpad has died.
The test takes a minute: put the mouse down and Tab through the page. You should always be able to see where you are, reach everything interactive, and never get stuck.
| Rule | Why |
|---|---|
| Never remove focus outlines without a replacement | Keyboard users lose their place entirely |
| Follow DOM order | Tab order comes from the DOM, not from CSS position |
Avoid positive tabindex | It jumps ahead of everything and breaks the sequence |
tabindex="-1" for programmatic focus | Focusable by script, skipped when tabbing |
| Trap focus inside a modal | Otherwise Tab wanders behind the overlay |
| Restore focus on close | Return it to the control that opened the dialog |
| Escape closes | Expected for anything overlaying the page |
visibility: hidden, display: none or the hidden attribute.A skip link as the first focusable element, jumping to the main content, saves keyboard users tabbing through the whole navigation on every page.
Colour Contrast
Contrast is a ratio between the text colour and what is behind it, from 1:1 to 21:1. WCAG AA sets the thresholds.
| Content | AA minimum | AAA |
|---|---|---|
| Body text | 4.5:1 | 7:1 |
| Large text (18pt, or 14pt bold) | 3:1 | 4.5:1 |
| UI components and graphics | 3:1 | — |
Colour must also never be the only carrier of meaning. Red for errors is fine, red alone is not — around 1 in 12 men has some colour vision deficiency. Add an icon, a label, or a shape.
Check both themes. A palette that inverts between light and dark will pass in one and fail in the other, and the failure is invisible from whichever one you happen to be looking at.
Testing With a Screen Reader
Nothing replaces listening to the page. Most of what a screen reader exposes — the heading outline, the landmarks, the accessible names — is invisible in the browser.
| Reader | Platform | Start it with |
|---|---|---|
| VoiceOver | macOS, iOS | Cmd + F5 |
| NVDA | Windows, free | Ctrl + Alt + N |
| Narrator | Windows, built in | Ctrl + Win + Enter |
| TalkBack | Android | Volume keys held |
You do not need fluency. Four checks catch most problems: browse by heading and see whether the outline makes sense; browse by landmark and see whether the regions are named; Tab through and listen to whether each control announces what it is and what it does; and trigger a status message to check it is announced at all.
Interview Questions
What is the first rule of ARIA?
Do not use it. Prefer the native element that already has the role, focus behaviour and keyboard handling. ARIA changes announcement only, never behaviour, so ARIA on a div still leaves it unfocusable and unusable by keyboard.
What are the WCAG AA contrast minimums?
4.5:1 for body text, 3:1 for large text — 18pt, or 14pt bold — and 3:1 for UI components and meaningful graphics.
How do you make a modal accessible?
Move focus into it on open, trap focus inside while it is open, close on Escape, restore focus to the trigger on close, and mark the content behind it as inert or hidden so a screen reader does not read through it.
Why is opacity a bad way to show a disabled or inactive state?
It silently lowers contrast, so the state that most needs to be legible becomes the least legible. Choose a specific colour that meets the ratio instead.
Automated tools pass. Are you accessible?
No. They catch roughly a third — contrast, missing alt, missing labels. They cannot judge whether the focus order is logical, whether alt text describes the right thing, or whether a custom widget behaves as announced. Keyboard and screen-reader testing are still required.
What does aria-live do?
Marks a region whose changes should be announced without moving focus — a results count, a save confirmation. "polite" waits for a pause; "assertive" interrupts and should be rare.
Quick Quiz
role="button" to a div makes it…tabindex is discouraged because it…