AtomLearnAtomLearn
DashboardGoalsPathAchievementsReviewSign In
DashboardHTMLHTML Accessibility Basics
HTMLNot Started

HTML Accessibility Basics

Write HTML that works for everyone — alt text, ARIA, keyboard navigation, and semantic structure

0%
Knowledge0%
Learn & Drill
Fluency0%
Drill & Speed
Retention0%
Mastery & Review
Confidence0%
All modes
Practice

Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.

Upgrade
Knowledge
Fluency
Retention

Knowledge Debt detected

You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.

Explanation

Accessibility (a11y) means your site works for people using screen readers, keyboard-only navigation, or low-vision tools. Most of it is good HTML — not extra work.

The first rule of ARIA: don't use it if native HTML does the job. ``html <!-- Bad: div pretending to be a button --> <div onclick="submit()">Submit</div> <!-- Good: actual button — keyboard, focus, ARIA, roles included for free --> <button type="submit">Submit</button>

Alt text — three cases:

html <!-- 1. Informational: describe what the image shows --> <img src="chart.png" alt="Bar chart showing 40% increase in Q2 revenue" /> <!-- 2. Decorative: empty alt — screen readers skip it --> <img src="divider.svg" alt="" /> <!-- 3. Functional (linked or button icon): describe the action --> <a href="/home"><img src="logo.png" alt="AtomLearn — go to home page" /></a>

ARIA — use sparingly, only when HTML has no equivalent:

html <!-- aria-label: names an element when visible text isn't enough --> <button aria-label="Close dialog">✕</button> <!-- aria-labelledby: points to another element's text as the label --> <section aria-labelledby="section-title"> <h2 id="section-title">Recent Atoms</h2> ... </section> <!-- aria-live: announces dynamic content changes to screen readers --> <div aria-live="polite" role="status">Form submitted successfully</div> <!-- aria-hidden: removes element from the accessibility tree --> <span aria-hidden="true">★★★★☆</span> <span class="sr-only">Rating: 4 out of 5 stars</span>

Keyboard navigation:

  • Every interactive element (links, buttons, inputs) must be focusable and usable with Enter/Space
  • Use tabindex="0" to make a non-interactive element focusable
  • Never use tabindex values > 0 — it breaks the natural tab order
  • tabindex="-1" makes an element focusable via JS only (not in tab order)

Visible focus indicator:

css /* Never do this — it makes keyboard navigation impossible */ :focus { outline: none; } /* Do this instead — custom focus ring that's still visible */ :focus-visible { outline: 2px solid #6366f1; outline-offset: 2px; }

Screen-reader-only text:

css .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }

Examples

Accessible icon button

`aria-hidden="true"` on decorative icons removes them from the a11y tree; the visible text or `aria-label` provides the accessible name

<!-- Bad: no accessible name, not keyboard accessible -->
<div onclick="handleClose()">✕</div>

<!-- Good: button with aria-label for the icon's meaning -->
<button type="button" aria-label="Close dialog" onclick="handleClose()">
  ✕
</button>

<!-- Good: icon + screen-reader-only text -->
<button type="button">
  <svg aria-hidden="true" focusable="false"><!-- icon svg --></svg>
  <span class="sr-only">Close dialog</span>
</button>

Accessible form error feedback

`aria-describedby` links an error message to its input; `role="alert"` triggers immediate announcement; `aria-invalid="true"` signals the error state

<form>
  <label for="email">Email</label>
  <input
    type="email"
    id="email"
    name="email"
    required
    aria-describedby="email-error"
    aria-invalid="true"
  />
  <!-- Error message linked to input via aria-describedby -->
  <p id="email-error" role="alert">
    Please enter a valid email address.
  </p>
</form>

<!-- Loading state announced to screen readers -->
<button type="submit" aria-busy="true" disabled>
  Saving...
</button>

How well did you understand this?