agentleFS
SurfrrosaSurfrrosa/claude-skills/a11y/SKILL.md

a11y

Skill1 starsChanged 5 months agoMITView on GitHub

Audit accessibility (WCAG) compliance in source code and optionally against live URLs

11 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use verify.

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.
  • Microsoft AzureSkill

    verify

    End-to-end app testing pipeline for browser-based web apps on cloud browsers via Azure Playwright Workspaces: verify the live app, author tests for working flows, heal failing tests, then re-run with video and produce one consolidated report. Three phases (app verification, test authoring, test healing) plus a scoped final sanity run; each phase emits a sub-report. Use when asked to "verify my app", "make sure my app works and has good test coverage", "do a full QA pass", "add tests where I'm missing coverage", "fix failing tests and add new ones", "smoke test before deploy", "QA my deployment", or any combined verify-and-test ask. Requires Azure CLI (`az login`) and a Playwright Workspace resource ID — bootstraps configs on first invocation. Browser-based web apps with Playwright only — not for unit/API tests, native mobile/desktop, load/perf, or full accessibility audits.

    Also in 3 projects that use this

The file

Show its rules and text

We split this skill into the rules it gives an agent

19 rules. 0 are common, 6 are distinctive, and 13 are very specific to this project.

  • 0

    Common

    Most of the 9 edited versions we read keep these.

  • 6

    Distinctive

    This author's own choices. Worth a look.

  • 13

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    No args: audit current working directory

    line 37
  2. Distinctive:

    Read CLAUDE.md for any project-specific accessibility notes or conventions

    line 68
  3. Distinctive:

    Search layouts and page templates for:

    line 73
  4. Very specific:

    If there are multiple <nav> elements, does each have an aria-label or aria-labelledby to distinguish them?

    line 76
  5. Distinctive:

    Do headings follow a logical order (no skipping from h1 to h3)?

    line 85
  6. Distinctive:

    Search all components/templates for interactive elements:

    line 99
  7. Very specific:

    Search for <form>, <input>, <select>, <textarea>:

    line 116
  8. Very specific:

    Every input must have an associated <label> (via for/htmlFor attribute matching id, or wrapping the input).

    line 117
  9. Very specific:

    Every <img> must have an alt attribute.

    line 128
  10. Very specific:

    If the project has <video> or <audio> elements:

    line 137
  11. Very specific:

    Check for CSS animations or transitions without prefers-reduced-motion media query support.

    line 167
  12. Very specific:

    When --live is passed, use WebFetch to check each page's rendered HTML for:

    line 203
  13. Very specific:

    Add lang="en" to <html> if missing

    line 272
  14. Very specific:

    Add alt="" to images that are clearly decorative (inside links with text, CSS background replacements).

    line 273
  15. Very specific:

    Add aria-hidden="true" to decorative SVG icons

    line 274
  16. Very specific:

    Remove role="button" from <button> elements (redundant)

    line 275
  17. Very specific:

    Add rel="noopener" to target="_blank" links if missing

    line 277
  18. Distinctive:

    Don't penalize what doesn't exist: No forms = no form audit. No media = no media audit.

    line 297
  19. Very specific:

    After completing this skill, append a log entry to ~/.claude/skill-log.md under the Run Log table.

    line 304

“Common” is measured: kept by at least 6 in 10 of the 9 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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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 <div> with no tabIndex or onKeyDown. Keyboard users can't open the menu. Fix: change to <button>.

### SERIOUS
1. Forms — src/app/contact/page.tsx:18 — <input name="email"> has placeholder but no <label>. Fix: wrap in <label> or add aria-label.
2. Images — src/components/Hero.tsx:24 — <img src="/hero.jpg"> 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 <path>: 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

AliasPathLive URL
myapp(add your projects here)(add your projects here)

Process

Step 0: Detect the project

  1. Resolve path from alias table or cwd
  2. Detect the stack (Next.js, Astro, Flask/Jinja, static HTML, React Native)
  3. 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 .html files 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.
  4. Read CLAUDE.md for 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 <main> element? (exactly one per page)
  • Does the page have a <nav> element for primary navigation?
  • If there are multiple <nav> elements, does each have an aria-label or aria-labelledby to distinguish them?
  • Is there a <header> and <footer> at the page level?
  • Are <section> elements used with headings or aria-label (a <section> without an accessible name is meaningless to screen readers)?

Flag: Missing <main>, navigation without labels when multiple exist, <section> without accessible name.

1b. Heading hierarchy

