01 / Run
Install and CLI
Run once with npx, or install the package in a project. The CLI writes a self-contained HTML report by default.
npx focuspath https://example.com
npm install --save-dev focuspath
npx focuspath https://example.com --output ./focus-report.html
npx focuspath --version
Public hostnames without a scheme default to HTTPS; localhost, loopback and local development hostnames default to HTTP. Embedded URL credentials are rejected by both the CLI and TypeScript API.
Traversal budgets
--direction- Use forward for Tab or reverse for Shift+Tab. Default: forward.
--max-steps- Maximum observable focus stops. Default: 50.
--max-tab-presses- Total keyboard traversal presses, including movement hidden inside opaque hosts. Default: 200.
--max-opaque-tab-presses- Repeated presses tolerated at one opaque host. Default: 100.
--max-requests- Maximum page requests. Default: 500.
--max-screenshot-height- Requested screenshot height. Default: 20,000px; maximum: 100,000px; the shared 40 MP safety cap still applies.
--unlimited- Removes request and requested-height limits for trusted pages; it does not bypass the 40 MP report safety cap.
-V, --version- Prints the installed FocusPath version without starting Chromium.
02 / Integrate
TypeScript API
The ESM-only public API is typed and returns both observed stops and the exact limits used by the scan.
import { scanFocusPath } from "focuspath";
import { generateHtmlReport } from "focuspath/reporter";
const report = await scanFocusPath("https://example.com", {
maxSteps: 50,
maxTabPresses: 200,
maxOpaqueTabPresses: 100,
maxRequests: 500,
maxScreenshotHeight: 20_000,
direction: "reverse",
timeoutMs: 30_000,
});
console.log(report.version, report.tabPressCount, report.limits);
const html = generateHtmlReport(report);
Schema v4: rect is geometry in the final screenshot state; observedRect preserves traversal-time geometry; capture.truncated reports a screenshot-height cutoff. The reporter still accepts saved v2 and v3 reports. Issue step numbers always reference a step present in report.steps.
scanFocusPath permits only absolute HTTP(S) URLs by default. Non-network schemes such as data: and file: require allowLocalProtocols: true, which is intended only for trusted local input. The isUrlAllowed callback does not provide DNS pinning or complete SSRF protection on its own.
Saved reports are validated at runtime before HTML generation. Counters, limits, contiguous step numbers, issue references, capture metadata and network totals must agree. Scanner and reporter share the same 40 MP evidence budget: larger requested or unlimited heights are reduced using the actual capture width, with source dimensions preserved and capture.truncated set. Screenshot evidence must use the JPEG format emitted by FocusPath, decode completely through native libjpeg-turbo and exactly match the declared captured dimensions; PNG and WebP report inputs are rejected. Report-only consumers can import focuspath/reporter without loading Playwright.
03 / Understand
Traversal model
Chromium performs native Tab or Shift+Tab navigation in the selected direction.
The deepest inspectable active element becomes a focus stop.
Deterministic findings are attached to that existing stop.
Opaque boundaries
Cross-origin iframes are known to be opaque. A custom element without an open shadow root is only treated as a candidate; FocusPath infers a closed shadow root only when repeated, uncanceled Tab presses keep focus on that host. A canceled Tab event is reported as stalled focus instead. Internal controls cannot be named or positioned, but traversal may continue within the independent Tab budget.
FocusPath detects missing accessible names, generic roles, positive tabindex, cycles and stalled focus. Element identity comes from Chromium rather than the display selector, so similar deep DOM branches remain distinct.
Visual evidence
A final screenshot cannot truthfully represent every intermediate scroll position. FocusPath follows scrolling and clipping ancestors—including overflow:hidden, overflow:clip, same-origin iframe viewports and parent-page scrollers. A stop can expose multiple scrollContexts and a visualEvidence status: plotted, partially-visible, outside-capture or sequence-only.
Traversal-time geometry remains available as observedRect. Before the screenshot, FocusPath removes focus, disables smooth scrolling and waits for a stable top-of-page capture state before asking Chromium for the border quad again. This keeps sticky controls and CSS-transformed iframe contents aligned with the image. Sequence-only steps remain in the ordered table but are omitted from the overlay. FocusPath is evidence for review—not a WCAG certification.
04 / Automate
HTTP API
The hosted beta accepts a public HTTP(S) URL from the FocusPath website and currently scans forward with stricter limits than the local package. Every response includes report schema version, viewport, captured pixel dimensions, direction, elapsed time, Tab count, traversal limits and findings.
POST /v1/scans
Content-Type: application/json
{ "url": "https://example.com" }
// Machine-readable pixels instead of portable reportHtml:
{ "url": "https://example.com", "format": "structured" }
Omit format for a self-contained reportHtml. Use structured to receive the screenshot data URL directly without duplicating it inside HTML. FocusPath 0.7.2 emits report schema v4 and reports both source dimensions and screenshot truncation.
The hosted beta blocks font and media requests to bound cost. Every response reports its network restrictions and blocked-request count because fallback fonts can change wrapping and geometry.
The canonical machine-readable contract is the OpenAPI specification; deterministic HTTP responses and both successful scan formats are validated against it in CI. Response evolution follows the compatibility policy. The public edge applies origin checks, request throttling, concurrency control, timeouts and response-size limits; those controls reduce abuse but do not make a browser API private.
05 / Boundaries
Security and privacy
Only scan pages you are authorized to test. The submitted URL is fetched in a temporary Chromium session on AWS. FocusPath does not intentionally persist page content, screenshots or generated reports after the response completes. Query strings, screenshots, accessible names and rendered report content may contain sensitive data; avoid secret-bearing URLs and review every report before sharing it.
- Infrastructure providers may retain request metadata in operational logs according to their configured retention.
- Private, loopback, link-local and cloud-metadata destinations are rejected, including redirects and browser subrequests.
- DNS validation attempts are preflight-limited before resolution; equivalent hostnames share a canonical target bucket.
- The hosted browser uses a local egress proxy that pins validated IPs, limits address attempts and cancels silent or abandoned upstream connections.
- Infrastructure-level egress filtering remains recommended defense in depth before operating a general-purpose service.
- The optional AWS WAF layer is intentionally disabled in the low-cost deployment.
FocusPath scans one selected direction at a time and does not compare both routes automatically. It cannot cover every browser, assistive technology, focus-indicator contrast or interaction state. Pair the report with manual keyboard and screen-reader testing.