HTML Accessibility and ARIA

Accessibility means the page works for everyone — screen-reader users, keyboard-only users, people who zoom, and people on slow devices. Most of it is simply correct HTML; ARIA is the smaller remainder, used with care.

Roughly one person in six lives with a disability, and everyone benefits from accessible markup: captions serve loud environments, semantics serves slow connections, labels serve everyone who has ever forgotten which field a cursor landed in. Accessibility is not a module to add later — it is the quality of your markup measured by a different standard, WCAG.

The accessibility tree

What screen readers actually read

The browser derives an accessibility tree from your DOM: each element contributes a role, a name, and a state. Assistive technology reads that tree — never your tags directly. Two elements can render identically while exposing completely different trees:

<!-- announced as: nothing useful (a plain div) -->
<div onclick="add()">Add to cart</div>

<!-- announced as: button, Add to cart, focusable, Enter/Space work -->
<button type="button">Add to cart</button>
Diagram of DOM transforming into the accessibility tree and reaching assistive technology

Figure 1 — Assistive technology reads derived semantics, not your source.

Name, role, value

Every interactive element must expose its role (button, link, checkbox), its name (what to call it), and where relevant its value or state (checked, expanded). Native HTML elements provide all three automatically; a div pretending to be a button provides none without ARIA and JavaScript.

Document-level basics: lang and title

The cheapest wins live in the <html> tag: lang chooses the screen reader's pronunciation voice, and the <title> identifies the page in every navigation list. Both are set once per page and influence every screen of the experience.

Keyboard and focus

Tab follows the source order

Keyboard users traverse the page with Tab in DOM order — not visual order. CSS can position anything anywhere, but the tab ring still walks the source. Keep the DOM order logical and most "tabindex management" becomes unnecessary:

Diagram of tab order following the DOM and the three tabindex values

Figure 2 — The tab ring walks the DOM; tabindex 0 joins it, positive values break it.

The tabindex rules

Three values cover everything: tabindex="0" joins the natural order (for custom widgets); tabindex="-1" makes an element programmatically focusable but not tabbable (modals); positive values are always wrong — they break the natural order for everyone.

Visible focus is not optional

outline: none without a replacement removes the only indicator keyboard users have. Style :focus-visible instead — a visible ring that matches your design is all WCAG 2.4.7 asks:

/* replace the ring, never just remove it */
:focus-visible { outline: 3px solid #58a6ff; outline-offset: 2px; }

Labels, alt text, and color

Every input has a label

Chapter 7 covered the mechanics; here is why they matter: an unlabeled field is announced as "edit text" with no hint of purpose. Visible <label> elements are the default; placeholders are hints, never labels.

Alt text quality

Describe the image's role in the page, not its pixels. "Chart of sales doubling" beats "bar chart". Decorative images take alt="" so they vanish from the reading flow — an absent attribute is the worst option because the file name gets read aloud.

Never color alone

Error states marked only by a red border fail users with color vision deficiency (~8% of men). Pair color with an icon, text, or underline. WCAG's contrast minimums (4.5:1 for body text) are checkable with any contrast tool — and zooming to 200% must not break the layout.

ARIA, used wisely

The first rule of ARIA

"Don't use ARIA" — the specification's own joke, with a serious core: native elements beat ARIA replicas because they ship keyboard behavior, roles, and states for free. Reach for ARIA only when no native element exists for the widget:

Decision flow: native element first, native plus ARIA attribute second, full ARIA pattern last

Figure 3 — The ARIA decision flow: native first, patterns last.

The ARIA attributes worth knowing

Three families cover most real use: states (aria-expanded, aria-checked), relationships (aria-labelledby, aria-describedby, aria-controls), and live regions (aria-live announces dynamic updates). They attach semantics; they do not create behavior — the JavaScript to move focus and respond to keys remains yours.

<!-- a disclosure built on a real button: behavior stays native -->
<button aria-expanded="false" aria-controls="panel">Details</button>
<div id="panel" hidden>...</div>

Testing with assistive technology

Turn on VoiceOver (macOS: Cmd+F5) or NVDA (Windows, free) and navigate your own page once a month. The a11y pane in DevTools shows names and roles without audio. Both take minutes and catch what automated tools miss.

Common mistakes

Div buttons

The most damaging single pattern: an unclickable-by-keyboard, unnamed, role-less "button". Use <button>. If the constraint is real (a full ARIA pattern with role, tabindex="0", and keydown handlers), that is the price — not a choice.

Removing focus styles

Global outline: none removes the page from keyboard reachability while looking clean to mouse users. Style the ring; never delete it.

ARIA overload

Sprinkling role="button" on links and aria-label on everything often lowers accessibility by overriding correct native semantics with worse ones. Audit first, add ARIA last.

Practice

Exercises

  1. Unplug the mouse and complete one full task on your own page — note every place you got stuck.
  2. Run the page through aXe (browser extension) and fix every reported violation.
  3. Use the DevTools accessibility pane to verify every interactive element has a name and role.
  4. Navigate with VoiceOver or NVDA for ten minutes; notice how landmarks and headings become the navigation.
  5. Check your colors against 4.5:1 contrast, and verify error states do not rely on color alone.

Demo lab

10_accessible_widgets.html in the Lab Examples page contrasts native and ARIA-repaired widgets — view the source, then preview it with keyboard and screen reader.

Next: Metadata, SEO and Social — how machines read your page and how to brief them well.