# pour engine

Our own accessibility engine (name/version defined in
`src/config/project.config.js`) — written from the W3C specifications, and
the product's only engine.

**WCAG only, by design.** pour engine tracks the WCAG spec exclusively — no
Section 508, EN 301 549, or other standards. The full WCAG 2.2 catalog (all
86 success criteria) lives in `wcag22.js`, and `COVERAGE.md` (generated by
`node scripts/wcag-coverage.js`) shows which criteria have automated assistance
and which currently rely on the manual checklist. Assistance includes checks
that only nominate candidates for human review. `AUDIT.html` is the latest full
spec-sweep audit of every rule against the spec — over-violation,
under-violation, and what the engine cannot test. It is a self-contained
page: open it straight from disk in any browser. Regenerated by running
the `spec-sweep` skill.

## Provenance

Every rule and helper is written from the W3C specifications — WCAG 2.2,
WAI-ARIA, and the accessible-name computation spec. Where a threshold
appears in the code (4.5:1, 24px, 72000 s), it comes from the spec text
itself.

The result vocabulary follows the conventions the accessibility-tooling
ecosystem already speaks, so reports interoperate with existing pipelines:
rule ids like `image-alt`, the `wcag2a`/`wcag111` tag scheme, the
`critical/serious/moderate/minor` impact scale, and the result schema
(`violations/passes/incomplete/inapplicable`, `helpUrl`, `failureSummary`).
All user-facing prose (`help`, `message`, `fix`) is original.

## What it aims to do well

1. **Actionable messages.** Every finding says *how to fix it*, with
   concrete suggested code for the specific element (see any rule's
   `fix()`), not just what failed in spec language.
2. **Readable source.** Each rule is one small file a contributor can read
   in a minute — no rule → checks → metadata indirection.
3. **Resolved verdicts over "incomplete" noise.** The engine works to
   resolve backgrounds, backdrops, and paint order before giving up — and
   when it genuinely can't judge, it says so plainly instead of guessing.
4. **Size.** The engine is a few KB, so the extension and bookmarklet stay
   light.
5. **Filters integration.** Vision/sensory filters and the engine share
   colour math and DOM helpers.
6. **"Overdoing it" rules.** Best-practice rules that flag *redundant*
   accessibility — role="list" on a `<ul>`, aria-required duplicating
   required, aria-label repeating visible text, alt text starting with
   "image of", tabindex="0" on a `<button>`. Leaner markup is better
   markup. (Enable "Best practices" in settings to see them.)

## Architecture

```
src/engine/
  index.js               run(context, {tags}, onProgress) → results
  wcag22.js              the full WCAG 2.2 success-criteria catalog
  lib/
    dom.js               visibility, CSS-path selectors, html snippets
    accessible-name.js   accessible-name computation
    contrast.js          colour parsing, luminance, contrast + pixel sampling
    roles.js             ARIA role model (implicit roles, allowed attributes)
  rules/
    index.js             rule registry
    wcag/<rule>.js       spec-required rules (tagged with wcag* criteria)
    best-practice/<rule>.js  opt-in extras (tagged 'best-practice')
```

A rule is a small object:

```js
export default {
  id: 'image-alt',
  impact: 'critical',            // critical | serious | moderate | minor
  tags: ['wcag2a', 'wcag111'],   // version/level tags + criterion tags
  help: 'Every <img> needs a text alternative',
  helpUrl: 'https://…',
  selector: 'img',               // which elements the rule applies to
  visibleOnly: true,             // skip hidden elements (default true)
  evaluate(element, helpers) {
    // → { status: 'pass' } or { status: 'fail'|'incomplete', message, fix }
  },
};
```

`run(context, { tags })` selects rules by tag, evaluates each matching
element, and buckets results into `violations / passes / incomplete /
inapplicable`, each node carrying its target selector, html snippet, and a
plain-language failure summary with a concrete fix.

Rules that need to see the whole page at once (e.g. heading order) implement
`evaluateAll(elements, helpers)` instead of `evaluate`.

## The spec is the authority

Every rule is adjudicated against the WCAG, accname, and ARIA specs — those
documents decide what is a violation. The engine is also validated
continuously against pixel-measured ground truth on real sites (see the
repository's engine lab) so its verdicts answer to rendered reality, not
just to static analysis.

## Full-spec coverage model

Rules alone can never "cover WCAG" — most criteria need human judgment.
pour engine therefore reports on **every** in-scope criterion, one of two ways:

1. **Automated assistance**: the rules in `rules/` either establish specific
   failures or nominate review candidates. A rule may return only review
   findings when the DOM cannot establish a violation.
2. **Manual review** — every audit's results include `manualReview`: the
   in-scope criteria (filtered by the requested WCAG version and level)
   that automation can't judge, for a host to render however it wants.

### WCAG 3 Working Draft

A check set tag, `wcag3-core` or `wcag3-supplemental`, reports against the
WCAG 3.0 Working Draft of 10 September 2026 instead (`wcag3.js`). The
rules are still WCAG 2 rules: each is matched, by reading both documents,
to the draft requirement its failure also fails, and a rule with no such
requirement does not run (`UNMAPPED_RULES` gives each reason). Where the
draft leaves a value to be determined, text contrast among them, the
rule's WCAG 2 value stands in. `wcag3-core` runs the rules matched to core
requirements, the ones the draft says must all be met to conform;
`wcag3-supplemental` adds the rules matched to supplemental requirements.
`results.standard` is stamped draft and names the set, `manualReview`
becomes the set's requirements, and each rule result carries `wcag3`.
The draft's Bronze, Silver and Gold sit above conformance and each needs
a number of supplemental requirements and assertions it has yet to
decide, so no check set stands for one. WCAG 2 stays the default.

The checklist accounts for the criteria; it does not mean every page state
or possible failure has been tested. A snapshot audit still needs human
assessment, including interaction states and any checks reported as skipped.

## Honest current state

99 rules, hardened on real sites and the `testbeds/` pages. Audits walk into
open shadow roots (targets come back as `host >>> inner` selector chains);
iframe traversal is the host's job, and the extension does it. Known gaps:
the full accname spec (ours is a close approximation) and the language
subtag registry (BCP 47 shape check only). Grow it rule by rule; the test
beds' `expect:` annotations and the spec cases are the regression suite.
