We built an accessibility tool because spreadsheet audits were killing us

Published: (April 9, 2026 at 03:45 AM EDT)
8 min read
Source: Dev.to

Source: Dev.to

The Problem

There’s a specific kind of despair that comes from opening the ninth spreadsheet in a WCAG audit—the one you’re pretty sure somebody duplicated from the wrong version two weeks ago—and finding that step 14 of the login journey is marked “Fail” in your copy and “Not Tested” in the reviewer’s copy.

Which one is right? Nobody knows. The tester who originally logged it is on PTO. The screenshot is somewhere in Slack, probably in a thread that got buried under a deployment argument.

That was us, about two years ago, during a healthcare accessibility audit in the US. The client was strict, the regulations were strict, everything was strict—except our tooling, which was held together with Google Sheets, manual dates, and hope.

We had nine spreadsheets open:

  • One tracked testers.
  • One tracked severity.
  • One tracked notes.
  • One was apparently a backup of another one, but with different data.

Screenshots lived in Slack channels, sometimes in DMs, sometimes attached to Jira tickets that referenced a different version of the WCAG criteria.

Then we found the conflicting reports. Same user flow, same step, two different testers, two different results. One said Fail with a note about missing alt text. The other said Not Tested. Both had been submitted to the client in the same week.

That was the moment we stopped patching the process and started building something.

The Tool Is Called Auditi

It lives at . We built it at because nothing else matched how we actually test accessibility: by user journeys, broken into steps, with everything traceable back to a specific tester, date, platform, and WCAG criterion.

Core Idea

  1. Model journeys the way a user experiences them – login flow, checkout flow, onboarding, etc.
  2. Each journey has steps.
  3. Each step gets an audit result: Pass, Fail, or Not Applicable.
  4. Every result stores: tester name, severity, notes, evidence files, and a timestamp.

That sounds obvious. It isn’t. In spreadsheet world you’re tracking all of that across columns, tabs, and files. Someone renames a column. Someone adds rows in the middle. Someone filters by severity and forgets to un‑filter before sending the report. I’ve watched an experienced QA engineer spend forty minutes rebuilding a pivot table that broke because Excel decided to reinterpret dates.

Auditi gives you filters by journey, tester, status, severity, platform, WCAG level, device, and date. If you’ve ever tried to find “that one iOS Safari fail from last Tuesday” in a spreadsheet, you understand why this matters.

What We Actually Built

  • Assignment dialogs and review queues so work gets distributed without a Slack message chain.
  • Pass/Fail/N‑A toggles per step—the atomic unit of an accessibility audit.
  • Notifications for deadlines and invites, because relying on people to check a spreadsheet daily doesn’t work.

Analytics

  • Pass rate over time.
  • WCAG compliance matrix.
  • Breakdown by tester, platform, severity.

This is the part that managers actually care about, and the part that’s almost impossible to maintain in a spreadsheet without a dedicated person updating charts.

Reporting

  • Export to Excel, PDF, and CSV (the formats most clients expect).
  • Generates Overview, Detailed, and Matrix reports.
  • AI‑powered Smart Report that produces an executive summary, scores by WCAG level, flags top issues by priority, and suggests fixes.
    • Honest note: AI summarization is useful because it compresses structured data, not because it makes judgment calls. The tester still decides what passes and what fails; the AI just writes the summary you’d otherwise spend an hour drafting.

If You’ve Tried to Make a React App WCAG‑Compliant

The other half of the problem: fixing things

We run thirteen products in the BetterQA ecosystem: Vite/React SPAs, Next.js apps, a Laravel app, a WordPress site. Earlier this year we decided to do an accessibility sweep across all of them using our own scanner tool, which runs axe‑core via Playwright.

The results were humbling

  • Eight of our thirteen sites had accessibility scores below 60.
  • The single biggest offender? Color contrast—specifically, Tailwind’s purple‑400 on a white background.

Every Vite/React SPA used text-purple-400 for links, badges, labels, secondary text. It’s a nice color, but it has a contrast ratio of about 3.3:1 against white. WCAG AA requires 4.5:1 for normal text. We were failing dozens of contrast checks per page, across eight different sites, and nobody had noticed because the pages looked fine to us.