For each page/template:

  • Is there exactly one <h1>?
  • Do headings follow a logical order (no skipping from h1 to h3)?
  • Are headings used for structure, not just styling? (A <h3> 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 <ul> or <ol>, not just stacked <a> tags with <br> 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:click on <div>, <span>, <td>, <img>, or other non-interactive elements without role and tabIndex. These are invisible to keyboard users.
  • Missing keyboard equivalents: onClick without corresponding onKeyDown / onKeyUp handler on non-button/non-link elements.
  • Positive tabindex: tabindex values greater than 0 break natural tab order. tabindex="0" (add to tab order) and tabindex="-1" (programmatic focus only) are fine.
  • Autofocus abuse: autoFocus on 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

  • <a> tags without href (or with href="#" or href="javascript:void(0)") -- these should be <button> elements.
  • <button> elements without visible text or aria-label. Icon-only buttons need labels.
  • Links that open in new tabs (target="_blank") without indicating this to the user (add aria-label suffix or visible indicator). Also check for rel="noopener".
  • Empty links: <a href="..."></a> 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 <form>, <input>, <select>, <textarea>:

  • Every input must have an associated <label> (via for/htmlFor attribute matching id, or wrapping the input). placeholder is NOT a substitute for a label.
  • Required fields should have aria-required="true" or the required attribute.
  • Error messages should be associated with their input via aria-describedby.
  • Form groups (e.g., radio buttons) should use <fieldset> + <legend>.
  • 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 <img> must have an alt 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, or role="img" + aria-label if meaningful.

Flag: Missing alt, filename alt text, generic alt text, meaningful SVGs without labels.

3b. Media

If the project has <video> or <audio> 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-motion media query support.
  • Flag any animation property 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).
  • role attribute with an invalid value.
  • aria-labelledby or aria-describedby pointing to an id that doesn't exist in the same page.
  • Redundant ARIA: role="button" on a <button>, role="link" on an <a>, role="navigation" on a <nav>. These are already implicit.
  • aria-label on elements that don't support it (e.g., <div> without a role, <span>).

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" or aria-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-live regions.

Flag: Modals without dialog role/focus management, dynamic content without live regions.

Step 6: Document-Level

  • <html> has lang attribute (e.g., lang="en")
  • Page <title> 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 <main>)
  • Viewport meta doesn't disable zoom: user-scalable=no or maximum-scale=1 are 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:

  1. Rendered heading hierarchy -- does the final HTML have correct h1-h6 order?
  2. Rendered landmarks -- are <main>, <nav>, <header>, <footer> present in output?
  3. Rendered form labels -- do inputs have associated labels in the final DOM?
  4. Contrast estimation -- parse inline styles and class-based colors in the rendered output
  5. ARIA reference integrity -- do all aria-labelledby/aria-describedby IDs resolve?

Compare live results against source findings. Flag discrepancies (SSR issues, hydration bugs that break accessibility).

Scoring

Max score: 100 points. Weighted by impact:

CategoryWeightRationale
Keyboard accessibility (2a)20Complete blocker for keyboard/switch users
Forms (2c)15Blocks task completion
Images and alt text (3a)15Core screen reader experience
Heading hierarchy (1b)10Navigation and comprehension
Landmarks (1a)10Screen reader navigation
Buttons and links (2b)10Core interaction
Document-level (Step 6)8Foundation
ARIA usage (5a, 5b)5Correctness
Color/contrast (4a, 4b)5Partial check from source (full check needs --live)
Motion (4c)2Important 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:

ScoreGradeMeaning
90-100ASolid. Minor polish only.
80-89BGood foundation, some gaps.
70-79CUsable but excluding people. Fix the keyboard and form issues.
60-69DSignificant barriers. Needs dedicated work.
0-59FMajor 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 <html> if missing
  • Add alt="" to images that are clearly decorative (inside links with text, CSS background replacements). Add alt="TODO: describe this image" to content images missing alt.
  • Add aria-hidden="true" to decorative SVG icons
  • Remove role="button" from <button> elements (redundant)
  • Remove role="link" from <a> elements (redundant)
  • Add rel="noopener" to target="_blank" links if missing

Requires confirmation:

  • Adding aria-label to icon buttons (needs the right label text)
  • Adding skip-to-content link (needs to match page structure)
  • Wrapping inputs with <label> (needs to not break layout)
  • Adding prefers-reduced-motion CSS (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 <Head>, Astro <Layout>, Jinja {% block %} are structural. Check what they render, not the templating syntax.
  • Stack-aware fixes. In JSX it's htmlFor, in HTML it's for. In JSX it's tabIndex, in HTML it's tabindex. 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=\"<describe the image content>\" to <img> at src/templates/<file>.html:<line>" 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 |

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.