Automated Accessibility Testing

Automated accessibility testing runs rules against rendered pages or components and reports conditions that are machine-checkable: an image without an alt attribute, a form control without an accessible name, text below the WCAG contrast threshold, an invalid ARIA attribute. It is fast, repeatable and belongs in every pipeline. It is also incomplete by design — W3C’s own evaluation overview states that no tool alone can determine whether a site meets accessibility standards, and that knowledgeable human evaluation is required.

This guide explains where the line between rules and judgement falls, what ACT rules are, how the common engines fit together, how to run checks at unit, component, end-to-end and CI level, and how to triage, baseline and report the results without letting a green scan pass for conformance.

What automated rules can decide — and what needs a human

A rule can decide a question when the answer is fully present in the DOM, the accessibility tree and the computed styles. It cannot decide questions about meaning, quality or experience. Many WCAG success criteria mix both: a tool can see that an alt attribute exists (part of 1.1.1) but not whether the text is an equivalent of the image. Treat any published claim that tools cover a fixed share of WCAG with care; what matters is knowing which of your checks are decisive and which only raise questions.

  • Usually decidable: missing alt attribute; form field or button with no accessible name; missing lang on <html>; invalid or misspelled ARIA roles, states and properties; required ARIA children or parents missing; text contrast for plain text on a solid background; zoom disabled via the viewport meta tag; empty headings and links.
  • Partly decidable (tool flags, human confirms): contrast of text over images or gradients; whether an image is decorative; whether a heading structure is sensible; target size where spacing exceptions may apply; whether a live region announces at the right time.
  • Needs human judgement: quality of alt text and labels; meaningful focus order and reading order; whether errors are described helpfully (3.3.3); keyboard operability of custom widgets beyond static attributes; captions and audio-description accuracy; motion that is essential or not; cognitive load and plain language; whether the whole task is achievable with a screen reader.
A rule decides only what is fully in the DOM, the accessibility tree and the computed styles; the rest needs a person, so a clean scan does not mean the page conforms.

Usually decidable

The answer is fully in the DOM, the accessibility tree and the computed styles.

Images:
Missing alt attribute
Structure:
Empty headings and links
Names and messages:
Form field or button with no accessible name
Typical tool signal:
Reported as a failure or a pass (e.g. violations and passes in axe-core)

Partly decidable

The tool flags it; a human confirms.

Images:
Whether an image is decorative
Structure:
Whether the heading structure is sensible
Names and messages:
Whether a live region announces at the right time
Typical tool signal:
Flagged for human review (e.g. incomplete in axe-core): send it to a person

Needs human judgement

A question of meaning, quality or experience.

Images:
Quality of the alt text
Structure:
Meaningful focus order and reading order
Names and messages:
Whether errors are described helpfully (3.3.3)
Typical tool signal:
Usually nothing: false negatives such as alt="image123" pass the rules

Examples from each group’s list above. Equal columns are not equal shares of WCAG; treat any claim of a fixed share with care.

ACT rules: a shared definition of a test

Two tools can report different results for the same page because they interpret a criterion differently. Accessibility Conformance Testing (ACT) rules address this: each rule is a precise, documented test procedure for part of a WCAG or ARIA requirement, with its applicability, expectations, assumptions and test cases. W3C lists them on the “All ACT Rules” page; they are developed by the ACT Rules Community Group and the ACT Task Force of the Accessibility Guidelines Working Group, and they are informative — not required for determining conformance.

Rules are marked proposed or approved; an approved rule has been implemented by at least one tool and reviewed by the relevant W3C working group. Implementations are listed per tool and can be automated, semi-automated, manual or linter-based. Examples of rule titles include “Image has non-empty accessible name” and “HTML page has lang attribute”. For a tester, ACT rules are a useful tie-breaker: when a tool’s result is disputed, compare it with the rule’s expectations and test cases.

Engines and tools, described neutrally

