Skip to content
Go back

10 Lessons from Leading a Web Accessibility Project

Published:  at  08:00 PM

Accessibility as a factor in web development is often overlooked. But ignoring it can lead to serious consequences. Organizations frequently face lawsuits over inaccessible websites as compliance isn’t optional. The specifics vary by country, state, and organization, but one thing is clear: web accessibility is essential.

WCAG defines three levels of compliance: A, AA, and AAA. Level A is minimal, and AAA is extremely difficult to achieve and maintain. Most organizations target AA compliance, as it offers a realistic balance between implementation effort and meaningful accessibility.

Recently my team reached WCAG 2.1 AA compliance, officially signed off by our accessibility partner Allyant. Allyant used both human auditors and automated tools to help us identify issues across our site. Leading this project gave me valuable experience that I’ll carry into future work. More importantly, it reinforced a key lesson: accessibility isn’t just a score to chase in Lighthouse. It’s an ongoing journey focused on improving user experience, where consistent progress matters more than perfection.

Below are some lessons I learned leading this project:

A worn sign that says "Accessible Entry" with a wheelchair symbol is attached to a colorful, painted brick wall.
Photo by Daniel Ali on Unsplash

Table of contents

Open Table of contents

Mindset & Planning

1. AA Compliance Doesn’t Mean Fully Accessible

When I initially started the project I thought AA compliance was the same as being fully accessible. But accessibility is not a one-size-fits-all solution. A site that works well for screen readers might still be hard to use for someone with motor impairments or dyslexia. WCAG 2.1 AA compliance is just a baseline. Real accessibility means meeting the actual needs of your users.

2. Know Your Audience from the Start

If I could redo the project, I’d begin by surveying our users.
Understanding their needs like whether they use screen readers, or keyboard navigation would have guided our approach better. Survey questions could be what type of screen reader they use? What type of disabilities they have? Is there a specific issue they notice on our site? Their answers would’ve helped us make more meaningful changes on top of meeting the AA baseline.

Team & Process

3. Team Training Is Non-Negotiable

Everyone involved in web development needs accessibility training:

Content Creators: Workshops on writing meaningful alt text (e.g., not just “duck-v3.jpg”), ensuring sufficient color contrast (using tools like the WebAIM Contrast Checker), and creating clear, descriptive captions.

Developers: Add accessibility linting to your workflow. Tools like Axe Linter give you warnings and suggestions while you code so you can catch issues early. There’s also Pa11y which can run axe-core from the command line, making it easy to include in your CI/CD pipeline. I regret not setting this up sooner. We could’ve avoided a lot of these issues early on.

It also helps to run workshops. When developers understand why accessibility matters they’re more likely to build it in from the start. Otherwise, fixing issues later and hiring a third-party vendor is slow, expensive, and frustrating process.

4. Visual Regression Testing Is Critical

During the project we didn’t implement a library for visual regression and that was a mistake. Fixing one accessibility issue can sometimes break another for instance, CSS changes might make focus indicators disappear. Creating proper test cases is also a lengthy process, but as developers we need to allocate time for this. It makes future projects easier to maintain and catches issues early on.

Tools like Chromatic and BrowserStack can help with visual regression testing. But they are also expensive for large sites we are currently looking into finding cheaper alternatives like Playright and Percy. That said, we likely need to allocate some budget for a paid tool regardless.

5. Automated Tools and AI Aren’t Enough

Automated accessibility tools like Lighthouse detect common issues such as missing alt attributes, poor color contrast, and improper heading structures. But they often fail to identify more complex or context-specific problems, including incorrect ARIA usage, keyboard traps, and incorrect focus management. Paid tools from third party vendors can help, but human testers especially users with disabilities are irreplaceable.

Also, AI can assist by refactoring code or suggesting fixes, but it lacks the semantic and full context needed to fix a component. For example, it might suggest adding alt="" to an image, but it won’t know if the image is decorative or critical to understanding the content. At the core, accessibility is a human issue and we need some form of human interaction to catch and fix critical issues.

Technical Implementation

6. Usage of Semantic HTML

Using native HTML elements properly is the foundation of accessibility. For example, a common pattern we found in our codebase was using divs as buttons:

<!-- Poor accessibility -->
<div class="button" onclick="submitForm()">Submit</div>

<!-- Good accessibility -->
<button type="submit">Submit</button>

In the example below, making a div behave like a button requires several additional elements. We need to add tabindex for keyboard focus, CSS focus styles, JavaScript for key press events (Enter and Space), a role attribute to indicate it’s a button, and additional ARIA attributes if needed:

<div
  role="button"
  tabindex="0"
  onclick="submitForm()"
  onkeydown="if(event.key==='Enter'||event.key===' ')submitForm()"
  aria-label="Submit form"
>
  Submit
</div>

7. Keyboard Focus Management

Keyboard navigation was one of the most important parts of our accessibility work. Even though elements like links, buttons, and form fields are focusable by default, we ran into several issues with how focus was handled in the code.

In some cases, a developer had removed focus outlines from buttons to match a certain design style. While this might look cleaner visually, it makes keyboard navigation much harder and we found this issue in multiple places.

<!-- Poor accessibility -->

button:focus { outline: none; }
<!-- We can keep the browser’s default style or customize it but we shouldn’t remove it. -->

button:focus { outline: 2px solid #0066CC; outline-offset: 2px; }

We also found several cases where tabindex was used incorrectly. For non-interactive elements that needed focus, we used tabindex="0" to keep them in the natural tab order. But we also found positive tabindex values (like tabindex="1" or higher). These should generally be avoided. Even though they force an element into the tab order, they mess up the natural flow and can make keyboard navigation confusing.

A tool that really helped us spot these issues was Taba11y, a browser extension that visualizes the tab order on the page.

This revealed areas where the tab order jumped erratically instead of following a logical reading flow.

8. ARIA: Use Sparingly and Correctly

ARIA should be used as a last resort when native HTML can’t provide the semantics needed. One of our biggest issues was overuse of ARIA attributes that either duplicated native semantics or conflicted with them.

Common ARIA mistakes we encountered:

<!-- Incorrect: Role redundant with native semantics -->
<button role="button">Submit</button>
<!--Not updating aria states correctly -->
<button aria-expanded="false" onclick="toggleContent()">Toggle</button>
<div id="content" hidden>Here is some content</div>

<script>
  function toggleContent() {
    const content = document.getElementById("content");
    content.hidden = !content.hidden;
    // Missing: aria-expanded is never updated
  }
</script>

<!--Correct way to update aria states -->
<button id="toggleBtn" aria-expanded="false" onclick="toggleContent()">
  Toggle
</button>
<div id="content" hidden>Here is some content</div>

<script>
  function toggleContent() {
    const content = document.getElementById("content");
    const button = document.getElementById("toggleBtn");

    const isHidden = content.hidden;
    content.hidden = !isHidden;
    button.setAttribute("aria-expanded", String(!isHidden));
  }
</script>
<!-- Incorrect: Conflicting semantics -->
<h2 role="button">Heading that doesn't act as a button</h2>

9. Error handling in forms

Accessible form validation requires clear error messages that are associated with their inputs and announced to screen reader users.

When working with MachForms, we discovered that error messages weren’t properly linked to their form fields. We implemented a solution that used aria-describedby to connect error messages to inputs:

<div class="form-group">
  <label for="email">Email Address</label>
  <input type="email" id="email" aria-describedby="email-error" />
  <div id="email-error" class="error" role="alert">
    Please enter a valid email address
  </div>
</div>

10. Usage of Animations and GIFs Require Caution

Animations can be distracting and even harmful to some users, especially if they last more than five seconds. It’s important to ask if a moving element actually adds value to the experience. For example, a constantly animating cursor can feel overwhelming.

Many content creators on our site use GIFs, but any GIF that plays longer than five seconds needs a play/pause option. To address this, we implemented GIFa11y, which adds an accessible and keyboard-friendly button on top of GIFs. It also reminds users to include alt text. This was a quick and effective solution to improve accessibility.

That said, I don’t think GIFs are the best way to show dynamic content. They tend to be low-res and large in size, which can slow things down. A small, crisp image usually works better.

This applies to any animation. We need to ask ourselves if it really improves the user experience or if it’s just there to look cool. We must ensure animations are accessible friendly for users, whether that means adding a reduced motion option or a play/pause button.

Conclusion

Reaching AA compliance was a big step for our team, but it doesn’t stop there. It’s something we need to keep working on through regular audits, code reviews, and team training.

We started this project mainly to stay ahead of legal risk but the real goal is to make our site better for everyone. What we still don’t know is how much these changes have improved the experience for our actual users mainly because accessibility metrics are hard to track with tools like GA4 and we didn’t survey users to understand their needs. That’s something we’ll focus on more going forward.

This post covered some of the key lessons we learned. In part two, I’ll dive deeper into some of the more complex technical challenges we faced like making tab-based interfaces, navigation menus, and filtered search lists more accessible. Stay tuned!