BarrierLab

CASOON Open Source

One rule set for web audits.

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.

cargo add a11y-rules a11y-dom a11y-report
npm i @casoon/a11y-wasm
From libraries to toolsParsers and a shared document and finding model carry the accessibility rules and HTML conformance; perception and web checks compute on their own. Everything ships as Rust crates on crates.io, the rules additionally as a WebAssembly package on npm. auditmysite, astro-post-audit and Relief use the crates, liveaudit the npm package.From libraries to toolsShared models → a11y-rulesParsers → a11y-rulesParsers → html-conforma11y-rules → crates.iohtml-conform → crates.ioa11y-perception → crates.ioweb-checks → crates.ioa11y-rules → npm: WebAssemblycrates.io → auditmysitecrates.io → astro-post-auditcrates.io → Reliefnpm → liveauditWebAssemblyParsers – HTML, CSS, CSP, XPathParsersHTML, CSS, CSP, XPathShared models – a11y-dom, a11y-reportShared modelsa11y-dom, a11y-reporta11y-rules – WCAG 2.2, accnamea11y-rulesWCAG 2.2, accnamehtml-conform – Conformancehtml-conformConformancea11y-perception – Perceptiona11y-perceptionPerceptionweb-checks – robots, meta, JSON-LDweb-checksrobots, meta, JSON-LDcrates.io – 20 Rust cratescrates.io20 Rust cratesnpm – a11y-wasmnpma11y-wasmauditmysite – Terminal auditauditmysiteTerminal auditastro-post-audit – after the Astro buildastro-post-auditafter the Astro buildRelief – BrowserReliefBrowserliveaudit – inside the live pageliveauditinside the live page
From libraries to toolsParsers and a shared document and finding model carry the accessibility rules and HTML conformance; perception and web checks compute on their own. Everything ships as Rust crates on crates.io, the rules additionally as a WebAssembly package on npm. auditmysite, astro-post-audit and Relief use the crates, liveaudit the npm package.From libraries to toolsShared models → a11y-rulesParsers → a11y-rulesParsers → html-conforma11y-rules → crates.iohtml-conform → crates.ioa11y-perception → crates.ioweb-checks → crates.ioa11y-rules → npm: WebAssemblycrates.io → auditmysitecrates.io → astro-post-auditcrates.io → Reliefnpm → liveauditWebAssemblyParsers – HTML, CSS, CSP, XPathParsersHTML, CSS, CSP, XPathShared models – a11y-dom, a11y-reportShared modelsa11y-dom, a11y-reporta11y-rules – WCAG 2.2, accnamea11y-rulesWCAG 2.2, accnamehtml-conform – Conformancehtml-conformConformancea11y-perception – Perceptiona11y-perceptionPerceptionweb-checks – robots, meta, JSON-LDweb-checksrobots, meta, JSON-LDcrates.io – 20 Rust cratescrates.io20 Rust cratesnpm – a11y-wasmnpma11y-wasmauditmysite – Terminal auditauditmysiteTerminal auditastro-post-audit – after the Astro buildastro-post-auditafter the Astro buildRelief – BrowserReliefBrowserliveaudit – inside the live pageliveauditinside the live page
From libraries to toolsParsers and a shared document and finding model carry the accessibility rules and HTML conformance; perception and web checks compute on their own. Everything ships as Rust crates on crates.io, the rules additionally as a WebAssembly package on npm. auditmysite, astro-post-audit and Relief use the crates, liveaudit the npm package.From libraries to toolsModels → a11y-rulesParsers → a11y-rulesParsers → html-conforma11y-rules → crates.io, npmhtml-conform → crates.io, npmPerception → crates.io, npmcrates.io, npm → auditmysite, astro-post-auditParsers – HTML, CSS, CSP, XPathParsersHTML, CSS, CSP,XPathModels – a11y-dom, a11y-reportModelsa11y-dom,a11y-reporta11y-rules – WCAG 2.2, accnamea11y-rulesWCAG 2.2,accnamehtml-conform – Conformancehtml-conformConformancePerception – a11y-perception, web-checksPerceptiona11y-perception,web-checkscrates.io, npm – 20 crates, a11y-wasmcrates.io, npm20 crates,a11y-wasmauditmysite, astro-post-audit – Relief, liveauditauditmysite,astro-post-auditRelief, liveaudit
From libraries to toolsParsers and a shared document and finding model carry the accessibility rules and HTML conformance; perception and web checks compute on their own. Everything ships as Rust crates on crates.io, the rules additionally as a WebAssembly package on npm. auditmysite, astro-post-audit and Relief use the crates, liveaudit the npm package.From libraries to toolsModels → a11y-rulesParsers → a11y-rulesParsers → html-conforma11y-rules → crates.io, npmhtml-conform → crates.io, npmPerception → crates.io, npmcrates.io, npm → auditmysite, astro-post-auditParsers – HTML, CSS, CSP, XPathParsersHTML, CSS, CSP,XPathModels – a11y-dom, a11y-reportModelsa11y-dom,a11y-reporta11y-rules – WCAG 2.2, accnamea11y-rulesWCAG 2.2,accnamehtml-conform – Conformancehtml-conformConformancePerception – a11y-perception, web-checksPerceptiona11y-perception,web-checkscrates.io, npm – 20 crates, a11y-wasmcrates.io, npm20 crates,a11y-wasmauditmysite, astro-post-audit – Relief, liveauditauditmysite,astro-post-auditRelief, liveaudit

Why it exists

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.