Most tools are a rules engine plus a way to run it. Knowing which engine sits underneath explains why two tools often agree.

  • axe-core — an open-source JavaScript accessibility rules engine that runs in a browser context. It returns violations, passes, incomplete results (items that need human review) and inapplicable rules, and tags rules by WCAG level. Many integrations wrap it.
  • Lighthouse accessibility audit — part of Google’s Lighthouse; its accessibility checks are based on axe-core and it produces a weighted score. A score of 100 means the audited rules passed, not that the page conforms.
  • Browser devtools accessibility panels — show the accessibility tree, computed name, role and states, and contrast information for a selected element. Indispensable for manual verification of automated findings.
  • pa11y — an open-source command-line tool for running automated accessibility checks against URLs, commonly scripted in CI.
  • Test-framework integrations — community and vendor packages add accessibility assertions to Playwright, Cypress, Jest and similar frameworks; static linters (for example for JSX) catch some issues in source code before anything renders.
  • Mobile — Android documents manual testing with TalkBack and automated checks through its testing frameworks and the Accessibility Scanner app; Apple’s XCTest provides accessibility audit support in recent Xcode versions.
  • Choosing — W3C’s Web Accessibility Evaluation Tools List lets you filter tools by guideline, type and environment; W3C states it does not endorse specific products.

Try it: HTML Accessibility Checker

Running checks in unit, component, E2E tests and CI

Put checks at the lowest level that can see the problem, then add broader scans higher up. Component and unit tests catch missing names and invalid ARIA on each variant of a design-system component, long before a page exists. Note that DOM emulations such as jsdom do not perform layout, and axe-core documents that its color-contrast rule does not work in JSDOM, so contrast and layout rules need a real browser. Querying elements by role and accessible name (the priority the Testing Library documentation recommends) is itself a lightweight accessibility check: if a test cannot find the button by its name, neither can a screen reader.

End-to-end tests run in real browsers and can scan pages in the states users see — after login, with a dialog open, with validation errors displayed. The Playwright documentation describes this pattern with an axe-based package; Cypress has equivalent community plugins. In CI, run the scans on every pull request for the pages and components the change touches, and a broader scheduled scan across the site.

// Example: Playwright + @axe-core/playwright, scanning a page in an error state
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('checkout form with errors has no WCAG A/AA violations', async ({ page }) => {
  await page.goto('/checkout');
  await page.getByRole('button', { name: 'Pay now' }).click(); // trigger validation
  await expect(page.getByRole('alert')).toBeVisible();

  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
    .exclude('#third-party-chat') // documented exclusion, tracked separately
    .analyze();

  expect(results.violations).toEqual([]);
});

Scan the states, not just the URLs

An engine checks what is rendered at the moment it runs. A page scanned on load says nothing about the menu that opens on click, the modal, the error summary, the empty state, the dark theme or the 320 px layout. Script the interactions that reveal those states and scan after each one. Include both themes if you ship them, at least one narrow viewport, and every locale with a different direction — an Arabic right-to-left page can have different defects from its English twin.

Triaging results: false positives and false negatives

Treat each finding as a hypothesis until verified in devtools or with assistive technology. False positives happen — contrast measured against the wrong background layer, a hidden element flagged, a legitimate ARIA pattern the engine does not model. False negatives are far more common and invisible: a meaningless alt such as “image123”, a label that names the wrong field, a custom dropdown with correct roles but no keyboard support all pass the rules.

Group findings by rule and component rather than by page: one missing name in a shared button component can produce hundreds of identical results. Map each group to the WCAG success criterion, set severity by user impact on the task (a blocked checkout outranks a decorative icon), and send items marked incomplete or needs review to a person rather than ignoring them. Note that WCAG 2.2 removed 4.1.1 Parsing, so markup-validity findings such as duplicate ids are not WCAG 2.2 failures in themselves — unless they break something else, for example a label or aria-labelledby that points to the wrong element.

Try it: AI Bug Severity Suggestion

Baselining legacy issues without hiding them

Switching on a zero-violations gate for a mature product usually fails every build. A baseline makes the gate useful: record the current violations (rule id plus a stable target such as component or selector), fail the build only on findings not in the baseline, and review the baseline file in pull requests like code. Each baselined item should reference a backlog ticket so it is fixed, not forgotten; the baseline should only ever shrink. Avoid blanket rule disables — exclude a specific region with a reason and an owner, as in the example above.

