pour engine
The engine, documented.
pour engine is the audit engine inside every pour tool: a WCAG 2.2 engine written from the spec (opens in a new tab), with zero dependencies. It runs entirely in the browser and returns structured, per-rule results with the exact failing elements and the evidence for each verdict. It's open source on GitHub (opens in a new tab) and published to npm (opens in a new tab), so you can run the same audit in your own tests and tools.
86 success criteria in WCAG 2.2
99 automated rules in pour engine
35 criteria that still need a human. We hand you the checklist
Install
npm install pour-engine
Source at github.com/pourdev/pour-engine (opens in a new tab), package at npmjs.com/package/pour-engine (opens in a new tab). The same engine ships inside the extension and the bookmarklet.
Quick start
The engine audits a live DOM: the top document, an iframe's document,
or anything with querySelectorAll. One call, one report.
import { run, name, version } from 'pour-engine';
const results = await run(document, {
// Tags are cumulative: WCAG 2.2 AA means every A and AA rule
// from 2.0 through 2.2. Omit tags to run everything.
tags: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'],
});
for (const violation of results.violations) {
console.log(violation.id, violation.impact, violation.nodes.length);
}
Where it runs
Anywhere a real browser renders a DOM. The engine ships inside the extension and the bookmarklet, and the same package drives headless browsers: point Puppeteer or Playwright at any URL, import the engine into the page, and audit. pour's own release checks run it in both Chromium and Firefox this way, headless, which makes CI a two-line job. Or skip the wiring entirely: the command line does it in one, and serves the same audit to an AI agent over the Model Context Protocol.
// npm i pour-engine puppeteer
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com', { waitUntil: 'load' });
const report = await page.evaluate(async () => {
const { run } = await import('https://unpkg.com/pour-engine@1/engine/index.js');
const { violations } = await run(document, { tags: ['wcag2a', 'wcag2aa'] });
return violations.map((v) => ({ id: v.id, impact: v.impact, count: v.nodes.length }));
});
await browser.close();
console.table(report);
Playwright is the same shape: swap the import and
chromium.launch(). Both are complete, runnable files in
the package:
pour-engine/examples (opens in a new tab)
(Puppeteer, Playwright, and a plain-browser page). One honest
constraint: results come from computed styles and real layout, and
contrast is measured rather than guessed, so the engine needs a real
rendering engine, headless included. A DOM emulator without layout
is not a supported target.
API
run(context, options, onProgress) returns a promise for the full report.
| Option | Type | What it does |
|---|---|---|
tags |
string[] |
Filter rules by WCAG version and level tags, for example wcag22aa. Empty or absent runs every rule. A WCAG 3 Working Draft tag runs the rules matched to the draft's requirements instead: wcag3-core its core requirements, wcag3-supplemental its core and supplemental ones. The draft is unfinished, so its results are marked draft and suit no conformance claim. |
exclude |
string |
CSS selector for elements to leave out of the audit, including their subtrees. |
signal |
AbortSignal |
Stops the run at the next rule boundary and throws an AbortError. |
The optional onProgress callback fires before and after
each rule with the rule id, counts so far, and timing, which is what
drives the live progress UI in pour tools.
| Result field | What's in it |
|---|---|
violations |
Rules that failed. Each carries its WCAG criterion, severity, help text, and per-element nodes with a CSS path and HTML snippet. |
passes |
Rules that ran and found nothing wrong, with how many elements they checked. |
incomplete |
Findings the engine cannot judge conclusively. Returned for a human, never guessed at. |
inapplicable |
Rules with nothing to test on this page. |
manualReview |
The criteria in your chosen scope that no tool can verify, as a checklist. Nothing silently skipped. For the WCAG 3 draft, its core requirements, or its core and supplemental ones. |
standard |
Set only for the WCAG 3 draft: the draft, its date, the requirements checked, a note and a link to the draft to show with the results. Each rule result then carries wcag3, the draft requirements it was matched to. |
ruleTimings |
Per-rule wall time, so slow rules have nowhere to hide. |
How it's built
-
Spec-first
Every rule and helper was written from the W3C specifications (WCAG, ARIA, accname). When a judgment call comes up, it's settled by reading the spec.
-
Zero dependencies
The engine ships nothing but its own code, so it runs anywhere a DOM exists: content scripts, bookmarklets, test harnesses. No bundling baggage, no supply-chain surface.
-
No false authority
A result is a violation only when the spec says so. Everything uncertain lands in
incompleteor the manual-review checklist, never a guess dressed up as a finding.
~3× faster than axe-core® on a typical page
5.8× less time to check all 138 websites in our benchmark
More accurate: finds all 38 known problems on our test page. axe-core® finds 28
Benchmarked
Timed in September 2026 on 138 real websites against axe-core®, the checker most accessibility tools are built on. Both engines check the same loaded page, each run three times in alternating order with the middle result kept, so neither gets a warmed-up advantage. A typical page takes a fraction of a second, three times faster than axe-core®, and the gap narrows a little on the heaviest pages. On the largest page we test, a technical specification with 194,000 elements, it is 24 times faster, done in seven seconds where axe-core® takes two minutes 43 seconds. The widest gap of all is 24 times, on ECMAScript spec (~190k nodes). On each engine's initial run, pour is 2.1× faster at the median, ahead on 124 of 138 pages.
On the 138 real websites it found more problems than axe-core® on 58 pages, fewer on 18 and the same on 62; on the two most common problems of all, images with no description and links with no name, the two agree to the element. When it cannot be sure it asks rather than guesses, and every place the two engines disagree is checked by hand against the standard before a number appears here.
Accuracy is measured where the right answers are known. We built Beta Block, a fictional gym website with 38 real accessibility problems planted in it and 8 things that need a person's judgement, and published the answer key so any tool can be marked against it. The engine finds all 38 problems, flags all 8 judgement calls, and reports nothing that isn't there. These totals count planted faults, not individual audit findings. One fault can affect several elements or trigger more than one check, so the extension can show a higher total. axe-core® finds 28 of the 38 (31 if you count the ones it only marks as “maybe”) and none of the 8. It is one page, and we wrote it, so treat it as a like-for-like test rather than a universal score.
axe® and axe-core® are registered trademarks of Deque Systems, Inc., which is not affiliated with and does not endorse pour. The figures are our own measurements.