168 rule IDs, grouped by what the host must provideRule IDs declared by a11y-rules 0.29. Structure rules run on any host; the others only run when the host supplies semantics, rendering data or parsed stylesheets, and are reported as not run otherwise.168 rule IDs, grouped by what the hostmust provide050100150Rule IDsStructure (Document): 107107Structure (Document)Semantics (role, accessible name): 3535Semantics (role, accessiblename)Rendering (styles, geometry): 2121Rendering (styles, geometry)Stylesheets: 55Stylesheets
168 rule IDs, grouped by what the host must provideRule IDs declared by a11y-rules 0.29. Structure rules run on any host; the others only run when the host supplies semantics, rendering data or parsed stylesheets, and are reported as not run otherwise.168 rule IDs, grouped by what the hostmust provide050100150Rule IDsStructure (Document): 107107Structure (Document)Semantics (role, accessible name): 3535Semantics (role, accessiblename)Rendering (styles, geometry): 2121Rendering (styles, geometry)Stylesheets: 55Stylesheets
html-conform against 4,655 vnu fixturesPer fixture: does the document produce findings or not. 3,745 documents vnu rejects are rejected, 909 clean ones stay clean; there are no false positives and one false negative.html-conform against 4,655 vnu fixtures01000200030004000FixturesTrue positives: 37453745True positivesTrue negatives: 909909True negativesFalse negatives: 11False negatives0False positives
html-conform against 4,655 vnu fixturesPer fixture: does the document produce findings or not. 3,745 documents vnu rejects are rejected, 909 clean ones stay clean; there are no false positives and one false negative.html-conform against 4,655 vnu fixtures01000200030004000FixturesTrue positives: 37453745True positivesTrue negatives: 909909True negativesFalse negatives: 11False negatives0False positives

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.

The core crates and their dependenciesAn arrow leads from a crate to the one that uses it. Dependencies only point down, from L0 to L3: html-conform builds on six parsers and engines, a11y-rules on a11y-dom, a11y-report and stylesheet-parse, and a11y-wasm bundles a11y-rules and accname for the browser. a11y-perception and web-checks stand alone.The core crates and their dependenciesL0 · Parsers and enginesL1 · ModelL2 · ComputationL3 · Deliverystylesheet-parse → a11y-rulesa11y-dom → a11y-rulesa11y-report → a11y-rulesa11y-dom → a11y-wasma11y-report → a11y-wasma11y-rules → a11y-wasmaccname → a11y-wasma11y-dom → accnamecsp-parse → html-conformhtml5-parser → html-conformmedia-query-parse → html-conformrelax-ng → html-conformschematron-engine → html-conformxpath-eval → html-conformxpath-eval → schematron-enginemedia-query-parse → stylesheet-parsehtml5-parser – HTML parserhtml5-parserHTML parsercsp-parse – CSPcsp-parseCSPmedia-query-parse – Media Queriesmedia-query-parseMedia Queriesxpath-eval – XPath 1.0xpath-evalXPath 1.0relax-ng – RELAX NGrelax-ngRELAX NGschematron-engine – Schematronschematron-engineSchematrona11y-dom – Document modela11y-domDocument modela11y-report – Finding modela11y-reportFinding modelstylesheet-parse – CSS stylesheetsstylesheet-parseCSS stylesheetshtml-conform – HTML conformancehtml-conformHTML conformanceaccname – Accessible NameaccnameAccessible Namea11y-rules – Rule seta11y-rulesRule seta11y-perception – Perceptiona11y-perceptionPerceptionweb-checks – robots, meta, JSON-LDweb-checksrobots, meta, JSON-LDa11y-wasm – WebAssembly, npma11y-wasmWebAssembly, npm
The core crates and their dependenciesAn arrow leads from a crate to the one that uses it. Dependencies only point down, from L0 to L3: html-conform builds on six parsers and engines, a11y-rules on a11y-dom, a11y-report and stylesheet-parse, and a11y-wasm bundles a11y-rules and accname for the browser. a11y-perception and web-checks stand alone.The core crates and their dependenciesL0 · Parsers and enginesL1 · ModelL2 · ComputationL3 · Deliverystylesheet-parse → a11y-rulesa11y-dom → a11y-rulesa11y-report → a11y-rulesa11y-dom → a11y-wasma11y-report → a11y-wasma11y-rules → a11y-wasmaccname → a11y-wasma11y-dom → accnamecsp-parse → html-conformhtml5-parser → html-conformmedia-query-parse → html-conformrelax-ng → html-conformschematron-engine → html-conformxpath-eval → html-conformxpath-eval → schematron-enginemedia-query-parse → stylesheet-parsehtml5-parser – HTML parserhtml5-parserHTML parsercsp-parse – CSPcsp-parseCSPmedia-query-parse – Media Queriesmedia-query-parseMedia Queriesxpath-eval – XPath 1.0xpath-evalXPath 1.0relax-ng – RELAX NGrelax-ngRELAX NGschematron-engine – Schematronschematron-engineSchematrona11y-dom – Document modela11y-domDocument modela11y-report – Finding modela11y-reportFinding modelstylesheet-parse – CSS stylesheetsstylesheet-parseCSS stylesheetshtml-conform – HTML conformancehtml-conformHTML conformanceaccname – Accessible NameaccnameAccessible Namea11y-rules – Rule seta11y-rulesRule seta11y-perception – Perceptiona11y-perceptionPerceptionweb-checks – robots, meta, JSON-LDweb-checksrobots, meta, JSON-LDa11y-wasm – WebAssembly, npma11y-wasmWebAssembly, npm

Tools built on it

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.