After this lesson, you will be able to: Build accessible webpages using semantic HTML, proper form labels, ARIA roles where appropriate, keyboard-friendly focus management, and WCAG AA color contrast — and explain why accessibility is a legal and professional requirement, not a nice-to-have.
Most web developers learn HTML and never learn accessibility properly, then ship pages that break for users with screen readers, keyboard-only navigation, or low vision. In 2025 that is no longer acceptable. The Americans with Disabilities Act has been the basis for thousands of US web accessibility lawsuits in the last decade, and the European Accessibility Act took effect in June 2025 with real fines attached. Beyond the legal angle, accessibility is also a hiring filter: a junior frontend candidate who can talk about semantic HTML, ARIA, and WCAG signals professional craft. This lesson takes you from zero to fluent on each of those.
Three reasons that override personal opinion on the subject. First, the law. The Americans with Disabilities Act (ADA) has been the basis for major web accessibility settlements at Target, Domino's, and Netflix; the European Accessibility Act took effect 28 June 2025 with serious enforcement penalties for consumer-facing sites and apps; US federal agencies are bound by Section 508. Second, the audience. Roughly 15% of the global population lives with some form of disability. A page that breaks for screen readers is a page that excludes a sixth of your potential users. Third, the job market. Every team you'll interview at considers accessibility a senior-engineer-level concern, and a junior candidate who can talk about it credibly stands out. That alone makes this lesson worth your time.
Most accessibility wins do not require ARIA or special attributes. They come from picking the right HTML tag. Use `<header>` for the top of the page, `<nav>` for the main navigation, `<main>` for the primary content (one per page), `<article>` for self-contained content like a blog post, `<section>` for thematic groupings inside that, `<aside>` for sidebars, and `<footer>` for the bottom. Screen readers use these landmarks to let users jump straight to navigation, main content, or footer with a keystroke. A page built entirely from `<div>` is a wall of text for a screen reader user. A page built from semantic tags is navigable from the first try.
The non-semantic version below renders identically to the semantic one in a sighted browser. To a screen reader, the second one is a navigable document; the first one is a single block.
<!-- Bad. Everything is a div. Screen reader sees one big block. --><div class="top"><div class="links"><div onclick="goHome()">Home</div><div onclick="goAbout()">About</div></div></div><div class="page"><div class="post"><div class="title">My Post</div><div>Post body...</div></div></div><!-- Good. Semantic landmarks, real buttons, real headings. --><header><nav><a href="/">Home</a><a href="/about">About</a></nav></header><main><article><h1>My Post</h1><p>Post body...</p></article></main>
Every form input must be associated with a `<label>`. The cleanest pattern is `<label for="id">…</label>` paired with `<input id="id">`. A label that just sits next to an input visually is invisible to a screen reader — the explicit `for` / `id` link is what makes it announceable. Group related fields with `<fieldset>` and `<legend>` (think: radio button group, billing address block). For inline error messages, link the error text to the input with `aria-describedby`, and use `aria-invalid="true"` on the input itself when there's a validation error so screen readers announce the failure. Never communicate state with color alone — a red border without text reads as 'normal input' to a screen reader.
Pattern you can copy. Notice the label/input ID pairing, the `aria-describedby` linking the error message back to the input, and the use of a real `<button>` instead of a `<div onclick>`.
<form><label for="email">Email address</label><inputid="email"name="email"type="email"requiredaria-describedby="email-error"aria-invalid="true"/><p id="email-error" role="alert">Enter a valid email like [email protected].</p><fieldset><legend>Preferred contact method</legend><label><input type="radio" name="contact" value="email" /> Email</label><label><input type="radio" name="contact" value="phone" /> Phone</label></fieldset><button type="submit">Sign up</button></form>
Roughly 8% of users browse without a mouse — power users, motor-impaired users, screen reader users. They all rely on the Tab key to walk through your interactive elements. Two rules. First, Tab order must match visual order. If a user tabs and the focus jumps from the header straight to the footer, you've broken the page. Second, every focusable element needs a visible focus ring. Browsers give you one for free; lots of CSS resets remove it (`*:focus { outline: none; }`) — never do that. Either keep the default outline or replace it with something equally visible.
WCAG 2.1 Level AA is the standard most laws point at. The contrast rule: normal text needs a contrast ratio of at least 4.5:1 against its background; large text (18pt+ or 14pt bold+) needs at least 3:1. Pure black on white is 21:1; light gray on white is often under 4:1 and fails. You don't measure this by eye. Tools do it for you — DevTools' Color picker shows the ratio inline, and design tools like Figma have plugins that check every text layer. If you're picking a brand color, test it against your background before you commit.
Three free tools cover most of what a manual audit would find. **Lighthouse** is built into Chrome DevTools (open DevTools → Lighthouse → Accessibility). It runs in 30 seconds and surfaces missing labels, low contrast, missing alt text, and tab-order issues. **axe DevTools** is a browser extension from Deque (the company that wrote the most-used accessibility testing library). More thorough than Lighthouse on edge cases. **A screen reader** — NVDA is free on Windows, VoiceOver ships with macOS (Cmd+F5). Spend ten minutes navigating one of your own pages with the screen reader and your eyes closed. It is the single most useful exercise on this list.
The starter code is a form that mostly works for a sighted mouse user and is broken for everyone else. Refactor it to: real `<button>` instead of `<div onclick>`; `<label for="…">` paired with `<input id="…">` on both inputs; `<fieldset>` + `<legend>` around the radio group. Pass once your code contains the four required patterns below.
Read each line and pick the worst offender.
Sign in and purchase access to unlock this lesson.