Resource library

QA How-To

Playwright Accessibility Automation Complete Guide (2026)

Use this playwright accessibility automation complete guide 2026 to build keyboard, name, live region, ARIA snapshot, and axe checks that run in CI today.

20 min read | 3,282 words

TL;DR

Build accessibility coverage as layers: semantic locators, keyboard and focus scenarios, accessible-name assertions, live-region checks, scoped ARIA snapshots, and axe scans. Automation catches repeatable regressions, while manual keyboard and screen-reader review covers usability and interpretation that rules cannot prove.

Key Takeaways

  • Use role and label locators to test the same semantic interface that assistive technology consumes.
  • Combine axe rules with keyboard, focus, accessible-name, live-region, and workflow assertions.
  • Scope axe scans to stable components when full-page results contain unrelated known issues.
  • Test focus movement and focus restoration explicitly instead of checking only that controls can be clicked.
  • Wait for user-visible live-region outcomes with web-first assertions, not arbitrary sleeps.
  • Treat suppressions as reviewed, owned exceptions with narrow rules and expiration criteria.
  • Keep automated checks in pull requests and reserve assistive-technology exploration for scheduled human review.

The playwright accessibility automation complete guide 2026 shows you how to build an accessibility suite that catches semantic, keyboard, focus, announcement, and rules-based regressions. You will use Playwright Test, its accessibility-aware locators and assertions, and the official axe Playwright integration. The result runs locally and in CI without pretending that automation replaces people who use assistive technology.

Accessibility automation is strongest when it verifies product behavior. A page can pass an automated scan while trapping keyboard focus, naming a button poorly, or announcing an update at the wrong time. Conversely, a single rule violation can matter greatly even when a mouse-driven happy path works. Build layers that answer different risk questions.

This pillar gives you a complete working pattern. Adapt the example routes and names to your application, keep the test mechanics, and use the linked focused tutorials when you need more depth.

TL;DR

Layer What it catches What it cannot prove
Semantic locators Missing or incorrect roles, labels, and names used by tests Whether every name is understandable
Keyboard and focus tests Unreachable controls, wrong focus movement, missing restoration Overall keyboard efficiency across the product
Live-region assertions Missing or late visible status and alert updates Exact output in every screen reader
ARIA snapshots Unexpected changes to a stable accessible subtree Standards compliance across the page
axe scans Programmatically detectable rule violations Complete accessibility or usability
Human review Context, comprehension, workflow quality, assistive-technology experience Fast regression coverage on every commit

Start with one critical workflow. Assert its semantics and keyboard behavior, scan its stable regions, then run the suite in a pull request. Triage every result as a product decision, not as test noise.

What You Will Build

You will build a TypeScript suite that can:

  • find controls through roles, labels, and accessible names;
  • walk a dialog with the keyboard and verify focus restoration;
  • detect missing or placeholder accessible names;
  • confirm that asynchronous status and error messages appear through live regions;
  • compare the accessible structure of a stable component;
  • scan a page or component with axe and attach readable results; and
  • publish an HTML report and failure artifacts from CI.

The sample application is assumed to run at http://127.0.0.1:3000. It has a /checkout page with a cart summary, an email field, a promotional-code form, and a dialog opened by an Edit shipping address button. Replace those routes and names with your product's interface.

Prerequisites

Use an actively supported Node.js LTS release. Create a project, install Playwright Test and the Playwright integration maintained for axe-core, then install Chromium:

mkdir playwright-a11y && cd playwright-a11y
npm init -y
npm install --save-dev @playwright/test @axe-core/playwright typescript
npx playwright install chromium

Use npx playwright install --with-deps chromium on a Linux CI runner that also needs browser system dependencies. Commit package-lock.json so developers and CI resolve the same dependency versions.

Add scripts to package.json:

{
  "scripts": {
    "test:a11y": "playwright test tests/accessibility",
    "test:a11y:ui": "playwright test tests/accessibility --ui",
    "report:a11y": "playwright show-report"
  }
}

Your application must expose stable test data and a deterministic way to reach each state. You also need permission to add or fix accessible names in the product. A test cannot compensate for missing semantics by hiding behind a CSS selector.

Step 1: Configure the playwright accessibility automation complete guide 2026 project

Create playwright.config.ts:

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests/accessibility',
  fullyParallel: true,
  forbidOnly: Boolean(process.env.CI),
  retries: process.env.CI ? 1 : 0,
  reporter: [
    ['list'],
    ['html', { outputFolder: 'playwright-report', open: 'never' }],
  ],
  use: {
    baseURL: process.env.BASE_URL ?? 'http://127.0.0.1:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },
  projects: [
    { name: 'chromium-a11y', use: { ...devices['Desktop Chrome'] } },
  ],
  webServer: {
    command: 'npm run dev',
    url: 'http://127.0.0.1:3000',
    reuseExistingServer: !process.env.CI,
    timeout: 120_000,
  },
});

One Chromium project is a sensible first gate because most accessibility checks inspect browser-exposed semantics rather than browser layout differences. Add Firefox or WebKit when your risk assessment identifies browser-specific keyboard, focus, or accessibility behavior. Do not multiply every rules scan across browsers without a reason.

trace: 'on-first-retry' captures actions, DOM snapshots, console messages, and network activity after an initial failure. It is useful when a status update never arrives or a dialog closes before focus moves. Screenshots supplement the trace, but they cannot show an accessible name or the active element reliably.

Verify the step: add tests/accessibility/smoke.spec.ts with import { test } from '@playwright/test'; test('smoke', async () => {});. Run npx playwright test --list. The output should list one test under chromium-a11y.

Step 2: Establish a semantic locator baseline

Use locators that reflect how users find controls. Create tests/accessibility/semantics.spec.ts:

import { test, expect } from '@playwright/test';

test('checkout exposes its primary controls', async ({ page }) => {
  await page.goto('/checkout');

  await expect(
    page.getByRole('heading', { name: 'Checkout', level: 1 }),
  ).toBeVisible();
  await expect(page.getByLabel('Email address')).toBeEditable();
  await expect(
    page.getByRole('button', { name: 'Apply promotional code' }),
  ).toBeEnabled();
  await expect(
    page.getByRole('region', { name: 'Order summary' }),
  ).toBeVisible();
});

getByRole resolves implicit and explicit roles and can filter by accessible name. getByLabel connects a form field to its associated label. These locators do two jobs: they interact like a user-facing test and fail when important semantic wiring disappears. They are usually preferable to deep CSS and XPath.

Do not conclude that a passing locator makes the page accessible. The name Button 4 may technically exist but remain meaningless. A heading may be visible while the heading order is confusing. Treat semantic locators as a strong baseline and add assertions for each workflow's actual risk.

When several controls share a valid name, scope through a landmark or row rather than calling .first(). Ambiguity may indicate that users also lack enough context. For wider locator strategy, read Playwright getByRole versus getByTestId.

Verify the step: run npx playwright test tests/accessibility/semantics.spec.ts. Then temporarily remove the email label's association in a local branch. The getByLabel assertion should fail instead of silently locating the field through CSS.

Step 3: Automate keyboard navigation and focus behavior

Keyboard coverage should exercise a user task, not count every Tab on a changing page. Verify the entry point, focus containment, action, and restoration for a modal dialog:

import { test, expect } from '@playwright/test';

test('shipping dialog manages keyboard focus', async ({ page }) => {
  await page.goto('/checkout');

  const trigger = page.getByRole('button', { name: 'Edit shipping address' });
  await trigger.focus();
  await expect(trigger).toBeFocused();

  await page.keyboard.press('Enter');
  const dialog = page.getByRole('dialog', { name: 'Shipping address' });
  await expect(dialog).toBeVisible();
  await expect(dialog.getByLabel('Street address')).toBeFocused();

  await page.keyboard.press('Shift+Tab');
  await expect(dialog.getByRole('button', { name: 'Cancel' })).toBeFocused();

  await page.keyboard.press('Escape');
  await expect(dialog).toBeHidden();
  await expect(trigger).toBeFocused();
});

Use locator.focus() only to place the test at a known starting point. Use page.keyboard for the behavior under test. This distinguishes keyboard support from mouse support and verifies the browser's active element through toBeFocused().

A correct dialog moves focus inside, prevents focus from escaping while open, closes through the documented keyboard interaction, and returns focus to the opener. The exact first and last focusable elements depend on the design. Assert the product contract rather than assuming every dialog has the same button order.

For page-level focus order, select representative transitions and landmarks. A complete tab count becomes brittle when optional navigation changes. The step-by-step Playwright keyboard and focus-order tutorial develops a maintainable focus map and shows how to diagnose traps.

Verify the step: run the test in headed mode with npx playwright test tests/accessibility/keyboard.spec.ts --headed. Watch focus enter the street field, wrap to Cancel, close on Escape, and return to the trigger. Remove the dialog's initial-focus logic locally and confirm the assertion fails.

Step 4: Detect missing and weak accessible names

A role locator can prove that a specific expected name exists. Add a broader component guard when a toolbar or form has several icon-only controls:

import { test, expect } from '@playwright/test';

test('cart controls have useful accessible names', async ({ page }) => {
  await page.goto('/checkout');

  const cart = page.getByRole('region', { name: 'Shopping cart' });
  const buttons = cart.getByRole('button');
  const count = await buttons.count();

  expect(count).toBeGreaterThan(0);
  for (let index = 0; index < count; index += 1) {
    const button = buttons.nth(index);
    const name = (await button.getAttribute('aria-label'))?.trim();
    const text = (await button.innerText()).trim();
    expect(name || text, `button ${index + 1} needs a name`).toBeTruthy();
  }

  await expect(
    cart.getByRole('button', { name: 'Remove Wireless Keyboard' }),
  ).toBeVisible();
});

The loop is intentionally a narrow guard for a known component, not a standards-complete accessible-name calculation. Native text, aria-label, and other referenced content can contribute to the computed name. Use expected getByRole locators for critical controls and axe for general name rules. Avoid evaluating a homegrown algorithm across the whole page.

Good names describe purpose in context. Remove may be ambiguous when several items exist, while Remove Wireless Keyboard identifies the target. Placeholder text is not a durable label, and a tooltip that appears only on hover does not necessarily name a control.

The tutorial for detecting missing accessible names with Playwright covers native controls, SVG icons, aria-labelledby, duplicate names, and diagnostics without confusing DOM attributes with the computed accessibility tree.

Verify the step: run the test, remove the icon button's visible text and aria-label, and run again. The component guard or expected role locator must fail with a control-specific message. Restore a meaningful product name rather than changing the test to accept an empty string.

Step 5: Validate live-region announcements through outcomes

Dynamic updates should be available without forcing the user to search the page. Playwright cannot certify the exact words spoken by every browser and screen-reader combination, but it can verify the live-region contract and the user-visible update.

import { test, expect } from '@playwright/test';

test('promotional code reports an asynchronous result', async ({ page }) => {
  await page.goto('/checkout');

  const status = page.getByRole('status');
  await expect(status).toHaveAttribute('aria-live', /polite|assertive/);

  await page.getByLabel('Promotional code').fill('SAVE10');
  await page.getByRole('button', { name: 'Apply promotional code' }).click();

  await expect(status).toHaveText('Promotional code applied. Total updated.');
  await expect(page.getByText('Discount')).toBeVisible();
});

test('invalid email exposes an alert', async ({ page }) => {
  await page.goto('/checkout');
  await page.getByLabel('Email address').fill('not-an-email');
  await page.getByRole('button', { name: 'Place order' }).click();

  await expect(page.getByRole('alert')).toContainText(
    'Enter a valid email address',
  );
});

A status role normally represents a polite status message, while alert represents urgent information. Do not add redundant or conflicting ARIA without understanding the native semantics. Keep the live-region element mounted when the application pattern requires assistive technology to observe later text changes.

Web-first assertions retry until the message arrives, so no waitForTimeout is needed. Assert the related visual or data outcome too. A success announcement without the discount is a false promise, and a discount without an announcement excludes some users.

Use the Playwright live-region announcement validation guide for repeated messages, atomic updates, error summaries, and browser-assisted manual verification.

Verify the step: delay the promotion response in a local stub. The status assertion should wait and pass. Remove the status text update, keep the visual discount, and confirm the test fails at the announcement assertion.

Step 6: Protect accessible structure with ARIA snapshots

ARIA snapshots describe a locator's accessible subtree in a concise YAML-like template. Use them for stable components where roles, names, and hierarchy form an intentional contract:

import { test, expect } from '@playwright/test';

test('order summary keeps its accessible structure', async ({ page }) => {
  await page.goto('/checkout');

  await expect(page.getByRole('region', { name: 'Order summary' }))
    .toMatchAriaSnapshot(`
      - region "Order summary":
        - heading "Order summary" [level=2]
        - text: Subtotal
        - text: Shipping
        - text: Total
        - button "Place order"
    `);
});

