focuspath
v0.7.2
Published
See where keyboard navigation breaks. A visual accessibility scanner for focus order.
Maintainers
Readme
FocusPath
FocusPath traverses a web page with Chromium, records its keyboard focus sequence, flags deterministic problems, and writes a portable visual HTML report.
npx focuspath https://example.comFocusPath complements manual accessibility testing and established rule engines; it is not a WCAG conformance test.
CLI
focuspath <url> [options]
-o, --output <file> Report path (default: focuspath-report.html)
--max-steps <number> Maximum observed focus stops (default: 50)
--max-tab-presses <number> Maximum total Tab presses (default: 4 × max-steps)
--max-opaque-tab-presses <number> Repeated Tab limit per opaque host (default: 100)
--max-requests <number> Maximum page requests (default: 500; max: 10000)
--max-screenshot-height <px> Requested height (default: 20000; max: 100000; 40 MP hard cap)
--unlimited Remove request/requested-height limits; the 40 MP safety cap remains
--direction <forward|reverse> Keyboard traversal direction (default: forward)
--viewport <width>x<height> Browser viewport (default: 1440x900)
--headed Show Chromium while scanning
-V, --version Show the installed FocusPath version
-h, --help Show command helpIf Chromium is not installed yet, run npx playwright install chromium.
Node.js 24+ is the intentional tested runtime for the CLI and library. FocusPath is ESM-only; use import rather than CommonJS require().
The command exits with code 1 when an error finding is present, 2 when scanning fails, and 0 otherwise.
URLs without a scheme default to HTTPS for public hosts and HTTP for local development hosts. Embedded URL credentials are rejected by both the CLI and scanFocusPath. Query strings, screenshots, accessible names, and rendered page content may still be sensitive; protect and review HTML reports before sharing them.
TypeScript API
import { scanFocusPath } from "focuspath";
import { generateHtmlReport } from "focuspath/reporter";
const report = await scanFocusPath("https://example.com", {
maxSteps: 60,
maxTabPresses: 240,
maxOpaqueTabPresses: 120,
maxRequests: 500,
maxScreenshotHeight: 20_000,
direction: "reverse",
focusSettleMs: 100,
viewport: { width: 1440, height: 900 },
});
const html = generateHtmlReport(report);Only absolute HTTP(S) top-level URLs are accepted by default. For a trusted local fixture, allowLocalProtocols: true explicitly enables schemes such as data: or file:; do not use that option with external input. The optional isUrlAllowed request callback is useful for application policy, but does not by itself provide DNS pinning, egress isolation, or complete SSRF protection.
The scanner currently targets Chromium and supports forward Tab or reverse Shift+Tab traversal. maxSteps limits observable report entries; maxTabPresses counts every Tab key press, including movement inside opaque hosts. Cross-origin frames and closed shadow roots inferred from repeated, uncanceled Tab movement use the independent maxOpaqueTabPresses budget. Canceled Tab events that leave focus in place are reported as stalled focus. Chromium DOM identity, not the display selector, drives stall and cycle detection. Stops affected by independently scrolling or clipping ancestors remain in the sequence with scrollContexts, including hidden/clip containers, same-origin iframe viewports and parent-page scrollers; decorative overflow does not hide a fully visible control from the overlay. Schema v4 reports use observedRect for traversal-time geometry; rect and the optional transformed quad are measured only after FocusPath interrupts smooth scrolling and stabilizes the final screenshot state. Chromium captures beyond the first viewport up to the default 20,000px maxScreenshotHeight; a shared 40-megapixel safety budget may reduce larger requested or unlimited captures according to their actual width. capture.truncated makes either cutoff explicit while capture.sourceWidth and capture.sourceHeight preserve the full page dimensions. Local scans allow 500 requests by default. visualEvidence.status distinguishes plotted, partially visible, outside-capture and sequence-only stops. network records request totals and configured resource blocking so geometry restrictions remain explicit. The HTML reporter accepts saved schema v2, v3 and v4 reports after runtime validation of their complete rendering surface. Counters, step identities, issue references, capture metadata and network totals must agree; fixed array, text, byte and 40-megapixel budgets bound processing. Screenshot evidence is restricted to the JPEG format emitted by FocusPath and must decode completely through native libjpeg-turbo with the declared dimensions. Importing focuspath/reporter keeps Playwright out of report-only consumers; the root export remains compatible. The deprecated scrollContext field mirrors the first entry for v0.4.1 compatibility. Computed outline and shadow values are recorded for manual review; FocusPath does not claim to automatically verify WCAG focus appearance.
Repository, documentation, and issue tracker: github.com/damianociarla/focuspath
MIT © Damiano Ciarla
