Skip to content

WCAG for Developers — Building Compliant Interfaces

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about WCAG for Developers. We cover key concepts, practical examples, and best practices to help you master this topic.

Developers implement WCAG compliance through semantic HTML, ARIA for custom widgets, keyboard event handling, form validation with accessible error messages, focus management, and automated Accessibility Testingity" >}} testing in CI/CD pipelines.

What You'll Learn

You will learn the practical implementation techniques developers need to meet WCAG criteria, from semantic HTML through automated testing.

Why It Matters

Developers are the last line of defense before accessibility issues reach users. Understanding WCAG implementation techniques is essential for building compliant applications.

Real-World Use

DodaTech's development Process includes accessibility acceptance criteria for every user story, automated axe-core testing in CI/CD, and mandatory screen reader testing before release.

flowchart TD
  A[WCAG for Developers] --> B[Semantic HTML]
  A --> C[ARIA]
  A --> D[Keyboard Support]
  A --> E[Forms and Validation]
  A --> F[Focus Management]
  A --> G[Testing]
  B --> H[Use correct elements]
  C --> I[Only when needed]
  D --> J[Tab, Enter, Escape, Arrows]
  E --> K[Labels, errors, suggestions]
  F --> L[Focus order, skip links, modals]
  G --> M[axe, WAVE, manual, screen reader]

Semantic HTML

Use the correct HTML element for each purpose. This is the single most impactful accessibility practice.

<!-- Good: semantic HTML -->
<nav aria-label="Main"><ul><li><a href="/">Home</a></li></ul></nav>
<main><h1>Dashboard</h1><p>Content</p></main>
<footer>&copy; 2026</footer>

<!-- Bad: div-based layout -->
<div class="nav">...</div>
<div class="main">...</div>
<div class="footer">...</div>

ARIA Implementation

Use ARIA only when native HTML semantics are insufficient. Follow the first rule of ARIA: do not use it if a native element already provides the semantics.

<!-- Good: custom tab panel with ARIA -->
<div role="tablist" aria-label="Settings">
  <button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">General</button>
  <button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2">Security</button>
</div>
<div role="tabpanel" aria-labelledby="tab-1" id="panel-1">General settings content</div>
<div role="tabpanel" aria-labelledby="tab-2" id="panel-2">Security settings content</div>

Keyboard Support

Every interactive element must be keyboard accessible.

// Keyboard event handling for custom button
function makeKeyboardAccessible(element) {
  element.setAttribute('tabindex', '0');
  element.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      element.click();
    }
  });
}

Forms and Validation

Associate labels with inputs. Provide clear error messages that suggest fixes.

<form novalidate>
  <label for="email">Email address</label>
  <input type="email" id="email" name="email" required
         aria-describedby="email-hint email-error">
  <p id="email-hint">Enter your work email</p>
  <p id="email-error" role="alert" hidden></p>
  <button type="submit">Subscribe</button>
</form>
function validateForm(form) {
  const errors = [];
  const email = form.querySelector('#email');

  if (!email.value.includes('@')) {
    errors.push({ field: 'email', message: 'Please enter a valid email address with @ symbol' });
  }

  errors.forEach(e => {
    const errorEl = document.getElementById(`${e.field}-error`);
    errorEl.textContent = e.message;
    errorEl.hidden = false;
  });

  return errors.length === 0;
}

Focus Management

Control where keyboard focus goes, especially in dynamic interfaces.

// Focus management for modal dialog
function openModal(modalId) {
  const modal = document.getElementById(modalId);
  const previousFocus = document.activeElement;
  modal.style.display = 'block';
  modal.querySelector('[autofocus]')?.focus() || modal.querySelector('button').focus();

  modal.addEventListener('keydown', function trapFocus(e) {
    if (e.key === 'Escape') closeModal(modalId, previousFocus);

    const focusable = modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
    const first = focusable[0];
    const last = focusable[focusable.length - 1];

    if (e.key === 'Tab' && e.shiftKey && document.activeElement === first) {
      e.preventDefault();
      last.focus();
    } else if (e.key === 'Tab' && !e.shiftKey && document.activeElement === last) {
      e.preventDefault();
      first.focus();
    }
  });
}

function closeModal(modalId, returnFocus) {
  document.getElementById(modalId).style.display = 'none';
  if (returnFocus) returnFocus.focus();
}

Automated Testing

Integrate accessibility testing into your CI/CD pipeline.

// CI/CD accessibility test with axe-core
import { axe } from 'axe-core';

describe('Component accessibility', () => {
  it('button has no violations', async () => {
    const html = '<button>Click me</button>';
    const results = await axe.run(html);
    expect(results.violations).toHaveLength(0);
  });
});

Common Mistakes

1. Using ARIA on Everything

ARIA should supplement, not replace, semantic HTML. Native HTML is more reliable.

2. Forgetting Keyboard for Custom Components

Custom dropdowns, sliders, and menus need keyboard handling. Test every custom component with the keyboard.

3. Not Managing Focus in Dynamic Content

Modals, notifications, and single-page app transitions need explicit focus management.

4. Relying Only on Automated Tests

Automated tests catch about 30 percent of issues. Manual testing with screen readers is essential.

5. Writing Vague Error Messages

Error messages must identify the problem and suggest a fix. Error code 401 is not helpful.

6. Breaking the Tab Order

CSS order changes can break the logical Tab sequence. Test Tab order on every page.

7. Not Testing with Real Assistive Technology

Test with NVDA or VoiceOver before every release. Hearing your component reveals issues code review cannot.

Practice Questions

1. What is the first rule of ARIA?

Do not use ARIA if a native HTML element already provides the semantics you need.

2. How do you make a custom button keyboard accessible?

Add tabindex 0 and handle Enter and Space key events to trigger the action.

3. What elements need focus management in dynamic content?

Modals, notifications, single-page app transitions, and any content added or removed from the DOM.

4. Why is automated testing insufficient?

Automated tools catch about 30 percent of issues. They miss contextual problems like meaningful alt text and logical focus order.

5. Challenge: Add keyboard event handling to a custom component in your codebase. Test it with Tab and Enter keys.

FAQ

How do I test keyboard accessibility?

Tab through all interactive elements. Use Enter, Space, Escape, and arrow keys to interact. The page should be fully usable.

What is the most common developer accessibility mistake?

Using div or span for interactive elements instead of native button or a elements.

Should I use role button on a div?

No. Use a native button element. ARIA role button on a div still requires tabindex and keyboard event handling.

How do I handle focus in a single-page app?

Manage focus when routes change. Announce page changes to screen readers using aria-live or a status region.

What is an aria-live region and when should I use it?

aria-live tells screen readers to announce content changes. Use polite for non-critical updates and assertive for urgent alerts.

Mini Project

Add accessibility testing to your CI/CD pipeline using axe-core. Write tests for three components. Fix any violations found and document the fixes.

What's Next

Learn about WCAG for Content with guidance for writers and editors. Then complete the WCAG Project to apply everything you have learned.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro