audit (EXPERIMENTAL)

auditAccessibility(agent.describe({ styles: true })) turns the affordance map into findings. It is the lint the map made obvious: every element with handlers should have a name, a role, and enough contrast and size to use — and because the map is the framework's own record of its wiring, a finding here is a real defect, not a scraper's guess.

import { enableAgentInterface, auditAccessibility } from 'tosijs'

const agent = enableAgentInterface()
const report = auditAccessibility(agent.describe({ styles: true }))
report.failed   // 0 when clean
report.findings // [{ rule, severity, message, record, index }]

Pure over the description — no DOM, so it runs on a map that arrived over a wire, in a test, or in CI. Pass styles: true to describe() or the contrast rule has nothing to measure and skips itself (it says so).

The rules

rule what it catches why
anonymous-affordance a wired element with no accessible name, text, or value a screen reader announces "button"; an agent sees <div>
unnameable-action a handler that is an anonymous function (ƒ) nothing can invoke or describe it but a click
missing-role a non-semantic tag (div/span) wired to act assistive tech has no idea it is a control
contrast text/background below the WCAG AA ratio measured with tosijs's own Color; needs styles: true
target-size interactive element below 24×24 (WCAG 2.5.8) too small to hit reliably; toggles exempt
label-hidden-by-placeholder a control whose only name is its placeholder the name vanishes the moment the user types

Findings carry the record and its index, so a caller can jump straight to the element — or hand the pair to schematicSVG's flags to draw them.

The divergence is closed (tosijs-floorplan 0.4.0, issue #4). target-size and "is this interactive" used to be implemented twice — here, and in the vendored renderer that draws the same map — and the two had drifted into contradicting each other on real elements. Both now come from ./schematic, so the audit and the drawing cannot disagree by construction. Geometry is judged where the geometry lives; this module keeps only what it uniquely knows (the accessible name, the contrast math) and the rule wording.

Adopting the shared rule changed five verdicts, deliberately, in both directions — see the 1.11.0 CHANGELOG entry.

It is one definition of evidence, not one set of answers. Three questions a drawing answers differently from a lint are adjusted here, by handing the shared predicate an adjusted record rather than by keeping a copy of it: zero-size (a 0×0 element is hidden, not a small target — the renderer draws nothing either way), list containers that are themselves controls (floorplan#7), and producer flags, which a renderer may honour because it already drew them and a lint may not because it never reads them (floorplan#8). All three are filed upstream; if they land, these become no-ops.

EXPERIMENTAL. Ships with the agent surface; rules and shapes may change.