Accessibility

Make a page usable with a keyboard and a screen reader, using HTML before ARIA.

17of 22
≈ 12Min read
8Parts

Accessibility is mostly HTML done properly

Most of what makes a page accessible is in the previous six lessons: real headings, real labels, real buttons, alt text, landmarks. Very little of it is an extra layer you add at the end.

The cheapest test you can run: put the mouse away. Press Tab through your page. Can you reach everything? Can you see where you are? Can you operate it?

Every control needs an accessible name

A name is the text a screen reader announces. It comes from the content of the element, or a <label>, or aria-label, or aria-labelledby — in that order of preference.

an icon button
<!-- No text inside, so it has no name at all -->
<button><svg>…</svg></button>

<!-- Named, and the icon is hidden from the reader -->
<button aria-label="Close">
  <svg aria-hidden="true" focusable="false">…</svg>
</button>

Use the element that already behaves

A <button> is focusable, fires on Enter and Space, and is announced as a button. A <div onclick> does none of those things, and rebuilding them correctly takes tabindex, key handlers and a role.

You wantUse
Something that does an action<button>
Something that goes somewhere<a href>
A show/hide panel<details> / <summary>
A modal<dialog> with showModal()
A choice from a list<select> or radios
The first rule of ARIA

Do not use ARIA if a native element will do. A wrong role is worse than no role: it overrides what the browser knew and tells the reader something untrue.

Keyboard order and focus

  • Tab order follows the source order. If CSS moves things visually, the tab order will no longer match what is on screen — fix the source, not the tab index.
  • tabindex="0" puts a non-interactive element in the tab order. tabindex="-1" makes it focusable by script only. Never use a positive number.
  • Never remove the focus outline without replacing it. :focus-visible lets you style focus for keyboard users without a ring appearing on every mouse click.
a visible focus style
:focus-visible {
  outline: 2px solid #4c8dff;
  outline-offset: 3px;
}

The first focusable thing on a page should let someone jump past the navigation. It can be visually hidden until it is focused.

skip to content
<a class="skip" href="#main">Skip to content</a>
…
<main id="main" tabindex="-1">…</main>

Images, colour and text

  • Alt text on every image: the message, or alt="" if it is decoration.
  • Never use colour alone to carry meaning — add a word, an icon or a pattern.
  • Body text needs a contrast ratio of at least 4.5:1 against its background; large text 3:1.
  • Do not disable zoom. user-scalable=no in the viewport meta is a barrier.

When you do need ARIA

aria-expanded tells the reader whether a disclosure is open. aria-live announces content that appeared without a navigation. aria-hidden="true" hides decoration — never put it on anything focusable.

state that HTML cannot express
<button aria-expanded="false" aria-controls="panel">Details</button>
<div id="panel" hidden>…</div>

<!-- announce changes that happen without a page load -->
<p role="status" aria-live="polite">Saved.</p>

Reduced motion

Some people get motion sickness from large movement. Respect the system setting.

honour the preference
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

Practice