Frontend · Guide

Accessibility

Semantics and ARIA, keyboard and focus, colour contrast, and how to actually test with a screen reader.

— min read Frontend

Why This Is Not Optional

Accessibility is a legal requirement in most markets, and it is the fastest way to tell a careful frontend from a careless one. It is also the cheapest thing to get right early and the most expensive to retrofit.

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.

AttributeSaysUse on
aria-labelThe accessible name, when no visible text existsAn icon-only button
aria-expandedWhether the thing it controls is openDisclosures, menus, accordions
aria-controlsWhich element it operatesPaired with aria-expanded
aria-currentThe current item in a setThe active nav link, current page
aria-liveAnnounce changes hereStatus messages, results counts
aria-hiddenIgnore this entirelyDecorative icons beside real text
The first rule of ARIA is not to use it. A native element with the behaviour built in beats any amount of ARIA on a div — and bad ARIA is worse than none, because it makes a broken control sound like a working one.

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.

RuleWhy
Never remove focus outlines without a replacementKeyboard users lose their place entirely
Follow DOM orderTab order comes from the DOM, not from CSS position
Avoid positive tabindexIt jumps ahead of everything and breaks the sequence
tabindex="-1" for programmatic focusFocusable by script, skipped when tabbing
Trap focus inside a modalOtherwise Tab wanders behind the overlay
Restore focus on closeReturn it to the control that opened the dialog
Escape closesExpected for anything overlaying the page
A collapsed panel that is merely zero height still holds its links in the tab order — focus disappears into something invisible. Hide collapsed content with 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.

ContentAA minimumAAA
Body text4.5:17:1
Large text (18pt, or 14pt bold)3:14.5:1
UI components and graphics3:1
Never convey state by lowering opacity. A "dimmed" label is text with its contrast quietly reduced, and it fails at exactly the moment it matters. Choose a different colour deliberately and measure it.

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.

ReaderPlatformStart it with
VoiceOvermacOS, iOSCmd + F5
NVDAWindows, freeCtrl + Alt + N
NarratorWindows, built inCtrl + Win + Enter
TalkBackAndroidVolume 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.

Automated tooling — axe-core in CI, Lighthouse locally — is worth having and catches roughly a third of issues. Treat a clean automated run as the floor, not the finish line.

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

1. WCAG AA minimum contrast for body text is…
2. Adding role="button" to a div makes it…
3. Collapsed content at zero height still…
4. Roughly what share of issues do automated tools catch?
5. A positive tabindex is discouraged because it…