Tutorial

Build sites everyone can use

Accessibility (a11y) isn't a compliance checkbox — it's good design. The same things that help a screen-reader user also help someone on a slow phone at the bus stop.

Who you're designing for

About 1 in 4 adults has some form of disability. But accessibility isn't only permanent — it's also situational (in bright sun, holding a baby, noisy subway). Here's who your choices affect:

Blind / low vision

Uses screen readers (VoiceOver, NVDA, JAWS). Needs semantic HTML and alt text.

Color blindness

~8% of men, 0.5% of women. Don't rely on color alone to convey meaning.

Deaf / hard of hearing

Needs captions on videos, transcripts for audio, no audio-only instructions.

Motor impairments

May navigate with keyboard only, voice, or switch devices — no mouse.

Cognitive

Dyslexia, ADHD, low literacy, translation users. Clear language, predictable UI.

Situational

Small phone, glare, one hand on stroller, bad connection. Same solutions apply.

1

Use semantic HTML — the biggest win

Screen readers and keyboard users get 80% of their accessibility for free when you use the right elements. A <div> with onclick is invisible to a screen reader; a <button> is a button.

Inaccessible <div onclick="save()">Save</div>
<div class="title">About us</div>
<div class="list"><div>...</div></div>
Accessible <button onclick="save()">Save</button>
<h1>About us</h1>
<ul><li>...</li></ul>
2

Alt text for images — the 30-second fix

Every <img> needs an alt attribute. It describes the image to people who can't see it, and shows as text if the image fails to load.

<!-- Meaningful image: describe it -->
<img src="sunset.jpg" alt="Orange sunset over the Pacific">

<!-- Decorative image: empty alt (tells screen reader to skip it) -->
<img src="divider.svg" alt="">

<!-- Image that IS the link text: describe the destination -->
<a href="/cart">
  <img src="cart.svg" alt="Shopping cart">
</a>
Rule of thumb: Would the page make sense if all images disappeared and only the alt text remained? If yes, your alt text is doing its job.
3

Color contrast — at least 4.5 : 1

WCAG AA requires 4.5:1 contrast for normal text and 3:1 for large text (18pt+). Light-gray-on-white links are the #1 accessibility sin on the web.

1.8 : 1 — fails color: #aaa;
background: #fff;
7.0 : 1 — passes AAA color: #555;
background: #fff;

Test every color pair at WebAIM Contrast Checker. Chrome DevTools also shows contrast live when you pick a color.

Never signal meaning with color alone. Red error text needs an icon or label too — colorblind users can't tell red from grey.
4

Keyboard navigation — can you use your site without a mouse?

Try it: unplug your mouse, press Tab to move through your site, Enter/Space to activate, Esc to close modals. If you get stuck, so does every keyboard and screen-reader user.

<!-- Skip link: first element in the body -->
<a href="#main" class="skip-link">Skip to main content</a>

/* Hidden until focused */
.skip-link {
  position: absolute; left: -9999px;
}
.skip-link:focus {
  position: fixed; top: 10px; left: 10px;
  background: #111; color: #fff; padding: 8px 16px;
}
5

Form labels — every input needs one

A placeholder is NOT a label. It disappears as soon as the user starts typing. Always pair inputs with <label>.

Broken <input placeholder="Email">
Fixed <label for="email">Email</label>
<input id="email" type="email">
6

ARIA — only when semantic HTML isn't enough

The first rule of ARIA is: don't use ARIA. A real <button> beats role="button" every time. But when you must enhance a non-standard widget:

<!-- Tell screen readers what a custom icon button does -->
<button aria-label="Close dialog"></button>

<!-- Show/hide accessible state -->
<button aria-expanded="false" aria-controls="menu">Menu</button>

<!-- Announce dynamic updates (toast messages, live counters) -->
<div role="status" aria-live="polite">5 items in cart</div>
Wrong ARIA is worse than no ARIA. If you're not sure, test with a real screen reader before shipping.
7

Motion — respect prefers-reduced-motion

For users with vestibular disorders, large motion can cause nausea or migraines. Their OS has a setting to reduce motion — honor it.

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

Test by flipping the System Preferences → Accessibility → Display → "Reduce motion" on Mac.

8

Quick audit — three free tools

Automated tools catch about 40% of issues. The other 60% require manual testing — keyboard nav + screen reader.

Launch checklist

Go deeper

Accessibility is the baseline, not a feature.

Start with semantic HTML, label every input, respect motion preferences, and test with your keyboard. That covers 90% of real-world issues — everything else is polish on top of a solid foundation.