Skip to main content
Frontend8 min readJuly 18, 2025

Web Accessibility Compliance: A Practical WCAG Guide

WCAG compliance isn't just legal protection — it's better engineering. Here's a practical guide to building accessible web applications from the start.

James Ross Jr.
James Ross Jr.

Strategic Systems Architect & Enterprise Software Developer

Accessibility Is an Engineering Discipline, Not a Checkbox

Most accessibility conversations start with compliance and lawsuits. That framing is backwards. Accessibility is a quality attribute of software, like performance or security. When you build accessible interfaces, you build better interfaces for everyone — keyboard users, screen reader users, people with low vision, people in bright sunlight, people with temporary injuries, and people using your app in ways you did not anticipate.

The Web Content Accessibility Guidelines (WCAG) 2.2 at Level AA is the standard that matters for most web applications. It is referenced by the ADA, Section 508, the European Accessibility Act, and most legal frameworks worldwide. Meeting AA is the baseline expectation for any professional web project.

But WCAG is a specification document, not a tutorial. It tells you what to achieve, not how to achieve it in Vue, React, or plain HTML. This guide bridges that gap with patterns I use in production across client projects.

The most important mindset shift: accessibility is not something you add at the end. It is dramatically cheaper and more effective to build it in from the beginning. Retrofitting accessibility onto a finished application is painful, expensive, and often results in bolted-on ARIA attributes that make things worse rather than better. Start with semantic HTML, build with keyboard navigation in mind, and test with assistive technology throughout development.


Semantic HTML Does Most of the Work

The single most impactful accessibility practice is using the correct HTML elements. A <button> is inherently keyboard accessible, focusable, and announced by screen readers as interactive. A <div onClick={...}> is none of those things without additional engineering. Every time you reach for a generic <div> or <span> for something interactive, you are creating accessibility debt that requires ARIA attributes, keyboard event handlers, and focus management to repay.

Use <nav> for navigation regions, <main> for primary content, <aside> for secondary content, <header> and <footer> for their obvious purposes. These landmark elements let screen reader users jump between sections of the page efficiently. Without them, navigating your site with a screen reader is like reading a book with no chapter titles — technically possible, but tedious.

Heading hierarchy matters. Every page should have exactly one <h1>. Sections within the page get <h2>. Subsections get <h3>. Never skip levels — going from <h2> to <h4> because you prefer the visual size of <h4> is a styling problem, not a heading problem. Use CSS to style headings. Use HTML to create structure.

Forms are where accessibility most commonly breaks. Every <input> needs an associated <label> element linked by for/id attributes, or wrapped in a <label> tag. Placeholder text is not a label — it disappears on focus, it has poor contrast by default, and screen readers handle it inconsistently. Error messages should be programmatically associated with their input using aria-describedby and announced to screen readers using aria-live="polite" regions.

<div>
 <label for="email">Email address</label>
 <input
 id="email"
 type="email"
 aria-describedby="email-error"
 aria-invalid="true"
 />
 <p id="email-error" role="alert">
 Please enter a valid email address.
 </p>
</div>

This pattern handles form validation in a way that works for sighted users, keyboard users, and screen reader users simultaneously. It costs nothing extra to implement if you do it from the start.


Keyboard Navigation and Focus Management

If you cannot use your application with only a keyboard — no mouse, no trackpad — it is not accessible. Full stop. Every interactive element must be reachable with Tab, activatable with Enter or Space, and dismissable with Escape where applicable.

The most common keyboard failures I encounter during web application audits are custom components that swallow focus. Modal dialogs that do not trap focus inside themselves, allowing Tab to reach hidden content behind the overlay. Dropdown menus that open on hover but have no keyboard trigger. Carousels with no arrow key navigation. Custom select inputs built with divs that are invisible to keyboard navigation.

Focus management rules for common patterns:

Modals: When a modal opens, move focus to the first interactive element inside it (or the modal container with tabindex="-1"). Trap Tab within the modal — after the last focusable element, Tab should return to the first. On close, return focus to the element that triggered the modal.

Dropdown menus: Open with Enter or Space on the trigger. Navigate options with arrow keys. Select with Enter. Close with Escape, returning focus to the trigger.

Single-page app navigation: When the route changes in a framework like Nuxt or React Router, announce the new page to screen readers using a live region and move focus to the page heading or main content area. Without this, screen reader users hear nothing when navigation happens — they have no idea the page changed.

A reliable way to test: unplug your mouse for 30 minutes and use your application. Every task a mouse user can complete should be completable with keyboard alone. Note every moment of confusion or frustration — those are accessibility bugs.


Automated Testing Gets You 30%, Manual Testing Gets the Rest

Automated tools like axe-core, Lighthouse accessibility audits, and eslint-plugin-jsx-a11y catch roughly 30% of WCAG violations. They are excellent at finding missing alt text, insufficient color contrast, missing form labels, and invalid ARIA attributes. Run them in CI. Integrate axe-core into your testing pipeline. There is no reason to let detectable issues reach production.

But automated tools cannot evaluate whether alt text is meaningful ("image" vs. "Dashboard showing monthly revenue trends"). They cannot tell if focus order is logical. They cannot determine if a custom widget is actually usable with a screen reader. Those require manual testing.

At minimum, test with VoiceOver on macOS (free, built in), NVDA on Windows (free, open source), and TalkBack on Android (free, built in). You do not need to be a screen reader expert — just try to complete the core user flows. If you cannot figure out how to complete a task, your screen reader users cannot either.

Color contrast must meet a 4.5:1 ratio for normal text and 3:1 for large text. Tools like the WebAIM contrast checker give instant results. Pay attention to text on images, text on gradients, and disabled states — these are the most common contrast failures. Dark mode implementations are particularly prone to contrast issues because designers often choose low-contrast color combinations that look sophisticated but fail WCAG requirements.

Build accessibility into your development process rather than treating it as a final audit. Include accessibility acceptance criteria in user stories. Test with keyboard and screen reader during development, not after. Make it part of your definition of done, and the cost of accessibility drops to nearly zero because you are simply building things correctly from the start.