DEV Community

ZNY
ZNY

Posted on

The Complete Guide to Building Accessible Web Apps in 2026: ARIA, Focus Management, and Screen Readers

The Complete Guide to Building Accessible Web Apps in 2026: ARIA, Focus Management, and Screen Readers

Web accessibility became a legal requirement in more jurisdictions by 2025-2026, and the business case is stronger than ever — 15-20% of the population has some form of disability. Building accessible apps isn't just ethical, it's now expected by employers and users alike.

Here's the practical guide.

Semantic HTML First

<!-- Bad -->
<div onclick="toggle()">
  <div class="btn">Click me</div>
</div>

<!-- Good -->
<button type="button" onclick="toggle()">Click me</button>

<!-- Native elements have built-in keyboard support and ARIA for free -->
<nav> → role="navigation"
<main> → role="main"
<aside> → role="complementary"
Enter fullscreen mode Exit fullscreen mode

Focus Management

/* Visible focus indicator (never remove, only style) */
:focus-visible {
  outline: 2px solid #4f46e5;
  outline-offset: 2px;
}

/* Skip link */
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  padding: 8px 16px;
  background: #000;
  color: #fff;
  z-index: 100;
}

.skip-link:focus {
  top: 0;
}
Enter fullscreen mode Exit fullscreen mode
<a href="#main-content" class="skip-link">Skip to main content</a>

<main id="main-content" tabindex="-1">
  <!-- Page content -->
</main>
Enter fullscreen mode Exit fullscreen mode

ARIA Attributes

<!-- Live region for dynamic updates -->
<div aria-live="polite" aria-atomic="true">
  <!-- Screen reader announces changes here -->
  <p>5 items in cart</p>
</div>

<!-- Expanded/collapsed -->
<button
  aria-expanded="false"
  aria-controls="menu-dropdown"
  onclick="toggleMenu()"
>
  Menu
</button>

<div id="menu-dropdown" hidden>
  <!-- Menu items -->
</div>

<!-- Progress bar -->
<div role="progressbar"
     aria-valuenow="65"
     aria-valuemin="0"
     aria-valuemax="100"
     aria-label="Upload progress">
  65%
</div>
Enter fullscreen mode Exit fullscreen mode

Keyboard Navigation

// Dropdown keyboard navigation
const menuItem = menu.querySelectorAll("li");

menu.addEventListener("keydown", (e) => {
  const currentIndex = Array.from(menuItem).indexOf(document.activeElement);

  switch (e.key) {
    case "ArrowDown":
      e.preventDefault();
      const next = menuItem[currentIndex + 1] || menuItem[0];
      next.focus();
      break;
    case "ArrowUp":
      e.preventDefault();
      const prev = menuItem[currentIndex - 1] || menuItem[menuItem.length - 1];
      prev.focus();
      break;
    case "Escape":
      closeMenu();
      trigger.focus();
      break;
  }
});
Enter fullscreen mode Exit fullscreen mode

Color Contrast

/* Pass WCAG AA (4.5:1 for text, 3:1 for large text) */
.text {
  color: #333;    /* on white background */
  /* Contrast ratio: 12.6:1 ✅ */
}

/* Don't rely on color alone */
.status {
  color: #d00; /* Also include icon or text */
  background: #fee;
}

/* Better: use both color AND icon */
<span class="status error">
   Error occurred
</span>
Enter fullscreen mode Exit fullscreen mode

Testing Tools

# axe-core (automated testing)
# Chrome: axe DevTools extension
# CLI: npx @axe-core/cli https://example.com

# Lighthouse accessibility audit
# In Chrome DevTools → Lighthouse → "Navigation" → check "Accessibility"

# Screen reader testing
# macOS: VoiceOver (Cmd + F5)
# Windows: NVDA
# Linux: Orca
Enter fullscreen mode Exit fullscreen mode

This article contains affiliate links. If you sign up through the links above, I may earn a commission at no additional cost to you.

Ready to Build Your Online Business?

Get started with Systeme.io for free — All-in-one platform for building your online business with AI tools.

Top comments (0)