Universal Design AI Systems: Automating WCAG Audits & Fixing Violations with AI Agents
The Vision
Universal Design (universell utforming) means building digital products that work for everyone. In Norway, it's the law. But manual accessibility audits don't scale β 400+ municipalities, each with dozens of websites.
Two projects solve this together:
| Project | Repo | Role |
|---|---|---|
| uustreak | uustreak | Automated WCAG auditing at scale |
| wcag-skill | wcag-skill | AI agent skill for detecting & fixing violations |
Part 1: uustreak β WCAG Leaderboard for Norway
Background
Launched at ODIN 2025 (Norwegian Agency for Public Management and eGovernment). uustreak continuously audits 400+ Norwegian public sector websites and publishes a public leaderboard.
Architecture
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β Scheduler ββββββΆβ Playwright ββββββΆβ axe-core β
β (cron) β β (Chromium) β β (WCAG 2.1) β
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β β
βΌ βΌ
βββββββββββββββ βββββββββββββββ
β Screenshotsβ β Violations β
β + HTML β β + Impact β
βββββββββββββββ βββββββββββββββ
β β
βββββββββββ¬βββββββββββ
βΌ
βββββββββββββββββββ
β PostgreSQL β
β + GitHub Pages β
βββββββββββββββββββ
GitHub Actions Workflow
# .github/workflows/audit.yml
name: WCAG Audit
on:
schedule:
- cron: '0 2 * * 1' # Weekly Monday 02:00 UTC
workflow_dispatch:
jobs:
audit:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install deps
run: npm ci
- name: Install Playwright
run: npx playwright install --with-deps chromium
- name: Run audit
env:
SUPABASE_URL: $
SUPABASE_KEY: $
run: node scripts/audit-all.js
- name: Deploy to Pages
if: success()
uses: peaceiris/actions-gh-pages@v3
with:
github_token: $
publish_dir: ./publicImpact-Weighted Scoring
Not all violations are equal. uustreak calculates a 0-100 score:
score = 100 - Ξ£(impactWeight Γ nodeCount)
impactWeight: critical=10, serious=5, moderate=2, minor=0.5
This produces a fair leaderboard β a site with 1 critical error ranks lower than one with 50 minor errors.
Results (First 6 Months)
| Metric | Value |
|---|---|
| Websites audited | 427 |
| Total violations found | 12,847 |
| Critical violations | 1,203 |
| Orgs improving score | 67% |
| Avg score improvement | +12.3 points |
Part 2: wcag-skill β AI Agent for WCAG Fixes
The Problem
Accessibility audits are slow, manual, and repetitive. Developers fix the same violation patterns (missing alt text, low contrast, improper heading order) across projects. CI catches syntax errors β why not accessibility errors?
Solution: wcag-skill for Hermes Agent
wcag-skill is a Hermes Agent skill that:
- Scans any URL or local HTML with axe-core (Playwright)
- Analyzes violations with LLM reasoning (context-aware fixes)
- Proposes concrete code patches (unified diffs)
- Integrates into CI/CD as a
wcag-checkstep
Architecture
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β Input ββββββΆβ Scanner ββββββΆβ Analyzer β
β (URL/File) β β (axe-core) β β (LLM) β
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β β
βΌ βΌ
βββββββββββββββ βββββββββββββββ
β Violations β β Fix Plan β
β (JSON) β β (Diff) β
βββββββββββββββ βββββββββββββββ
β β
βββββββββββ¬βββββββββββ
βΌ
βββββββββββββββββββ
β Output: β
β - SARIF β
β - PR comments β
β - Local patchesβ
βββββββββββββββββββ
LLM-Powered Fix Generation
The skill doesn't just report violations β it proposes fixes:
# skills/wcag_skill/analyzer.py
class WCAGAnalyzer:
def __init__(self, llm_client):
self.llm = llm_client
async def propose_fix(self, violation: Violation, context: str) -> FixProposal:
prompt = f"""
WCAG Violation: {violation.rule_id} ({violation.impact})
Element: {violation.target}
HTML: {violation.html}
Surrounding Context: {context}
Provide a minimal fix as a unified diff. Only change what's necessary.
"""
response = await self.llm.complete(prompt, model="gpt-4o-mini")
return parse_diff(response)Example output for missing alt text:
--- a/src/components/Hero.astro
+++ b/src/components/Hero.astro
@@ -12,7 +12,7 @@
<div class="hero-content">
- <img src="/hero-bg.jpg" class="hero-image" />
+ <img src="/hero-bg.jpg" class="hero-image" alt="Sunrise over Oslo fjord" />
<h1>Welcome to Our Service</h1>
</div>
CI/CD Integration (GitHub Actions)
# .github/workflows/wcag.yml
name: WCAG Check
on: [pull_request]
jobs:
wcag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build
run: npm run build
- name: WCAG Skill
uses: turbolego/wcag-skill-action@v1
with:
path: ./dist
fail-on: "critical,serious"
comment-pr: true
env:
HERMES_API_KEY: $Supported Rules (WCAG 2.1 AA + AAA)
| Category | Rules | Auto-fixable |
|---|---|---|
| Perceivable | color-contrast, image-alt, text-spacing | β Most |
| Operable | keyboard, focus-order, focus-visible | β οΈ Partial |
| Understandable | label, heading-order, language | β Most |
| Robust | parse, name-role-value | β οΈ Partial |
Together: Audit β Fix β Prevent
uustreak (weekly audit) β wcag-skill (PR fixes) β Clean CI gate
- uustreak runs weekly, finds regressions
- wcag-skill runs on every PR, blocks new violations
- Developers get instant fix proposals in PR comments
- Leadership sees trends on public leaderboard
Links
- uustreak Repository: https://github.com/turbolego/uustreak (GPL-3.0)
- wcag-skill Repository: https://github.com/turbolego/wcag-skill (MIT, 1 β, 1 fork)
- Live Dashboard: https://turbolego.github.io/uustreak/
- ODIN 2025 Slides: https://turbolego.github.io/uustreak/odin-2025.pdf
- Hermes Skill Registry: https://hermes.nousresearch.com/skills/wcag-skill
Accessibility is not a feature β it's a requirement. Automation makes it enforceable.