Ad – 728Γ—90
🌐 Advanced HTML

HTML Accessibility – ARIA, alt Text, Keyboard Navigation

Web accessibility means building pages that everyone can use β€” including the 1.3 billion people worldwide who have a disability. It is also a legal requirement in many countries (ADA, EN 301 549, EAA) and a direct SEO signal. The good news: semantic HTML gets you 80% of the way there for free.

⏱️ 22 min read🎯 BeginnerπŸ“… Updated 2026

Why Accessibility Matters

The Web Content Accessibility Guidelines (WCAG) define four principles for accessible content, summarised as POUR:

  • Perceivable β€” information must be presentable in ways all users can perceive (e.g., alt text for images).
  • Operable β€” interface components must be operable by keyboard, not just mouse.
  • Understandable β€” content and UI must be understandable.
  • Robust β€” content must be interpreted reliably by assistive technologies.

WCAG 2.1 has three conformance levels: A (minimum), AA (standard legal target), and AAA (enhanced). Most accessibility legislation requires AA compliance.

Legal note: The US ADA, UK Equality Act, and EU European Accessibility Act all require accessible websites. Lawsuits for inaccessible websites cost US companies over $3.5 billion in 2023 alone.

Semantic HTML is the Foundation

The single most impactful accessibility action is to use the right HTML element for the right job. Semantic elements carry built-in accessibility information that browsers expose to screen readers automatically β€” no extra work required.

HTML
<!-- Bad: div soup β€” screen readers have no context -->
<div class="header">
  <div class="nav">
    <div class="nav-item" onclick="go('/')">Home</div>
  </div>
</div>

<!-- Good: semantic HTML is accessible by default -->
<header>
  <nav aria-label="Main navigation">
    <ul>
      <li><a href="/">Home</a></li>
    </ul>
  </nav>
</header>

A screen reader announces a <button> as "button, activatable". A plain <div> with an onclick handler announces nothing useful. Use native elements whenever possible β€” <button>, <a>, <input>, <select> are all keyboard-focusable and announced correctly out of the box.

Images: Descriptive alt Text

Every <img> element must have an alt attribute. Screen readers read the alt text aloud. Search engines also use it for image indexing.

HTML
<!-- Informative image: describe what the image shows and why it matters -->
<img src="html-structure.png" alt="Diagram showing the HTML document tree with html, head, and body elements">

<!-- Functional image (linked image): describe the destination/action, not the image -->
<a href="/">
  <img src="logo.png" alt="ylearner – Go to homepage">
</a>

<!-- Decorative image: empty alt (screen readers skip it entirely) -->
<img src="decorative-wave.svg" alt="">

<!-- Bad: filename as alt (useless to screen reader users) -->
<img src="img_001.jpg" alt="img_001.jpg">

<!-- Bad: "image of" / "photo of" prefix (redundant β€” screen readers announce it is an image) -->
<img src="chart.png" alt="Image of a bar chart">

<!-- Good: describe content meaningfully -->
<img src="chart.png" alt="Bar chart showing 40% increase in web traffic from January to June 2026">
Missing alt attribute vs empty alt: A missing alt attribute is a WCAG failure. An empty alt="" is intentional β€” it tells screen readers "skip this image." Never omit the attribute entirely.

Forms: Labels, Fieldsets, and Error Messages

Forms are the area where accessibility failures are most common. Every form control must have a visible, programmatically associated label.

HTML
<!-- Method 1: label with for + matching input id (preferred) -->
<label for="email">Email address</label>
<input type="email" id="email" name="email" required autocomplete="email">

<!-- Method 2: wrapping label -->
<label>
  Username
  <input type="text" name="username" required>
</label>

<!-- Bad: placeholder is NOT a label -->
<input type="email" placeholder="Email address">

<!-- Grouping related inputs with fieldset and legend -->
<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>

<!-- Accessible error message linked to input -->
<label for="password">Password</label>
<input type="password" id="password" name="password"
       aria-describedby="password-error" aria-invalid="true">
<span id="password-error" class="error" role="alert">
  Password must be at least 8 characters.
</span>

Keyboard Navigation: tabindex, Focus Styles, Skip Links

Many users β€” including those with motor disabilities and power users β€” navigate entirely by keyboard. Tab moves forward through focusable elements; Shift+Tab moves backward; Enter/Space activates buttons and links.

HTML
<!-- Skip link: allows keyboard users to jump past repeated navigation -->
<!-- Place as the very first element in body -->
<a href="#main-content" class="skip-link">Skip to main content</a>

<!-- tabindex="0": adds a non-interactive element to the tab order -->
<div role="button" tabindex="0" onclick="doAction()"
     onkeydown="if(event.key==='Enter'||event.key===' ')doAction()">
  Click me
</div>
<!-- Note: use a real <button> instead when possible! -->

<!-- tabindex="-1": removes element from tab order but allows programmatic focus -->
<div id="modal" tabindex="-1">…modal content…</div>
<!-- document.getElementById('modal').focus() works, Tab key skips it -->

<!-- tabindex values greater than 0 create unpredictable tab order β€” AVOID -->
<!-- Bad: -->
<input tabindex="3">
<input tabindex="1">
<input tabindex="2">
Never remove focus styles: outline: none or outline: 0 without a replacement focus style is a WCAG 2.1 Level AA failure (Success Criterion 2.4.7). Always provide a visible focus indicator.

