Skip to main content
Ganesh Joshi
Back to Blogs

Web accessibility basics every developer should know

February 9, 20264 min read
Tutorials
Code on screen, accessible web

Web accessibility ensures everyone can use your site, regardless of ability. About 15% of the world's population has some form of disability. Beyond ethics and inclusion, accessibility improves SEO, reaches more users, and is often legally required. These fundamentals cover most common issues.

Semantic HTML

The foundation of accessibility is using HTML elements for their intended purpose:

Don't use Use instead Why
<div onclick> <button> Keyboard accessible, announced as button
<div> for navigation <nav> Screen readers identify it as navigation
<span> for links <a href> Keyboard accessible, focusable
<b> <strong> Conveys importance, not just visual
<i> <em> Conveys emphasis

Document structure

<body>
  <header>
    <nav><!-- Main navigation --></nav>
  </header>

  <main>
    <h1>Page Title</h1>
    <article>
      <h2>Section</h2>
      <p>Content...</p>
    </article>
  </main>

  <aside><!-- Related content --></aside>

  <footer><!-- Footer content --></footer>
</body>

Heading hierarchy

Use headings in order—don't skip levels:

<!-- Good -->
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>

<!-- Bad -->
<h1>Page Title</h1>
<h3>Skipped h2!</h3>

Keyboard navigation

Everything must work with keyboard alone:

Key Action
Tab Move to next focusable element
Shift+Tab Move to previous element
Enter Activate buttons, links
Space Activate buttons, toggle checkboxes
Escape Close modals, menus
Arrow keys Navigate within components

Focus visibility

Never remove focus outlines without replacement:

/* Bad */
button:focus {
  outline: none;
}

/* Good */
button:focus-visible {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

Focus management

Move focus appropriately:

// After opening modal
modalRef.current?.focus();

// After closing modal
triggerButtonRef.current?.focus();

Skip links

Help keyboard users bypass navigation:

<a href="#main-content" class="skip-link">
  Skip to main content
</a>

<main id="main-content" tabindex="-1">
  <!-- Page content -->
</main>
.skip-link {
  position: absolute;
  left: -9999px;
}

.skip-link:focus {
  position: static;
  left: auto;
}

Color and contrast

Contrast ratios

WCAG Level Normal text Large text
AA 4.5:1 3:1
AAA 7:1 4.5:1

Large text is 18pt (24px) or 14pt (18.5px) bold.

Use a contrast checker to verify.

Don't rely on color alone

<!-- Bad: only color indicates error -->
<input style="border-color: red;" />

<!-- Good: color + icon + text -->
<input aria-invalid="true" aria-describedby="error-msg" />
<span id="error-msg">
  <svg aria-hidden="true"><!-- Error icon --></svg>
  Please enter a valid email
</span>

Images and media

Alt text guidelines

Image type Alt text
Informative Describe content and purpose
Decorative alt="" (empty)
Functional (button/link) Describe the action
Complex (chart/graph) Brief alt + detailed description
<!-- Informative -->
<img src="chart.png" alt="Sales increased 25% in Q4 2025" />

<!-- Decorative -->
<img src="decoration.png" alt="" />

<!-- Functional -->
<button>
  <img src="search.svg" alt="Search" />
</button>

<!-- Complex with description -->
<figure>
  <img src="chart.png" alt="Q4 sales chart" aria-describedby="chart-desc" />
  <figcaption id="chart-desc">
    Detailed breakdown: January $100k, February $120k...
  </figcaption>
</figure>

Video and audio

Content Required
Video Captions
Audio Transcript
Complex video Audio description

Forms

Labels

Every input needs a label:

<!-- Explicit label -->
<label for="email">Email address</label>
<input type="email" id="email" name="email" />

<!-- Implicit label -->
<label>
  Email address
  <input type="email" name="email" />
</label>

<!-- Visually hidden label -->
<label for="search" class="sr-only">Search</label>
<input type="search" id="search" placeholder="Search..." />

Error messages

Connect errors to inputs:

<label for="email">Email</label>
<input
  type="email"
  id="email"
  aria-invalid="true"
  aria-describedby="email-error"
/>
<span id="email-error" role="alert">
  Please enter a valid email address
</span>

Required fields

<label for="name">
  Name <span aria-hidden="true">*</span>
  <span class="sr-only">(required)</span>
</label>
<input type="text" id="name" required aria-required="true" />

ARIA basics

ARIA supplements HTML when semantic elements aren't enough:

Attribute Purpose
aria-label Provide accessible name
aria-labelledby Reference visible label
aria-describedby Reference description
aria-hidden Hide from assistive tech
aria-live Announce dynamic updates
role Define element purpose
<!-- Icon button -->
<button aria-label="Close menu">
  <svg aria-hidden="true"><!-- X icon --></svg>
</button>

<!-- Live region for updates -->
<div aria-live="polite" aria-atomic="true">
  3 items in cart
</div>

First rule of ARIA

Don't use ARIA if native HTML works:

<!-- Bad -->
<div role="button" tabindex="0" onclick="...">Click me</div>

<!-- Good -->
<button onclick="...">Click me</button>

Testing accessibility

Tool Purpose
axe DevTools Automated testing
WAVE Visual feedback
Lighthouse Audit with scoring
Screen reader Manual testing
Keyboard Navigation testing

Manual testing checklist

  1. Navigate with Tab only
  2. Verify focus is visible
  3. Test with screen reader (NVDA, VoiceOver)
  4. Check color contrast
  5. Zoom to 200%
  6. Verify heading structure

Summary

Use semantic HTML as the foundation. Ensure keyboard navigation works everywhere with visible focus. Meet contrast ratios (4.5:1 for text). Provide meaningful alt text for images. Label all form inputs and connect error messages. Use ARIA sparingly when HTML isn't enough. Test with keyboard, screen readers, and automated tools.

Frequently Asked Questions

Web accessibility means making websites usable by everyone, including people with disabilities. This includes users of screen readers, keyboard-only navigation, and those with visual, motor, or cognitive impairments.

WCAG (Web Content Accessibility Guidelines) is the international standard for web accessibility. Level A is minimum, Level AA is the common target, and Level AAA is the highest standard.

Semantic HTML (button, nav, main, heading tags) provides meaning that assistive technologies understand. Screen readers use these elements to navigate and describe content to users.

WCAG Level AA requires 4.5:1 contrast for normal text and 3:1 for large text. Level AAA requires 7:1 and 4.5:1 respectively. Use a contrast checker to verify.

Add descriptive alt text for meaningful images. Use empty alt (alt='') for decorative images. The alt text should convey the image's purpose or content, not just describe it.

Related Posts