{
  "generated": "2026-09-28",
  "known": [
    { "rule": "color-contrast", "target": ".legacy-footer a", "ticket": "A11Y-214" },
    { "rule": "label", "target": "#old-search input", "ticket": "A11Y-219" }
  ]
}

Reporting automated results honestly

A useful report states the scope (pages, states, viewports, themes, locales), the engine and ruleset version, the WCAG version and level targeted, the counts by success criterion and severity, new versus baselined issues, and explicitly what was not tested automatically. Never label a clean automated run as “WCAG compliant”. Write individual defects the same way as manual ones — component, observable failure, criterion, affected users — and link the rule documentation.

Try it: AI Bug Title Generator

Combining automation, manual testing and WCAG-EM sampling

A sustainable programme layers the methods: automated rules on every change; manual checks for keyboard, visual presentation and forms on every feature; assistive-technology testing of key journeys with screen readers and zoom; and, where possible, sessions with people with disabilities, which W3C’s evaluation guidance encourages throughout development.

For a formal evaluation of a whole site, the W3C WCAG Evaluation Methodology (WCAG-EM 2.0) gives the structure: define the evaluation scope, explore the product, select a representative sample, evaluate the sample, and report the findings. The sample combines a structured selection — common views, essential functionality, different types of content and technologies, and every page of each complete process — with a randomly selected set. Automated scans are well suited to exploring the whole site and choosing that sample; the sample itself is then evaluated against every applicable success criterion, largely by hand.

Try it: AI QA Checklist Generator

Frequently asked questions

What percentage of WCAG can automated tools test?

There is no authoritative single figure, and it depends on the engine, the content and how you count partially automatable criteria. W3C’s position is that no tool alone can determine conformance. Plan for automation to catch a subset reliably and for manual and assistive-technology testing to cover the rest.

Does a Lighthouse accessibility score of 100 mean my site is accessible?

No. It means the audited automated rules passed on that page in that state. Keyboard operability, alt-text quality, error handling and screen-reader experience are outside what the score measures.

Should the CI build fail on accessibility violations?

Yes for new violations in the code a change touches, with a reviewed baseline for existing ones. Failing on everything at once usually leads teams to disable the check.

What do I do with “needs review” or incomplete results?

Route them to a person. They are cases the engine could not decide — for example text over a background image — and are often where real defects hide.

Can I run accessibility checks in unit tests?

Yes, for structure and names: rendering a component and running an accessibility engine or role-based queries catches missing labels and invalid ARIA early. Contrast and layout checks need a real browser, so keep them in component-in-browser or end-to-end tests.

Do automated tools work for native mobile apps?

There are platform tools — Android documents automated accessibility checks and the Accessibility Scanner app, and Apple’s XCTest includes accessibility audits in recent versions — but, as on the web, they complement testing with TalkBack and VoiceOver rather than replace it.

References

  • Evaluating Web Accessibility OverviewW3C Web Accessibility Initiative (WAI)W3C's entry point for accessibility evaluation: why to evaluate early and throughout development, and how manual checks, tools and user involvement fit together.
  • All ACT RulesW3C Web Accessibility Initiative (WAI)Accessibility Conformance Testing rules: precise, documented test procedures for WCAG and ARIA requirements, used to make automated and manual results consistent.
  • Web Content Accessibility Guidelines (WCAG) 2.2W3C · 2024The W3C Recommendation that defines the testable success criteria for accessible web content, organised under four principles and three conformance levels (A, AA, AAA).
  • WCAG Evaluation Methodology (WCAG-EM) 2.0W3C · 2026The W3C method for evaluating a whole website against WCAG: define scope, explore, select a representative sample, audit it and report.
  • Web Accessibility Evaluation Tools ListW3C Web Accessibility Initiative (WAI)A filterable list of accessibility evaluation tools by type, guideline and environment — W3C lists them without endorsing any.
  • Playwright DocumentationMicrosoft (Playwright)Official documentation for Playwright, an end-to-end test framework that drives Chromium, WebKit and Firefox.
  • Cypress DocumentationCypress.ioOfficial documentation for end-to-end and component testing in the browser with Cypress.
  • About Queries — Testing LibraryTesting LibraryQuery types and their priority — role and label before text, test ids last — so tests find elements the way users and assistive technology do.

