Implement WCAG 2.1 AA / 2.2 accessibility: semantic HTML audit, ARIA implementation, keyboard navigation, screen reader testing, color contrast. Use when user asks to "implement accessibility", "fix a11y issues", "make WCAG compliant", or "audit for accessibility".
From sanpx claudepluginhub javimontano/jm-adk --plugin sovereign-architectThis skill is limited to using the following tools:
agents/accessibility-implementation-agent.mdagents/aria-implementation-agent.mdagents/screen-reader-tester-agent.mdevals/evals.jsonexamples/sample-output.mdprompts/use-case-prompts.mdreferences/body-of-knowledge.mdreferences/knowledge-graph.mmdreferences/state-of-the-art.mdImplement web accessibility to WCAG 2.1 AA standard (with WCAG 2.2 additions) through semantic HTML, correct ARIA usage, keyboard operability, and screen reader compatibility.
"ARIA is a repair tool, not a building material. Use semantic HTML first. Add ARIA only where HTML semantics are insufficient."
npx axe-core in CI against Playwright-rendered pages:
const { axe } = require('@axe-core/playwright');
const results = await axe(page);
expect(results.violations).toHaveLength(0);
npx eslint-plugin-jsx-a11y if React project — catches compile-time issues.[HECHO] + tool name and severity (critical/serious/moderate/minor).<h1> per page, no skipped levels (h1→h3 without h2).<header>, <nav>, <main>, <footer>, <aside>, <section> with labels.<button> (not <div onclick>)<a href> (not <button> or <div>)<input>, <select>, <textarea> (not custom styled divs)<th scope="col/row"> and <caption>.<ul>/<ol>/<li> not styled <div> elements.<img> has meaningful alt text (not "" for informative images, "" only for decorative).Use ARIA only when HTML is insufficient. Rule: first try to use the native HTML element.
For custom widgets, implement the correct ARIA role + required states + keyboard pattern:
| Widget | Role | Key States | Keyboard Pattern |
|---|---|---|---|
| Custom checkbox | checkbox | aria-checked | Space to toggle |
| Accordion | button in <h3> | aria-expanded | Enter/Space |
| Tab panel | tablist/tab/tabpanel | aria-selected | Arrow keys navigate tabs |
| Modal dialog | dialog | aria-modal, aria-labelledby | Trap focus inside |
| Combobox | combobox | aria-expanded, aria-activedescendant | Arrow keys, Escape |
| Alert | alert | — (live region) | Auto-announced |
Add aria-label or aria-labelledby to landmark regions with multiple of same type.
Use aria-describedby for error messages linked to form fields.
Use aria-live="polite" for dynamic content updates (status messages, search results count).
Use aria-live="assertive" sparingly — only for critical, time-sensitive alerts.
tabindex > 0).tabindex="-1" is used only for programmatically focused elements.// Focus trap: keep focus inside modal
const focusable = modal.querySelectorAll(
'a[href], button:not([disabled]), input, select, textarea, [tabindex="0"]'
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
last.addEventListener('keydown', (e) => {
if (e.key === 'Tab' && !e.shiftKey) { e.preventDefault(); first.focus(); }
});
color-contrast-checker.app, coolors.co/contrast-checker, browser DevTools.--color-text-muted fails on --color-bg-surface, fix at token level.<h1> or new content heading.| Need | Correct ARIA | Wrong Approach |
|---|---|---|
| Toggle button | <button aria-pressed> | <div role="button"> with click handler |
| Navigation | <nav> (no ARIA needed) | <div role="navigation"> |
| Progress | role="progressbar" aria-valuenow aria-valuemin aria-valuemax | Text "Loading..." only |
| Error message | role="alert" or aria-describedby on field | Red text with no ARIA connection |
| Dynamic update | aria-live="polite" | No announcement |
| Criterion | Level | What Changed |
|---|---|---|
| 2.4.11 Focus Appearance | AA | Focus indicators must have 3:1 contrast |
| 2.4.12 Focus Appearance (Enhanced) | AAA | Larger focus indicator requirements |
| 2.5.7 Dragging Movements | AA | Draggable operations need non-drag alternative |
| 2.5.8 Target Size (Minimum) | AA | 24×24px minimum (up from 44×44 as guideline) |
| 3.2.6 Consistent Help | A | Help mechanism in same location across pages |
| 3.3.7 Redundant Entry | A | Don't ask users to re-enter data already provided |
| 3.3.8 Accessible Authentication | AA | No cognitive tests unless alternative provided |
role="button" on a <div> without keydown handler for Enter/Space is broken. Use <button>.aria-label on non-interactive elements — Labels are for interactive elements and landmarks. Adding them to paragraphs confuses screen readers.alt="" on informative images — Screen readers skip empty alt. Decorative images get alt="", informative images get descriptive alt text.tabindex="1" (or any positive value) — Creates a separate, confusing tab order. Use tabindex="0" or -1 only.display: none for skip links — They become unfocusable. Use position: absolute; left: -9999px and reveal on focus.aria-label="icon" — Icon buttons must describe action, not appearance: aria-label="Close dialog" not aria-label="X".accessibility-audit-{date}.md — Findings with severity and WCAG criteriontests/accessibility.spec.ts — axe-core automated test suitedocs/keyboard-navigation-map.md — Keyboard interaction documentationADR-A11Y-001.md — Accessibility architecture decisionsSearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.