This assertion checks the accessibility tree exposed for the selected region. It is more meaningful than an HTML snapshot when the risk is a changed heading level, missing name, or incorrect role. Keep the scope small. A full-page tree usually contains changing totals, account names, dates, or feature flags that create noisy diffs.

Review snapshot changes like API contract changes. A new button may be correct, but its placement and name still deserve review. Never approve a generated update solely to make CI green. Use targeted behavioral assertions alongside the snapshot because a matching tree does not prove that clicking Place order works or that focus moves correctly.

Verify the step: run npx playwright test tests/accessibility/aria.spec.ts. Change the summary heading from level two to a styled div in a test branch. The snapshot should display a focused diff showing that the heading node disappeared.

Step 7: Add axe scans for pages and components

Create tests/accessibility/axe.spec.ts. Import AxeBuilder from @axe-core/playwright, analyze after the page reaches a stable state, and assert that no violations remain:

import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('checkout has no detectable accessibility violations', async ({
  page,
}, testInfo) => {
  await page.goto('/checkout');
  await expect(page.getByRole('heading', { name: 'Checkout' })).toBeVisible();

  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
    .analyze();

  await testInfo.attach('axe-results', {
    body: JSON.stringify(results.violations, null, 2),
    contentType: 'application/json',
  });
  expect(results.violations).toEqual([]);
});

test('cart component has no detectable violations', async ({ page }) => {
  await page.goto('/checkout');
  await expect(page.getByTestId('shopping-cart')).toBeVisible();

  const results = await new AxeBuilder({ page })
    .include('[data-testid="shopping-cart"]')
    .analyze();

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

Tags select rules associated with the listed WCAG conformance groups. Confirm the policy your organization intends to enforce, because tags are a technical filter, not a legal conclusion. The unfiltered component scan uses axe defaults. Both patterns use supported builder methods and return structured results containing rule IDs, impact, help text, and affected nodes.

Scan after loading, opening, or submitting the state you care about. A scan of the initial page cannot inspect a closed dialog or an error message that has not been rendered. Add state-specific tests for menus, modals, validation, and signed-in workflows.

When legacy violations block adoption, prefer a narrow component scan or a reviewed rule exclusion with an owner. Do not disable every rule in a category. The Playwright axe component scanning examples show inclusion, exclusion, state setup, attachments, and incremental rollout.

Verify the step: run the axe file and inspect the attached JSON in the HTML report. Introduce an unlabeled form field locally. Confirm the scan reports the relevant rule and affected node, then fix the application and return the result to an empty array.

Step 8: Run accessibility automation in CI

Add .github/workflows/accessibility.yml:

name: accessibility
on:
  pull_request:
  workflow_dispatch:

jobs:
  playwright-a11y:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npm run test:a11y
        env:
          CI: true
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: accessibility-report
          path: |
            playwright-report/
            test-results/
          retention-days: 7

Pin actions according to your organization's supply-chain policy. Major tags keep this sample readable, but a security-sensitive repository may require commit SHAs. Keep accessibility tests deterministic and use dedicated nonproduction data. Upload reports even on failure so developers can read axe attachments and traces.

Run fast component and critical-flow checks on pull requests. Put a broader page inventory and manual-assisted review on a schedule if runtime or environment setup is expensive. Both layers should use the same test source and issue ownership model. A violation that remains indefinitely in an ignored nightly report is not useful coverage.

Your failure policy should distinguish new regressions from documented debt without hiding either. Store approved exceptions in code review, name the exact rule and scope, link an issue, and define removal conditions. A broad catch block or snapshot overwrite makes the pipeline green while removing its value.

Verify the step: push a branch with one intentionally failing assertion. Confirm the job fails and uploads the HTML report and test results. Download the artifact, read the trace or axe attachment, restore the product behavior, and confirm the next run passes.

The Complete Series

Use these focused tutorials to deepen each major layer of this pillar:

The four guides turn each survey-level technique into a complete implementation. Start with the guide closest to your current defect pattern, then bring its checks back into the shared accessibility project.

Troubleshooting

Problem: getByRole cannot find a control that is visible -> Inspect its computed role and accessible name in browser developer tools. Fix missing native semantics, label association, or ARIA in the product. Do not replace the locator with CSS unless the element is genuinely nonsemantic.

Problem: a focus assertion passes locally but fails in CI -> Confirm that the same element is enabled and rendered, animation is complete, and no environment-specific banner enters the tab order. Use the trace to inspect the active action. Avoid adding a sleep, which only changes the timing guess.

Problem: axe reports known violations outside the component under test -> Scope the scan with include, fix the shared shell, or add a narrowly reviewed exclusion tied to an issue. Never discard the entire result array because one legacy area is noisy.

Problem: live-region text is visible but getByRole('status') fails -> Verify that the mounted message container has appropriate semantics and that the application updates its text after the user action. A visually styled message is not automatically a status region.

Problem: an ARIA snapshot changes on every run -> Select a smaller stable region and omit volatile branches from the template. Assert totals, dates, and account-specific text separately. Do not normalize away roles or names that carry accessibility meaning.

Problem: the axe scan runs before a dialog or error state exists -> Perform the user action, wait for the state through a web-first assertion, then call analyze(). Scans inspect the current DOM and cannot predict hidden future states.

Best Practices and Common Mistakes

Do organize tests around user journeys and states. Scan the initial checkout, the invalid form, the open dialog, and the successful update separately when each state contains distinct UI. Use role and label locators in regular functional tests too, so semantic regressions receive broad coverage rather than living in one special suite.

Do make failures actionable. Attach axe violations, name the component in test titles, retain a trace on retry, and keep assertions close to the behavior they explain. Review exceptions in source control with an owner and a linked remediation issue. Remove the exception when the issue closes.

Do not claim WCAG conformance from a green pipeline. Automated rules detect only issues that can be evaluated programmatically. Keyboard flow, zoom, reflow, content clarity, error recovery, and real assistive-technology use require deliberate human evaluation. Include disabled users and accessibility specialists in product research and testing where possible.

Avoid brittle tab counts, full-page ARIA snapshots, arbitrary timeouts, generic locator('button') selectors, and global rule suppression. Avoid testing implementation trivia such as a specific aria-label when the real contract is the computed accessible name. Prefer user-facing assertions, then use DOM attributes only when the attribute itself is the requirement.

Finally, treat accessibility defects like product defects. Give them severity based on blocked tasks, affected users, frequency, and workaround quality. A keyboard trap in checkout deserves different urgency from a redundant landmark on an internal prototype, even if both appear in the same report.

Where To Go Next

Choose one production-critical workflow and implement Steps 2, 3, 5, and 7. That combination verifies semantics, keyboard operation, dynamic feedback, and rule-based issues. Add an ARIA snapshot only for a stable component whose accessible hierarchy is worth protecting.

Work through the complete series above for deeper keyboard, naming, announcement, and component-scan patterns. Then connect these checks to your broader Playwright TypeScript framework from scratch and review accessibility testing with Playwright for a wider test strategy. If your suite is unstable, use Playwright trace viewer debugging before increasing timeouts.

Create a lightweight coverage inventory. For each critical page, record semantic smoke coverage, keyboard scenarios, dynamic announcements, scanned states, manual keyboard review, and assistive-technology review. The gaps become an explicit backlog instead of an assumption hidden behind one green scan.

Interview Questions and Answers

Q: Why is axe alone insufficient for accessibility testing?

Axe detects rules that can be evaluated from the current document and browser state. It cannot prove that a workflow is understandable, efficient with a keyboard, or usable with every assistive technology. Combine it with behavioral automation and human evaluation.

Q: Why prefer getByRole in accessibility-focused Playwright tests?

It queries the semantic role and accessible name exposed by the page. A test therefore fails when important user-facing semantics disappear. It still does not judge whether every existing name is clear or whether the whole workflow is usable.

Q: How do you test a modal dialog's focus management?

Focus the opener, activate it with the keyboard, and assert that focus enters the dialog at the intended control. Verify focus stays within the dialog, Escape behavior if supported, and restoration to the opener after close. Keep assertions tied to the design contract.

Q: What is the difference between an ARIA snapshot and an axe scan?

An ARIA snapshot compares an expected accessible subtree, so it detects contract changes in roles, names, and hierarchy. Axe evaluates a library of accessibility rules against the current page. They complement each other and neither proves complete conformance.

Q: How should a team handle known axe violations?

Fix high-impact issues first and roll coverage out by component or state. If an exception is necessary, scope it narrowly, record the rule and reason, link an owned issue, and define when it must be removed. Never suppress all violations globally.

Q: How can Playwright test live-region behavior?

Trigger the asynchronous action and use a web-first assertion against the status or alert region. Assert the related product outcome as well. Perform manual browser and screen-reader checks for exact announcement timing and phrasing.

Q: Which accessibility checks belong in pull requests?

Run deterministic semantic, keyboard, critical-state, and scoped axe checks that provide quick actionable feedback. Keep broader inventories and manual assistive-technology sessions on an appropriate schedule. New critical regressions should still block delivery.

Conclusion

This playwright accessibility automation complete guide 2026 gives you a practical layered system: semantic locators, keyboard and focus scenarios, accessible-name checks, live-region assertions, scoped ARIA snapshots, axe scans, and CI evidence. Each layer catches a different class of regression, and together they are much stronger than a single page scan.

Start with one critical workflow, force each check to fail once, and confirm the report explains the user impact. Keep manual keyboard and assistive-technology review in the quality plan, then expand automation state by state as defects and product risk guide you.

Interview Questions and Answers

Why is axe alone insufficient for accessibility testing?

Axe evaluates programmatically detectable rules against the current page state. It cannot prove that a workflow is understandable, efficient by keyboard, or usable with a particular assistive technology. I combine axe with behavioral automation and scheduled human evaluation.

Why use getByRole for accessibility-focused tests?

It finds elements through their semantic role and accessible name, which aligns the test with a user-facing contract. Missing semantics cause an actionable failure instead of being hidden by CSS. A passing role locator is a baseline, not proof that the entire experience is accessible.

How would you test focus management in a modal dialog?

I focus and activate the opener with the keyboard, assert that focus moves to the intended control inside, and verify that tab navigation remains in the dialog. I then close it through the documented keyboard action and assert that focus returns to the opener.

What is the difference between an ARIA snapshot and an axe scan?

An ARIA snapshot compares the accessible subtree with an expected semantic structure. Axe evaluates many rules against the rendered state and reports affected nodes. The snapshot protects a known contract, while axe detects broader rule violations.

How should known accessibility violations be managed in CI?

I prefer fixing them or adopting scans component by component. Any temporary exception should name the exact rule and scope, record the reason, link an owned issue, and have clear removal criteria. Broad suppression destroys the regression signal.

How do you automate live-region testing with Playwright?

I trigger the user action and use a web-first assertion against the expected status or alert. I also assert the corresponding visual or data outcome so the message cannot become a false promise. I use manual screen-reader checks for exact speech and timing.

Which accessibility tests should block a pull request?

Deterministic checks for critical semantics, keyboard workflows, dynamic feedback, and newly introduced axe violations should normally block. The policy should reflect user impact and provide actionable reports. Broad exploratory and assistive-technology reviews can run on a schedule but must feed owned defects back into delivery.

Frequently Asked Questions

Can Playwright automate accessibility testing?

Yes. Playwright can verify semantic locators, keyboard and focus behavior, dynamic status messages, and accessible-tree snapshots, and it can run axe through `@axe-core/playwright`. Automation finds repeatable regressions but does not replace manual accessibility and assistive-technology testing.

Does Playwright include axe-core?

No. Install `@axe-core/playwright` as a development dependency and import its `AxeBuilder`. The integration injects and runs axe-core against the current page state.

Should accessibility tests run in every browser?

Begin with one intentional browser project for rules and semantic checks. Add other engines for browser-specific keyboard, focus, or accessibility risks rather than duplicating every scan automatically.

How do I test accessible names in Playwright?

Use `getByRole` with the expected `name` for critical controls and `getByLabel` for labeled fields. Add axe scans for general name rules, and avoid treating the presence of one ARIA attribute as a complete accessible-name calculation.

Can Playwright verify screen-reader announcements?

Playwright can verify the live-region semantics, text update, and related user-visible outcome. It cannot guarantee identical speech across every browser and screen-reader combination, so perform targeted manual checks for exact announcement behavior.

What should I do with known axe violations?

Fix them or roll checks out through narrow component scopes. If an exception is temporarily necessary, document the exact rule and scope, assign an owner, link a remediation issue, and define removal criteria.

Do passing automated accessibility tests prove WCAG conformance?

No. Automated checks cover only programmatically detectable conditions and explicitly scripted behaviors. Conformance assessment also requires human judgment, complete scope, and testing of interactions and content that automation cannot evaluate.

Related Guides