Building Accessible Web Applications: A Developer's Guide

Building Accessible Web Applications: A Developer's Guide

BySanjay Goraniya
3 min read
Share:

Building Accessible Web Applications: A Developer's Guide

Accessibility isn't optional—it's a requirement. Building accessible applications means building applications that everyone can use, including people with disabilities. After making applications accessible and seeing the impact, I've learned that accessibility benefits everyone, not just people with disabilities.

Why Accessibility Matters

  • ADA (Americans with Disabilities Act) - Legal requirement in US
  • WCAG (Web Content Accessibility Guidelines) - International standard
  • Section 508 - US federal requirements

Business Benefits

  • Larger audience - 15% of world population has disabilities
  • Better SEO - Accessible sites rank better
  • Better UX - Accessible sites are easier to use for everyone
  • Legal compliance - Avoid lawsuits

WCAG Principles

1. Perceivable

Information must be perceivable to users.

2. Operable

Interface components must be operable.

3. Understandable

Information and UI must be understandable.

4. Robust

Content must be robust enough for assistive technologies.

Semantic HTML

Use Proper Elements

Code
<!-- Bad: Divs for everything -->
<div onclick="submit()">Submit</div>

<!-- Good: Semantic HTML -->
<button type="submit">Submit</button>

Common Semantic Elements

Code
<!-- Headings -->
<h1>Main Title</h1>
<h2>Section Title</h2>

<!-- Navigation -->
<nav>
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>

<!-- Main content -->
<main>
  <article>
    <h1>Article Title</h1>
    <p>Content...</p>
  </article>
</main>

<!-- Forms -->
<form>
  <label for="email">Email</label>
  <input type="email" id="email" name="email" required />
  <button type="submit">Submit</button>
</form>

ARIA Attributes

When to Use ARIA

Use ARIA when HTML isn't enough:

Code
<!-- Bad: Custom button without ARIA -->
<div class="button" onclick="handleClick()">Click me</div>

<!-- Good: Use button -->
<button onclick="handleClick()">Click me</button>

<!-- When you must use div, add ARIA -->
<div 
  role="button" 
  tabindex="0"
  onclick="handleClick()"
  onkeydown="handleKeyDown(event)"
>
  Click me
</div>

Common ARIA Attributes

Code
<!-- Labels -->
<button aria-label="Close dialog">×</button>

<!-- Descriptions -->
<input 
  type="text"
  aria-describedby="email-help"
/>
<span id="email-help">Enter your email address</span>

<!-- States -->
<button aria-expanded="false" aria-controls="menu">
  Menu
</button>
<div id="menu" aria-hidden="true">
  <!-- Menu items -->
</div>

<!-- Live regions -->
<div aria-live="polite" aria-atomic="true">
  <!-- Dynamic content updates -->
</div>

Keyboard Navigation

Focus Management

Code
// Ensure all interactive elements are keyboard accessible
function handleKeyDown(event) {
  if (event.key === 'Enter' || event.key === ' ') {
    event.preventDefault();
    handleClick();
  }
}

// Manage focus
function openModal() {
  modal.show();
  // Focus first interactive element
  modal.querySelector('input').focus();
}

function closeModal() {
  modal.hide();
  // Return focus to trigger
  triggerButton.focus();
}

Tab Order

Code
<!-- Logical tab order -->
<button>First</button>
<button>Second</button>
<button>Third</button>

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

Forms

Proper Labels

Code
<!-- Bad: No label -->
<input type="text" name="email" />

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

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

Error Messages

Code
<!-- Associate errors with inputs -->
<label for="email">Email</label>
<input 
  type="email" 
  id="email"
  name="email"
  aria-invalid="true"
  aria-describedby="email-error"
/>
<span id="email-error" role="alert">
  Please enter a valid email address
</span>

Required Fields

Code
<label for="name">
  Name
  <span aria-label="required">*</span>
</label>
<input 
  type="text" 
  id="name"
  name="name"
  required
  aria-required="true"
/>

Images

Alt Text

Code
<!-- Decorative image -->
<img src="decoration.jpg" alt="" />

<!-- Informative image -->
<img 
  src="chart.jpg" 
  alt="Sales increased 25% from Q1 to Q2"
