Accessibility Audits
Run axe-core audits from PHP: WCAG, ARIA, contrast, and best-practice checks on real pages.
The playwright-php/accessibility package runs axe-core inside the page Playwright is driving and returns the violations as PHP value objects. It checks automated rules mapped to WCAG criteria, ARIA usage, color contrast, and best practices.
composer require --dev playwright-php/accessibility
Basic analysis
use Playwright\Accessibility\AxeBuilder;
$builder = new AxeBuilder($page);
$results = $builder->analyze();
if ($results->hasViolations()) {
foreach ($results->violations as $violation) {
echo "{$violation->id}: {$violation->help}\n";
}
}
PHPUnit integration
The AssertsAccessibility trait turns an audit into one assertion:
use Playwright\Accessibility\AssertsAccessibility;
final class HomepageTest extends TestCase
{
use AssertsAccessibility;
public function testPageIsAccessible(): void
{
$page->goto('https://example.com');
$this->assertIsAccessible($page);
}
}
Scoping and rules
// Audit one region only
$builder->within('#main-content')->analyze();
// Filter by WCAG level
$builder->withTags([WcagTag::WCAG_2_1_AA])->analyze();
// Disable specific rules
$builder->withoutRules([RuleId::COLOR_CONTRAST])->analyze();
Where it fits
Role-first locators already push pages toward accessible markup; the audit catches what locators cannot see: contrast, landmark structure, ARIA misuse. The Accessibility testing guide covers the testing strategy; this package is the tooling.
Recommended path
Start with one focused accessibility suite instead of adding audits to every browser test. Pick pages that represent real product risk: login, checkout, account settings, one dense form, and one modal or custom control.
Keep the flow explicit:
- navigate to the state the user can actually use;
- assert the visible state first;
- run the accessibility audit;
- keep the rule id and selector in the failure output.
This makes failures easier to triage. If the visible assertion fails, the page is not ready for an audit. If the visible assertion passes but axe fails, the page state is correct but the accessibility structure needs work.
Operational notes
Use within() to audit owned regions when the page contains third-party widgets. Use exclude() for known out-of-scope regions, but document why the exclusion exists. Disable rules only when the team has made a deliberate decision and the issue is tracked elsewhere.
In CI, upload the test report and browser artifacts on failure. Accessibility failures often need both the selector from axe and the screenshot or trace that shows the page state.
Adoption checklist
Before making accessibility audits mandatory, confirm:
- the audited pages have stable fixture data;
- the suite records the page state on failure;
- third-party widgets are scoped or excluded deliberately;
- rule disables are rare and documented;
- at least one keyboard flow covers critical interactions.
This matters because automated accessibility checks can be noisy when they are attached too broadly. A small, trusted suite creates better pressure than a large suite everybody reruns or ignores.
What this integration is not
It is not a replacement for semantic HTML, design review, assistive-technology testing, or keyboard usability checks. It is a fast automated layer around real browser pages.
Use it to catch regressions early. Use manual review and product testing to decide whether the whole task is accessible.
Troubleshooting
- No violations locally, violations in CI: compare viewport, color scheme, rendered fonts, and fixture data.
- Many duplicate failures: audit fewer states or scope the audit to the component under test.
- Rule is noisy on third-party content: exclude the third-party container and track it separately.
- Test is slow: keep axe audits in a focused suite rather than every scenario.
FAQ
Should a clean audit block release?
A clean audit is useful evidence, but not full sign-off. Critical flows still need keyboard and assistive-technology review.
Should violations be grouped by rule or by page?
Use both when reporting. Developers usually fix by component or page, while product quality tracking often groups by rule and severity.