npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@a11y-skills/audit

v0.5.0

Published

Playwright + axe-core based WCAG 2.2 accessibility audit functions (axe, focus indicator, keyboard trap, reflow, target size, text spacing, zoom, orientation, autocomplete, time limit, auto-play).

Readme

@a11y-skills/audit

Playwright + axe-core based WCAG 2.2 accessibility audit functions.

This package is the functional core extracted from the auditing-wcag Claude Code skill. It ships ten checks as plain functions plus ready-to-run Playwright test entries.

日本語版は README.ja.md を参照してください。

Scope. Automated testing detects only ~30–40% of WCAG issues. Manual testing is required for full conformance. This package automates a subset.

Checks

| Function | WCAG | | --- | --- | | runAxeAudit | axe-core broad coverage (2.0/2.1/2.2 A & AA) | | runFocusIndicatorCheck | 2.4.7 Focus Visible / 2.4.12 Focus Not Obscured / 3.2.1 On Focus | | runReflowCheck | 1.4.10 Reflow | | runTargetSizeCheck | 2.5.5 / 2.5.8 Target Size | | runTextSpacingCheck | 1.4.12 Text Spacing | | runZoomCheck | 1.4.4 Resize Text (200% zoom) | | runOrientationCheck | 1.3.4 Orientation | | runAutocompleteAudit | 1.3.5 Identify Input Purpose | | runTimeLimitDetector | 2.2.1 Timing Adjustable | | runAutoPlayDetection | 1.4.2 Audio Control / 2.2.2 Pause, Stop, Hide |

Most checks take an already-navigated page. A few own navigation and take a targetUrl instead (or TEST_PAGE): runOrientationCheck and runTimeLimitDetector (and runZoomCheck when a URL is given). runFocusIndicatorCheck takes a browser. runAutoPlayDetection needs the optional pixelmatch + pngjs deps (see Install).

Install

npm install -D @a11y-skills/audit @playwright/test @axe-core/playwright

@playwright/test and @axe-core/playwright are peer dependencies.

runAutoPlayDetection additionally needs pixelmatch and pngjs (declared as optional dependencies, installed by default). They are loaded lazily, so the other nine checks work even if you install with --omit=optional:

npm install -D pixelmatch pngjs   # only if you use runAutoPlayDetection

ESM only. This package does not ship a CommonJS build; import it from ESM (or a TypeScript project compiled to ESM).

CLI

Run all ten checks against a URL with a single command — no test runner needed.

npx -y @a11y-skills/audit --url https://example.com

Peer dependencies (@playwright/test, @axe-core/playwright) are pulled in automatically by npm 7+. Install Chromium on first use:

npx playwright install chromium

Flags

| Flag | Default | Description | | --- | --- | --- | | --url <url> | TEST_PAGE env | Target URL. --url takes priority over the env var. If neither is set, usage is printed and the process exits 2. | | --checks <list> | all | Comma-separated check names (see list below). | | --output-dir <dir> | ./a11y-audit-results | Directory for result JSON files. | | --screenshot | off | Enable focus-indicator screenshot. | | --list-checks | — | Print check names and exit 0. | | --version | — | Print version and exit 0. | | --help | — | Print usage and exit 0. |

Available check names

axe-audit
focus-indicator-check
reflow-check
text-spacing-check
zoom-200-check
orientation-check
autocomplete-audit
time-limit-detector
auto-play-detection
target-size-check

Exit codes

| Code | Meaning | | --- | --- | | 0 | All checks completed, no violations found. | | 1 | All checks completed, at least one violation found. | | 2 | Runtime error — bad arguments, missing peers, navigation failure, or check crash. |

Completed-check JSON is always written even when the exit code is non-zero.

Peer requirements

@playwright/test >=1.50.0 and @axe-core/playwright >=4.10.0 must be resolvable. If they are not found, the CLI exits 2 with an install hint. --list-checks, --help, and --version work without peers.

auto-play-detection and optional deps

auto-play-detection needs pixelmatch and pngjs. If they are absent, that check is skipped with a SKIPPED message and does not affect the exit code. The other nine checks continue normally.

GitHub Actions

Copy-paste workflow for a manually triggered audit. Violations fail the job (exit code 1); add continue-on-error: true to the audit step if you want the result to be informational only.

# .github/workflows/a11y-audit.yml
name: A11y Audit

on:
  workflow_dispatch:
    inputs:
      target_url:
        description: 'Audit target URL'
        required: true
        default: 'https://example.com'

jobs:
  a11y-audit:
    runs-on: ubuntu-latest
    steps:
      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version: '24'

      - name: Install audit package
        run: npm install --no-save @a11y-skills/audit @playwright/test @axe-core/playwright

      - name: Install Playwright browsers
        run: npx playwright install --with-deps chromium

      - name: Run a11y audit
        run: npx a11y-audit --url "$TARGET_URL" --output-dir a11y-audit-results
        env:
          TARGET_URL: ${{ inputs.target_url }}

      - name: Upload audit results
        uses: actions/upload-artifact@v7
        if: always()
        with:
          name: a11y-audit-results
          path: a11y-audit-results/
          retention-days: 30

Notes:

  • Add push / schedule triggers (with a hardcoded URL) to run the audit as a recurring gate.
  • Result JSON files (and the focus-indicator screenshot when --screenshot is set) are uploaded as a build artifact.
  • To speed up repeat runs, cache ~/.cache/ms-playwright keyed on the installed @playwright/test version — see this repository's ci.yml for the pattern.

