Install
$ agentstack add skill-surfrrosa-claude-skills-a11y ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Accessibility Audit
Scan a project's source code for WCAG 2.1 accessibility violations. Optionally check live URLs for rendered accessibility issues. Can auto-fix safe categories.
Sample output
A successful run produces something like:
## Accessibility Audit — marketing-site
Score: 76/100 (C)
Stack: Next.js 16
### CRITICAL (blocks entire user groups)
1. Keyboard accessibility — src/components/NavMenu.tsx:42 — onClick handler on with no tabIndex or onKeyDown. Keyboard users can't open the menu. Fix: change to .
### SERIOUS
1. Forms — src/app/contact/page.tsx:18 — has placeholder but no . Fix: wrap in or add aria-label.
2. Images — src/components/Hero.tsx:24 — missing alt attribute.
### MODERATE
1. Heading hierarchy — src/app/about/page.tsx — page jumps from h1 to h3, skipping h2.
### Summary
- 1 critical, 2 serious, 1 moderate
- Top priority: NavMenu keyboard accessibility (blocks every keyboard user)
Arguments
The user may specify:
- No args: audit current working directory
- A project name or path (e.g.,
myapp,marketing-site) --fix: auto-fix safe categories (missing alt placeholders, missing ARIA labels on obvious elements, lang attribute, etc.)--live: also fetch live URLs and audit the rendered HTML--scope: limit to a specific directory or file
Parse the argument string. Examples:
/a11y-- audit current project, source only/a11y myapp-- audit myapp source/a11y myapp --live-- audit myapp source + live URL/a11y --fix-- audit and auto-fix current project/a11y --scope src/components-- audit only components directory
Project Registry
| Alias | Path | Live URL | |-------|------|----------| | myapp | (add your projects here) | (add your projects here) |
Process
Step 0: Detect the project
- Resolve path from alias table or cwd
- Detect the stack (Next.js, Astro, Flask/Jinja, static HTML, React Native)
- Identify template/component directories where HTML is generated:
- Next.js:
app/,src/app/,src/components/,components/ - Astro:
src/pages/,src/layouts/,src/components/ - Flask/Jinja:
src/templates/,templates/,src/**/templates/ - Static HTML: all
.htmlfiles in root and subdirectories - React Native / Expo: skip this audit entirely -- native apps use a different accessibility model (accessibilityLabel, accessibilityRole, etc.). Note this and suggest a native-specific check instead.
- Read
CLAUDE.mdfor any project-specific accessibility notes or conventions
Step 1: Structural / Semantic HTML
1a. Landmark regions
Search layouts and page templates for:
- Does the page have a `` element? (exactly one per page)
- Does the page have a `` element for primary navigation?
- If there are multiple `
elements, does each have anaria-labeloraria-labelledby` to distinguish them? - Is there a `
and` at the page level? - Are `
elements used with headings oraria-label(a` without an accessible name is meaningless to screen readers)?
Flag: Missing `, navigation without labels when multiple exist, ` without accessible name.
1b. Heading hierarchy
For each page/template:
- Is there exactly one ``?
- Do headings follow a logical order (no skipping from h1 to h3)?
- Are headings used for structure, not just styling? (A `` used because it "looks right" instead of using CSS is a smell)
Flag: Missing h1, skipped heading levels, multiple h1s on a single page.
1c. Lists
- Are groups of related links wrapped in `
or, not just stackedtags with` between them? - Are navigation menus using list markup?
Flag: Navigation links not in list elements.
Step 2: Interactive Elements
2a. Keyboard accessibility
Search all components/templates for interactive elements:
- Click handlers on non-interactive elements:
onClick/@click/on:clickon `,,,, or other non-interactive elements withoutroleandtabIndex`. These are invisible to keyboard users. - Missing keyboard equivalents:
onClickwithout correspondingonKeyDown/onKeyUphandler on non-button/non-link elements. - Positive tabindex:
tabindexvalues greater than 0 break natural tab order.tabindex="0"(add to tab order) andtabindex="-1"(programmatic focus only) are fine. - Autofocus abuse:
autoFocuson elements that aren't the primary action on the page (disorienting for screen reader users).
Flag: Click handlers on divs/spans without role+tabIndex, positive tabindex values.
2b. Buttons and links
- `
tags withouthref(or withhref="#"orhref="javascript:void(0)") -- these should be` elements. - `
elements without visible text oraria-label`. Icon-only buttons need labels. - Links that open in new tabs (
target="_blank") without indicating this to the user (addaria-labelsuffix or visible indicator). Also check forrel="noopener". - Empty links: `` with no text content or aria-label.
Flag: Fake links (href="#"), unlabeled icon buttons, empty links, new-tab links without indication.
2c. Forms
Search for `, , , `:
- Every input must have an associated `
(viafor/htmlForattribute matchingid, or wrapping the input).placeholder` is NOT a substitute for a label. - Required fields should have
aria-required="true"or therequiredattribute. - Error messages should be associated with their input via
aria-describedby. - Form groups (e.g., radio buttons) should use `
+`. - Submit buttons should have clear text (not just "Submit" -- what are you submitting?).
Flag: Inputs without labels, placeholder-only inputs, unassociated error messages.
Step 3: Images and Media
3a. Images
- Every `
must have analt` attribute. - Decorative images should have
alt=""(empty alt, not missing alt). - Informative images should have descriptive alt text, not filenames (
alt="IMG_2847.jpg") or generic text (alt="image",alt="photo",alt="icon"). - CSS background images that convey information need an alternative (text or aria-label on the container).
- SVG icons used inline: should have
aria-hidden="true"if decorative, orrole="img"+aria-labelif meaningful.
Flag: Missing alt, filename alt text, generic alt text, meaningful SVGs without labels.
3b. Media
If the project has ` or ` elements:
- Videos should have captions or a transcript
- Audio should have a transcript
- Media players should be keyboard-operable
Flag: Video without captions/transcript, audio without transcript.
Step 4: Color and Visual
4a. Color contrast
This is hard to check from source alone, but look for:
- Inline styles or CSS with text color + background color combinations that can be evaluated. Common failures:
- Light gray text on white (
#999 on #fff= 2.8:1, needs 4.5:1) - White text on bright colors (yellow, light green, light blue backgrounds)
- Tailwind classes: check for low-contrast combinations (e.g.,
text-gray-400 bg-white,text-white bg-yellow-300) - CSS custom properties: if the project defines color tokens/variables, check them against WCAG AA minimums:
- Normal text: 4.5:1 ratio minimum
- Large text (18px+ bold or 24px+): 3:1 ratio minimum
- UI components and graphical objects: 3:1 ratio minimum
Flag: Detectable low-contrast combinations. Note that full contrast audit requires --live mode.
4b. Color as sole indicator
- Links distinguished from body text only by color (no underline, no other visual indicator). Links within body text must have a non-color indicator.
- Form errors indicated only by turning the border red. Must also have text or icon.
- Status indicators (success/error/warning) using only color. Must also have text or icon.
Flag: Color-only link differentiation in body text, color-only error states.
4c. Motion and animation
- Check for CSS animations or transitions without
prefers-reduced-motionmedia query support. - Flag any
animationproperty used without a corresponding@media (prefers-reduced-motion: reduce)rule somewhere in the project's CSS. - Autoplay video or animated content without pause controls.
Flag: Animations without reduced-motion support, autoplay without controls.
Step 5: ARIA Usage
5a. ARIA done right
aria-hidden="true"on elements that contain focusable children (traps keyboard users in invisible elements).roleattribute with an invalid value.aria-labelledbyoraria-describedbypointing to anidthat doesn't exist in the same page.- Redundant ARIA:
role="button"on a `,role="link"on an,role="navigation"on a`. These are already implicit. aria-labelon elements that don't support it (e.g., `without a role,`).
Flag: Broken ARIA references, ARIA on focusable hidden elements, redundant roles.
5b. Dynamic content
- Modals/dialogs: should have
role="dialog",aria-modal="true", focus trap, and return focus on close. - Toast/notification components: should use
role="alert"oraria-live="polite". - Loading states: should use
aria-busy="true"on the container, or a live region announcing the state change. - Content that updates without page reload: should use
aria-liveregions.
Flag: Modals without dialog role/focus management, dynamic content without live regions.
Step 6: Document-Level
- `
haslangattribute (e.g.,lang="en"`) - Page `` is unique and descriptive per page (not just the site name everywhere)
- Skip-to-content link exists (a visually hidden link at the top of the page that jumps to ``)
- Viewport meta doesn't disable zoom:
user-scalable=noormaximum-scale=1are violations
Flag: Missing lang, missing/duplicate titles, missing skip link, zoom disabled.
Live Check (--live flag)
When --live is passed, use WebFetch to check each page's rendered HTML for:
- Rendered heading hierarchy -- does the final HTML have correct h1-h6 order?
- Rendered landmarks -- are `
,,,` present in output? - Rendered form labels -- do inputs have associated labels in the final DOM?
- Contrast estimation -- parse inline styles and class-based colors in the rendered output
- ARIA reference integrity -- do all
aria-labelledby/aria-describedbyIDs resolve?
Compare live results against source findings. Flag discrepancies (SSR issues, hydration bugs that break accessibility).
Scoring
Max score: 100 points. Weighted by impact:
| Category | Weight | Rationale | |----------|--------|-----------| | Keyboard accessibility (2a) | 20 | Complete blocker for keyboard/switch users | | Forms (2c) | 15 | Blocks task completion | | Images and alt text (3a) | 15 | Core screen reader experience | | Heading hierarchy (1b) | 10 | Navigation and comprehension | | Landmarks (1a) | 10 | Screen reader navigation | | Buttons and links (2b) | 10 | Core interaction | | Document-level (Step 6) | 8 | Foundation | | ARIA usage (5a, 5b) | 5 | Correctness | | Color/contrast (4a, 4b) | 5 | Partial check from source (full check needs --live) | | Motion (4c) | 2 | Important but narrower impact |
Formula: score = (earned_points / applicable_points) * 100
Skip checks that don't apply (no forms = skip form checks, no images = skip image checks).
Grade scale:
| Score | Grade | Meaning | |-------|-------|---------| | 90-100 | A | Solid. Minor polish only. | | 80-89 | B | Good foundation, some gaps. | | 70-79 | C | Usable but excluding people. Fix the keyboard and form issues. | | 60-69 | D | Significant barriers. Needs dedicated work. | | 0-59 | F | Major accessibility failures. Prioritize before shipping. |
Output Format
## Accessibility Audit -- [project name]
**Score:** [score]/100 ([grade])
**Stack:** [detected stack]
**WCAG Level:** AA (target)
### CRITICAL (blocks entire user groups)
1. **[Category]** [file:line] -- [what's wrong] -- [how to fix]
### SERIOUS (degrades experience significantly)
1. **[Category]** [file:line] -- [what's wrong] -- [how to fix]
### MODERATE (should fix)
1. **[Category]** [file:line] -- [what's wrong] -- [how to fix]
### MINOR (polish)
1. **[Category]** [file:line] -- [what's wrong] -- [how to fix]
### Summary
- [X] critical, [Y] serious, [Z] moderate, [W] minor issues
- Top priority: [the one thing to fix first]
Fix Mode (--fix flag)
Safe to auto-fix:
- Add
lang="en"to `` if missing - Add
alt=""to images that are clearly decorative (inside links with text, CSS background replacements). Addalt="TODO: describe this image"to content images missing alt. - Add
aria-hidden="true"to decorative SVG icons - Remove
role="button"from `` elements (redundant) - Remove
role="link"from `` elements (redundant) - Add
rel="noopener"totarget="_blank"links if missing
Requires confirmation:
- Adding
aria-labelto icon buttons (needs the right label text) - Adding skip-to-content link (needs to match page structure)
- Wrapping inputs with `` (needs to not break layout)
- Adding
prefers-reduced-motionCSS (needs to know which animations to disable)
Never auto-fix:
- Color contrast (requires design decisions)
- Focus management in modals (requires understanding the component logic)
- Heading hierarchy (restructuring headings can break design intent)
- ARIA live regions (requires understanding what content updates dynamically)
Rules
- React Native / Expo projects: Skip this audit. Native accessibility uses
accessibilityLabel,accessibilityRole,accessibilityState, etc. -- a completely different model. Note this and stop. - Don't flag framework internals. Next.js `
, Astro, Jinja{% block %}` are structural. Check what they render, not the templating syntax. - Stack-aware fixes. In JSX it's
htmlFor, in HTML it'sfor. In JSX it'stabIndex, in HTML it'stabindex. Get it right. - Severity by impact. A missing skip link is moderate. A click handler on a div with no keyboard equivalent is critical -- it's a complete blocker for keyboard users.
- Don't penalize what doesn't exist. No forms = no form audit. No media = no media audit.
- Be specific about fixes. "Add alt text" is useless. "Add
alt=\"\"to `atsrc/templates/.html:`" is useful. - Prefer semantic HTML over ARIA. The fix for a clickable div is almost always "make it a button," not "add role=button and tabindex=0 and onKeyDown."
Skill Run Logging
After completing this skill, append a log entry to ~/.claude/skill-log.md under the Run Log table. New entries at the top (most recent first).
Format: | YYYY-MM-DD | /skill-name | target (details) | one-line result |
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Surfrrosa
- Source: Surfrrosa/claude-skills
- License: MIT
- Homepage: https://shainapauley.com/skills
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.