/>

<!-- Complex image -->
<img 
  src="diagram.jpg" 
  alt=""
  aria-describedby="diagram-description"
/>
<p id="diagram-description">
  System architecture showing three tiers: frontend, API, database
</p>

Color and Contrast

Contrast Ratios

  • Normal text: 4.5:1 minimum
  • Large text: 3:1 minimum
  • UI components: 3:1 minimum
Code
/* Good: High contrast */
.button {
  background: #0066cc;
  color: #ffffff; /* Contrast: 7:1 */
}

/* Bad: Low contrast */
.button {
  background: #cccccc;
  color: #ffffff; /* Contrast: 1.6:1 - fails */
}

Don't Rely on Color Alone

Code
<!-- Bad: Color only -->
<span style="color: red">Error</span>

<!-- Good: Color + icon/text -->
<span style="color: red" aria-label="Error">
  <span aria-hidden="true">⚠️</span> Error: Invalid input
</span>

Focus Indicators

Visible Focus

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

/* Bad: Remove focus */
button:focus {
  outline: none; /* Don't do this! */
}

Screen Readers

Testing with Screen Readers

  • NVDA (Windows, free)
  • JAWS (Windows, paid)
  • VoiceOver (macOS/iOS, built-in)
  • TalkBack (Android, built-in)

Screen Reader Best Practices

Code
<!-- Use headings for structure -->
<h1>Page Title</h1>
<h2>Section Title</h2>

<!-- Use landmarks -->
<nav aria-label="Main navigation">
  <!-- Navigation -->
</nav>

<main>
  <!-- Main content -->
</main>

<aside aria-label="Related articles">
  <!-- Sidebar -->
</aside>

Common Accessibility Issues

1. Missing Alt Text

Code
<!-- Bad -->
<img src="photo.jpg" />

<!-- Good -->
<img src="photo.jpg" alt="Team meeting in conference room" />

2. Poor Color Contrast

Code
/* Bad */
.text { color: #999999; } /* Low contrast */

/* Good */
.text { color: #333333; } /* High contrast */

3. Missing Labels

Code
<!-- Bad -->
<input type="text" name="email" />

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

4. Keyboard Inaccessible

Code
<!-- Bad -->
<div onclick="handleClick()">Click me</div>

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

Accessibility Testing

Automated Testing

Code
// axe-core
const axe = require('axe-core');

async function testAccessibility() {
  const results = await axe.run();
  console.log(results.violations);
}

Manual Testing

  1. Keyboard navigation - Tab through entire page
  2. Screen reader - Test with NVDA/VoiceOver
  3. Color contrast - Use contrast checker
  4. Zoom - Test at 200% zoom

Real-World Example

Challenge: E-commerce site not accessible, losing customers.

Improvements Made:

  1. Semantic HTML - Replaced divs with proper elements
  2. ARIA labels - Added where needed
  3. Keyboard navigation - All features keyboard accessible
  4. Color contrast - Improved to WCAG AA
  5. Alt text - All images have descriptive alt text
  6. Form labels - All inputs properly labeled

Result:

  • Accessibility score: 45 → 95
  • More users able to complete purchases
  • Better SEO rankings
  • Legal compliance

Best Practices

  1. Start early - Build accessibility in from the start
  2. Use semantic HTML - Let HTML do its job
  3. Test regularly - Automated and manual testing
  4. Keyboard navigation - Everything should be keyboard accessible
  5. Color contrast - Meet WCAG standards
  6. Screen reader testing - Test with actual screen readers
  7. Document patterns - Share accessible patterns with team
  8. Continuous improvement - Accessibility is ongoing

Conclusion

Accessibility isn't a feature—it's a requirement. Building accessible applications means:

  • More users - Reach everyone
  • Better UX - Easier for all users
  • Legal compliance - Avoid issues
  • Better code - Semantic, maintainable

Remember: Accessibility benefits everyone. A site that's easy to navigate with a keyboard is easier for everyone. Good contrast helps everyone read. Clear labels help everyone understand.

What accessibility challenges have you faced? What improvements have you made to your applications?

Share:

Related Posts