Web Accessibility Guide — WCAG 2.2, ARIA, and Inclusive Design
Web accessibility ensures websites are usable by everyone, including people with disabilities — covering visual, auditory, motor, and cognitive impairments through inclusive design and assistive technology support.
What You’ll Learn
- WCAG 2.2 guidelines and compliance levels
- ARIA attributes for dynamic content
- Keyboard navigation patterns
- Color contrast requirements and tools
- Screen reader testing techniques
Why It Matters
Over 1 billion people worldwide have some form of disability. Inaccessible websites exclude 15-20% of users, risk legal liability (ADA lawsuits in the US, EU Accessibility Act), and lose business. Accessible websites also rank better in SEO, load faster, and work better on all devices.
Real-world use: Durga Antivirus Pro includes a screen reader accessible dashboard so security analysts with visual impairments can monitor threats. Doda Browser highlights accessibility issues in its DevTools-style inspector for web developers.
flowchart LR
A[Perceivable] --> B[Operable]
B --> C[Understandable]
C --> D[Robust]
D --> A
style A fill:#4af,color:#fff
style B fill:#f90,color:#fff
style C fill:#4a4,color:#fff
style D fill:#f44,color:#fff
WCAG 2.2 Guidelines
WCAG is organized around four principles (POUR):
Perceivable
Users must be able to perceive the content through at least one of their senses.
<!-- Text alternatives for non-text content -->
<img src="chart.webp" alt="Bar chart showing monthly threat detections: January 150, February 230, March 310">
<!-- Captions for video -->
<video controls>
<source src="tutorial.mp4" type="video/mp4">
<track kind="captions" src="captions-en.vtt" srclang="en" label="English">
</video>
<!-- Adaptable content — correct heading hierarchy -->
<h1>Page Title</h1>
<h2>Section One</h2>
<h3>Subsection A</h3>
<h2>Section Two</h2>
<!-- Distinguishable — don't rely on color alone -->
<span style="color: red">* Required fields</span>
<!-- Better: add text indicator -->
<span style="color: red">* <span class="sr-only">Required</span></span>Operable
User interface components and navigation must be operable by everyone.
<!-- All functionality available via keyboard -->
<button onclick="submit()">Submit</button> <!-- Works with Enter/Space -->
<a href="/page">Link</a> <!-- Works with Enter -->
<!-- Skip link for keyboard users -->
<a href="#main-content" class="skip-link" style="position: absolute; left: -9999px;">
Skip to main content
</a>
<!-- Focus indicators — especially for custom elements -->
<style>
:focus-visible {
outline: 3px solid #4a90d9;
outline-offset: 2px;
}
/* Don't remove focus outlines without replacing them */
*:focus { outline: none; } /* BAD — invisible focus */
*:focus-visible { outline: 3px solid blue; } /* GOOD */
</style>Understandable
Content and interface must be understandable.
<!-- Clear language and predictable behavior -->
<html lang="en">
<!-- Consistent navigation — same position across pages -->
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/support">Support</a></li>
</ul>
</nav>
<!-- Error suggestions -->
<label for="email">Email</label>
<input type="email" id="email" required
aria-describedby="email-error"
aria-invalid="true">
<span id="email-error" role="alert">
Please enter a valid email address (e.g., user@example.com).
</span>Robust
Content must work with current and future assistive technologies.
<!-- Valid HTML (parses without errors) -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accessible Page</title>
</head>
<!-- ARIA roles and properties -->
<div role="alert" aria-live="assertive">
Scan complete: 3 threats detected.
</div>
<button aria-expanded="false" aria-controls="menu">
Menu
</button>
<div id="menu" hidden>
<a href="#home">Home</a>
<a href="#settings">Settings</a>
</div>ARIA (Accessible Rich Internet Applications)
ARIA provides attributes that supplement HTML semantics when native elements aren’t enough.
ARIA Rules
- Don’t use ARIA when native HTML works — Use
<button>instead of<div role="button"> - Don’t change native semantics — Don’t add
role="heading"to a<button> - All interactive ARIA controls must be keyboard accessible
- Use
aria-labelonly when visible label isn’t possible
<!-- GOOD: Using native HTML -->
<button>Save</button>
<!-- ACCEPTABLE: Custom control with ARIA -->
<div role="button" tabindex="0"
aria-label="Save changes"
onclick="save()"
onkeydown="if(event.key==='Enter'||event.key===' ')save()">
<span aria-hidden="true">💾</span>
</div>
<!-- Live regions for dynamic updates -->
<div aria-live="polite" aria-atomic="true">
<!-- Screen reader announces changes here automatically -->
Scanning: 45% complete
</div>
<!-- Progress bar -->
<div role="progressbar" aria-valuenow="45" aria-valuemin="0"
aria-valuemax="100" aria-label="Scan progress">
<div style="width: 45%; height: 20px; background: blue;"></div>
</div>Keyboard Navigation
Many users navigate entirely by keyboard — no mouse, no touch.
Focus Management
// Manage focus for dynamic content
class AccessibleModal {
constructor(modalElement) {
this.modal = modalElement;
this.focusable = modalElement.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
this.firstFocusable = this.focusable[0];
this.lastFocusable = this.focusable[this.focusable.length - 1];
}
open() {
this.modal.hidden = false;
this.firstFocusable.focus();
document.addEventListener('keydown', this.trapFocus);
}
close() {
this.modal.hidden = true;
document.querySelector('[data-trigger]').focus();
document.removeEventListener('keydown', this.trapFocus);
}
trapFocus = (e) => {
if (e.key === 'Escape') {
this.close();
return;
}
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === this.firstFocusable) {
e.preventDefault();
this.lastFocusable.focus();
} else if (!e.shiftKey && document.activeElement === this.lastFocusable) {
e.preventDefault();
this.firstFocusable.focus();
}
}
};
}
// Usage
const modal = new AccessibleModal(document.getElementById('settings-modal'));
document.getElementById('settings-btn').addEventListener('click', () => modal.open());Color Contrast
Text must have sufficient contrast against its background.
Contrast Ratios
| Level | Normal Text | Large Text (18px+ bold or 24px+) |
|---|---|---|
| AA | 4.5:1 | 3:1 |
| AAA | 7:1 | 4.5:1 |
/* Testing contrast in code */
:root {
/* These all pass AA for normal text */
--text-primary: #1a1a1a; /* on white #fff: 15.3:1 ✓ */
--text-secondary: #4a4a4a; /* on white #fff: 8.6:1 ✓ */
--text-muted: #6b7280; /* on white #fff: 4.8:1 ✓ (passes AA) */
--text-error: #dc2626; /* on white #fff: 4.5:1 ✓ (just passes) */
--text-green: #16a34a; /* on white #fff: 4.5:1 ✓ (just passes) */
}Testing Tools
- WebAIM Contrast Checker — Enter hex values to get ratio
- Chrome DevTools — Inspect element > color picker shows contrast ratio
- Lighthouse — Automatically flags insufficient contrast
Screen Reader Testing
Test with real screen readers, not just automated tools:
| Screen Reader | Platform | Notes |
|---|---|---|
| NVDA | Windows | Free, most popular |
| JAWS | Windows | Paid, widely used in enterprise |
| VoiceOver | macOS/iOS | Built-in, excellent |
| TalkBack | Android | Built-in, good |
Testing Checklist
<!- Test each of these with a screen reader -->
<nav aria-label="Main">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<main>
<h1>Test Page</h1>
<!-- Does the screen reader announce this as a link or button? -->
<a href="/download" role="button">Download</a>
<!-- Are all images described? -->
<img src="logo.webp" alt="DodaTech logo">
<!-- Are form labels associated correctly? -->
<label for="search">Search products</label>
<input type="search" id="search">
<!-- Is dynamic content announced? -->
<div aria-live="polite" id="updates"></div>
<!-- Can you navigate with arrow keys? -->
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</main>What Screen Reader Users Hear
Screen readers announce:
- Element role (button, link, heading, image)
- Element name (text content, aria-label)
- Element state (expanded, selected, disabled)
- Element description (title, aria-describedby)
Common Errors
- Missing form labels — Every
<input>,<select>, and<textarea>needs a<label>oraria-label. Placeholder text is not a label. - Focus outline removed —
outline: nonewithout replacement makes keyboard navigation invisible. Always provide a visible focus indicator. - Low contrast text — Gray text on white backgrounds often fails contrast requirements. Grey #999 on white #fff is only 2.8:1 (fails AA).
- Missing alt text on images — Informative images need meaningful alt text. Decorative images need
alt=""(not just missing the attribute). - Dynamic content without announcements — Content that updates without page reload needs
aria-liveregions so screen readers announce the change.
Practice Questions
What does WCAG stand for and what are its four principles? Web Content Accessibility Guidelines. The four principles are Perceivable, Operable, Understandable, and Robust (POUR).
When should you use ARIA roles instead of native HTML elements? Only when native HTML doesn’t provide the needed semantics. Always prefer
<button>over<div role="button">.What is the minimum color contrast ratio for normal text at AA level? 4.5:1. Large text (18px+ bold or 24px+ regular) requires 3:1.
How do skip links help keyboard users? Skip links allow keyboard users to jump directly to main content, skipping repeated navigation links that require many Tab presses.
What is the purpose of
aria-live="polite"? It tells screen readers to announce changes in that region when they finish their current announcement, without interrupting. Use="assertive"for urgent messages.
Challenge
Take a non-accessible web page (or a page you’ve built) and audit it against WCAG 2.2 AA using the WAVE browser extension or axe DevTools. Identify and fix at least five accessibility violations covering each POUR principle.
Real-World Task
Build an accessible file upload component for Durga Antivirus Pro that includes keyboard navigation, screen reader announcements for upload progress, proper focus management after upload completes, error messages with suggestions, and high-contrast visual indicators for file types.
Previous: HTML Fundamentals | Related: ARIA Basics | Related: CSS Fundamentals
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro