← Back to the Build Your Homepage series
♿
EPISODE 01
WCAG · POUR · screen readers · why it matters · Roles · states · properties · the first rule of ARIA · label · fieldset · errors · keyboard · focus management · alt text · captions · contrast · prefers-reduced-motion

Accessibility Concepts · ARIA & Semantic HTML · Accessible Forms · Images, Media, Color & Motion

Accessibility is not a nice-to-have. Learn the WCAG framework (POUR), how screen readers consume your page, and the legal/business case. ARIA exists for cases where HTML semantics fall short. Use semantic HTML first; reach for ARIA only when needed. Master the basics: role, aria-label, aria-expanded. Forms are the most accessibility-critical part of your app. Pair labels properly, group with fieldset, announce errors clearly, and manage focus. Make images, video, color, and animation usable for everyone. Write meaningful alt text, caption video, hit contrast ratios, and respect reduced-motion preferences.

a11yWCAGPOURscreen readerARIArolearia-labelsemanticformlabelfieldseterrorsfocusaltcaptionscontrastreduced motion
Duration
⏱ About 1.5 hours + About 1.5 hours + About 1.5 hours + About 1.5 hours
Level
📊 Intermediate
Prerequisite
🎯 HTML + CSS basics
OUTCOME
Audit a page with a screen reader and a checklist / Mark up custom widgets so screen readers can understand them / Forms that any keyboard or screen-reader user can complete / Pages that pass automated contrast checks and respect user motion preferences

What you'll learn

  • 1Explain WCAG and the four POUR principles
  • 2Run a basic screen-reader test (VoiceOver, NVDA)
  • 3Recognize common a11y wins (alt text, headings, focus)
  • 4Use Lighthouse and axe-core for automated audits
  • 5Apply the first rule of ARIA
  • 6Use roles to describe widget purpose
  • 7Announce dynamic state with aria-expanded, aria-checked
  • 8Label icon-only buttons with aria-label
  • 9Pair every input with a real <label>
  • 10Group related inputs with <fieldset> + <legend>
  • 11Show validation errors with aria-describedby and aria-invalid
  • 12Move focus to the first error on submit
  • 13Write alt text that conveys meaning, not just description
  • 14Add captions and transcripts to video
  • 15Meet WCAG AA contrast (4.5:1 for normal text)
  • 16Respect prefers-reduced-motion

Accessibility Concepts

Accessibility is not a nice-to-have. Learn the WCAG framework (POUR), how screen readers consume your page, and the legal/business case.

1. Why Accessibility?

  • ~15% of the world lives with a disability — your users
  • Better a11y = better SEO and better usability for everyone
  • Legally required in many countries (ADA, EAA)
  • Costs almost nothing if built in from day one

2. POUR — The WCAG Pillars

PrincipleMeans
PerceivableContent can be perceived by sight, sound, or touch (alt text, captions, contrast)
OperableAll actions work with keyboard alone (no mouse-only menus)
UnderstandablePredictable navigation, clear errors, plain language
RobustWorks with assistive technologies and future browsers

3. WCAG Levels

  • A — minimum
  • AA — most regulations target this
  • AAA — gold standard, rarely fully achievable site-wide

4. Quick Audit Tools

  • Lighthouse (Chrome DevTools) — automated checks
  • axe DevTools — deeper accessibility issues
  • VoiceOver (macOS) Cmd+F5 / NVDA (Windows) — real screen-reader testing
  • Tab through your site with the keyboard — focus order should make sense
💡

Automated tools catch ~30% of issues. Always do a manual keyboard + screen-reader test before shipping.

ARIA & Semantic HTML

ARIA exists for cases where HTML semantics fall short. Use semantic HTML first; reach for ARIA only when needed. Master the basics: role, aria-label, aria-expanded.

1. The First Rule of ARIA

⚠️

"If you can use a native HTML element instead of an ARIA role/property to achieve the same accessibility, do so." — W3C

html
<!-- Bad: re-implementing a button -->
<div role="button" tabindex="0" onclick="...">Click</div>

