Codeskill

Learn to code, step by step

Accessibility deep dive – ARIA patterns that are actually needed

We’ll look at aRIA patterns that are actually needed in real projects. Not the full WAI-ARIA catalogue, but the roles and attributes you reach for when native HTML cannot express the behaviour or state you need. The first rule has not changed: use the right HTML element first. ARIA is for gaps, not decoration.

When ARIA helps

Reach for ARIA when you have built a custom widget (tabs, menu button, combobox) and need to tell assistive tech what it is and what state it is in. Reach for it when visible text and accessible name diverge – an icon-only button needs aria-label or visually hidden text. Reach for live regions when content updates without a full page reload and users need to hear the change.

Do not reach for ARIA to fix bad structure. Adding role="main" to a <div> when you could use <main> is wasted effort. Adding role="button" to a <div> when you could use <button> means you also owe keyboard handlers, focus management, and activation on Enter and Space.

Naming and descriptions

Every interactive control needs an accessible name. Usually that comes from visible label text or alt on an image inside a link. When it does not, use aria-label for a short name or aria-labelledby to point at existing text elsewhere:

<button type="button" aria-label="Close dialog">
  <svg aria-hidden="true" focusable="false"><!-- icon --></svg>
</button>

<h2 id="billing-heading">Billing address</h2>
<section aria-labelledby="billing-heading">
  <!-- fields -->
</section>

aria-describedby links helper text or error messages without duplicating them in the name:

<label for="postcode">Postcode</label>
<input type="text" id="postcode" name="postcode"
       aria-describedby="postcode-hint postcode-error">
<p id="postcode-hint">UK format, e.g. SW1A 1AA</p>
<p id="postcode-error" role="alert" hidden>Enter a valid postcode.</p>

State and properties you will use often

  • aria-expanded – disclosure, menu, or accordion open/closed
  • aria-selected – current item in a tab list or listbox
  • aria-current="page" – current nav link (prefer this over aria-selected for navigation)
  • aria-hidden="true" – decorative icons and duplicate text (never on focusable elements)
  • aria-live="polite" or assertive – dynamic status updates
  • aria-disabled="true" – disabled-looking custom control (native disabled is still better on real form controls)

Live regions

When JavaScript updates a cart total or form status, screen reader users may not notice unless you announce it. A live region does that job:

<div aria-live="polite" aria-atomic="true" class="visually-hidden" id="cart-status">
  Cart updated: 3 items, total &pound;42.00
</div>

Use polite for non-urgent updates. Use assertive sparingly – it interrupts. Keep the text short and meaningful. Update the region’s text when state changes; do not spam it on every keystroke unless you must.

Landmarks without over-ARIA

Prefer native landmarks from the intermediate tutorials: <header>, <nav>, <main>, <aside>, <footer>. When you have two <nav> elements, distinguish them with aria-label:

<nav aria-label="Primary">...</nav>
<nav aria-label="Footer legal">...</nav>

Testing and restraint

Validate with the browser accessibility tree, keyboard-only use, and a screen reader when you can. The ARIA Authoring Practices Guide (APG) shows complete patterns for complex widgets – use it as a reference, not a shopping list. If you cannot explain why an attribute is there, remove it. Wrong ARIA is worse than none.

PreviousGoing deep with HTML