Babysitter axe-accessibility

Deep integration with axe-core for automated accessibility testing. Execute accessibility scans, interpret WCAG violations, generate compliance reports, and integrate with Playwright/Cypress for comprehensive a11y testing.

install
source · Clone the upstream repo
git clone https://github.com/a5c-ai/babysitter
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/a5c-ai/babysitter "$T" && mkdir -p ~/.claude/skills && cp -r "$T/library/specializations/qa-testing-automation/skills/axe-accessibility" ~/.claude/skills/a5c-ai-babysitter-axe-accessibility && rm -rf "$T"
manifest: library/specializations/qa-testing-automation/skills/axe-accessibility/SKILL.md
source content

axe-accessibility

You are axe-accessibility - a specialized skill for axe-core accessibility testing integration, providing comprehensive WCAG compliance validation capabilities.

Overview

This skill enables AI-powered accessibility testing including:

  • Executing axe-core accessibility scans
  • Interpreting WCAG violations and impacts
  • Generating accessibility compliance reports
  • Configuring rule inclusion/exclusion
  • Integrating with Playwright/Cypress for automated a11y testing
  • Handling dynamic content scanning
  • Mapping violations to WCAG criteria
  • Providing remediation guidance

Prerequisites

  • Node.js environment for axe-core
  • Test automation framework (Playwright, Cypress, or Selenium)
  • Browser automation capability
  • Optional: axe-playwright or axe-cypress packages

Capabilities

1. Playwright Integration

Use axe-core with Playwright for accessibility testing:

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

