a11y-web
RGAA 4.1.2 Accessibility Auditor — Framework Agnostic
Audit and fix accessibility issues in any web framework that renders HTML, following RGAA 4.1.2 (aligned with WCAG 2.1 AA). Static analysis only — no runtime behavior.
When to Use This Skill
Activate whenever:
- A developer writes or modifies a component or template (even without mentioning a11y)
- A developer asks to "review", "audit", or "check" a component
- A developer generates new component code from scratch
- Any
.jsx,.tsx,.vue,.svelte,.astro,.html,.htm,.erb,.njk,.hbsfile is in scope
The goal is accessibility by default, not as an afterthought.
Framework Syntax Differences
RGAA criteria are about the HTML output, not the framework syntax. Keep these differences in mind when writing fixes:
| Concept | React/JSX | Vue / Svelte / Astro / HTML |
|---|---|---|
| Label association | htmlFor="id" |
for="id" |
| CSS class | className="..." |
class="..." |
| Autocomplete | autoComplete="..." |
autocomplete="..." |
| ARIA attributes | aria-label, aria-hidden |
same in all frameworks |
| Event handlers | irrelevant (static only) | irrelevant (static only) |
When reporting a fix, always use the syntax of the framework being audited.
Modes
Audit Mode (default for review/check requests)
Scan files, report violations grouped by RGAA topic with severity, suggest fixes — do not apply them.
Fix Mode (for generate/write/create requests)
Build accessible code from the start, or apply fixes directly to existing files using Edit.
How to determine mode:
- "review", "check", "audit", "report" → Audit Mode
- "fix", "make accessible", "apply", "generate", "create", "write" → Fix Mode
- Writing new code from scratch → Fix Mode (build correctly from the start)
Workflow
Step 1 — Identify Scope and Detect Framework
# Discover component/template files
Glob: src/**/*.{jsx,tsx} # React
Glob: src/**/*.vue # Vue
Glob: src/**/*.svelte # Svelte
Glob: src/**/*.astro # Astro
Glob: **/*.{html,htm} # Plain HTML / Angular templates
Glob: **/*.{erb,njk,hbs} # Server-side templates
# Root layout / app shell — check for Topic 8 and landmarks
Glob: src/App.{jsx,tsx,vue,svelte}
Glob: src/app/layout.{jsx,tsx} # Next.js App Router
Glob: src/routes/+layout.svelte # SvelteKit
Glob: src/layouts/*.{astro,html,vue}
Detect framework by file extension and package.json presence:
.jsx/.tsx→ React / Next.js / Remix.vue→ Vue / Nuxt.svelte→ Svelte / SvelteKit.astro→ Astro.html+ Angular signals → Angular
Use the detected framework to format all fix suggestions with correct syntax.
Step 2 — Per-Topic Scan
Run checks in order. Collect all violations before reporting. For full criterion text and examples per framework, read references/rgaa-static-criteria.md.
Topic 1 — Images
# 1.1 img without alt attribute
Pattern: <img(?![^>]*\balt=)
Files: *.{jsx,tsx,vue,svelte,astro,html,htm,erb,njk,hbs}
# 1.2 Decorative img: alt="" but missing aria-hidden="true" or role="presentation"
Pattern: alt=""
→ On same element, verify aria-hidden="true" OR role="presentation" is present
# 1.1 SVG without accessible name
Pattern: <svg(?![^>]*aria-label)(?![^>]*aria-labelledby)
→ Check if a <title> child exists inside the SVG block
# 1.6/1.7 Complex images (charts, infographics)
Pattern: <img
→ Flag imgs appearing to be charts/diagrams without aria-describedby or figcaption
Topic 3 — Colors
# 3.1 Color-only information
Pattern: style=.*color
→ Flag for manual review: verify information is not conveyed by color alone
Always add a [MANUAL] note for RGAA 3.2 (contrast ratios — requires rendered output).
Topic 5 — Tables
# 5.3 Layout table without role="presentation"
Pattern: <table(?![^>]*role=)
# 5.4 Table without <caption>
Pattern: <table
→ Check for <caption> as first child
# 5.6/5.7 th without scope
Pattern: <th(?![^>]*\bscope=)
Topic 6 — Links
# 6.1 Empty link text
Pattern: <a\b[^>]*>\s*</a>
# 6.1 Ambiguous link text (case-insensitive)
Pattern: >click here<|>read more<|>learn more<|>here<
# 6.2 Image link: verify img has non-empty alt
Pattern: <a\b[^>]*><img
→ Verify the img has a non-empty alt describing the destination
Topic 8 — Mandatory Elements
# 8.3/8.4 html element without lang
Pattern: <html(?![^>]*\blang=)
Files: **/*.{html,htm,jsx,tsx,vue,svelte,astro}
# 8.5/8.6 Page title
Pattern: <title → in HTML files, verify non-empty
Pattern: <Helmet|<Head → React / Next.js
Pattern: <svelte:head → SvelteKit
Pattern: <head> → Astro/Vue layouts
→ Check for <title> inside
# 8.1 Charset
Pattern: <meta\s+charset
Files: **/*.{html,htm}
Topic 9 — Information Structure
# 9.1 Heading inventory
Pattern: <h[1-6]
→ Count h1 occurrences (flag if 0 or >1 in main layout)
→ List all levels found; flag skipped levels
# 9.3 Presentational elements
Pattern: <b>|<i>
→ Suggest <strong>/<em> if semantic meaning is intended
Topic 10 — Information Presentation
Cannot verify statically. Always add:
[MANUAL]for RGAA 10.1 (shape/size/position-only information)[MANUAL]for RGAA 10.4 (text resize to 200%)
Topic 11 — Forms
# 11.1 Input/select/textarea without label
Pattern: <input(?![^>]*type="hidden")(?![^>]*aria-label)(?![^>]*aria-labelledby)
Pattern: <select(?![^>]*aria-label)(?![^>]*aria-labelledby)
Pattern: <textarea(?![^>]*aria-label)(?![^>]*aria-labelledby)
→ Cross-check: does a <label for/htmlFor="id"> exist with matching id?
# 11.2 Placeholder without label
Pattern: placeholder=
→ Verify a <label> also exists for the same field
# 11.5/11.6 Radio/checkbox without fieldset+legend
Pattern: type="radio"|type="checkbox"
→ Check for wrapping <fieldset> with <legend>
# 11.10 Required field not indicated
Pattern: required
→ Verify visible text/symbol AND aria-required="true"
# 11.13 Personal data fields without autocomplete
Pattern: type="email"|type="tel"|name="email"|name="phone"|name="firstname"|name="lastname"
→ Verify autocomplete / autoComplete attribute present
Topic 12 — Navigation
# 12.6 Landmark presence
Pattern: <main
Pattern: <nav
Pattern: <header
Pattern: <footer
→ Flag if absent from app/layout component
# 12.6 Multiple nav without accessible name
Pattern: <nav(?![^>]*aria-label)(?![^>]*aria-labelledby)
→ Flag if more than one <nav> found without labels
# 12.7/12.8 Skip link
Pattern: skip|eviter|aller.au.contenu (case-insensitive)
→ Flag if absent, pointing to #main-content
Step 3 — Report (Audit Mode)
## RGAA Accessibility Audit — [filename or "Project"]
Framework: [React | Vue | Svelte | Astro | HTML | ...]
Date: YYYY-MM-DD
### Topic 1 — Images
[FAIL] RGAA 1.1 — src/components/Hero.tsx:14
<img src="..." /> is missing an alt attribute.
Fix: alt="Descriptive text" (informative) or alt="" aria-hidden="true" (decorative)
### Topic 3 — Colors
[MANUAL] RGAA 3.2 — Contrast ratios cannot be verified statically.
Action: Run axe-core, Lighthouse, or check via DevTools > Accessibility.
---
Summary: X violations (Y FAIL, Z WARN, W MANUAL)
Reference: https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/
Severity:
[FAIL]— Definite violation detectable in static code[WARN]— Likely violation; context verification needed[MANUAL]— Requires runtime or visual verification
Step 4 — Apply Fixes (Fix Mode)
For each [FAIL] and [WARN], apply the fix with Edit using the correct syntax for the detected framework. After editing, re-run the relevant Grep check to confirm.
Quick-Reference Fix Patterns
Syntax shown as: HTML / Vue / Svelte / Astro first, then React (JSX) variant if different.
Images
<!-- Informative image — all frameworks -->
<img src="chart.png" alt="Bar chart: revenue grew 12% in Q3" />
<!-- Decorative image — all frameworks -->
<img src="divider.svg" alt="" aria-hidden="true" />
<!-- Informative SVG — all frameworks -->
<svg aria-label="Bar chart: revenue grew 12%" role="img" viewBox="0 0 100 100">
<title>Bar chart: revenue grew 12%</title>
</svg>
<!-- Decorative SVG — all frameworks -->
<svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">...</svg>
Links
<!-- Ambiguous text — HTML/Vue/Svelte/Astro -->
<a href="/article/123" aria-label="Read full article: Paris Resort Review">Read more</a>
<!-- Image link — all frameworks -->
<a href="/home"><img src="logo.png" alt="Club Med — Home" /></a>
Forms — HTML / Vue / Svelte / Astro
<label for="email">Email address</label>
<input id="email" type="email" name="email" autocomplete="email" />
<!-- Required field -->
<label for="name">Full name <span aria-hidden="true">*</span></label>
<input id="name" type="text" required aria-required="true" />
<!-- Radio group -->
<fieldset>
<legend>Preferred contact method</legend>
<label><input type="radio" name="contact" value="email" /> Email</label>
<label><input type="radio" name="contact" value="phone" /> Phone</label>
</fieldset>
Forms — React (JSX differences)
// React uses htmlFor and autoComplete (camelCase)
<label htmlFor="email">Email address</label>
<input id="email" type="email" name="email" autoComplete="email" />
<label htmlFor="name">Full name <span aria-hidden="true">*</span></label>
<input id="name" type="text" required aria-required="true" />
Navigation landmarks — all frameworks
<!-- First element in DOM -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<header>
<nav aria-label="Main navigation">...</nav>
</header>
<main id="main-content">
<h1>Page title</h1>
</main>
<aside aria-label="Related resources">...</aside>
<footer>...</footer>
<!-- Multiple navs — each needs a unique aria-label -->
<nav aria-label="Main navigation">...</nav>
<nav aria-label="Footer links">...</nav>
Tables — all frameworks
<table>
<caption>Hotel rates by season — 2025</caption>
<thead>
<tr>
<th scope="col">Hotel</th>
<th scope="col">Low season</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Punta Cana</th>
<td>1 200€</td>
</tr>
</tbody>
</table>
Mandatory elements — title management per framework
<!-- Plain HTML -->
<html lang="fr">
<head>
<meta charset="UTF-8" />
<title>Page Title — Site Name</title>
</head>
// React — Next.js App Router
export const metadata = { title: 'Page Title — Site Name' };
// React — Next.js Pages Router / react-helmet
<Head><title>Page Title — Site Name</title></Head>
<!-- Vue / Nuxt -->
<Head><Title>Page Title — Site Name</Title></Head>
<!-- SvelteKit -->
<svelte:head><title>Page Title — Site Name</title></svelte:head>
<!-- Astro -->
<html lang="fr">
<head>
<meta charset="UTF-8" />
<title>Page Title — Site Name</title>
</head>
Limitations (Static Analysis)
| Criterion | Why it needs manual review |
|---|---|
| RGAA 3.1 | Color-only info requires visual inspection |
| RGAA 3.2 | Contrast ratios require rendered colors |
| RGAA 10.1 | Shape/position-only info requires visual review |
| RGAA 10.4 | Text resize to 200% requires browser interaction |
| RGAA 12.4 | Breadcrumb presence requires site-level context |
For runtime analysis, recommend: axe-core, Lighthouse, browser DevTools Accessibility panel, or Storybook a11y addon.
Reference
Full criterion details, examples per framework, and fix patterns:
→ references/rgaa-static-criteria.md
Official RGAA 4.1.2 specification: → https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/
More from clubmediterranee/ai-core
git-commit
Execute git commit with conventional commit message analysis, intelligent staging, and message generation. Use when user asks to commit changes, create a git commit, or mentions "/commit". Supports: (1) Auto-detecting type and scope from changes, (2) Generating conventional commit messages from diff, (3) Interactive commit with optional type/scope/description overrides, (4) Intelligent file staging for logical grouping
14clean-code
This skill embodies the principles of \"Clean Code\" by Robert C. Martin (Uncle Bob). Use it to transform \"code that works\" into \"code that is clean.\"
13agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction.
12skill-creator
Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
12react-best-practices
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
12typescript-advanced-types
Master TypeScript's advanced type system including generics, conditional types, mapped types, template literals, and utility types for building type-safe applications. Use when implementing complex type logic, creating reusable type utilities, or ensuring compile-time type safety in TypeScript projects.
12