Frontend & Accessibility

Accessibility Audit

Check a UI against WCAG basics that automated tools miss -- keyboard navigation, focus order, and whether screen-reader text actually makes sense, not just whether alt attributes exist.

When to use this

  • A new UI component or page before it ships.
  • An automated a11y linter/scanner passed but the component still needs a manual keyboard/screen-reader check.
  • Not a replacement for an automated accessibility scanner (axe, Lighthouse) -- run that first for the mechanical checks; this is for what those miss.

The skill file

Copy this verbatim. It's written in the SKILL.md format (YAML frontmatter + markdown instructions) that Claude Code, and increasingly other agent tools, read directly.

SKILL.md
---
name: accessibility-audit
description: Audit a UI component or page for accessibility issues that automated scanners typically miss -- keyboard navigation and focus order, meaningful screen-reader announcements, and color contrast in context. Use after an automated scanner has already run, for what it can't catch.
---

# Accessibility Audit

Automated tools catch missing `alt` text and contrast ratios well. This
skill is for what they can't check automatically: does it actually work
for someone using a keyboard or screen reader.

## Keyboard navigation

- Can every interactive element be reached and operated via keyboard
  alone (Tab, Shift+Tab, Enter/Space, Arrow keys where the pattern
  expects them -- e.g. a listbox/menu)?
- Is the tab order logical, matching visual/reading order, not jumping
  unexpectedly because of CSS positioning or DOM order mismatch?
- Is focus visible at every step (no `outline: none` without a
  replacement focus style)?
- For a modal/dialog: does focus move into it on open, get trapped
  inside while open, and return to the triggering element on close?

## Screen reader sense-check

- Read through the component's accessible name/role/state as a screen
  reader would announce it (via the accessibility tree, not just the
  visible text) -- does an icon-only button have a meaningful accessible
  name, not just "button"? Does a dynamic state change (loading,
  expanded/collapsed, error) get announced, not just shown visually?
- Are form inputs properly associated with their labels (not just
  visually adjacent)?
- Is decorative content (purely visual icons/images) hidden from the
  accessibility tree so it doesn't add noise?

## Output

Findings grouped keyboard / screen-reader / other, each with the
specific element, what's wrong, and the fix (usually a specific ARIA
attribute or a documented interaction pattern to follow, like WAI-ARIA
Authoring Practices for the component type).

Installing it elsewhere

The frontmatter/body split above is Claude Code's convention. Here's how to carry the same instructions into other tools:

Claude Code
.claude/skills/accessibility-audit/SKILL.md

Save the file below verbatim (frontmatter included) at that path, project-local or in ~/.claude/skills/ for a user-level skill. Claude Code loads the name/description pair to decide when to pull it in, or you invoke it directly as /accessibility-audit.

Cursor
.cursor/rules/accessibility-audit.mdc

Convert the YAML frontmatter to Cursor's rule format (description, globs, alwaysApply: false) and keep the markdown body as the rule content. Cursor surfaces it by description match, same idea as Claude Code's auto-load.

Codex CLI / Copilot
AGENTS.md

Codex CLI (and increasingly other agentic CLIs) read AGENTS.md at the repo root as always-on instructions. Paste the markdown body under a heading like ## {title}; for GitHub Copilot's coding agent, the equivalent file is .github/copilot-instructions.md.

Windsurf
.windsurfrules

Append the markdown body to .windsurfrules at the repo root. Windsurf treats the whole file as always-on context, so keep only the instructions you want applied on every request.

Where this goes wrong
  • Stopping at "the automated scanner passed" without a manual keyboard-only pass.
  • Adding ARIA attributes that duplicate or contradict native semantics instead of using the correct native element.
  • Checking visual focus indicators but not checking that focus actually moves logically through custom-built components.