The fix: switch to purple-600 (#9333ea), which gives 4.6:1—just barely over the threshold, but it passes.

/* Before: 3.3:1 contrast – fails WCAG AA */
.text-purple-400 { color: #a855f7; }

/* After: 4.6:1 contrast – passes WCAG AA */
.text-purple-600 { color: #9333ea; }

We made the change across all eight sites. Some required 24 individual class updates. One site, BetterFlow (a Laravel/Blade app), had the same pattern in Blade templates.

That got us from the 50s to the 70s‑80s in accessibility scores, but it only caught the low‑hanging fruit.

The Deeper Fixes Taught Us More

After the color‑contrast sweep we went deeper.

  • Icon‑only buttons (e.g., on jrny.ro) had no accessible name. Screen readers announced them simply as “button.”

    • Fix: add aria-label attributes.
  • Form inputs without labels (e.g., on menute.ro). Sighted users rely on placeholder text; screen readers hear nothing useful.

    • Fix: add aria-label (or associate a <label> element) to each input.

These deeper issues reminded us that accessibility is more than a checklist—it’s about intentional, traceable, and collaborative work, which is exactly what Auditi enables.

most was nis2manager.ro

The site uses CSS custom properties for its primary color. The original --primary value was set to an oklch lightness of 0.65. Changing it to 0.48 fixed over seventy contrast violations in one line of CSS. Seventy. From a single variable change.

/* One variable, seventy fixes */
--primary: oklch(0.48 0.2 270);  /* was 0.65 */

Enter fullscreen mode
Exit fullscreen mode

That’s the lesson we keep coming back to: if your design system uses CSS custom properties or Tailwind theme colors, check the contrast of your base tokens first. You can hunt individual elements for hours, or you can fix the source and watch dozens of violations disappear.

What Automated Tools Actually Catch

I want to be direct about this because I’ve seen too many articles claim that automated accessibility testing solves the problem. It doesn’t—not even close.

  • Automated tools like axe‑core catch maybe 30‑40 % of WCAG issues.
    • Good at: color contrast, missing alt text, missing form labels, duplicate IDs, broken ARIA attributes.
    • Bad at: anything that requires context—whether alt text is meaningful, whether focus order makes sense, whether a custom widget is operable with a keyboard, whether content is understandable when read linearly by a screen reader.

WCAG has roughly 80 success criteria across levels A, AA, and AAA. Automated tools can reliably check maybe 25‑30 of them. The rest need a human who understands the user flow, the intent of the content, and what the experience is like without a mouse.

That’s why Auditi is structured around human‑driven audits with journeys and steps, not around automated scan results. Automation is useful for catching regressions, but it’s not a substitute for a tester who actually navigates the site with a screen reader.

What We Got Wrong

A few things, since we’re being honest:

  1. Complexity – The first version of Auditi modeled every WCAG criterion as a separate audit point, forcing testers to click through dozens of criteria per step, most of which weren’t applicable. We simplified it so testers can mark what matters and skip the rest.
  2. Export format – Early exports were clean but didn’t match what compliance officers expected. We added specific report layouts that map to the documentation formats our healthcare and government clients already use.
  3. Self‑audit failure – Our own ecosystem sweep revealed that we’d been shipping inaccessible products while building an accessibility tool. That stung. We fixed it, but it’s a reminder that building a tool and actually using it consistently are two different things.

The Honest Numbers

Our ecosystem scores before and after the sweep:

SiteBeforeAfterPrimary fix
betterqa.co5484Plugin color updates
betterflow.eu558424 Blade template fixes
auditi.ro5482purple‑400 → purple‑600
electricworks.ro5380Tailwind primary classes
psysign.ro5380Same pattern
nis2manager.ro5176CSS custom property (one‑liner)
factos.ro4772Same pattern

Not perfect scores—far from it. But a 25‑30‑point jump across eight sites, and a process we can repeat.

Where This Matters for Your Stack

If you’re running a React or Vite SPA and haven’t run axe‑core against it, do that first. You’ll probably find contrast issues, missing labels, and button‑name violations—fixable in an afternoon.

After that, the harder work begins:

  • Keyboard navigation
  • Focus management in modals and dynamic content
  • Screen‑reader announcements for state changes

That’s where spreadsheets fall apart and you need actual audit tracking.

We built Auditi because we couldn’t do that work well with the tools we had. It’s available at auditi.ro if you want to look at it.

For more about how we approach QA across different domains, check out the BetterQA blog.

0 views
Back to Blog

Related posts

Read more »

Gitnova

!open-sourced-ithttps://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fupload...