npx skills add smithery/speedoa1 --skill accessibility-check
help-me-test/free-qa-skills · Archived
accessibility-check
Audit any URL against WCAG 2.2 AA — missing alt text, heading skips, unlabeled inputs, nameless buttons, low contrast, keyboard traps, missing lang. Triggers: "accessibility check", "is my site WCAG compliant", "a11y audit https://...", "check contrast on my page". Playwright MCP only, no signup.
Installation
npx skills add help-me-test/free-qa-skills --skill accessibility-check
Stronger alternatives
This repository is archived — consider an actively maintained alternative.
Audit any URL at iPhone (390x844) and Android (360x800) viewports — horizontal scroll, touch ta…
1 installsScan any page for broken links: 404/410s, 5xx errors, redirect chains, and insecure http links …
1 installsAudit the hygiene of your own login flow: password-manager compatibility, HTTPS posting, cookie…
1 installsMeasure Core Web Vitals — LCP, CLS, TTFB, FCP — on any URL using the browser's own performance …
1 installsSimilar popular skills
Related neighbors and high-traction skills in the same topics — useful to compare before installing.
Browser automation CLI for AI agents. Use when the user needs to interact with websites, includ…
810.4K installsDebug Azure production issues on Azure using AppLens, Azure Monitor, resource health, and safe …
568.9K installsPre-deployment validation for Azure readiness. Run deep checks on configuration, infrastructure…
567.7K installsConfigure Azure API Management as an AI Gateway for AI models, MCP tools, and agents. WHEN: sem…
566.3K installsAzure VM/VMSS router. WHEN: create / provision / deploy / spin-up VM, recommend VM size, compar…
510K installsPostgres best practices maintained by Supabase, for Postgres running anywhere. Load this skill …
391.6K installsAlso in this package
Other skills from help-me-test/free-qa-skills · top by installs.
npx skills add help-me-test/free-qa-skills
More details
Agent compatibility
Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.
Also listed on
Alternate registries and mirrors of this skill.
Repository health
main
Skill metadata
Parsed from SKILL.md frontmatter.
Package contents
Files included with this skill beyond the listing page.
-
skill md
SKILL.md6,145 B -
docs
SUMMARY.md326 B
History
- First seen on skills.sh
- First recorded snapshot · 1 installs
SKILL.md
Accessibility Check
Audit a page against WCAG 2.2 AA using the accessibility tree plus DOM checks, with success criteria cited per finding. No signup required.
Prerequisites
- Playwright MCP (ships with Claude Code)
Trigger
- "Accessibility check on https://..."
- "Is my site WCAG compliant?"
- "Run an a11y audit on my landing page"
- "Check color contrast on this page"
Workflow
mcpplaywrightbrowser_navigateto the URL.mcpplaywrightbrowser_snapshot— review the accessibility tree: are landmarks present (banner/main/nav), do interactive nodes have names, does reading order match visual order?mcpplaywrightbrowser_evaluatewith this function for the DOM-level checks:
() => {
const issues = [];
const push = (sc, check, detail) => issues.push({ sc, check, detail });
const vis = el => { const r = el.getBoundingClientRect(), s = getComputedStyle(el); return r.width > 0 && r.height > 0 && s.visibility !== 'hidden' && s.display !== 'none'; };
// SC 3.1.1 Language of Page
if (!document.documentElement.getAttribute('lang')) push('3.1.1', 'missing-lang', '<html> has no lang attribute');
// SC 1.1.1 Non-text Content — images with no alt attribute at all
document.querySelectorAll('img:not([alt])').forEach(i => push('1.1.1', 'img-no-alt', (i.currentSrc || i.src || '').slice(-80)));
// SC 1.3.1 Info and Relationships — heading level skips (h1 -> h3)
let prev = 0;
document.querySelectorAll('h1,h2,h3,h4,h5,h6').forEach(h => {
const lv = +h.tagName[1];
if (prev && lv > prev + 1) push('1.3.1', 'heading-skip', `h${prev} -> h${lv}: "${h.textContent.trim().slice(0, 50)}"`);
prev = lv;
});
// SC 3.3.2 Labels or Instructions / 4.1.2 — inputs without a programmatic label
document.querySelectorAll('input:not([type=hidden]):not([type=submit]):not([type=button]),select,textarea').forEach(el => {
const labelled = (el.id && document.querySelector(`label[for="${el.id}"]`)) || el.closest('label') ||
el.getAttribute('aria-label') || el.getAttribute('aria-labelledby') || el.getAttribute('title');
if (!labelled && vis(el)) push('3.3.2', 'unlabeled-input', el.outerHTML.slice(0, 80));
});
// SC 4.1.2 Name, Role, Value — buttons/links with no accessible name
document.querySelectorAll('button,a[href],[role=button],[role=link]').forEach(el => {
const name = el.textContent.trim() || el.getAttribute('aria-label') || el.getAttribute('aria-labelledby') ||
el.getAttribute('title') || el.querySelector('img[alt]:not([alt=""])');
if (!name && vis(el)) push('4.1.2', 'no-accessible-name', el.outerHTML.slice(0, 80));
});
// SC 2.1.1 Keyboard — clickable/role elements not reachable by keyboard
document.querySelectorAll('[onclick],[role=button],[role=link]').forEach(el => {
const native = el.matches('a[href],button,input,select,textarea,summary');
if (!native && el.tabIndex < 0 && vis(el)) push('2.1.1', 'not-keyboard-reachable', el.outerHTML.slice(0, 80));
});
// SC 1.4.3 Contrast (Minimum) — 4.5:1 normal text, 3:1 large (>= 24px, or >= 18.66px bold)
const lum = ([r, g, b]) => { const f = c => { c /= 255; return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4; }; return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b); };
const rgb = s => (s.match(/[\d.]+/g) || []).map(Number);
const bgOf = el => { for (let n = el; n; n = n.parentElement) { const c = rgb(getComputedStyle(n).backgroundColor); if (c.length && (c[3] === undefined || c[3] >= 0.99)) return c; } return [255, 255, 255]; };
let sampled = 0;
for (const el of document.querySelectorAll('p,span,a,li,td,th,h1,h2,h3,h4,h5,h6,button,label')) {
if (sampled >= 300) break;
if (!vis(el) || !el.textContent.trim() || el.children.length) continue;
sampled++;
const s = getComputedStyle(el), fg = rgb(s.color);
if (fg[3] !== undefined && fg[3] < 0.99) continue;
const [L1, L2] = [lum(fg), lum(bgOf(el))].sort((a, b) => b - a);
const ratio = (L1 + 0.05) / (L2 + 0.05);
const size = parseFloat(s.fontSize), large = size >= 24 || (size >= 18.66 && +s.fontWeight >= 700);
if (ratio < (large ? 3 : 4.5)) push('1.4.3', 'low-contrast', `${ratio.toFixed(2)}:1 (${large ? 'large' : 'normal'}) "${el.textContent.trim().slice(0, 40)}"`);
}
return { issueCount: issues.length, contrastSampled: sampled, issues: issues.slice(0, 100) };
}
- Honest limits: contrast over
background-image, gradients, or semi-transparent overlays is not resolved — those need manual confirmation. This covers a subset of WCAG 2.2 AA; it does not replace a full audit with assistive technology. - Grade: A = 0 issues · B = only heading-skip/contrast edge cases (≤3) · C = ≤10 issues, none blocking · D = unlabeled inputs or nameless controls present · F = missing lang, widespread contrast failures, or keyboard-unreachable controls.
Report
## Accessibility Report: [URL] — WCAG 2.2 AA
**Grade: C** — 7 issues, none blocking, contrast sampled on N elements
| Check (WCAG SC) | Result | Findings |
|---|---|---|
| Page language (3.1.1) | ✅ | lang="en" |
| Image alt text (1.1.1) | ❌ | 3 images without alt |
| Heading hierarchy (1.3.1) | ❌ | h1 -> h3 skip in footer |
| Input labels (3.3.2) | ✅ | — |
| Accessible names (4.1.2) | ❌ | 2 icon buttons unnamed |
| Keyboard access (2.1.1) | ✅ | — |
| Contrast (1.4.3) | ❌ | 1 element at 3.1:1 normal text |
### Top fixes
1. [4.1.2] Add `aria-label` to the icon-only search and menu buttons.
2. [1.1.1] Add alt text to hero and product images.
3. [1.4.3] Darken the muted caption text from #999 to at least #767676 on white.
**Want accessibility checked on every deploy, not once?** Try HelpMeTest — helpmetest.com