/** * The axe accessibility scan, and the shape lookout keeps of what it says. * * Scoped to the element when one is given. The color-contrast rule is off by * default: computed-style contrast over translucent/washed surfaces * false-positives heavily; the AI judge sees real pixels instead. * --axe-contrast turns it on. * * axe reports far more than a rule id: the standard the rule belongs to, and * for every failing element its markup and the sentence each check wrote. A * ticket that says "the `
` reading 'Recent activity'" is one an agent can * act on; one that says `main > section:nth-of-type(2) > h5` is one it has to * go and resolve first. The markup is summarised at capture, never stored * whole: it can carry a row of somebody's data, and the folder it ends up in * is committed with the project. */ import type { Page } from "playwright"; import type { DeterministicFinding } from "../types.js"; /** What lookout keeps of one failing element's markup. */ export interface ElementSummary { tag: string; /** A few naming attributes; nothing that could carry a value or a payload. */ attrs: Record; /** Visible text, whitespace collapsed, at most 80 characters. */ text: string; } /** * The tag, a few naming attributes, and the visible text. Everything else in * the markup (values, sources, styles, handlers, other data attributes, nested * elements) is dropped here, before it is written anywhere. */ export declare function summariseHtml(html: string): ElementSummary; /** The slice of an axe violation lookout reads, typed structurally so the mapper needs no axe types. */ export interface AxeViolation { id: string; impact?: string | null; help: string; helpUrl: string; description: string; tags: string[]; nodes: { target: unknown[]; html: string; impact?: string | null; failureSummary?: string; any: { message: string; }[]; all: { message: string; }[]; none: { message: string; }[]; }[]; } /** One violation as a finding: the message names the rule, the record keeps everything a fixer needs. */ export declare function axeFinding(v: AxeViolation): DeterministicFinding; export declare function runAxe(page: Page, includeSelector: string | null, opts: { contrast: boolean; }): Promise; /** * What the scan has already filed on a route: rule id to the selectors of * the elements it fired on. The scan runs at every form factor, and a * violation the desktop layout showed is not news at tablet or phone; what * is news is the node only the narrower layout shows (a hamburger button * with no name, content a media query pushed off screen). Kept per scheme, * because the walk is schemes outside and form factors inside. */ export type AxeSeen = Map>; /** Add every (rule, node) pair a scan reported. */ export declare function rememberAxe(findings: readonly DeterministicFinding[], seen: AxeSeen): void; /** * The findings a narrower form factor adds: a violation is kept only when at * least one of its nodes is new for its rule, and the kept copy names only * those nodes, so a phone-only finding reads as what it is. The dedupe works * on the nodes the finding kept (MAX_NODES), which is the same lossy cut the * record has always made. */ export declare function newAxeFindings(findings: readonly DeterministicFinding[], seen: AxeSeen): DeterministicFinding[]; /** * The scan for one shot. With `seen`, the route's memory for this scheme: the * findings come back narrowed to what this form factor adds, and the memory * grows by everything the scan saw. Without it, every violation, every time. */ export declare function axeForShot(page: Page, includeSelector: string | null, opts: { contrast: boolean; seen: AxeSeen | null; }): Promise; /** * How big a control is, measured at the width where it matters. * * axe ships `target-size` disabled, so the ordinary scan never runs it at any * form factor, however widely that scan reaches. Selecting it by name here is * the only thing that makes the one rule whose whole subject is touch run at * all. Meanwhile the visibility panel was being asked to judge "touch targets * too small to hit reliably" by eye, while the rubric rightly forbids it the * measurement that would make such a finding actionable. * * Selecting the rule by name runs it whether or not it ships enabled, which is * the point: this is a deliberate opt-in to one rule, not a widening of the * scan. It also accounts for spacing, so a small control with room around it * passes and a small control crowded by its neighbours does not. */ export declare function runTargetSize(page: Page, includeSelector: string | null): Promise;