agentsclimarketplace

Accessibility audit

Skill IfyN/frontend-accessibility-audit/accessibility-audit

A Claude skill for running structured frontend accessibility audits and writing shippable engineering tickets. Covers React and Flutter/iOS/Android with a 30-pattern production library.

Install
npx -y skills add IfyN/frontend-accessibility-audit --skill accessibility-audit

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Run a structured frontend accessibility audit and produce shippable engineering tickets. Use this skill whenever the user asks for an accessibility review, a11y audit, WCAG check, screen reader review, or ticket-writing for accessibility findings , on web (HTML/React) or Flutter/iOS. Trigger this even when the user just pastes a component and asks "is this accessible?" or "any a11y issues here?", and whenever they mention VoiceOver, TalkBack, focus order, contrast, alt text, ARIA, or semantic HTML. Produces findings in a Jira-ready ticket format grounded in a 22-pattern library from real production audits.

SKILL.md

16.5 KB, as published. Nobody here has run it

Frontend Accessibility Audit

A software engineer's methodology for auditing frontend accessibility and writing shippable tickets. Based on real production audit work across web and Flutter/iOS apps.

This skill does three things:

  1. Runs a structured audit in a specific order that surfaces issues efficiently
  2. Matches findings to a 22-pattern library of real issues with detection heuristics and fixes
  3. Outputs findings as Jira-ready tickets rather than loose observations

When to use this skill

Use this whenever the user asks for an accessibility review of code, a component, a screenshot, or a page. Common triggers:

  • "Audit this for a11y"
  • "Is this component accessible?"
  • "Check this for WCAG issues"
  • "Write accessibility tickets for this page"
  • "Review this for VoiceOver / TalkBack"
  • Any mention of: focus order, contrast, alt text, ARIA, semantic HTML, screen reader behaviour

For Flutter/iOS-specific audits, always read flutter-accessibility-skill.md before starting , detection heuristics and fixes differ from web, and the reference includes 8 additional Flutter-specific production patterns (A–H) that don't map cleanly onto the web-focused main library.

Audit methodology

Work through these seven steps in order. The order matters: structural issues cascade into interactive and content issues, so fixing them first prevents re-auditing later.

1. Page structure

Check the scaffolding before anything else:

  • <title> is present and descriptive (not empty, not just the app name)
  • Headings are in logical order (h1h2h3, no skipped levels)
  • Landmarks exist: <header>, <nav>, <main>, <footer>, or ARIA equivalents
  • Language is declared: <html lang="en">

2. Keyboard navigation

Tab through the entire page without a mouse:

  • Every interactive element is reachable
  • Tab order follows visual/logical order
  • Focus is visible at every step
  • Focus is trapped inside modals and released on close
  • No keyboard traps

3. Screen reader pass

Run the page through VoiceOver (iOS/Mac) and TalkBack (Android). Use rotor/quick nav to check:

  • Headings make sense as an outline
  • Links are descriptive out of context (no "click here")
  • Landmarks are announced
  • Form fields have accessible names
  • State changes are announced (expanded/collapsed, loading, errors)

4. Interactive components

