DEV Community

Cover image for I Built a WCAG 2.2 Accessibility Checker and Shipped Its SDK to Three Package Registries as a CS Student published: true
Abhiram
Abhiram

Posted on

I Built a WCAG 2.2 Accessibility Checker and Shipped Its SDK to Three Package Registries as a CS Student published: true

I Built a WCAG 2.2 Accessibility Checker and Shipped Its SDK to Three Package Registries as a CS Student

Most of my college projects lived and died on a USB drive. This one ended up on NuGet, npm, and Cargo. Here's how it happened and what I learned.

The Problem

While working on a web project, I wanted to check if my site was accessible. Every tool I found was either a browser extension that was useless for CI, a paid SaaS, or a basic linter that didn't handle JavaScript-rendered content at all.

WCAG 2.2 had just introduced new criteria — target size minimums, redundant entry prevention — and nothing free was checking for those yet. So I built my own.

What I Built

AXIS is a desktop accessibility auditor for Windows that renders JavaScript-heavy pages using a headless Chrome engine, audits against WCAG 2.2, WCAG 2.1, Section 508, and India's RPwD Act, scores pages from 0–100 with a compliance tier, estimates the page's carbon footprint, and exports TXT and PDF reports.

But the more interesting part was what came next.

The SDK Idea

Once the core audit engine was working, I realized it was completely decoupled from the UI. So I extracted it into AXIS-CORE — a standalone library that any developer could drop into their own tooling or CI/CD pipeline.

var checker = new AxisCore();
var report = await checker.CheckUrlAsync("https://example.com");
Console.WriteLine($"Score: {report.AccessibilityScore}/100");
Enter fullscreen mode Exit fullscreen mode

Then I thought — why stop at .NET?

Shipping to Three Registries

I ported the SDK to JavaScript and Rust, and published all three:

Runtime Package
.NET AXIS-CORE on NuGet
JavaScript axis-core-sdk on npm
Rust axis-core on Cargo

Each one went through the full publish cycle — versioning, package metadata, README, API surface design. It took longer than I expected. Way longer.

What I didn't expect

Publishing to NuGet is surprisingly strict compared to npm. You need proper package metadata, a valid license SPDX expression, and a README that actually renders correctly in their portal. My first few attempts were rejected silently — the package appeared listed but showed no description.

Cargo was the smoothest. The cargo publish workflow is well-documented and the tooling catches most mistakes before you even push.

npm was the fastest to publish but the hardest to get right structurally. CommonJS vs ESM compatibility and making sure the package resolves correctly across Node versions took a few iterations.

WCAG 2.2 — What's Actually New

This was the part I had to research most deeply. Two new criteria stood out.

2.5.8 — Target Size Minimum (Level AA)

Clickable elements need a minimum area of 24×24 CSS pixels. Sounds obvious, but a surprising number of icon buttons and inline links fail this.

button, a {
  min-width: 24px;
  min-height: 24px;
  padding: 8px 12px;
}
Enter fullscreen mode Exit fullscreen mode

3.3.7 — Redundant Entry (Level A)

Forms collecting personal data must use autocomplete attributes. This one is almost universally ignored.

<input type="email" name="email" autocomplete="email">
<input type="tel"   name="phone" autocomplete="tel">
Enter fullscreen mode Exit fullscreen mode

AXIS checks both automatically.

The Environmental Angle

One feature I added on a whim turned out to be the most talked-about when I demoed it — a carbon footprint estimate per page load.

It factors in page weight, request count, CDN usage, and estimated server energy draw, then gives a rating:

Rating CO₂ per page load
🌱 Eco < 10 g
🟡 Moderate 10 – 50 g
🔴 High Impact > 50 g

It's not scientific-grade, but it makes people think — which was the point.

What I Learned

Designing a public API is hard. When it's just your own app, you can change internals freely. Once it's a published package, breaking changes have real consequences. I had to think carefully about naming, return types, and async patterns before publishing v1.

Documentation is the product. Nobody will use your package if the README doesn't immediately show them what it does and how to install it. I rewrote the README three times.

Multi-runtime is a commitment. Keeping three SDKs in sync across .NET, JavaScript, and Rust means any core logic change needs to propagate to all three. I underestimated this. Python support is planned but I'm being careful about when I take that on.

Organic downloads feel different. Seeing strangers install something you built without you telling them to is a genuinely different feeling from a project that only your professor sees.

What's Next

Python SDK on PyPI, a GitHub Actions integration so teams can run AXIS checks in CI without writing any code, and a VS Code extension companion — A11Y: Lazy Edition is already live for in-editor checks.

Try it

  • Release
  • NuGet: dotnet add package AXIS-CORE
  • npm: npm install axis-core-sdk
  • Cargo: cargo add axis-core

If you're building anything where accessibility matters or just want to know your page's carbon footprint, give it a try. And if you've shipped a multi-runtime SDK before, I'd love to know how you handled keeping them in sync.

Built with WPF · .NET 9 · C# · HtmlAgilityPack · PuppeteerSharp · QuestPDF


Main changes: collapsed the bullet-heavy "What's Next" into a paragraph, merged the fragmented "What I Learned" bullets into flowing prose, and removed the section separators that were making it feel templated.

Top comments (0)