Usage — function API (recommended)

You navigate the page; the function runs the check, writes a result JSON, and returns the parsed result.

import { test } from "@playwright/test";
import { runAxeAudit } from "@a11y-skills/audit/playwright";

test("axe audit", async ({ page }, testInfo) => {
  await page.goto("https://example.com");
  const result = await runAxeAudit({
    page,
    outputDir: testInfo.outputDir,   // where to write axe-result.json
    // outputFile: "axe-result.json", // optional override
    // tags: ["wcag2a", "wcag2aa", "wcag21aa", "wcag22aa"],
  });
  expect(result.summary.violationCount).toBe(0);
});

The focus indicator check owns its browser context (it restarts in a fresh context when focus triggers a navigation), so it takes a browser and a targetUrl instead of a page:

import { runFocusIndicatorCheck } from "@a11y-skills/audit/playwright";

test("focus indicators", async ({ browser }, testInfo) => {
  const result = await runFocusIndicatorCheck({
    browser,
    targetUrl: "https://example.com", // or set TEST_PAGE env var
    outputDir: testInfo.outputDir,
    screenshot: true,                 // default: false
    // contextOptions: { locale: "ja-JP" }, // forwarded to browser.newContext()
  });
  expect(result.details.elementsWithoutFocusStyle).toBe(0);
});

Output location resolution

For every check:

  1. outputPath — full path (mutually exclusive with outputDir/outputFile).
  2. otherwise outputDirA11Y_OUTPUT_DIR env → process.cwd(), joined with outputFile → the check's default filename.

Screenshots (when enabled) are written next to the result file. outputFile must be a bare filename; use outputPath for an absolute location.

Reflow note. runReflowCheck sets the narrow viewport itself, so it works on an already-navigated page. For pages that read the viewport only at load time, set the viewport before page.goto(...) for results identical to the legacy script (the compatibility entry does this).

Usage — compatibility test entries

If you prefer not to write test bodies, re-export the bundled entries from a one-line local spec. The entries call test(...) at import time and read TEST_PAGE (target URL) and A11Y_OUTPUT_DIR (output directory), capturing screenshots — reproducing the legacy script behavior.

// tests/a11y/axe.spec.ts
import "@a11y-skills/audit/test-entries/axe-audit";
// tests/a11y/focus.spec.ts
import "@a11y-skills/audit/test-entries/focus-indicator-check";
// tests/a11y/reflow.spec.ts
import "@a11y-skills/audit/test-entries/reflow-check";
// tests/a11y/target-size.spec.ts
import "@a11y-skills/audit/test-entries/target-size-check";
// ...also: text-spacing-check, zoom-200-check, orientation-check,
// autocomplete-audit, time-limit-detector, auto-play-detection
import "@a11y-skills/audit/test-entries/text-spacing-check";
TEST_PAGE=https://example.com A11Y_OUTPUT_DIR=./a11y-results npx playwright test

Why not testMatch into node_modules? Playwright excludes node_modules from test collection, so pointing testMatch at **/node_modules/@a11y-skills/audit/dist/test-entries/*.js finds no tests. The one-line re-export specs above are the supported way to run the entries.

Result format

Every check returns — and saves as JSON — the same axe-style envelope:

interface AuditCheckResult<TDetails> {
  source: CheckSource;            // e.g. "reflow-check"
  url: string;
  timestamp: string;
  violations: NormalizedRuleResult[];   // confirmed findings
  incomplete: NormalizedRuleResult[];   // needs manual review
  passes: NormalizedRuleResult[];       // rules that ran and found nothing
  inapplicable: NormalizedRuleResult[]; // nothing to examine
  summary: { violationCount; incompleteCount; passCount; checkedNodes? };
  details: TDetails;              // check-specific evidence (measurements, screenshots, ...)
  disclaimer: { ... };
}

Each rule result is axe-shaped (id / impact / description / help / helpUrl / tags / nodes[], with nodes[].target / html / htmlTruncated / failureSummary). Custom rules are namespaced (a11y-skills/focus-visible, a11y-skills/target-size-minimum, ...) and tagged with accurate WCAG version/level tags (wcag2aa, wcag21aa, wcag22aa, wcag247-style SC tags).

Classification is conservative. A finding lands in violations only when the detection has no blind spot and no WCAG exception can apply (on-focus context change, text-spacing clipping, invalid autocomplete tokens). All other detections — reflow/zoom overflow, meta refresh, orientation lock, missing focus styles, undersized targets — are incomplete: treat that bucket as the manual-review queue, not as noise.

To combine several checks for the same page into one view:

import { mergeNormalizedResults } from "@a11y-skills/audit";

const merged = mergeNormalizedResults([axeResult, reflowResult, targetResult]);

mergeNormalizedResults throws on URL mismatches, deduplicates nodes by target + failureSummary, and resolves a rule appearing in several buckets by priority (violations > incomplete > passes > inapplicable). Identical selectors inside different frames or shadow roots are not distinguished.

Result types & schemas

import type { AxeAuditResult, FocusCheckResult } from "@a11y-skills/audit/schemas";
import { RESULT_SCHEMAS } from "@a11y-skills/audit/schemas";

RESULT_SCHEMAS maps each check id to a hand-written JSON Schema (draft 2020-12) for validating the *-result.json files at runtime. The normalization mappers (normalize*) and buildAuditResult are exported from the package root, so the buckets can be re-derived from a saved result's details at any time.

License

MIT