test.describe('Accessibility Tests', () => {
  test('homepage should have no violations', async ({ page }) => {
    await page.goto('https://example.com');

    const accessibilityScanResults = await new AxeBuilder({ page }).analyze();

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

  test('homepage should have no critical violations', async ({ page }) => {
    await page.goto('https://example.com');

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

    const criticalViolations = accessibilityScanResults.violations
      .filter(v => v.impact === 'critical' || v.impact === 'serious');

    expect(criticalViolations).toEqual([]);
  });

  test('form page accessibility', async ({ page }) => {
    await page.goto('https://example.com/contact');

    // Wait for form to be fully loaded
    await page.waitForSelector('form');

    const results = await new AxeBuilder({ page })
      .include('form')
      .analyze();

    // Log violations for debugging
    if (results.violations.length > 0) {
      console.log('Violations found:', JSON.stringify(results.violations, null, 2));
    }

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

2. Cypress Integration

Use axe-core with Cypress:

// cypress/support/commands.js
import 'cypress-axe';

// cypress/e2e/accessibility.cy.js
describe('Accessibility Tests', () => {
  beforeEach(() => {
    cy.visit('/');
    cy.injectAxe();
  });

  it('has no detectable accessibility violations on load', () => {
    cy.checkA11y();
  });

  it('has no violations in main content', () => {
    cy.checkA11y('#main-content');
  });

  it('has no critical violations', () => {
    cy.checkA11y(null, {
      includedImpacts: ['critical', 'serious']
    });
  });

  it('logs violations for review', () => {
    cy.checkA11y(null, null, (violations) => {
      violations.forEach((violation) => {
        cy.log(`${violation.id}: ${violation.description}`);
        violation.nodes.forEach((node) => {
          cy.log(`  - ${node.target}`);
        });
      });
    }, true);
  });
});

3. Standalone axe-core Usage

Direct axe-core usage:

import { chromium } from 'playwright';
import axe from 'axe-core';

async function runAccessibilityAudit(url) {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto(url);

  // Inject axe-core
  await page.addScriptTag({ path: require.resolve('axe-core') });

  // Run analysis
  const results = await page.evaluate(async () => {
    return await axe.run();
  });

  await browser.close();
  return results;
}

// Usage
const results = await runAccessibilityAudit('https://example.com');
console.log(`Found ${results.violations.length} violations`);

4. WCAG Compliance Configuration

Configure axe for specific WCAG standards:

import AxeBuilder from '@axe-core/playwright';

// WCAG 2.1 Level AA compliance
const wcag21AAResults = await new AxeBuilder({ page })
  .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
  .analyze();

// WCAG 2.2 Level AA compliance
const wcag22AAResults = await new AxeBuilder({ page })
  .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
  .analyze();

// Section 508 compliance
const section508Results = await new AxeBuilder({ page })
  .withTags(['section508'])
  .analyze();

// Best practices (not strict compliance)
const bestPracticesResults = await new AxeBuilder({ page })
  .withTags(['best-practice'])
  .analyze();

5. Rule Configuration

Include/exclude specific rules:

import AxeBuilder from '@axe-core/playwright';

const results = await new AxeBuilder({ page })
  // Include only specific rules
  .withRules(['color-contrast', 'image-alt', 'label', 'link-name'])
  .analyze();

// Or exclude rules
const resultsExcluding = await new AxeBuilder({ page })
  .disableRules(['region', 'landmark-one-main'])
  .analyze();

// Focus on specific element
const formResults = await new AxeBuilder({ page })
  .include('#contact-form')
  .analyze();

// Exclude problematic areas
const mainResults = await new AxeBuilder({ page })
  .exclude('#third-party-widget')
  .analyze();

6. Violation Analysis

Analyze and categorize violations:

function analyzeViolations(results) {
  const summary = {
    total: results.violations.length,
    byImpact: {},
    byWCAG: {},
    criticalIssues: []
  };

  for (const violation of results.violations) {
    // Count by impact
    summary.byImpact[violation.impact] =
      (summary.byImpact[violation.impact] || 0) + violation.nodes.length;

    // Count by WCAG criteria
    for (const tag of violation.tags) {
      if (tag.startsWith('wcag')) {
        summary.byWCAG[tag] = (summary.byWCAG[tag] || 0) + 1;
      }
    }

    // Collect critical issues
    if (violation.impact === 'critical' || violation.impact === 'serious') {
      summary.criticalIssues.push({
        id: violation.id,
        impact: violation.impact,
        description: violation.description,
        help: violation.help,
        helpUrl: violation.helpUrl,
        affectedElements: violation.nodes.length
      });
    }
  }

  return summary;
}

7. Report Generation

Generate accessibility reports:

function generateA11yReport(results, format = 'html') {
  const report = {
    timestamp: new Date().toISOString(),
    url: results.url,
    summary: {
      violations: results.violations.length,
      passes: results.passes.length,
      incomplete: results.incomplete.length,
      inapplicable: results.inapplicable.length
    },
    violations: results.violations.map(v => ({
      id: v.id,
      impact: v.impact,
      description: v.description,
      help: v.help,
      helpUrl: v.helpUrl,
      wcagCriteria: v.tags.filter(t => t.startsWith('wcag')),
      nodes: v.nodes.map(n => ({
        target: n.target,
        html: n.html,
        failureSummary: n.failureSummary
      }))
    }))
  };

  if (format === 'html') {
    return generateHtmlReport(report);
  }

  return JSON.stringify(report, null, 2);
}

function generateHtmlReport(report) {
  return `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Accessibility Report</title>
  <style>
    body { font-family: -apple-system, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }
    .violation { border: 1px solid #ddd; margin: 10px 0; padding: 15px; border-radius: 4px; }
    .critical { border-left: 4px solid #d32f2f; }
    .serious { border-left: 4px solid #f57c00; }
    .moderate { border-left: 4px solid #fbc02d; }
    .minor { border-left: 4px solid #388e3c; }
    .impact { padding: 2px 8px; border-radius: 4px; font-size: 12px; }
    h1 { color: #333; }
    pre { background: #f5f5f5; padding: 10px; overflow-x: auto; }
  </style>
</head>
<body>
  <h1>Accessibility Report</h1>
  <p>URL: ${report.url}</p>
  <p>Generated: ${report.timestamp}</p>

  <h2>Summary</h2>
  <ul>
    <li>Violations: ${report.summary.violations}</li>
    <li>Passes: ${report.summary.passes}</li>
    <li>Incomplete: ${report.summary.incomplete}</li>
  </ul>

  <h2>Violations (${report.violations.length})</h2>
  ${report.violations.map(v => `
    <div class="violation ${v.impact}">
      <h3>${v.id} <span class="impact">${v.impact}</span></h3>
      <p>${v.description}</p>
      <p><strong>How to fix:</strong> ${v.help}</p>
      <p><a href="${v.helpUrl}" target="_blank">Learn more</a></p>
      <p><strong>WCAG:</strong> ${v.wcagCriteria.join(', ')}</p>
      <p><strong>Affected elements (${v.nodes.length}):</strong></p>
      ${v.nodes.map(n => `
        <pre>${n.target.join(' > ')}</pre>
        <p>${n.failureSummary}</p>
      `).join('')}
    </div>
  `).join('')}
</body>
</html>
  `;
}

8. CI/CD Integration

Integrate accessibility testing into pipelines:

# GitHub Actions
name: Accessibility Tests

on: [push, pull_request]

jobs:
  a11y:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

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

      - name: Run accessibility tests
        run: npm run test:a11y

      - name: Upload report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: accessibility-report
          path: accessibility-report.html

WCAG Criteria Reference

ImpactExamplesPriority
CriticalMissing alt text, missing form labelsP1 - Fix immediately
SeriousLow color contrast, missing ARIAP2 - Fix before release
ModerateSkip link issues, heading orderP3 - Fix soon
MinorRedundant ARIA, best practicesP4 - Backlog

Common Violations

Rule IDIssueFix
color-contrast
Insufficient color contrastAdjust foreground/background colors
image-alt
Missing image alt textAdd descriptive alt attribute
label
Form inputs without labelsAdd associated label elements
link-name
Links without accessible namesAdd link text or aria-label
button-name
Buttons without accessible namesAdd button text or aria-label
html-has-lang
Missing lang attributeAdd lang to html element
region
Content not in landmarkUse semantic elements or ARIA

MCP Server Integration

This skill can leverage the following MCP servers for enhanced capabilities:

ServerDescriptionInstallation
ronantakizawa/a11ymcpVerified A11y MCP ServerGitHub
priyankark/a11y-mcpaxe-core MCP integrationGitHub
PashaBoiko/playwright-axe-mcpPlaywright Accessibility MCPGlama
mcp-accessibility-scannerPlaywright + Axe-core scannerPlaybooks

Best Practices

  1. Test early - Include a11y tests in development workflow
  2. Automate continuously - Run a11y tests in CI/CD
  3. Focus on impact - Prioritize critical and serious issues
  4. Manual verification - Complement automated tests with manual review
  5. Screen reader testing - Test with actual assistive technology
  6. Keyboard navigation - Verify full keyboard accessibility
  7. Document exceptions - Track and justify any rule exclusions

Process Integration

This skill integrates with the following processes:

  • accessibility-testing.js
    - All phases of a11y testing
  • e2e-test-suite.js
    - A11y integration in E2E tests
  • quality-gates.js
    - A11y compliance gates
  • continuous-testing.js
    - CI/CD a11y integration

Output Format

When executing operations, provide structured output:

{
  "operation": "scan",
  "url": "https://example.com",
  "wcagLevel": "AA",
  "status": "completed",
  "summary": {
    "violations": 5,
    "passes": 42,
    "incomplete": 2,
    "inapplicable": 15
  },
  "criticalIssues": [
    {
      "id": "color-contrast",
      "impact": "serious",
      "count": 3,
      "wcag": "wcag143"
    }
  ],
  "reportPath": "./accessibility-report.html",
  "compliance": {
    "wcag2a": true,
    "wcag2aa": false,
    "section508": true
  }
}

Error Handling

  • Handle page load failures gracefully
  • Timeout handling for slow pages
  • Retry logic for dynamic content
  • Fallback for unsupported elements
  • Clear error messages for configuration issues

Constraints

  • Automated tests cannot catch all accessibility issues
  • Manual testing with assistive technology is essential
  • Some issues require human judgment
  • Document false positive handling
  • Consider user testing for complete validation