← Back to recipes

Monitor website accessibility issues

complianceintermediateproven

The problem

Your website might have accessibility barriers but you don't know where they are. Manual WCAG checking is time-consuming and inconsistent. By the time someone complains they can't use your site, you've already excluded them. You need to catch issues before they affect users: missing alt text, poor colour contrast, broken keyboard navigation, unclear headings.

The solution

Use automated accessibility testing tools that scan your website against WCAG standards. They check every page for common issues: images without alt text, insufficient colour contrast, missing form labels, improper heading structure. You get a report of violations ranked by severity (critical, serious, moderate) with specific fixes. Run checks regularly to catch new issues as your site changes.

What you get

A list of accessibility violations on your website: 'Critical: 12 images missing alt text on /services page', 'Serious: Colour contrast ratio 3.5:1 on donation button (needs 4.5:1)', 'Moderate: Skip to content link missing'. Each issue shows: which page, what's wrong, WCAG guideline violated, how to fix it. You fix high-severity issues first and monitor over time.

Before you start

  • A website you can access (public or staging environment)
  • List of key pages to check (homepage, donation, contact, services)
  • Basic understanding of WCAG guidelines (or willingness to learn)
  • Someone responsible for fixing issues (developer or content editor)

When to use this

  • You suspect your website has accessibility barriers but don't know where
  • You're required to meet WCAG standards (Public Sector Bodies regulation, or Equality Act 2010 for most non-public-sector charities)
  • You want to catch accessibility issues before they affect users
  • You're redesigning your site and want to build accessibility in

When not to use this

  • You need 100% WCAG compliance certification - automated tools find 30-40% of issues, humans find the rest
  • You don't have capacity to fix issues - finding problems without fixing them wastes effort
  • Your website builder handles accessibility automatically (check this is actually true)
  • You're looking for a one-time check - accessibility needs ongoing monitoring as content changes