ARIA Attributes

ARIA (Accessible Rich Internet Applications) is a set of HTML attributes that supplement the accessibility information of elements when native semantics are insufficient. The first rule of ARIA: don't use ARIA if a native HTML element provides the semantics you need.

HTML
<!-- aria-label: provides an accessible name when no visible text label exists -->
<button aria-label="Close dialog">βœ•</button>
<input type="search" aria-label="Search tutorials">

<!-- aria-describedby: links to text that provides additional description -->
<input type="text" id="zip" aria-describedby="zip-hint">
<p id="zip-hint">Enter your 5-digit US ZIP code.</p>

<!-- aria-hidden: hides element from accessibility tree (e.g. decorative icons) -->
<span aria-hidden="true">🏠</span>
<span>Home</span>

<!-- aria-expanded: communicates open/closed state of collapsible content -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden><li><a href="/">Home</a></li></ul>

<!-- aria-live: announces dynamic content changes to screen readers -->
<div aria-live="polite" aria-atomic="true">
  <!-- Updated content will be announced when changed -->
  Form submitted successfully.
</div>

<!-- aria-current: marks the current item in a set (e.g. current nav link) -->
<nav>
  <a href="/" aria-current="page">Home</a>
  <a href="/about">About</a>
</nav>

ARIA Landmark Roles

Landmark roles allow screen reader users to jump directly to major page regions. HTML5 semantic elements map to landmark roles automatically β€” use the native elements and you get the landmarks for free.

Landmark RoleNative HTML ElementPurpose
banner<header> (top-level)Site header with logo and primary navigation
navigation<nav>Group of navigational links
main<main>Primary page content (one per page)
complementary<aside>Content that supports but is not essential to main
contentinfo<footer> (top-level)Footer with copyright, legal links
search<search> or role="search"Search functionality
form<form> with aria-labelLabelled form region
region<section> with aria-labelLabelled section of significant content
HTML
<!-- When you have multiple nav elements, label them for distinction -->
<nav aria-label="Main navigation">…</nav>
<nav aria-label="Breadcrumb">…</nav>
<nav aria-label="Pagination">…</nav>

<!-- Explicit landmark role when element cannot be semantic -->
<div role="main">…</div>  <!-- Only if <main> is not usable -->

Color Contrast

Approximately 8% of men and 0.5% of women have some form of color vision deficiency. WCAG 2.1 defines minimum contrast ratios for text against its background:

Text TypeLevel AA (minimum)Level AAA (enhanced)
Normal text (under 18pt / 14pt bold)4.5:17:1
Large text (18pt+ / 14pt+ bold)3:14.5:1
UI components and graphics3:1β€”

Use the WebAIM Contrast Checker or browser DevTools' accessibility panel to verify contrast. Never convey information using color alone β€” also use shape, pattern, or text labels.

Testing Accessibility

Automated tools catch about 30–40% of accessibility issues. Manual testing is essential:

  • Browser DevTools Accessibility panel β€” Chrome and Firefox both have built-in accessibility trees showing element roles, names, and states.
  • axe DevTools (free browser extension) β€” scans the page and reports WCAG violations with links to how-to-fix guides.
  • Lighthouse β€” built into Chrome DevTools, produces an accessibility score with actionable items.
  • Keyboard-only testing β€” unplug your mouse and navigate your entire page using only Tab, Shift+Tab, Enter, Space, and arrow keys.
  • Screen reader testing β€” NVDA (Windows, free), JAWS (Windows, paid), VoiceOver (macOS/iOS, built in), TalkBack (Android, built in).
Ad – 336Γ—280

πŸ“‹ Summary

  • Semantic HTML (<button>, <nav>, <main>, etc.) provides accessibility for free β€” use it instead of div soup.
  • Every <img> needs an alt attribute: descriptive for informative images, empty (alt="") for decorative ones.
  • Every form control needs a programmatically associated <label>; use <fieldset> and <legend> for groups of related inputs.
  • Add a skip link as the first element in <body> so keyboard users can bypass repeated navigation.
  • Never remove focus styles without providing a visible replacement.
  • ARIA attributes supplement native semantics β€” do not use ARIA where a native element already provides the role.
  • Aim for WCAG 2.1 Level AA compliance: 4.5:1 contrast for normal text, 3:1 for large text and UI components.

FAQ

Is ARIA necessary if I use semantic HTML?

Often not. Semantic HTML elements carry their own ARIA roles implicitly. For example, <button> has an implicit role="button", <nav> has role="navigation". You only need explicit ARIA when you are building a custom interactive widget (like a combobox, date picker, or carousel) that has no semantic HTML equivalent, or when you need to communicate dynamic state changes (like aria-expanded or aria-live).

What is the difference between aria-label and aria-labelledby?

aria-label provides an accessible name as a direct string value: aria-label="Close". aria-labelledby references the ID of an existing element whose text content becomes the accessible name: aria-labelledby="dialog-title". Use aria-labelledby when the label text is already visible on screen (avoiding duplication), and aria-label when there is no visible text to reference (icon-only buttons, etc.).

Do placeholder attributes count as labels?

No. Placeholder text fails as a label for several reasons: it disappears when the user starts typing (making it impossible to check what was required), it often has insufficient color contrast, and older screen readers do not consistently announce placeholders as labels. Always use a visible <label> element. Placeholder text can supplement a label but must never replace it.