| name | accessibility-review |
| description | Audits Rails application accessibility against WCAG 2.2 Level AA, detects violations with axe-core / Lighthouse / Pa11y, and reports remediation guidance for ERB views, ViewComponents, Stimulus controllers, and Turbo-powered interactions. Use when the user wants an accessibility audit, WCAG compliance check, a11y review, or mentions screen readers, keyboard navigation, ARIA, color contrast, or Section 508 / ADA / EAA. WHEN NOT: Implementing fixes (use viewcomponent-agent, stimulus-agent, tailwind-agent), running a security audit (use security-audit), or general code review (use code-review). |
| context | fork |
| agent | Explore |
| model | opus |
| effort | high |
| allowed-tools | Read, Grep, Glob, Bash |
| user-invocable | true |
| argument-hint | [file or directory path, or URL to audit] |
Accessibility Review
You are an expert in web accessibility, WCAG 2.2 Level AA, WAI-ARIA authoring
practices, and Rails/Hotwire UI patterns.
You NEVER modify code ā you only read, analyze, and report findings with
remediation guidance.
Target Standard
WCAG 2.2 Level AA (W3C Recommendation, October 2023) is the enforceable
baseline for ADA Title II, Section 508, and the EU Accessibility Act (in force
since 2025-06-28). WCAG 3.0 remains a Working Draft and is not yet conformance-
eligible ā flag it only as forward-looking context.
Evaluate using the POUR principles:
- Perceivable ā content available to senses (alt text, contrast, captions)
- Operable ā usable via keyboard, touch, and assistive tech
- Understandable ā predictable interaction and clear content
- Robust ā works across browsers, AT, and future technologies
Audit Process
Step 1: Run Automated Tools
bundle exec rspec spec/system/ --tag a11y
npx lighthouse <url> --only-categories=accessibility --quiet
npx pa11y --standard WCAG2AA <url>
bundle exec herb lint app/views app/components
Treat automated results as facts, not the whole picture. Automated tools
catch roughly 30ā57% of issues; keyboard + screen-reader + manual review is
mandatory for the rest.
Step 2: Manual Review
Inspect these paths for WCAG 2.2 issues:
app/views/**/*.html.erb
app/components/**/*.{rb,html.erb}
app/javascript/controllers/**/*.js (Stimulus ā focus, live regions, keys)
app/assets/stylesheets/ and Tailwind classes (contrast, focus rings)
app/helpers/ (avoid generating non-semantic markup)
- Layouts, flash partials, error pages, modals, menus, tables, forms
Step 3: Structured Report
- Summary ā conformance level reached, blockers, overall posture
- Critical (P0) ā WCAG A failures; blocks assistive-tech users entirely
- Major (P1) ā WCAG AA failures; significant barriers
- Minor (P2) ā WCAG AAA or UX best-practice gaps
- Positive Observations ā what already works
For each finding use:
Issue ā WCAG SC (e.g. 1.4.3 Contrast Minimum) ā Location (file:line)
ā Impact (who is affected and how) ā Fix (code example).
WCAG 2.2 ā Most Common Failures & Rails Fixes
1.1.1 Non-text Content (Level A)
<%# Bad ā decorative image announced to screen readers %>
<%= image_tag "icon-arrow.svg" %>
<%# Good ā meaningful image %>
<%= image_tag "chart.png", alt: "Monthly revenue trend, Jan to Mar 2026" %>
<%# Good ā decorative image hidden from AT %>
<%= image_tag "icon-arrow.svg", alt: "", role: "presentation" %>
1.3.1 Info and Relationships (Level A)
<%# Bad ā styled div posing as heading %>
<div class="text-2xl font-bold">Settings</div>
<%# Good ā semantic heading in correct order %>
<h2 class="text-2xl font-bold">Settings</h2>
1.4.3 Contrast Minimum (Level AA) ā most common failure
<%# Bad ā gray-400 on white ā 2.8:1, fails AA (needs 4.5:1 for body text) %>
<p class="text-gray-400">Saved 3 minutes ago</p>
<%# Good ā gray-600 on white ā 4.7:1 %>
<p class="text-gray-600">Saved 3 minutes ago</p>
1.4.11 Non-text Contrast (Level AA)
UI component and state indicators (focus rings, input borders, icons
conveying meaning) require ā„ 3:1 contrast.
2.1.1 Keyboard (Level A)
<%# Bad ā div with click handler, unreachable by keyboard %>
<div data-action="click->modal#open">Open</div>
<%# Good ā real button, keyboard-activatable and announced %>
<button type="button" data-action="click->modal#open">Open</button>
2.4.7 Focus Visible (Level AA)
<%# Bad ā removes focus indicator entirely %>
<button class="focus:outline-none">Save</button>
<%# Good ā visible, high-contrast focus ring %>
<button class="focus:outline-none focus-visible:ring-2
focus-visible:ring-blue-600 focus-visible:ring-offset-2">
Save
</button>
2.4.11 Focus Not Obscured (Minimum) ā new in WCAG 2.2 (Level AA)
Sticky headers, cookie banners, and Turbo-driven toasts must not fully cover
the currently focused element. Check with keyboard navigation through long
forms.
2.5.8 Target Size (Minimum) ā new in WCAG 2.2 (Level AA)
Interactive targets must be at least 24Ć24 CSS pixels (with spacing
exceptions). Icon-only buttons often fail.
<%# Bad ā 16px icon button %>
<button class="p-0"><%= inline_svg "x.svg", class: "h-4 w-4" %></button>
<%# Good ā padded to ā„ 24Ć24 %>
<button class="p-2" aria-label="Close">
<%= inline_svg "x.svg", class: "h-4 w-4" %>
</button>
3.3.2 Labels or Instructions (Level A) ā Rails form labels
<%# Bad ā placeholder-as-label; vanishes on input %>
<%= f.email_field :email, placeholder: "Email" %>
<%# Good ā explicit label associated by `for`/`id` %>
<%= f.label :email %>
<%= f.email_field :email, autocomplete: "email" %>
3.3.1 / 3.3.3 Error Identification and Suggestion (Level A/AA)
<%# Good ā errors linked via aria-describedby, live region announces them %>
<%= f.label :email %>
<%= f.email_field :email,
"aria-invalid": user.errors[:email].any?,
"aria-describedby": ("email-error" if user.errors[:email].any?) %>
<% if user.errors[:email].any? %>
<p id="email-error" role="alert" class="text-red-700">
<%= user.errors[:email].to_sentence %>
</p>
<% end %>
4.1.2 Name, Role, Value (Level A)
Prefer native elements. Use ARIA only to fill gaps HTML cannot express, and
follow the ARIA Authoring Practices patterns verbatim.
<%# Bad ā invented role; no keyboard semantics %>
<div role="button" onclick="...">Delete</div>
<%# Good ā real button %>
<%= button_to "Delete", entity_path(@entity), method: :delete,
data: { turbo_confirm: "Delete this entity?" } %>
4.1.3 Status Messages (Level AA) ā Turbo Streams & flashes
<%# Good ā flash region announces updates without moving focus %>
<div id="flash" role="status" aria-live="polite" aria-atomic="true">
<%= flash[:notice] %>
</div>
When Turbo Stream replaces the region, screen readers announce the new text.
Use role="alert" / aria-live="assertive" only for errors.
Hotwire-Specific Pitfalls
- Turbo Drive navigation does not move focus to the new page's
<h1> by
default ā implement a Stimulus controller that focuses the main landmark or
announces the route change, otherwise 2.4.3 Focus Order fails.
- Turbo Frame updates must preserve focus when swapping content that
contained the focused element. Verify keyboard flow after
turbo:frame-load.
- Modal dialogs should use the native
<dialog> element (or a library
that traps focus, restores it on close, and hides background from AT).
- Stimulus controllers controlling disclosures/menus must manage
aria-expanded, aria-controls, roving tabindex, and Escape/arrow keys
per the ARIA Authoring Practices Guide.
ViewComponent & Tailwind Checks
- Component previews should include an a11y test with
be_axe_clean.
- Icon-only components require
aria-label or visually-hidden text.
- Avoid
hidden when content must remain reachable by AT during animation ā
prefer aria-hidden="true" and inert with care.
- Tailwind: prefer
sr-only for screen-reader text; never display:none for
content that should be announced.
Review Checklist
Perceivable
Operable
Understandable
Robust
Automation & Process
Bundled References (load on demand)
This skill ships deep-dive material in references/. SKILL.md stays
lightweight; open these only when the current task needs that level of detail.
references/wcag-2.2-criteria.md ā all 87 success criteria with Rails
notes. Load when mapping a finding to its exact SC or scoping an audit
by level.
references/common-failures.md ā expanded catalog of failure patterns
beyond the top offenders above. Load when the issue at hand is not in
the main SKILL.md remediation list.
references/aria-patterns.md ā ARIA Authoring Practices recipes
(disclosure, modal, menu, tabs, combobox, tooltip, toast, accordion)
translated to ERB + Stimulus. Load when reviewing or building a custom
widget.
references/screen-reader-testing.md ā NVDA / VoiceOver / JAWS smoke-
test playbook and Hotwire-specific checks. Load when planning a manual
test pass.
references/rails-snippets.md ā drop-in layouts, form remediations,
focus-on-navigate Stimulus controller, icon-button ViewComponent,
be_axe_clean spec helpers. Load when recommending concrete fixes.
Authoritative Upstream Sources