accessibility-audit
Skill4 starsChanged 11 days agoMITView on GitHub
Audit a web page, local file, or published artifact against WCAG 2.2 AA, then fix what it finds. Use whenever the user asks whether something is accessible, says "a11y", "WCAG", "ADA", "Section 508", "screen reader", "color contrast", "keyboard navigation", "tab order", or asks to run Lighthouse for accessibility - and also proactively right after building or restyling any page, because contrast and keyboard defects ship silently. Covers the theme matrix (one Lighthouse run only tests whichever theme the browser happens to be in), computed contrast sweeps, the checks Lighthouse does not run, and auditing published artifacts, which cannot be audited at their claude.ai URL.
55 other projects use a version of this.
Adopted by
Most recently taken up by
- ishandutta2007/Awesome-Claude-Skillstheir own version, first seen Nov 8, 2026
- ainurdev/mystical-assistanttheir own version, first seen Nov 8, 2026
- mzeleniuk/ClaudeVueProjectTemplatetheir own version, first seen Nov 8, 2026
- Smaui94/ClaudeSkillCollectiontheir own version, first seen Nov 8, 2026
- memi-design/design-skillstheir own version, first seen Nov 8, 2026
- Cure-Consulting-Group/ProductEngineeringSkillstheir own version, first seen Nov 4, 2026
Used with
Projects with it also use design-system-audit, flaky-test-triage and 9 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
jpoindexterSkilldesign-system-audit
Audit code for design system violations - grid spacing, tokens, existing components
Also in 4 projects that use this
SUDARSHANCHAUDHARISkillflaky-test-triage
Diagnoses inconsistent test results by separating product nondeterminism, automation defects, shared data, timing, infrastructure, environment, order, and concurrency effects.
Also in 3 projects that use this
tekumaraSkillapi-design-review
Reviews REST API design by inventorying routes, finding overlapping functionality, comparing payloads and pagination, and checking resource naming, URL structure, identifiers, errors, idempotency, and timestamps. Use when asked to review API routes, compare endpoints, find redundant or superseding operations, assess contract consistency, or run a REST API design review.
Also in 3 projects that use this
BufferoverflovvSkillobservability-setup
Add production observability to a FastMCP server: request IDs, slow-request logging, structured log fields, and optional Prometheus metrics. Use when: hardening logging for production, adding request tracing or correlation IDs, setting up slow-request alerts, adding a /metrics endpoint, or improving structured log output beyond the base middleware.
Also in 3 projects that use this
mmondoraSkillinfrastructure-as-code
Infrastructure as Code with Terraform and Pulumi. State management, modularity, drift detection, secrets handling. Use when provisioning cloud resources, writing Terraform/Pulumi modules, or managing infrastructure configuration.
Also in 3 projects that use this
OpenAISkilldesign-qa
Internal prototype QA helper. Use only after a Product Design prototype, URL-to-code build, or image-to-code build has a source visual target and a rendered implementation to compare before handoff. Do not use for broad UX critique, design critique, product audits, or flow reviews; route those user-facing requests to audit.
Also in 3 projects that use this
Other versions55 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 55
- me2resh/apexyard★ 542same name
- github/replica★ 230same name
- cuellarfr/design-skills★ 94same name
- adityareddy-dev/deskstudio★ 43same name
- launchapp-dev/animus-cli★ 39same name
- kumaran-is/claude-code-onboarding★ 35same name
- 5throck/ai-workspace-standards★ 25same name
- Indium-AI-Labs/indium-agentkit★ 18same name
- Ionate-Technologies/indium-agentkit★ 17same name
- ishandutta2007/Awesome-Claude-Skills★ 16same name
45 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository10 other files it gives its agents.
The file
We split this skill into the rules it gives an agent
7 rules. 0 are common, 4 are distinctive, and 3 are very specific to this project.
0
Common
Most of the 46 edited versions we read keep these.
4
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 22
If a page has forms, modals, or heavy ARIA, run that skill too.
- Very specific: line 55
Run it with run_in_background: true, and kill it when finished.
- Distinctive: line 59
Instead, wrap your source file in the same skeleton the publisher applies and serve that locally:
- Distinctive: line 75
Never a single run. Minimum three:
- Very specific: line 154
Never read report.json raw.
- Very specific: line 195
If width reads 980 instead of 320, the emulation worked and the page has no <meta name="viewport">.
- Distinctive: line 251
If a script-detectable defect is missed, the skill has regressed.
“Common” is measured: kept by at least 6 in 10 of the 46 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Accessibility Audit
Audit a page, report findings with severity, fix on approval, then re-run to prove the score moved.
Relationship to a11y-debugging
The chrome-devtools plugin ships an a11y-debugging skill. It is good and this does not replace it. Split the work:
Use a11y-debugging for | Use this skill for |
| Reading the accessibility tree | The theme matrix |
| Form labels, orphaned inputs | Computed contrast sweeps |
| ARIA attributes and roles | Checks Lighthouse omits entirely |
| Focus traps in modals | Auditing published artifacts |
| Per-element tap-target checks | The fix-and-verify loop |
If a page has forms, modals, or heavy ARIA, run that skill too. Do not re-derive its material here.
The one rule that matters most
Lighthouse only ever audits the theme the browser is currently rendering. It does not test both. A theme-aware page audited once has been half audited, and the half you skipped is usually the worse one.
Two measured examples, both reproducible:
A real page scored 87 in dark mode with zero contrast findings. Forced to light, the same page scored 77 with 41 failing elements. The single run was not a smaller version of the truth. It was the wrong answer.
Worse, the score does not have to move at all. references/fixture.html scores 62 in both themes - and the contrast failures behind that identical number do not overlap at all:
| Elements Lighthouse flags | |
| Dark | p.dark-fail, p.fade-fail, span.status.good |
| Light | p.light-fail, p.fade-fail, and four <th> |
Fix everything the dark run reports and the page still has five contrast failures in light, still scoring 62. A stable score across themes is not evidence the themes are equally sound. Always run the matrix in Step 2.
Step 1 - Get an auditable URL
Three cases. Pick the right one before running anything.
A live site. Audit the URL directly. Nothing to set up.
A local file. Serve it over HTTP. Never audit file:// - Lighthouse behaves differently and some audits silently skip.
cd <dir> && python3 -m http.server 8731 --bind 127.0.0.1Run it with run_in_background: true, and kill it when finished.
A published artifact. This is the case people get wrong. Pointing Lighthouse at https://claude.ai/code/artifact/<uuid> audits the claude.ai wrapper page, not your content. The score is meaningless.
Instead, wrap your source file in the same skeleton the publisher applies and serve that locally:
<!doctype html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<style>*{margin:0;padding:0}</style></head>
<body>
<!-- your artifact source file, verbatim -->
</body></html>Build a second copy with <html lang="en">. You author body content, not the <html> tag, so html-has-lang is the publisher's to fix and it costs 7 points. Auditing both copies separates your score from theirs - report the with-lang number as your content's true score and name the gap.
Step 2 - Run the theme matrix
Never a single run. Minimum three:
| Run | Setup |
| Desktop light | emulate with colorScheme: "light", then lighthouse_audit |
| Desktop dark | emulate with colorScheme: "dark", then lighthouse_audit |
| Mobile | lighthouse_audit with device: "mobile" - reflow and tap-target rules only run here |
emulate persists across navigations, so set it once per theme and run everything for that theme before switching.
Running it from the CLI instead
lighthouse_audit needs the chrome-devtools MCP. When you do not have it - CI, a
git hook, a headless box - the CLI does the same job:
npx -y lighthouse@latest "$URL" \
--only-categories=accessibility \
--output=json --output-path=./report.json \
--chrome-flags="--headless=new --blink-settings=preferredColorScheme=1" \
--quietFeed report.json straight to the extractor in references/scripts.md.
The default theme is dark, and almost nobody expects that. A single
npx lighthouse run with no theme flag audits the dark half and reports a number
that reads like the whole answer. Measured on Lighthouse 13.4.1 with
--headless=new, against a page built to fail contrast only in dark:
--chrome-flags | Painted | Score |
| (none) | dark | 84 |
--blink-settings=preferredColorScheme=0 | dark | 84 |
--blink-settings=preferredColorScheme=1 | light | 100 |
--blink-settings=preferredColorScheme=2 or =3 | light | 100 |
--force-dark-mode | dark | 84 |
So the enum is 0=dark, 1=light, anything above clamps to light. Every dark run
painted the page's own @media (prefers-color-scheme: dark) colours, so these
set the real media query rather than Chrome's auto-darkening. Always pass the
flag explicitly, both themes, rather than leaning on a default that is not what
you would guess and is free to change.
This inverts the usual advice. lighthouse-ci#1022
is the canonical thread on testing dark mode, and it closed without a working
answer - Sec-CH-Prefers-Color-Scheme, --force-dark-mode and
--enable-features=WebContentsForceDark were all reported as no-ops. It was
written when dark was the hard one to reach. Today dark is free and light is
the one you have to ask for. Do not follow that thread's advice; measure instead.
WebContentsForceDark in particular is Chrome repainting a light page by
algorithm, which is not the dark theme your users see.
For a true 320px reflow run, the CLI takes the width directly, which is more reliable than resizing a window:
--screenEmulation.mobile --screenEmulation.width=320 \
--screenEmulation.height=800 --screenEmulation.deviceScaleFactor=2Confirm it landed rather than assuming - the report records what was used:
json.load(open("report.json"))["configSettings"]["screenEmulation"]["width"] # 320, not 412Keep the MCP for the matrix when you have it. The CLI cannot run script in
the page, so while it can prove the viewport it used, it cannot prove which
theme it actually painted - there you are trusting a flag. With emulate plus the contrast sweep you read matchMedia back in the
scheme field and catch an emulation call that silently did not take. Use the
CLI for CI, for viewport precision, and when there is no MCP; use the MCP when
you need the run to prove what it tested.
Gotchas that cost real time:
- A run reporting 0 across every category, with 0 passed and 0 failed, is a failed run, not a score. Nothing was measured. Check
runtimeErrorin the JSON and retry before you conclude anything. Live sites behind a WAF or CDN throw this: a real audit of a production WordPress site returnedERRORED_DOCUMENT_REQUESTwith a 403 on the first attempt and scored 96 on an immediate retry, with no change to anything. The distinction matters because a genuine 0 and a failed run look identical in the summary, and only one of them is worth reporting. lighthouse_auditrejectsoutputDirPathoutside the configured workspace roots. Omit it and take the temp path from the result, then copy the HTML report somewhere durable if you want to keep it.- The result summary's "Failed: N" counts every category, not just accessibility. Read the JSON for the real accessibility failures.
- Never read
report.jsonraw. It runs to hundreds of KB. Extract with the script inreferences/scripts.md.
Step 3 - Sweep contrast by computation
Lighthouse samples. It also cannot see a failure in the theme it did not render. Compute contrast yourself against what the browser actually painted.
Run the contrast sweep from references/scripts.md via evaluate_script, once per theme. It walks every text-bearing element, resolves the real effective background by climbing ancestors until it finds a non-transparent one, and applies the correct threshold for that element's computed size and weight (3:1 for large text, 4.5:1 otherwise).
It reports exact ratios, which is what you need to pick a replacement color.
It also composites opacity. opacity fades the painted text toward its background and leaves getComputedStyle(el).color completely unchanged, so a sweep that reads the computed colour reports a clean pass on text that fails. Measured on a page already scoring 100 in both themes: an opacity: .82 label put three light-theme cases under 4.5:1, worst 3.78, with the sweep still reporting zero failures. Check the alpha field on any finding - below 1 means the fade is part of the cause. This applies to your own fixes too: dimming a newly added label is the easiest way to introduce a failure while the tooling insists nothing changed.
Lighthouse's contrast audit (axe) also composites opacity, so neither tool is better at everything. The sweep adds exact ratios, every element instead of a sample, and whichever theme you point it at. When the two disagree, find out which is right before believing either.
If you are also authoring the page, check the palette before building. Contrast is far cheaper to fix in six token values than in 41 rendered elements. Run the token pre-check from references/scripts.md against your palette, in both themes, before writing components. Test each foreground against the darkest light surface and the lightest dark surface - those are the worst cases, and a token that clears white may still fail on a tinted panel.
Step 4 - Run what Lighthouse does not
These are real WCAG failures with no Lighthouse audit behind them. Run the structural check from references/scripts.md, then verify by hand:
- Keyboard access to scrollable regions. Any
overflow-x: autocontainer holding a wide table or code block is unreachable by keyboard withouttabindex="0". Lighthouse does not run axe'sscrollable-region-focusablerule at all. - Table header semantics.
thwithoutscope, and first-column cells marked up astdwhen they are functionally row headers. Lighthouse's table audits return "not applicable" and tell you nothing. - Color as the only signal. Status shown by red text alone, a colored rail with no text label, a chart series distinguished only by hue. Run the polarity probe from
references/scripts.md, then read what it surfaces. The trap is not the empty cell, it is the full one.+$1,000and+$130are both complete, specific, correctly formatted values, and on a real page the first was good news and the second was bad, distinguishable only by being green or red. Asking "what does a colorblind user lose here" answers "nothing, the numbers are right there" and you move on. Ask instead whether the text alone reveals which direction is better. A signed number, a bare percentage, a date, a name: none of them carry polarity. - Two colors encoding opposite states. Run the status-pair luminance check from
references/scripts.md. Contrast tools compare each color to its background; both can pass and still be the same brightness as each other, leaving hue as the only difference. Greyscale and color vision deficiency both erase hue. - Focus visibility. Tab through interactively. Anything focusable needs a visible
:focus-visiblestate, including containers you just gavetabindex="0". Reflow. At 320px wide, the page body must not scroll sideways. Wide content scrolls inside its own container. Use
emulateto get to 320px, notresize_page-resize_pagesets the browser window, which has a floor around 500px on macOS and silently gives you a 500px viewport while reporting success. Ask the page what it actually got:// set BOTH in one call - see below emulate({ viewport: "320x800x2,mobile,touch", colorScheme: "light" }) // then confirm, before trusting anything measured at this size evaluate_script(() => ({ width: innerWidth, scheme: matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light', sideways: document.documentElement.scrollWidth > document.documentElement.clientWidth }))A run that believes it tested 320 and actually tested 500 will pass a page that breaks on a real phone.
Each
emulatecall resets every setting it leaves out. Measured:emulate({colorScheme:"light"}), thenemulate({viewport:"320x800x2,mobile,touch"}), and the page read backscheme: "dark". The reflow run had silently changed theme, so it was a dark 320px run wearing a light label. It works the other way too: a later call with onlycolorSchemeputs the viewport back to the window's width. So pass every setting you need in each call, and readwidthandschemeback every time.If
widthreads 980 instead of 320, the emulation worked and the page has no<meta name="viewport">. Phones will show it as a shrunken desktop page. That is a finding, not a failed run.
Step 5 - Report
Group by severity, and state clearly which findings are yours to fix versus the host's. A score with an unexplained cap is not useful information.
Report per theme. "95 in both themes" and "95 light, 77 dark" are very different results.
If the page ships no prefers-color-scheme rules at all, say the matrix was confirmatory. Plenty of sites have one theme. Reporting three identical runs as though they were three independent findings overstates the work and buries the real ones. Say the site has a single theme, that you verified it rather than assumed it, and move on. The structural check reports prefersDarkSupported for exactly this call - but treat a false there as a prompt to confirm by running the other theme, never as permission to skip it.
Watch the denominator on mobile. Mobile runs fewer audits than desktop, so a one-point gap between them is usually arithmetic rather than a defect. Compare the failure lists, not the scores, before reporting a difference.
Step 6 - Fix, then verify
Apply fixes, then re-run the full matrix. A fix verified in one theme is not verified.
State the before and after numbers per theme. If a number did not move, say so rather than implying it did.
Red flags
| Thought | Reality |
| "Lighthouse says 95, we're good" | In which theme? Run the matrix. |
| "The contrast audit passed" | It only checked the rendered theme, and it samples. Sweep it. |
| "The sweep reported zero failures" | Does it composite opacity? A faded label fails while computed colour looks fine. |
| "I resized the window to 320px" | resize_page floors near 500 on macOS. Use emulate and read back innerWidth. |
"I ran npx lighthouse and it scored 95" | That was the dark theme. The CLI defaults to it. Pass preferredColorScheme both ways. |
| "The polarity count didn't move, so the probe is broken" | Or your label has no leading space and really is one word. Read an element's textContent. |
| "I'll audit the artifact URL" | That audits claude.ai's wrapper. Serve it locally. |
| "No tables or forms, so semantics are fine" | Scroll containers and heading order still fail silently. |
| "It's a static page, keyboard access is moot" | Scrollable regions are interactive whether you intended it or not. |
| "I'll read the Lighthouse JSON to find failures" | It is hundreds of KB. Use the extractor. |
| "Score went 77 to 95, done" | Re-run every theme. And name what caps the remaining 5. |
| "It scored 0, the page must be broken" | 0 with nothing passed and nothing failed means the run failed. Check runtimeError and retry. |
| "Desktop 96, mobile 95, so mobile is worse" | Mobile runs fewer audits. Compare failure lists, not scores. |
Verifying this skill still works
references/fixture.html has eleven planted defects and a manifest in its source comment. Serve it and run the full workflow against it.
Expected result. Ten of the eleven are caught by script:
| Found in | Defect |
| Light run only | .light-fail contrast, 2.75:1 |
| Dark run only | .dark-fail contrast, 2.03:1 |
| Both runs | no <main>, no lang, H1 H3 heading skip, missing alt |
| Both runs | scroll region with no tab stop and no label |
| Both runs | table with no caption and four th with no scope |
| Both runs | .fade-fail at 2.45:1 light and 3.71:1 dark - only if the sweep composites opacity |
| Focus probe | link with outline:none and no replacement |
Defect 11 catches a sweep that does not composite opacity. Its token is redefined for both themes and clears 4.5:1 on either ground at full strength, so getComputedStyle reports a passing colour in both runs. Only compositing the opacity reveals it. A run that finds defects 1 and 2 but reports the page clean of 11 has a sweep that skips opacity, which is the regression this defect exists to catch.
Defect 10 - state carried by color alone in the status column - has no definitive check. The polarity probe surfaces it as a candidate (span.status.good, text "caught", which does not reveal whether being caught is good or bad), alongside two false positives from .light-fail and .dark-fail, whose class names describe the planted defect rather than a UI state. Three candidates, one real. That ratio is the point: the probe generates leads, you decide. If a run reports the fixture clean on defect 10 without looking at the candidates, that is the failure the manual pass in Step 4 exists to prevent.
If a script-detectable defect is missed, the skill has regressed. Fix the skill, not the fixture.
References
references/scripts.md- contrast sweep, token pre-check, structural check, Lighthouse JSON extractorreferences/fixes.md- finding-to-fix patternsreferences/fixture.html- page with planted defects, for verifying this skill still works
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

