Skip to content
Web Accessibility Guide — WCAG 2.2, ARIA, and Inclusive Design

Web Accessibility Guide — WCAG 2.2, ARIA, and Inclusive Design

DodaTech Updated Jun 20, 2026 7 min read

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

  1. Don’t use ARIA when native HTML works — Use <button> instead of <div role="button">
  2. Don’t change native semantics — Don’t add role="heading" to a <button>
  3. All interactive ARIA controls must be keyboard accessible
  4. Use aria-label only 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

LevelNormal TextLarge Text (18px+ bold or 24px+)
AA4.5:13:1
AAA7:14.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 ReaderPlatformNotes
NVDAWindowsFree, most popular
JAWSWindowsPaid, widely used in enterprise
VoiceOvermacOS/iOSBuilt-in, excellent
TalkBackAndroidBuilt-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

  1. Missing form labels — Every <input>, <select>, and <textarea> needs a <label> or aria-label. Placeholder text is not a label.
  2. Focus outline removedoutline: none without replacement makes keyboard navigation invisible. Always provide a visible focus indicator.
  3. Low contrast text — Gray text on white backgrounds often fails contrast requirements. Grey #999 on white #fff is only 2.8:1 (fails AA).
  4. Missing alt text on images — Informative images need meaningful alt text. Decorative images need alt="" (not just missing the attribute).
  5. Dynamic content without announcements — Content that updates without page reload needs aria-live regions so screen readers announce the change.

Practice Questions

  1. 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).

  2. 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">.

  3. 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.

  4. 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.

  5. 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