BarrierLab is the shared engine behind the CASOON audit tools: WCAG 2.2 accessibility rules, HTML conformance, perception and web checks as pure-Rust libraries, with the accessibility rules also compiled to WebAssembly. Whether a page is checked during the Astro build, in a terminal audit or live in the browser, a finding has the same ID and means the same thing.
$cargoadda11y-rulesa11y-doma11y-report
$npmi@casoon/a11y-wasm
Why it exists
01
Write a check once
A tool that audits web pages should not have to reimplement the rules. It supplies the document; BarrierLab computes, in Rust or as WebAssembly in the page. Three tools already do exactly that.
02
Read results without translating
A report from a build pipeline and one from a browser can be laid side by side: same rule ID, same severity, same JSON from a11y-report. When two tools used to carry their own copy of a check, they disagreed about the same page; with one implementation, such a case shows up as a bug in one place.
03
“Did not run” is not “passed”
If a rule’s prerequisite is missing (no accessible names, no computed styles, no focus), it is listed in the report with the reason. There is no silent pass. The boundary is in the type system: a rule that needs rendering cannot be called on a host that has none.
Two calls, two answers
The accessibility rules are generic over a document model, so the host decides what it can supply. The simplest host is the Arena from a11y-dom, which holds structure only. Rules that need more are not skipped quietly — they come back as NotRun.
AccessibilityRust
use a11y_dom::Arena;use a11y_rules::run;let doc = Arena::builder() .open("html") .open("body") .open("img").attr("src", "logo.png").close() .close() .close() .build();let report = run(&doc);// Found: no lang, no title, no alt.assert!(report.findings.iter() .any(|f| f.rule_id == "images/alt-missing"));// Not judged: rules that need semantics or rendering.assert_eq!(report.summary.rules_not_run, 7);
HTML conformanceRust
let html = std::fs::read_to_string("index.html")?;let report = html_conform::check(&html)?;for f in &report.findings { let at = f.location.map(|l| l.to_string()) .unwrap_or_default(); println!("{:?} {at} {} {}", f.severity, f.rule_id, f.message);}// Error 8:17 parser.html5 an unknown named character reference// Error 9:25 parser.html5 a duplicate attribute on a tag
Findings are plain data with a stable JSON form:
a11y-reportJSON
{ "rule_id": "images/alt-missing", "outcome": "fail", "severity": "high", "message": "The image has no alt attribute.", "location": { "node": "15" }, "wcag": ["1.1.1"]}
Rust crates, all on crates.io
20
accessibility rule IDs in a11y-rules
168
vnu fixtures html-conform is measured against
4,977
finding model for every package
1
What the numbers look like
Both charts are generated from the repository: the rule IDs are the output of examples/a11y, the fixture counts come from the differential test against the Nu Html Checker. Rules are grouped by what the host has to provide; structure rules run anywhere, the rest declare their tier.
The vnu comparison is per fixture: does the document produce findings or not. It does not compare message text or positions, and html-conform is not a port of vnu. CI fails if the false-positive or false-negative count rises.
How it fits together
Only libraries live here. None of them opens a page or speaks a browser protocol: the tool obtains the data, BarrierLab computes. That is why the same rule set runs in a terminal, after a build and as WebAssembly inside an open page.
Dependencies point down through four layers: parsers, models, computation, delivery. No package imports one from its own or a higher layer, and each package names the specification it implements, from WHATWG HTML to XPath 1.0 to WCAG 2.2.
auditmysiteAudits rendered pages in the terminal, using the rules, accessible names and html-conform.
astro-post-auditAudits a finished Astro build, using the same rules plus web-checks.
liveauditAudits the running page inside the browser, via the WebAssembly package.
Frequently asked questions
Is BarrierLab itself a tool that checks a page?
No. This repository contains libraries only: Rust crates for accessibility, HTML conformance, perception and web checks. To audit a page you need one of the tools that embed them, such as auditmysite or liveaudit.
Which tools use the libraries today?
auditmysite, astro-post-audit and liveaudit use the crates from crates.io and npm. Because they share the rule set, a finding ID means the same thing in all three.
What does it mean when a rule is marked “not run”?
Some rules need data not every host can supply, such as focus or computed styles for static HTML. When the prerequisite is missing, the rule is listed in the report with the reason instead of counting as passed.
Can I use a single parser without the audit code?
Yes. The L0 crates (html5-parser, csp-parse, media-query-parse, xpath-eval, relax-ng, schematron-engine) are independent of the audit code. xpath-eval and schematron-engine are generic over the document model and also work outside HTML.
Why is there no shared changelog?
BarrierLab is a monorepo without a common version number. Each crate keeps its own changelog next to its source, so changes to one package are recorded there.