<!-- Good: just use a button -->
<button onclick="...">Click</button>

2. Common Roles

roleUse
bannerTop of page header
navigation<nav> equivalent
main<main> equivalent
contentinfo<footer> equivalent
searchSearch region
dialogModal dialog
alertTime-sensitive notice (assertive)
statusStatus update (polite)

3. State & Properties

html
<!-- A toggle button -->
<button aria-pressed="false">Mute</button>

<!-- An accordion -->
<button aria-expanded="false" aria-controls="panel-1">FAQ</button>
<div id="panel-1" hidden>Answer...</div>

<!-- An icon-only button -->
<button aria-label="Close dialog">
  <svg ...></svg>
</button>

<!-- A live region -->
<div role="status" aria-live="polite">3 items saved.</div>

4. Landmarks Help Navigation

Screen-reader users press a shortcut to jump between landmarks. Semantic HTML (header/nav/main/footer) creates them for free.

Accessible Forms

Forms are the most accessibility-critical part of your app. Pair labels properly, group with fieldset, announce errors clearly, and manage focus.

1. Real Labels (not Placeholders)

html
<!-- Bad: placeholder is not a label -->
<input placeholder="Email">

<!-- Good: explicit label -->
<label for="email">Email</label>
<input id="email" name="email" type="email">

<!-- Or wrapped -->
<label>
  Email
  <input name="email" type="email">
</label>

2. fieldset for Groups

html
<fieldset>
  <legend>Shipping address</legend>
  <label>Street <input name="street"></label>
  <label>City <input name="city"></label>
</fieldset>

<fieldset>
  <legend>Newsletter</legend>
  <label><input type="radio" name="news" value="weekly"> Weekly</label>
  <label><input type="radio" name="news" value="monthly"> Monthly</label>
</fieldset>

3. Error Messages

html
<label for="pw">Password</label>
<input id="pw" type="password" aria-describedby="pw-help pw-err" aria-invalid="true">
<span id="pw-help">At least 8 characters.</span>
<span id="pw-err" role="alert">Password too short.</span>
💡

Show errors near the input, link them with aria-describedby, and announce them with role="alert".

4. Focus on Submit Error

javascript
function onSubmit(e) {
  e.preventDefault();
  const errors = validate(form);
  if (errors.length) {
    const first = form.querySelector(`[name="${errors[0].name}"]`);
    first?.focus();
    return;
  }
  // ... submit ...
}

Images, Media, Color & Motion

Make images, video, color, and animation usable for everyone. Write meaningful alt text, caption video, hit contrast ratios, and respect reduced-motion preferences.

1. Alt Text That Helps

html
<!-- Informative image -->
<img src="chart.png" alt="Revenue grew from $1M in Q1 to $1.8M in Q3 2024">

<!-- Decorative — use empty alt to skip in screen readers -->
<img src="border.svg" alt="" aria-hidden="true">

<!-- Image is the link content -->
<a href="/profile"><img src="alice.jpg" alt="Alice — go to profile"></a>

2. Video & Audio

html
<video controls>
  <source src="demo.mp4" type="video/mp4">
  <track kind="captions" src="demo.en.vtt" srclang="en" label="English">
</video>

<details><summary>Transcript</summary>
  <p>[00:00] Welcome to the demo...</p>
</details>
  • Captions for hearing impaired
  • Audio descriptions for blind users
  • Transcripts help everyone (and SEO)

3. Color Contrast

Text sizeWCAG AA minimum
Normal text4.5:1
Large text (18pt+ / 14pt+ bold)3:1
UI components & graphics3:1
💡

Never convey information by color alone. Use icons or text labels too.

4. Reduced Motion

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

Some users get nauseous from heavy animation. Respect their OS-level preference.

Example code / lecture materials

All lecture materials and example code are openly available on GitHub.

View on GitHub ↗

Comments 0

No comments yet. Be the first.

Only members can comment.

Comments with ads, abuse, personal information or illegal content are hidden or removed. Comments are shown with your nickname. Privacy Policy