Practice exercise

Triage an automated accessibility scan on a pull request

Your task

You are the QA engineer at Darb Bus, a coach ticket booking web app (fictional). Pull request #482 adds a new page with a seat map and a passenger details form. Every pull request runs axe-core in a Playwright end-to-end test, with a baseline for legacy issues. The first run failed; the developer then pushed commit a2, which made the job green, and the PR description now says the page is WCAG 2.2 AA compliant. Before the merge, you review the results, the check and the claim. The material below gives the context, the check, both runs, the output of an HTML validator, the baseline, the PR text and a markup extract of the page.

Work on paper with the guide: what rules can and cannot decide, triage, baselining, scanning states and honest reporting. Map each finding to a WCAG 2.2 success criterion and set severity by the impact on the booking task, on the team’s scale: Blocker, Critical, Major, Minor, Trivial. The contrast ratios are given, so you need no contrast tool. Allow 20–30 minutes.

Material
Darb Bus - coach ticket booking web app (fictional). English and Arabic UI, light
and dark theme. The team targets WCAG 2.2 level AA.
PR #482 "Seat map and passenger details" adds the page /booking/details: choose a
seat on the seat map (required), enter the passenger details (all fields required
except the emergency contact), then "Continue to payment".
Every pull request runs the E2E job a11y-e2e: axe-core through @axe-core/playwright.

1. The check after commit a2 (e2e/booking-details.a11y.spec.ts)

// imports as in the guide, plus the baseline file and the team helper newFindings(),
// which drops every finding whose rule + target pair is listed in a11y-baseline.json
test('booking details page is accessible', async ({ page }) => {
  await page.goto('/booking/details?trip=RUH-DMM-0715');
  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa'])
    // contrast is noisy here and is covered by the Jest unit tests (jsdom)
    .disableRules(['color-contrast'])
    .analyze();
  expect(newFindings(results.violations, baseline)).toEqual([]);
});

2. Job a11y-e2e, run 1 on commit a1 (before the disableRules line): FAILED
   Scanned: /booking/details on page load, 1280 x 720, English UI, light theme

   #   rule               elements     target                           baseline
   Violations
   V1  button-name        40 elements  .seat-map button.seat            NEW
   V2  duplicate-id-aria   2 elements  input#phone                      NEW
   V3  image-alt           1 element   .trip-summary img.operator-logo  NEW
   V4  color-contrast      1 element   button#continue                  NEW
                                       #FFFFFF on #4CAF50 = 2.78:1, 16px bold;
                                       expected 4.5:1
   V5  color-contrast      3 elements  .legacy-footer a                 KNOWN A11Y-88
   V6  link-name           1 element   footer a.social-x                KNOWN A11Y-91
   Incomplete (needs review)
   I1  color-contrast      1 element   .trip-banner h2
                                       "Riyadh to Dammam", white, 24px;
                                       background could not be determined
                                       (background image)

   Comment, developer: "V1 is a false positive: every seat button contains an SVG
   icon."
   Manual check of I1, Hana (devtools colour picker at the lightest point behind
   the heading): #5C5C5C, 6.7:1 against white. The banner photo changes with the
   destination.

3. Job markup-check on commit a1 (an HTML validator; its messages cite WCAG 2.1)

   duplicate id "phone"   2 elements   WCAG 4.1.1 Parsing - failure
   duplicate id "promo"   2 elements   WCAG 4.1.1 Parsing - failure

   No label or ARIA attribute refers to the id "promo": it is on two decorative
   div wrappers.

4. a11y-baseline.json on main
   Tickets: A11Y-88 Open; A11Y-91 Open; A11Y-95 Done on 2 Sep 2026 (the promo code
   field, on the payment page, was rebuilt with a visible label)

{
  "generated": "2026-08-14",
  "known": [
    { "rule": "color-contrast", "target": ".legacy-footer a", "ticket": "A11Y-88" },
    { "rule": "link-name", "target": "footer a.social-x", "ticket": "A11Y-91" },
    { "rule": "label", "target": "#promo-code", "ticket": "A11Y-95" },
    { "rule": "image-alt", "target": ".promo-banner img" }
  ]
}

