pour engine

The engine, documented.

pour engine is the audit engine inside every pour tool: a WCAG 2.2 engine written from the spec, 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 and published to npm, so you can run the same audit in your own tests and tools.

86 success criteria in WCAG 2.2

82 automated rules in pour engine

52 criteria that still need a human. We hand you the checklist

Install

npm install pour-engine

Source at github.com/pourdev/pour-engine, package at npmjs.com/package/pour-engine. 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.

// 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 (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.

Options accepted by run()
OptionTypeWhat it does
tags string[] Filter rules by WCAG version and level tags, for example wcag22aa. Empty or absent runs every rule.
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.

Fields on the result object
Result fieldWhat'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.
ruleTimings Per-rule wall time, so slow rules have nowhere to hide.

How it's built

~4× faster than axe-core® on the typical page

9.5× faster across the whole 138-page benchmark

the failing elements found

Benchmarked

Measured August 2026 across 138 live pages: three interleaved cold runs per engine on the same loaded DOM, medians reported, so neither engine benefits from a warm cache. The typical page audits in tens of milliseconds, around four times faster than axe-core®, and the gap widens with the page: more than nine times faster across the whole set, and twenty-one times on the heaviest page tested, at 190,000 DOM nodes.

In the same run the engine reported twice the failing elements (21,179 to 10,700) while sending just over a third as many findings to human review (7,282 to 20,870): a verdict it can prove is a verdict it gives. Every disagreement family between the two engines is adjudicated element-by-element against the spec before a number is quoted here.

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.