Skip to main content
Cypress AccessibilityPremium Solution

Accessibility feedback during local development

Accessibility fixes are easy to get subtly wrong. An aria-label added to a button can override the visible text a speech-recognition user says out loud. Correcting one heading level can break the outline further down the page. Reordering a form to fix focus order can strip a label off the field below it. In each case the rule you were working on starts passing and a different one starts failing, and you don't find out until the next CI run.

Recording a spec from your own machine closes that gap. One command produces the same Cypress Accessibility report your CI runs produce, processed in Cypress Cloud with the same App Quality Config and the same Axe Coreยฎ version. You can confirm a fix, spot the violation the fix introduced, and iterate before you write the commit message.

Record the specs you're working onโ€‹

Record only the specs that exercise the code you changed:

npx cypress run --record --key <record_key> --spec "cypress/e2e/checkout.cy.js"

Recording is what produces the report, so this has to be cypress run --record. Interactive mode (cypress open) never records a run to Cypress Cloud, so it never generates accessibility results.

When the run finishes, Cypress prints its URL at the end of the run summary:

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

Recorded Run: https://cloud.cypress.io/projects/7s5okt/runs/1042

Open that URL and go to the Accessibility tab.

caution

CYPRESS_RECORD_KEY must be set as an actual operating system environment variable (for example via export CYPRESS_RECORD_KEY=... or your CI provider's secrets). Cypress reads it directly from your shell or CI environment โ€” it is not read from cypress.env.json or the env block of your Cypress configuration, since those only populate test environment variables. If you don't want to set an environment variable, pass the key inline with the --key flag instead.

Nothing else is required.

The requirements are the same as for any recorded run, with one that catches people locally: reports need a Chromium-based or Electron browser, so a habit of running --browser firefox locally produces no report at all. See No accessibility report was generated for a run for the full list.

To stay in your editor for the whole loop rather than switching to the browser after each recording, configure Cypress Cloud MCP and ask your agent for the run's results. See Work with AI agents for prompt patterns.

When the report is readyโ€‹

Each spec's data starts processing as soon as that spec finishes uploading, but the run-level report is assembled only once Cypress Cloud marks the run complete.

For grouped and parallelized runs, completion waits out the project's Run Completion Delay, a grace period (60 seconds by default) that lets late machines join the run before it closes. Report finalization waits with it, so on a CI run the accessibility report can trail the last machine by that much.

A single local run without --group or --parallel skips the delay entirely. Cypress Cloud completes it as soon as its specs are done, which is why the loop on this page stays fast. You inherit the delay only when you reproduce a CI invocation locally with grouping or parallelization. If you do, shorten the delay in project settings or close the run yourself with the Run Completion API.

Compare two local runs to prove a fix landedโ€‹

Seeing zero violations for the rule you fixed is weaker evidence than seeing that rule move from failing to resolved. Branch Review gives you that, and it works just as well with two runs recorded from your own machine. Cypress reads your branch and commit from your local .git directory on every recorded run, whether or not it detects a CI provider, so local runs land on the correct branch and can be compared like any other run.

  1. Record the specs from your base branch first, before you start work, or by stashing your changes:

    npx cypress run --record --spec "cypress/e2e/checkout.cy.js"
  2. Make your fix, then record the same specs again from your branch.

  3. In Cypress Cloud, open Branch Review with your branch's run as the Changed run and the base branch's run as the Base run.

    The Branch Review header in Cypress Cloud, showing the base and feature branch labels with the Cypress Cloud run number selected for each side of the comparison

The Resolved elements section should list the rule you worked on in every view where it was failing, which is how you confirm the fix reached more than the one element you tested by hand. The New failed elements section is the more valuable half: it's where the aria-label that duplicates visible text shows up.

Recording the same specs in both runs is what makes the comparison trustworthy. Branch Review compares what each run actually saw, so a base run covering the whole suite and a changed run covering one spec produces a long list of "resolved" elements that only means those pages weren't visited.

caution

The score for a two-spec local run isn't comparable to the score for a full CI run. A run's accessibility score averages every snapshot in that run, so changing which specs ran changes the denominator. Compare rules and element counts between local runs, and leave score tracking to your full suite.

Make your configuration match localhostโ€‹

The one thing that may differ between a local run and a CI run is the URL your tests visit. viewFilters patterns that name a hostname match that hostname exactly, so a configuration written for a deployed environment silently stops applying when you run against http://localhost:3000. Pages you meant to exclude reappear, and the local report stops matching the one your team sees in CI.

Path-only patterns match any hostname, so they work in both places:

App Quality Config
{
"viewFilters": [
{
"pattern": "/auth/*",
"include": false,
"comment": "Hosted sign-in provider, out of scope. Path-only so it matches localhost and deployed environments alike"
}
]
}

When a rule has to name a hostname, add a profile for local runs and override just that setting:

App Quality Config
{
"viewFilters": [
{ "pattern": "https://app.example.com/*", "include": true },
{
"pattern": "*",
"include": false,
"comment": "Report only on our own application"
}
],
"profiles": [
{
"name": "aq-config-local",
"comment": "Local runs serve the app from localhost, so the hostname rules above never match",
"config": {
"viewFilters": [
{ "pattern": "http://localhost:*/*", "include": true },
{ "pattern": "*", "include": false }
]
}
}
]
}

Select the profile by tagging the run:

npx cypress run --record --tag "aq-config-local" --spec "cypress/e2e/checkout.cy.js"

A profile replaces each setting it defines rather than merging with it, so repeat any base rules the local run still needs. See profiles for how selection and overriding work.

Keep local runs from cluttering the projectโ€‹

Local runs land in the same project as your CI runs, and their reports are just as real. To keep them recognizable, tag them:

npx cypress run --record --tag "local" --spec "cypress/e2e/checkout.cy.js"

Tags are shown on the run and are filterable in Cypress Cloud, so a shared local tag lets anyone skip past them when scanning the run list.

Every recorded run also consumes test results from your plan, whether it came from CI or your laptop. That's an argument for narrow --spec runs, which you want anyway.

What a local run can't doโ€‹

The Results API is a CI tool and doesn't have a local equivalent. getAccessibilityResults() identifies the run to report on from the CI environment variables present when the run was recorded, so calling it from your machine fails with "It appears you are not running in CI." Keep policy enforcement in your pipeline and use Cypress Cloud, Branch Review, or Cloud MCP for local feedback.

See alsoโ€‹