Skip to content
telerikPublic

Latest commit

 

History

171 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Keylens

Press Tab. Record what actually happens.

npm version CI node WCAG 2.2 License: Apache 2.0

Static accessibility tools tell you whether markup could work. Keylens launches a real Playwright browser, presses Tab, and records the keyboard path users actually get.

Documentation · Getting started · Rules · API · Contributing

What Keylens tests

Keylens complements static accessibility analysis; it does not replace it. It evaluates the runtime focus sequence with nine deterministic rules:

  • keyboard traps and incomplete Tab cycles;
  • interactive elements that Tab never reaches;
  • large differences between focus order and DOM order;
  • positive tabindex values;
  • missing visual focus changes, with optional screenshot evidence;
  • missing or nonfunctional skip links;
  • focus obscured at the focused element's center point;
  • invalid focus after bounded, opt-in activations;
  • composite widgets (tabs, menus, listboxes) unreachable via arrow keys despite roving-tabindex markup.

It audits one page per invocation, emits CLI, JSON, HTML, or Markdown reports, and runs as a library with cancellation, progress events, phase deadlines, typed errors, and explicit rendering.

Before the crawl starts, a bounded prepare phase auto-dismisses cookie/consent banners (built-in presets for major CMPs, plus a generic fallback), so audits of real sites aren't dominated by banner noise. Disable with --keep-overlays; see Configuration.

Important

Keylens is available as a stable package from the public npm registry. MCP distribution and interaction testing may change independently.

Install

Keylens is distributed through the public npm registry. Node.js 22 or later is required.

npm install --save-dev @progress/keylens
npx playwright install chromium

Without installing, run Keylens with the full package name, npx @progress/keylens. After the local install, npx keylens also works because npx uses the project's binary. The bare keylens name on npm is a placeholder that only prints a warning.

Run an audit

npx @progress/keylens audit https://example.com
🔍 Keylens v1.0.0 — Keyboard Navigation Audit
URL: https://example.com
Focusable elements: 42
Interactive elements: 40 (2 unreachable)
Tab cycle completed: Yes
Duration: 3120ms

Rules:
● unreachable-elements   FAIL  2 interactive elements never reached via Tab
● keyboard-trap          PASS
● focus-order-mismatch   PASS
● missing-focus-indicator PASS
...

Score: 78/100

The default balanced profile runs headless Chromium, captures no screenshots unless an HTML report needs a page image, does not activate controls, and prints the CLI report.

# CI-oriented JSON
npx @progress/keylens audit https://example.com --output cli,json

# HTML focus map (implicitly requests a full-page capture)
npx @progress/keylens audit https://example.com --output html

# Exhaustive within explicit enforced budgets
npx @progress/keylens audit https://example.com --profile thorough \
  --interactions --output cli,json,html,markdown \
  --config keylens.config.json

For the last command, set capture, interaction, and timeout budgets in the config. “Thorough” increases crawl timing and maxTabs; it does not remove resource limits. See Configuration.

Exit codes

Code Meaning
0 Audit completed with no error-severity violations or rule evaluation errors
1 Audit completed and found one or more error-severity accessibility violations
2 Configuration, navigation, timeout, reporter, or rule evaluation failure made the audit incomplete

Warnings alone return 0.

Programmatic API

Programmatic analysis is silent and in-memory by default. It does not print reports or write files unless a renderer is called explicitly.

import { audit, renderAuditReport } from "@progress/keylens";

const controller = new AbortController();
const report = await audit("https://example.com", {
  profile: "balanced",
  capture: { page: "none" },
  signal: controller.signal,
  timeouts: { total: 60_000, crawl: 45_000, rules: 10_000 },
  onEvent(event) {
    if (event.type === "crawl-progress") {
      console.log(event.tabsAttempted, event.elementsFocused);
    }
  },
});

await renderAuditReport(report, ["json"], "./keylens-report");

Use audit() for deterministic analysis and renderAuditReport() for output.

The browser-safe @progress/keylens/guidance subpath exposes rule remediation and WCAG references without importing Playwright:

import { getRuleRemediation } from "@progress/keylens/guidance";

Page screenshots and interactions

  • capture.page / --page-screenshot controls one page image (none, viewport, or full). HTML output defaults it to full; other CLI output defaults it to none.
  • Page capture is bounded by dimensions, decoded pixels, and encoded bytes. Reports expose skipped/failed capture totals.
  • Interactions are experimental and off by default. They are bounded, reload-isolated by default, skip likely destructive controls, and block top-level navigation. Inspect interactionResults in the in-memory report or JSON projection for every passed, failed, skipped, and errored case.

See Known limitations before treating results as complete accessibility conformance evidence.

Reports

Reporter Output
cli Human-readable terminal report and capture coverage
json keylens-report-<timestamp>.json; semantic data with binary assets omitted
html keylens-report-<timestamp>.html; focus map when a page image is available
markdown keylens-report-<timestamp>.md

A -YYYY-MM-DDTHH-mm-ss timestamp is appended to file reporter names by default so repeated runs against the same --output-dir never overwrite a prior report. Use --output-name <name> for a custom base name (e.g. a per-URL prefix) or --no-timestamp for a stable name.

Reports carry an independent schemaVersion. A score is marked incomplete when a rule could not be evaluated; do not compare that score as if it covered all enabled rules.

MCP distribution

The @progress/keylens/mcp subpath and keylens-mcp binary expose the deterministic single-page audit and rule guidance tools over stdio. Review MCP before use.

Telemetry

Keylens can collect anonymous, aggregate usage data (audit counts, options used, pass/fail results, and accessibility issue categories), correlated with a one-way hashed machine identifier, to help improve the tool. No URLs, page content, selectors, accessible names, screenshots, reports, or IP addresses are ever collected. Telemetry is enabled by default, and a one-time notice is always shown before anything is collected. Opt out anytime with KEYLENS_TELEMETRY_OFF=1 or TELERIK_TELEMETRY_OFF=1. See src/telemetry/README.md for the full data-collection policy.

Support and security

License

Apache License 2.0

Copyright 2026 Progress Software Corporation. See NOTICE.

Created by the Progress Telerik team.

Releases

Packages

Used by

Contributors

Languages