Inspect each interactive pattern against the pattern library (issues #1, #2, #5–#9, #13, #19, #20):

  • Buttons, links, inputs, selects
  • Modals, accordions, tabs, menus
  • Custom controls (graphs, animations, drag-and-drop)

5. Content accessibility

Check non-interactive content (issues #3, #4, #11, #14, #15, #18):

  • Images have meaningful or empty alt
  • Videos have captions (including for ambient/non-speech audio)
  • Tables have captions and proper headers
  • PDFs are tagged and readable

6. Visual accessibility

Run contrast checks and resize testing (issues #10, #17, #21):

  • Normal text ≥ 4.5:1 contrast (WCAG AA)
  • Large text and UI components ≥ 3:1
  • Text scales up to 200% without breaking layout
  • No typography issues (spacing, typos, unclear hierarchy)

7. Edge cases

Finally, the easy-to-miss ones (issues #12, #22):

  • Animations respect prefers-reduced-motion or have a pause control
  • Orientation is not locked , page works in portrait and landscape
  • Dynamic content changes are announced via aria-live

Tools used

  • VoiceOver (iOS + macOS) and TalkBack (Android) , primary screen readers
  • Chrome DevTools Accessibility tree + contrast picker
  • Axe DevTools for automated first-pass
  • WebAIM Contrast Checker for colour verification
  • Adobe Acrobat Pro for PDF audits

Automated tools catch roughly 30% of issues. Always follow up with manual keyboard and screen reader testing.

Team conventions

These are the defaults , deviate only with a stated reason.

  • Decorative images: alt="" and aria-hidden="true". Do not describe them.
  • Meaningful images: descriptive alt that conveys the information or function.
  • Contrast thresholds: 4.5:1 for normal text, 3:1 for large text and UI components.
  • Interactive elements must be keyboard-reachable and have both a visible label and a programmatic name.
  • ARIA is a fallback, not a first choice. Prefer semantic HTML (<button> over <div role="button">). Every ARIA attribute is a maintenance liability.
  • Focus management: moved deliberately on route changes, modal opens, and after destructive actions.

Ticket format

Every finding produced by this skill uses this template. Do not output loose observations , always a ticket.

**Title:** [Concise issue summary]

**WCAG Criteria:**

- [Criterion number and name] (Level A/AA/AAA)

**Affected Users:**
[Specific user groups impacted, e.g. screen reader users on iOS (VoiceOver), keyboard-only users, users with low vision, users with cognitive disabilities. Be specific , this drives prioritisation and grounds the issue in real human impact.]

**Severity:** [Blocker / Major / Minor] , [one-line justification]

**Description:**
[What is happening and why it's a problem]

**Steps to Reproduce:**

1. [Step]
2. [Step]
3. [Observed behaviour]

**Expected Behaviour:**
[What should happen instead]

**Proposed Solution:**

```[language]
[Code example showing the fix]
```

Why This Fix Works: [1–2 sentence explanation of the underlying principle]

Common False Positives: [Cases that look like this issue but aren't, if any]


See `references/ticket-examples.md` for fully worked examples.

## Severity guidance

- **Blocker** , the feature is unusable for someone using assistive tech (e.g. a modal that traps focus incorrectly, an unlabelled primary CTA, a form that can't be submitted via keyboard). Ship-stopper.
- **Major** , the feature works but is significantly degraded (e.g. state changes not announced, low but near-threshold contrast, missing captions on a content table). Fix this sprint.
- **Minor** , polish issues that affect experience but not task completion (e.g. non-descriptive link text when context makes it clear, decorative image missing `aria-hidden`). Backlog.

Default to the higher severity when in doubt , accessibility bugs tend to be underestimated.

---

## Pattern library , 22 production issues

When the audit surfaces a finding, match it to a pattern below. The pattern gives you the WCAG criterion, detection heuristic, and fix. Use it to write the ticket.

### 1. Modal focus issues

**Platforms:** Web + Flutter · VoiceOver
**WCAG:** 2.4.3 Focus Order (A), 4.1.2 Name Role Value (A)
**Severity:** Blocker , modal is unusable

**Detection:** Open the modal, check: does focus move into the modal? Does Tab cycle within the modal? Does the title announce correctly? Does Escape close it?

**Broken:**
```html
<div class="modal">
  <h2>Title</h2>
</div>

Fixed:

<div role="dialog" aria-modal="true" aria-labelledby="modal-title">
  <h2 id="modal-title">Title</h2>
  <button autofocus>Close</button>
</div>

Why: role="dialog" + aria-labelledby gives screen readers the semantic contract. autofocus moves focus into the modal on open. aria-modal="true" tells assistive tech to treat content outside as inert.

False positives: Static, non-interactive overlays (e.g. a loading scrim) don't need dialog semantics.

2. Accordion accessibility

Platforms: Web + Flutter · VoiceOver WCAG: 4.1.2 Name Role Value (A) Severity: Major

Detection: Does the trigger announce its expanded/collapsed state? Is closed content still read by the screen reader?

Broken:

<div onclick="toggle()">Section</div>
<div class="panel">...</div>

Fixed:

<button aria-expanded="false" aria-controls="panel1">Section</button>
<div id="panel1" hidden>...</div>

Why: aria-expanded exposes state. hidden removes collapsed content from the accessibility tree , not just visually. Using <button> gives you keyboard support for free.

3. Image without alt

WCAG: 1.1.1 Non-text Content (A) Severity: Blocker (for meaningful images)

Detection: Any <img> missing the alt attribute entirely.

Fixed:

<img src="img.png" alt="Robot illustration welcoming new users" />

False positives: Decorative images should use alt="" (empty), not omit the attribute.

4. Linked image without label

WCAG: 2.4.4 Link Purpose (A) Severity: Blocker

Broken:

<a href="/"><img src="logo.png" /></a>

Fixed:

<a href="/"><img src="logo.png" alt="Home" /></a>

Why: The link's accessible name comes from its content. An unlabelled image means an unlabelled link.

5. Input without label

WCAG: 3.3.2 Labels or Instructions (A), 4.1.2 (A) Severity: Blocker

Fixed:

<label for="name">Name</label> <input id="name" name="name" />

False positives: Inputs wrapped in a <label> don't need for/id. Inputs labelled via aria-labelledby or aria-label are also valid (use visible labels where possible).

6. Select without label/name

WCAG: 3.3.2 (A), 4.1.2 (A) Severity: Blocker

Fixed:

<label for="country">Country</label>
<select id="country" name="country">
  ...
</select>

7. Missing ARIA for expand/collapse

WCAG: 4.1.2 (A) Severity: Major

Detection: Any toggle control (menu, disclosure, tree) that changes content visibility without announcing state.

Fix: Add aria-expanded to the trigger and aria-controls pointing to the target region.

8. Button missing accessible name

WCAG: 4.1.2 (A) Severity: Blocker

Broken:

<button><img src="icon.png" /></button>

Fixed:

<button aria-label="Download report"><img src="icon.png" alt="" /></button>

Why: Icon-only buttons need an explicit accessible name. The inner image should then be alt="" to avoid double announcement.

9. Redundant aria-label

WCAG: 2.5.3 Label in Name (A) Severity: Minor to Major (depends on mismatch severity)

Issue: aria-label overrides visible text. If they mismatch, voice-control users can't activate the control by its visible name.

Fix: Remove aria-label when visible text is sufficient, or ensure aria-label includes the visible text verbatim.

10. Text contrast issues

WCAG: 1.4.3 Contrast Minimum (AA), 1.4.11 Non-text Contrast (AA) Severity: Major

Detection: Run WebAIM Contrast Checker on all text/background pairs. Normal text needs 4.5:1, large text (18pt+ or 14pt+ bold) and UI components need 3:1.

Fix: Darken text or lighten background until the ratio passes. Never rely on colour alone to convey information.

11. Empty title element

WCAG: 2.4.2 Page Titled (A) Severity: Major

Fixed:

<title>Dashboard , AppName</title>

Why: Screen readers read the title on page load. Empty or generic titles destroy orientation in tab-switching scenarios.

12. Animation cannot be stopped

WCAG: 2.2.2 Pause Stop Hide (A), 2.3.3 Animation from Interactions (AAA) Severity: Major

Fixed:

@media (prefers-reduced-motion: reduce) {
  .animated {
    animation: none;
    transition: none;
  }
}

Why: Vestibular disorders are real. Respect the OS-level preference; add explicit pause controls for animations longer than 5 seconds.

13. Robot animation , no interaction feedback

WCAG: 4.1.3 Status Messages (AA) Severity: Major

Detection: Any custom interactive control (mascot, Easter egg, gamified element) where the visual state change isn't announced.

Fix: Add a live region that updates on interaction:

<div aria-live="polite" class="sr-only">Robot is waving</div>

14. Table missing caption

WCAG: 1.3.1 Info and Relationships (A) Severity: Major

Fixed:

<table>
  <caption>
    Active users by region, Q3 2024
  </caption>
  <thead>
    ...
  </thead>
</table>

Why: Captions give screen reader users the table's purpose before they navigate its cells.

15. Video without captions (including ambient audio)

WCAG: 1.2.2 Captions (A) Severity: Blocker (if dialogue), Major (if ambient)

Fix: Provide .vtt captions. For non-speech audio, include descriptions like [ambient sound], [keyboard clacking], [music swells].

Why: Deaf and hard-of-hearing users need to know audio is present even when there's no dialogue.

16. Footer focus skips links

WCAG: 2.4.3 Focus Order (A), 2.4.7 Focus Visible (AA) Severity: Major

Detection: Tab into the footer. If focus jumps past links, check for aria-hidden="true", tabindex="-1", display: none, or visibility: hidden on the links or their parent.

Fix: Remove the hidden attributes. If content must be hidden from some users but not others, rethink the pattern.

17. Mobile text scaling

WCAG: 1.4.4 Resize Text (AA) Severity: Major

Fix (web): Use relative units (rem, em) instead of px for font sizes. Ensure containers don't clip at 200% zoom.

Fix (Flutter): Respect MediaQuery.textScaleFactor , see flutter-skill.md for the full pattern.

18. Table icons not announced

WCAG: 1.1.1 (A) Severity: Major

Detection: Status icons in tables (, , warning triangle) without text labels.

Fix:

<img src="check.svg" alt="Correct" />

Or, better, combine an icon with sr-only text:

<span aria-hidden="true"></span><span class="sr-only">Correct</span>

19. Graph not tabbable

WCAG: 2.1.1 Keyboard (A) Severity: Major (if interactive), N/A (if static)

Fix:

<div role="img" aria-label="Revenue chart, Q1 to Q4 2024" tabindex="0">...</div>

For static charts, provide a text summary or data table alternative. For interactive charts, ensure all interactions work with keyboard.

20. Span used as link

WCAG: 4.1.2 (A), 2.1.1 (A) Severity: Blocker

Broken:

<span onclick="navigate()">Read more</span>

Fixed:

<a href="/article">Read more</a>

Why: <a> gives you keyboard support, correct role announcement, right-click menus, and middle-click-to-new-tab for free. Never reinvent links.

21. Typography issues

WCAG: 1.4.12 Text Spacing (AA) Severity: Minor

Fix: Proofread content. Ensure line-height ≥ 1.5× font size, paragraph spacing ≥ 2× font size, letter-spacing ≥ 0.12× font size, word-spacing ≥ 0.16× font size when users override.

22. Orientation locked

WCAG: 1.3.4 Orientation (AA) Severity: Major

Fix (web): Don't use CSS to restrict orientation. Design layouts that work in both portrait and landscape.

Fix (Flutter): Remove SystemChrome.setPreferredOrientations unless there's a WCAG-exempted reason (e.g. a piano app). See flutter-accessibility-skill.md.

Why: Users with mounted devices (wheelchairs, stands) can't rotate their screen.


How to use this skill in a session

When auditing, follow this flow:

  1. Scope the audit. Ask what's being audited (single component, page, flow, whole app) and on what platform.
  2. Walk the methodology. Go through steps 1–7 in order, noting findings as you go.
  3. Match each finding to the pattern library. If a finding doesn't match any of the 22 patterns, still write a ticket , the library is a starting point, not an exhaustive list.
  4. Write tickets in the exact format above. One ticket per finding. Group related findings only when they share a single root cause.
  5. Prioritise by severity. Deliver blockers first, then majors, then minors.

When in doubt, err toward the user's assistive tech experience. A "probably fine" on an audit is a "broken" in production.

For Flutter-specific detection and fixes , including 8 additional production patterns (A–H) covering dropdowns, text field focus, VoiceOver rotor links, date pickers, text scaling with country lists and flags, merged card semantics, and screen-load focus , read flutter-accessibility-skill.md. For fully worked ticket examples, read ticket-examples.md.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.