WCAG for Developers — Building Compliant Interfaces
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>© 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
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