Steps

  1. 1

    Install a browser extension

    Start simple: install WAVE or axe DevTools in Chrome/Firefox. Free, works instantly, no setup. Visit your homepage and run the check. You'll see a list of issues colour-coded by severity. This gives you a feel for how many problems exist.

  2. 2

    Run your first scan

    Check your key pages: homepage, donation page, contact form, main service pages. For each page, the tool highlights violations: missing alt text here, colour contrast problem there, form label missing. Focus on severity rather than total numbers. Ten critical issues matter more than fifty minor ones.

  3. 3

    Understand common issues

    Most sites have similar problems: images without alt text, poor colour contrast (light grey on white), forms without labels, headings in wrong order (h1 to h4 with no h2). These aren't obscure - they're basics that exclude screen reader users, people with low vision, keyboard users.

  4. 4

    Prioritise by severity and impact

    Critical issues (screen reader can't access content): fix immediately. Serious issues (unusable for some people): fix within weeks. Moderate issues (best practice violations): fix when you can. Focus on high-traffic pages first - fixing the donation page matters more than a rarely-visited archive.

  5. 5

    Learn to fix common issues

    Alt text: describe what the image conveys (not 'image of person', but 'volunteer serving food at community lunch'). Colour contrast: use the tool's suggested colours that pass. Form labels: every input needs a visible label. Heading structure: use h1, h2, h3 in order, not based on size.

  6. 6

    Set up automated monitoring(optional)

    For ongoing checking, use Pa11y (command-line tool) to scan multiple pages automatically. Note: Steps 1-5 (browser extensions) are Beginner level; this step onwards requires Intermediate/Technical skills. Run it weekly and get a report of new issues. Catches problems when someone adds an unlabelled form or uploads an image without alt text. Prevention not just fixing.

  7. 7

    Combine with manual testing(optional)

    Automated tools find 30-40% of WCAG issues. They can't detect: unclear link text ('click here'), confusing page structure, missing skip links, keyboard traps. Test manually: can you navigate your whole site with just a keyboard (no mouse)? Try a screen reader. Better yet: test with disabled users.

  8. 8

    Track progress over time(optional)

    Record your baseline (e.g., 45 critical issues, 120 serious). Fix issues and re-scan monthly. Are numbers going down? Are new issues appearing as fast as you fix old ones? This tells you if your processes are working or if you need to train content editors.

Example code

Automated accessibility scanning with Pa11y

This scans multiple pages and generates a report of WCAG violations. Install first with: npm install pa11y. Adapt the URLs to your website's key pages.

// First run: npm install pa11y
const pa11y = require('pa11y');
const fs = require('fs');

// Pages to check
const urls = [
  'https://your-charity.org.uk/',
  'https://your-charity.org.uk/services',
  'https://your-charity.org.uk/donate',
  'https://your-charity.org.uk/contact',
  'https://your-charity.org.uk/get-involved'
];

// WCAG 2.1 AA standard (UK legal requirement)
const options = {
  standard: 'WCAG2AA',
  runners: ['axe'],  // Use axe-core rules
  includeNotices: false,
  includeWarnings: true
};

async function scanWebsite() {
  console.log(`Scanning ${urls.length} pages for accessibility issues...\n`);

  const allResults = [];
  let totalIssues = 0;

  for (const url of urls) {
    console.log(`Checking: ${url}`);

    try {
      const results = await pa11y(url, options);

      const pageResults = {
        url: url,
        issueCount: results.issues.length,
        issues: results.issues.map(issue => ({
          type: issue.type,  // error, warning, notice
          code: issue.code,  // WCAG code (e.g., WCAG2AA.Principle1.Guideline1_4.1_4_3.G18)
          message: issue.message,
          context: issue.context,  // HTML snippet
          selector: issue.selector  // CSS selector to find element
        }))
      };

      allResults.push(pageResults);
      totalIssues += results.issues.length;

      console.log(`  Found ${results.issues.length} issues`);

    } catch (error) {
      console.error(`  Error scanning ${url}: ${error.message}`);
    }
  }

  // Categorise by severity
  let errors = 0;
  let warnings = 0;

  allResults.forEach(page => {
    page.issues.forEach(issue => {
      if (issue.type === 'error') errors++;
      if (issue.type === 'warning') warnings++;
    });
  });

  console.log(`\n============================================================`);
  console.log('Summary:');
  console.log(`  Total issues: ${totalIssues}`);
  console.log(`  Errors (must fix): ${errors}`);
  console.log(`  Warnings (should fix): ${warnings}`);

  // Group by type of issue
  const issueTypes = {};
  allResults.forEach(page => {
    page.issues.forEach(issue => {
      const message = issue.message;
      if (!issueTypes[message]) {
        issueTypes[message] = { count: 0, examples: [] };
      }
      issueTypes[message].count++;
      if (issueTypes[message].examples.length < 3) {
        issueTypes[message].examples.push({
          url: page.url,
          context: issue.context
        });
      }
    });
  });

  // Show most common issues
  const sorted = Object.entries(issueTypes)
    .sort((a, b) => b[1].count - a[1].count)
    .slice(0, 10);

  console.log(`\nTop 10 most common issues:`);
  sorted.forEach(([message, data], index) => {
    console.log(`\n${index + 1}. ${message}`);
    console.log(`   Occurs ${data.count} times`);
    console.log(`   Example: ${data.examples[0].url}`);
    console.log(`   HTML: ${data.examples[0].context.substring(0, 100)}...`);
  });

  // Detailed results by page
  console.log(`\n============================================================`);
  console.log('Detailed results by page:\n');

  allResults.forEach(page => {
    console.log(`${page.url} (${page.issueCount} issues)`);

    // Group by severity
    const errors = page.issues.filter(i => i.type === 'error');
    const warnings = page.issues.filter(i => i.type === 'warning');

    if (errors.length > 0) {
      console.log(`  ERRORS (${errors.length}):`);
      errors.slice(0, 5).forEach(issue => {
        console.log(`    - ${issue.message}`);
        console.log(`      Selector: ${issue.selector}`);
      });
    }

    if (warnings.length > 0) {
      console.log(`  WARNINGS (${warnings.length}):`);
      warnings.slice(0, 3).forEach(issue => {
        console.log(`    - ${issue.message}`);
      });
    }

    console.log('');
  });

  // Save detailed report
  const report = {
    scanDate: new Date().toISOString(),
    summary: {
      totalPages: urls.length,
      totalIssues: totalIssues,
      errors: errors,
      warnings: warnings
    },
    pages: allResults,
    topIssues: sorted.map(([message, data]) => ({
      message,
      count: data.count,
      examples: data.examples
    }))
  };

  fs.writeFileSync('accessibility-report.json', JSON.stringify(report, null, 2));
  console.log('Detailed report saved to accessibility-report.json');

  console.log('\nNext steps:');
  console.log('1. Fix all errors (these violate WCAG standards)');
  console.log('2. Review warnings and fix where possible');
  console.log('3. Re-run this scan to verify fixes');
  console.log('4. Schedule regular scans (weekly/monthly)');
  console.log('5. Combine with manual testing for full compliance');
}

scanWebsite().catch(console.error);

Tools

axe DevToolsservice · freemium
Visit →
WAVEservice · free
Visit →
Pa11ylibrary · free · open source
Visit →

Resources

At a glance

Time to implement
hours
Setup cost
free
Ongoing cost
free
Cost trend
stable
Organisation size
small, medium, large
Target audience
operations-manager, data-analyst, ceo-trustees

Free tools handle most accessibility checking. Browser extensions work instantly. Command-line tools (Pa11y) can check multiple pages automatically. Paid tools (axe Pro) add more sophisticated checks and integration with development workflows. Finding issues: hours. Fixing them: days to weeks depending on technical complexity. Budget £500-2,000 for developer time if issues are complex or numerous.

Written by Edd Baldry

Last updated: 2026-01-10

Photo by Dima on Unsplash