5. Commit a2 "Make a11y job green" - run 2: PASSED, 0 new violations
   It adds the disableRules line shown in part 1, and these entries to
   a11y-baseline.json:

    { "rule": "button-name", "target": ".seat-map button.seat", "ticket": "A11Y-140" },
    { "rule": "duplicate-id-aria", "target": "input#phone", "ticket": "A11Y-141" },
    { "rule": "image-alt", "target": ".trip-summary img.operator-logo" }

6. PR description (draft)
   "Accessibility: axe reports 0 violations, and Lighthouse gives the home page an
   accessibility score of 100. The new booking page is WCAG 2.2 AA compliant."

7. Markup extract of /booking/details (English UI, after choosing a seat; one seat of
   each kind, 40 in all)

<div class="trip-summary">
  <img class="operator-logo" src="/img/op/17.png"> <span>Eastern Coaches</span>
  <span>Riyadh to Dammam, Fri 16 Oct 2026, 07:15</span>
</div>
<img src="/img/seat-legend.png" alt="legend">
<div class="seat-map">
  <button class="seat"><svg class="i-seat"><path d="..."/></svg></button>
  <button class="seat seat--taken"><svg class="i-seat"><path d="..."/></svg></button>
  <button class="seat seat--selected"><svg class="i-seat"><path d="..."/></svg></button>
</div>
<label for="dob">Passport number</label>
<input id="passport" name="passport">
<label for="passport">Date of birth</label>
<input id="dob" name="dob" placeholder="DD/MM/YYYY">
<span id="nat-label">Nationality</span>
<div role="combobox" aria-labelledby="nat-label" aria-expanded="false"
     aria-controls="nat-list" tabindex="0" onclick="toggleList()">Select</div>
<ul id="nat-list" role="listbox" hidden>
  <li role="option" onclick="pickNationality(this)">Saudi Arabia</li> ...
</ul>
<label for="phone">Mobile phone</label> <input id="phone" type="tel">
<label for="phone">Emergency contact phone</label> <input id="phone" type="tel">
<p class="error" hidden>Invalid input</p>
<button id="continue">Continue to payment</button>

   The legend image shows three seat icons with the words Available (white),
   Taken (grey) and Selected (green). A seat’s state is shown only by its fill
   colour (classes seat--taken, seat--selected). toggleList() shows or hides the
   list and updates aria-expanded; there are no key handlers. Each p.error sits
   under its field and is shown on submit.
Steps
  1. Triage run 1: for each violation and for the incomplete result, decide whether it is a true or a false positive and how you would verify it, group the results by component, name the WCAG 2.2 criterion and set the severity of the new ones. Decide on the developer’s comment about V1 and on both markup-check findings.
  2. Review commit a2 and the baseline: what do you accept or reject, and why? What should the baseline contain after this PR?
  3. Review the check itself: list what it cannot see, and write a better scan plan — test levels, tags, states, locales, viewports and themes.
  4. Read the markup extract for what the rules pass but a person must judge. For each finding give the criterion, a severity and the manual or assistive-technology check that finds it.
  5. Rewrite the accessibility statement in the PR description as an honest report.
Show the model answer
Model answer

V1 — true positive, and the developer’s comment is wrong. An svg with no title, no aria-label and no text gives a button no accessible name: the devtools accessibility pane shows role button with an empty name, and the ACT rule “Button has non-empty accessible name” expects a non-empty one. The 40 elements are one defect in the seat button component, not 40 defects. Criterion: 4.1.2 Name, Role, Value. Severity: Blocker — a screen reader user hears “button” 40 times, cannot tell the seats apart or know which are free, and cannot continue without a seat, so the booking can be completed only by guessing. Expected: each seat named by its number and state, for example “Seat 12, available”; the state itself is covered in the markup review below.

V2 — true positive, and worse than its rule name suggests. Both labels say for="phone", and a label points to the first element with that id, so the mobile phone field gets both labels and the emergency contact field gets none. Criterion: 4.1.2 Name, Role, Value, because that field has no accessible name. Severity: Major — the field is optional, but a screen reader user cannot tell which number goes where, and the wrong number can end up as the emergency contact. Fix: unique ids, for example emergency-phone. The markup-check line for phone is the same defect, so one ticket covers both. The duplicate id promo is not a WCAG 2.2 failure: WCAG 2.2 removed 4.1.1 Parsing, and no label or ARIA attribute refers to that id, so it breaks no name or relationship. Log it, if at all, as a low-priority code-quality item, and ask for the validator’s messages to stop citing 4.1.1 against a WCAG 2.2 target.

V3 — true positive, 1.1.1 Non-text Content. The operator’s name is already in the text next to the logo, so the right fix is alt="", which marks the image as decorative; an alt such as “Eastern Coaches logo” would make the name be read twice. Severity: Minor — a screen reader may fall back to the file name, which is noise, but the task is not affected.

V4 — true positive, 1.4.3 Contrast (Minimum). Bold text counts as large-scale only from 14 pt, about 18.7 px, so the 16 px bold label needs 4.5:1, and 2.78:1 fails. Severity: Major — the only way on to payment is hard to read for users with low vision, although the task can still be completed. Fix the colour (white on #2E7D32, for example, is about 5.1:1) and re-check it in a real browser in both themes. This is the real failure that commit a2 hid.

V5 and V6 are baselined legacy findings with open tickets (A11Y-88, A11Y-91): they do not fail this PR and this PR did not cause them, but they belong in the report as known issues. I1 is neither a violation nor a false positive: the engine could not decide and handed the question to a person. Hana’s measurement, 6.7:1 at the lightest point, passes: 24 px is large-scale text, which needs 3:1, and it would pass 4.5:1 as well. Record it as passed for this route, with the evidence. Because the photo changes with the destination, ask for the design to guarantee that contrast on any photo, for example with a dark overlay behind the text, or check the lightest photos too.

Commit a2 — reject it. The disableRules line switches off a whole rule: it hid V4, a real failure in this PR, and would hide every future contrast regression on the page. Its reason is wrong too: jsdom does not perform layout, and axe-core documents that its color-contrast rule does not work in JSDOM, so the Jest tests check no contrast at all. If one element really must be left out, exclude that element alone, with a reason, an owner and a ticket. The three new baseline entries hide violations in code this PR adds: a baseline records legacy issues and should only shrink, and the gate exists to fail exactly these; the image-alt entry does not even have a ticket. Fix V1 to V4 in this PR instead. The baseline on main needs two corrections as well. Remove the A11Y-95 entry: the issue is fixed, and an entry for it would hide its regression — confirm it first in the scheduled full-site scan, because this page’s scan does not reach the promo code field. And open a ticket for .promo-banner img. After the PR the baseline holds three entries — A11Y-88, A11Y-91 and the new ticket — and none from this PR.

The check scans one state: the page on load, in English, light theme, 1280 × 720. It never sees a selected seat, the open nationality list, the validation errors, the Arabic right-to-left page, a narrow viewport or the dark theme. Its tags, wcag2a and wcag2aa, run only the rules that axe-core files under WCAG 2.0 criteria; for a WCAG 2.2 AA target add wcag21a, wcag21aa and wcag22aa, as in the guide’s example — axe-core files its autocomplete check, for instance, under 1.3.5 Identify Input Purpose, a WCAG 2.1 criterion. A better plan: in the end-to-end test, scan (1) on load, (2) after selecting a seat, (3) with the nationality list open and (4) after “Continue to payment” with the required fields empty; repeat the four scans on the Arabic UI, at a 320 px wide viewport and in the dark theme; keep color-contrast on. Lower down, add a component test for the seat button that finds each seat by role and name, for example getByRole('button', { name: 'Seat 12, available' }): it fails today, runs in jsdom and catches a missing name long before a page exists. Higher up, keep a broader scheduled scan of the whole site.

What the rules pass and a person must judge: five findings in the markup. (1) The legend image has an alt, so image-alt passes, but “legend” is not an equivalent of what the image says: 1.1.1. Its words are also an image of text where real text would do: 1.4.5 Images of Text. State the three colours and their meanings in the text alternative, or better, make the legend real text, which fixes both. Severity: Major while it is the only explanation of the seat colours. Found by reviewing each image’s alt against its content. (2) A seat’s state is shown only by colour: 1.4.1 Use of Color; and the taken and selected states are not exposed to assistive technologies at all: 4.1.2. Fix it together with V1 — for example a taken seat as a disabled button, the selected seat with aria-pressed="true", and a visible difference other than colour. It is part of the seat map Blocker. Found by viewing the page in greyscale and by testing with a screen reader.

(3) The passport and date of birth labels are swapped. Each field has a label, so the label rule passes, but the passport field is announced as “Date of birth” and the other way round: 1.3.1 Info and Relationships and 2.5.3 Label in Name. Severity: Critical — screen reader and voice control users type into the wrong field; the best case is an error message, the worst is wrong passenger data in the booking. Found by comparing each field’s computed name in devtools with its visible label. (4) The nationality combobox has a correct role, name and state, but only an onclick handler: Enter, Space and the arrow keys do nothing, so keyboard users cannot choose a nationality: 2.1.1 Keyboard. Severity: Blocker for keyboard users, because the field is required. Only a keyboard test finds it. (5) “Invalid input” says neither what is wrong nor how to fix it, although the expected format is known: 3.3.3 Error Suggestion, for example “Enter the date of birth as DD/MM/YYYY”. Severity: Major. The scan never reached this state, and the wording needs a person anyway. Two more things to check: nothing ties each message to its field or announces it (no aria-describedby, no aria-invalid, no live region), so confirm with a screen reader in the error state that the user hears which field is wrong and why (3.3.1 Error Identification); and nothing in the markup marks the required fields (no “required” in the labels, no required attribute), so if the visual design does not mark them either, users find out only from the errors (3.3.2 Labels or Instructions).

The honest statement that replaces the draft: “Accessibility, automated part. Scope: /booking/details on page load only; English UI, light theme, 1280 × 720. Engine: axe-core (version from the lockfile) through @axe-core/playwright; tags wcag2a and wcag2aa, so WCAG 2.0 A and AA rules only, against a WCAG 2.2 AA target. Run 1: 4 new violations in code this PR adds — 4.1.2 seat buttons without names (40 elements, one component, Blocker), 4.1.2 duplicate id phone that leaves the emergency contact phone without a label (Major), 1.4.3 Continue button at 2.78:1 (Major), 1.1.1 operator logo without alt (Minor); 2 baselined findings (A11Y-88, A11Y-91); 1 incomplete result checked by hand and passed. Commit a2 is reverted, the four findings are fixed in this PR, and the gate stays on with color-contrast enabled. Not covered by automation: the other states, the Arabic UI, the dark theme, narrow viewports, keyboard operability, the correctness of labels and text alternatives, error messages and the screen reader journey; a manual review of the markup found five further defects (tickets linked). This is not a conformance claim.” Drop the Lighthouse sentence: a score for the home page says nothing about this page, and even there 100 only means that the audited rules passed in that state.

If your answers differ, check that each finding has its evidence, its criterion, a severity with a reason, and an owner or a ticket. Reasonable variations: Critical instead of Blocker for V1 or the nationality combobox if your team keeps Blocker for outages of a whole service, or a descriptive alt with the operator’s name if the name were not shown as text. Not acceptable: calling V1 a false positive, accepting any part of commit a2, treating the duplicate id promo as a WCAG 2.2 failure, closing I1 without a person’s check, or any claim that the page is WCAG compliant because a scan is green.

Your progress on this topic

Part of the QA Roadmap

Tools mentioned in this guide

Check HTML for common accessibility problems — missing alt text, unlabeled fields, nameless buttons and links, ARIA errors, headings, contrast — mapped to WCAG 2.2.

Validation New

Generate a tailored QA checklist (functional, UI, API, security, performance, accessibility, release) for any feature with Mutqan AI.

Test Generation AI

Generate clear, searchable bug titles from a messy description with Mutqan AI.

Bug Analysis AI

Get a suggested severity (Blocker / Critical / Major / Minor / Trivial) with justification from Mutqan AI.

Bug Analysis AI

How Mutqan Soft can help

More guides

More guides