Loading...
Loading...
Audit RGAA 4.1.2 accessibility on live pages using a sitemap or URL list. Uses Playwright to render each page, injects axe-core for automated checks, and runs custom DOM queries for RGAA-specific criteria. Triggers on: "audit the site accessibility", "check a11y on production", "run RGAA audit", "accessibility audit from sitemap", "check deployed pages for WCAG", "audit live site", "run axe on the site", or when the user provides a sitemap URL or a list of URLs to audit. Complements a11y-web (static) with runtime checks: real contrast ratios, rendered landmarks, focus order, dynamic content.
npx skill4agent add clubmediterranee/ai-core a11y-audita11y-webhttps://site.com/sitemap.xmla11y-web# Check Playwright
npx playwright --version
# Check axe-core (used via CDN injection — no local install needed)
# OR install locally:
npm install --save-dev axe-corebash skills/a11y-audit/scripts/setup.shscripts/setup.shhttps://example.com/pagehttps://example.com/sitemap.xml# Ensure Playwright browser is available
mcp__playwright__browser_install (if needed)WebFetch: GET [sitemap_url]<loc>// Sitemap parsing — extract URLs
const urls = xmlContent.match(/<loc>(.*?)<\/loc>/g)
.map(loc => loc.replace(/<\/?loc>/g, '').trim());<sitemap>browser_navigate: url
browser_wait_for: { state: "load" }[SKIP]// browser_evaluate — inject axe-core
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js';
document.head.appendChild(script);
await new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = reject;
setTimeout(reject, 5000);
});// browser_evaluate — run axe with WCAG 2.1 AA (covers most RGAA criteria)
const results = await axe.run(document, {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice']
}
});
return {
violations: results.violations,
passes: results.passes.length,
incomplete: results.incomplete.length
};// Fallback — inline axe-core from local install
// (only if npm install axe-core was run)// browser_evaluate — custom RGAA checks
const customChecks = {
// RGAA 8.3 — lang attribute value
langValue: document.documentElement.lang,
// RGAA 9.1 — heading hierarchy
headings: Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6'))
.map(h => ({ level: parseInt(h.tagName[1]), text: h.textContent.trim().slice(0, 60) })),
// RGAA 12.7 — skip link: first focusable element
firstFocusable: (() => {
const el = document.querySelector(
'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
return el ? { tag: el.tagName, href: el.getAttribute('href'), text: el.textContent.trim().slice(0, 60) } : null;
})(),
// RGAA 12.6 — landmark presence
landmarks: {
main: !!document.querySelector('main, [role="main"]'),
nav: document.querySelectorAll('nav, [role="navigation"]').length,
header: !!document.querySelector('header, [role="banner"]'),
footer: !!document.querySelector('footer, [role="contentinfo"]')
},
// RGAA 11.1 — inputs without labels
unlabeledInputs: Array.from(
document.querySelectorAll('input:not([type="hidden"]):not([aria-label]):not([aria-labelledby])')
).filter(input => {
const id = input.id;
return !id || !document.querySelector(`label[for="${id}"]`);
}).map(i => ({ type: i.type, name: i.name, id: i.id })),
// RGAA 8.5 — page title
title: document.title,
// RGAA 3.1 — inputs/spans with only color-based class names (heuristic)
colorOnlyIndicators: Array.from(
document.querySelectorAll('[class*="red"],[class*="green"],[class*="error"],[class*="success"]')
).filter(el => !el.textContent.trim()).length,
// RGAA 5.4 — tables without caption
tablesWithoutCaption: Array.from(document.querySelectorAll('table'))
.filter(t => !t.querySelector('caption') && t.getAttribute('role') !== 'presentation')
.length,
// RGAA 1.1 — images without alt
imgsWithoutAlt: Array.from(document.querySelectorAll('img:not([alt])'))
.map(i => i.src.split('/').pop())
};
return customChecks;browser_take_screenshot: { fullPage: false }--screenshotsbrowser_snapshot## Page: [URL]
Status: 200 OK | Audited at: YYYY-MM-DD HH:MM
### axe-core Violations
| RGAA | axe Rule | Severity | Element | Message |
|------|----------|----------|---------|---------|
| 3.2 | color-contrast | critical | `<p class="subtitle">` | Contrast ratio 2.8:1 (min 4.5:1) |
| 1.1 | image-alt | critical | `<img src="hero.jpg">` | Missing alt attribute |
| 12.7 | skip-link | moderate | — | No skip navigation link found |
### Custom RGAA Checks
- [PASS] RGAA 8.3 — lang="fr" ✓
- [FAIL] RGAA 9.1 — Heading hierarchy: h1 → h3 (h2 missing)
- [FAIL] RGAA 12.6 — No <main> landmark found
- [PASS] RGAA 12.7 — Skip link present: "Aller au contenu" → #main
- [FAIL] RGAA 11.1 — 2 unlabeled inputs: input[type=email], input[type=search]
### Summary
- axe-core violations: X (Y critical, Z serious, W moderate)
- Custom RGAA failures: N
- axe-core passes: P checks passed
- Incomplete (needs manual review): Icritical[FAIL]serious[FAIL]moderate[WARN]minor[WARN]# RGAA 4.1.2 Accessibility Audit Report
Site: [base URL]
Sitemap: [sitemap URL]
Date: YYYY-MM-DD
Pages audited: N / Total in sitemap
## Critical Issues (across all pages)
| RGAA | Issue | Occurrences | Pages affected |
|------|-------|-------------|----------------|
| 3.2 | Insufficient contrast | 47 | 12/20 pages |
| 1.1 | Missing alt on images | 23 | 8/20 pages |
| 12.6 | Missing <main> landmark | 3 | 2/20 pages |
## Per-Page Summary
| Page | Violations | Critical | Warnings | Status |
|------|-----------|----------|----------|--------|
| /home | 12 | 3 | 9 | ⚠ |
| /about | 0 | 0 | 0 | ✓ |
| /contact | 5 | 2 | 3 | ⚠ |
## Compliance Estimate
Estimated RGAA compliance rate: XX% (based on auditable criteria)
## Recommended Priority Actions
1. Fix contrast issues (affects X% of users, critical severity, XX occurrences)
2. Add alt text to images (XX occurrences on X pages)
3. Add <main> landmark to layout template (affects all pages)
## Limitations
- Interactive content (modals, tooltips, carousels) requires manual testing
- Authentication-protected pages were skipped
- Dynamic content loaded after interaction not audited
- Recommended: complement with manual screen reader testing (NVDA/JAWS/VoiceOver)
Reference: https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/Write: a11y-audit-report-YYYY-MM-DD.md| RGAA Criterion | axe-core Rule(s) |
|---|---|
| 1.1 — Image alt | |
| 1.2 — Decorative images | |
| 3.2 — Contrast | |
| 6.1 — Link names | |
| 6.2 — Image links | |
| 8.3 — HTML lang | |
| 8.4 — Lang valid | |
| 8.5 — Page title | |
| 9.1 — Headings | |
| 11.1 — Form labels | |
| 11.2 — Placeholder | |
| 11.5 — Fieldset | |
| 12.1 — Multiple nav | — (custom check) |
| 12.6 — Landmarks | |
| 12.7 — Skip link | |
| 12.8 — Skip link functional | |
| What cannot be audited automatically | Why |
|---|---|
| Authentication-protected pages | Requires login — provide session cookies if needed |
| Dynamic content (modals, live regions) | Requires interaction to trigger |
| PDF / Office documents | Not browser-renderable |
| Video captions (RGAA topic 4) | Requires human review |
| Touch target sizes | Requires device-specific testing |
| Screen reader announcements | Requires real AT (NVDA, JAWS, VoiceOver) |
a11y-web