Web Accessibility (a11y): Build Sites That Everyone Can Use
Web accessibility guide for developers — learn WCAG principles, semantic HTML, ARIA, keyboard navigation, color contrast, and how to audit your site for a11y issues.
Get more content like this on Telegram!
Daily AI tips, notes & resources — free
Web Accessibility (a11y): Build Sites That Everyone Can Use
I used to treat accessibility as an afterthought — something to add if a client specifically requested it. Then I watched someone navigate a website using only a keyboard, and I realized my "accessible" site was completely unusable for them.
There was no visible focus indicator. The navigation menu wasn't keyboard-accessible. The modal dialog trapped the focus and couldn't be closed. Forms had inputs with no labels.
I had built something that looked functional but was actively hostile to a significant portion of potential users.
1.3 billion people — 16% of the global population — live with some form of disability. Accessibility isn't a niche concern. And in many countries, it's a legal requirement.
This guide teaches you the practical techniques: semantic HTML, ARIA, keyboard navigation, color contrast, and how to audit your site with free tools.
The Four Principles of Accessibility (POUR)
WCAG (Web Content Accessibility Guidelines) organizes requirements around four principles:
- Perceivable — Information must be perceivable, not just visual/auditory (alt text for images, captions for videos)
- Operable — Users must be able to operate the interface (keyboard navigation, sufficient time to interact)
- Understandable — Content must be understandable (clear language, consistent navigation, helpful error messages)
- Robust — Content must work with current and future assistive technologies (valid HTML, proper ARIA)
Semantic HTML: The Foundation
The single most impactful accessibility improvement is using correct HTML elements. Semantic HTML communicates meaning to screen readers and assistive technologies.
<!-- Non-semantic — screen reader sees generic containers -->
<div class="header">
<div class="nav">
<div onclick="navigate()">Home</div>
</div>
</div>
<div class="main">
<div class="title">About Us</div>
</div>
<!-- Semantic — screen reader understands structure -->
<header>
<nav>
<a href="/">Home</a>
</nav>
</header>
<main>
<h1>About Us</h1>
</main>
Heading Hierarchy
Headings create the document outline. Screen reader users often navigate by jumping between headings:
<!-- Wrong — skipping heading levels -->
<h1>Page Title</h1>
<h3>Section</h3> <!-- jumped from h1 to h3 -->
<h5>Subsection</h5>
<!-- Correct — logical hierarchy -->
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
One <h1> per page. Don't use headings for visual styling — use CSS for that.
Buttons vs Links
<!-- Link: navigates to a URL -->
<a href="/about">About Us</a>
<!-- Button: triggers an action -->
<button type="button" onclick="openModal()">Open Settings</button>
<!-- Never do this -->
<div onclick="doSomething()">Click me</div>
<!-- Divs aren't keyboard accessible or announced correctly by screen readers -->
Images and Alternative Text
Every <img> needs an alt attribute. The text should describe what the image communicates, not just what it shows:
<!-- Informative image — describe the content/meaning -->
<img src="bar-chart.png" alt="Q3 revenue up 23% compared to Q2, reaching $4.2M" />
<!-- Decorative image — empty alt (screen reader ignores it) -->
<img src="decorative-divider.png" alt="" role="presentation" />
<!-- Functional image (linked) — describe what it does -->
<a href="/home">
<img src="logo.png" alt="AiTechWorlds — Home" />
</a>
<!-- Avoid -->
<img src="photo.jpg" alt="image" /> <!-- meaningless -->
<img src="photo.jpg" alt="photo of" /> <!-- incomplete -->
<img src="photo.jpg" /> <!-- missing alt entirely -->
Keyboard Navigation
Every interactive element must be usable without a mouse. Test by pressing Tab to move forward, Shift+Tab to move backward, Enter to activate links and buttons, and Space to activate buttons and checkboxes.
Focus Visible
WCAG 2.1 requires visible focus indicators. Many developers remove the default outline for aesthetics:
/* Never do this */
* { outline: none; }
button:focus { outline: none; }
/* Custom focus styles — visible, branded */
button:focus-visible {
outline: 3px solid #3b82f6;
outline-offset: 2px;
}
/* Use :focus-visible — only shows on keyboard navigation, not mouse clicks */
a:focus-visible {
outline: 3px solid #3b82f6;
border-radius: 4px;
}
:focus-visible is the modern approach — shows focus rings for keyboard users but not mouse users (no blue ring when clicking a button).
Skip Navigation Link
For pages with navigation before main content, provide a skip link:
<!-- First element in body -->
<a href="#main-content" class="skip-link">
Skip to main content
</a>
<nav>...</nav>
<main id="main-content">...</main>
.skip-link {
position: absolute;
top: -100%; /* Hidden by default */
}
.skip-link:focus {
top: 0; /* Visible when focused via keyboard */
z-index: 100;
background: #000;
color: #fff;
padding: 0.75rem 1.5rem;
}
Screen reader users and keyboard users skip straight to main content, bypassing repeated navigation.
Color Contrast
Text must have sufficient contrast against its background. WCAG 2.1 AA minimums:
| Text Type | Minimum Ratio |
|---|---|
| Normal text (under 18px) | 4.5:1 |
| Large text (18px+ or 14px+ bold) | 3:1 |
| UI components (buttons, inputs) | 3:1 |
/* These fail 4.5:1 contrast */
color: #999999; background: #ffffff; /* 2.85:1 ❌ */
color: #767676; background: #ffffff; /* 4.48:1 ❌ */
/* These pass */
color: #595959; background: #ffffff; /* 7.0:1 ✓ */
color: #3b82f6; background: #ffffff; /* 3.0:1 — borderline, use darker */
color: #1d4ed8; background: #ffffff; /* 5.1:1 ✓ */
Use the free WebAIM Contrast Checker to verify any color combination.
Forms: Labels and Error Messages
Every form input needs a visible, associated label:
<!-- Correct — label linked via for/id -->
<label for="email">Email address</label>
<input type="email" id="email" name="email" required />
<!-- Also correct — label wrapping input -->
<label>
Email address
<input type="email" name="email" required />
</label>
<!-- Wrong — placeholder is not a label -->
<input type="email" placeholder="Email address" />
<!-- Disappears when typing, low contrast, not announced as a label -->
Accessible Error Messages
<div>
<label for="email">Email</label>
<input
type="email"
id="email"
aria-describedby="email-error"
aria-invalid="true"
/>
<p id="email-error" role="alert">
Please enter a valid email address.
</p>
</div>
aria-describedby associates the error with the input. role="alert" announces the error to screen readers immediately when it appears.
ARIA: When HTML Isn't Enough
ARIA attributes communicate additional semantics when native HTML is insufficient:
<!-- Modal dialog -->
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<h2 id="modal-title">Confirm Delete</h2>
<p>Are you sure? This cannot be undone.</p>
<button>Cancel</button>
<button>Delete</button>
</div>
<!-- Icon button with no visible text -->
<button aria-label="Close dialog">
<svg aria-hidden="true"><!-- X icon --></svg>
</button>
<!-- Dynamic content updates -->
<div aria-live="polite" id="status">
<!-- Changes here are announced by screen readers -->
Saving your changes...
</div>
The first rule of ARIA: use native HTML elements when possible. <button> is always preferable to <div role="button">.
For ensuring your accessible site is also performant, see our web performance guide. And our HTML and CSS beginners guide covers semantic HTML from scratch.
Quick Accessibility Audit
- Run Lighthouse — Chrome DevTools → Lighthouse → Accessibility → Generate report
- Install axe DevTools — Browser extension that highlights issues directly on the page
- Tab through your site — Can you reach and operate everything without a mouse?
- Check contrast — Does all text meet 4.5:1 ratio?
- Zoom to 200% — Does content reflow or overflow?
- Turn on screen reader — VoiceOver (Mac) or NVDA (Windows) and try your main flows
Frequently Asked Questions
What is web accessibility?
Building websites usable by people with disabilities. 1.3 billion people (16% of population) have some disability. Legal requirement in many countries.
What is WCAG and what level to target?
WCAG 2.1 AA is the standard target. Minimum legal requirement in most countries. Covers contrast, keyboard navigation, text alternatives, and focus.
What is ARIA?
Attributes that add semantic meaning for assistive technologies. Use only when native HTML is insufficient. <button> beats <div role="button"> every time.
Minimum color contrast?
4.5:1 for normal text, 3:1 for large text and UI components (WCAG 2.1 AA).
How to test for accessibility?
Lighthouse and axe DevTools (automated), tab-only navigation test, screen reader test, contrast checker.
Frequently Asked Questions
AiTechWorlds Team
✓ Verified WriterThe AiTechWorlds team is passionate about AI, technology, and education. We create high-quality, research-backed content to help you learn, grow, and succeed in the modern digital world.
Related Articles
Understanding APIs: A Beginner's Story About How Apps Talk
API tutorial for beginners — understand what APIs are, how REST APIs work, HTTP methods, JSON, authentication, and how to call APIs in JavaScript with real examples.
The Web Developer's Guide to Chrome DevTools (Hidden Features)
Chrome DevTools guide for web developers — master the Elements panel, Network tab, Console, Performance profiler, and hidden features most developers never use.
CSS Grid vs Flexbox: When to Use Which Layout Method
CSS Grid vs Flexbox explained clearly — understand the difference, when each layout method excels, and how to choose the right one for every UI pattern.
Docker for Beginners: Containers Explained Without the Jargon
Docker tutorial for beginners — learn containers vs VMs, Docker images, Dockerfiles, docker-compose, and how to containerize a real web application step by step.