Skip to content
Back to skills

Frontend A11y

ASecurity

Use when a ticket requires accessibility work or when delivering UI that must be usable by everyone — keyboard navigation, screen-reader labelling, focus management, colour contrast, target size, or reduced-motion support to WCAG 2.2 AA. Invoke for "make X accessible", "fix the a11y issues", "add keyboard support", or as a companion check on any new component.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 5, 2026
ai-agentsgoreactvuefrontend

Works with

  • cli
  • mcp

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add tmj-90/gaffer --skill frontend-a11y --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Frontend A11y?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Frontend A11y
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tmj-90-frontend-a11y/badge)](https://www.skillsdirectory.com/skills/tmj-90-frontend-a11y)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: frontend-a11y
description: Use when a ticket requires accessibility work or when delivering UI that must be usable by everyone — keyboard navigation, screen-reader labelling, focus management, colour contrast, target size, or reduced-motion support to WCAG 2.2 AA. Invoke for "make X accessible", "fix the a11y issues", "add keyboard support", or as a companion check on any new component.
stack: []
area: frontend
---

# Make the UI accessible

Bring the UI the ticket touches to **WCAG 2.2 Level AA** and prove it with automated
checks plus a keyboard walk. Inaccessible UI is broken UI: a control a keyboard cannot
reach does not work for that user. The reviewer's `accessibility-review` lens checks the
same list below, so build to it.

## Steps

1. **Scope and conventions.** List every control, form, dialog, menu, live update and
   media element the change adds or alters. Call `search_lore` for the repo's a11y helpers
   (focus-trap, visually-hidden class, announcer) and reuse them.
2. **Native element first.** `button` for actions, `a href` for navigation, `label` +
   `input`, `fieldset`/`legend` for groups, `dialog` (opened with `showModal()`) for modals,
   `details`/`summary` for disclosure, landmarks (`header`, `nav`, `main`, `footer`), one
   `h1` and no skipped heading levels. ARIA only when no native element exists — and then
   follow the matching **WAI-ARIA Authoring Practices (APG)** pattern exactly (roles,
   states, keys). A `div onClick` is a defect. Do not use `role="menu"` for site
   navigation; APG menus are for application-style menus.
3. **Keyboard (WCAG 2.1.1, 2.1.2, 2.4.3, 2.4.7).** Everything operable with Tab,
   Shift+Tab, Enter, Space, Escape, and arrows where the APG pattern says so (tabs,
   listbox, radio group, grid use a roving `tabindex` or `aria-activedescendant`). Focus
   order follows reading order; no positive `tabindex`; no trap outside a modal.
4. **Focus visible and not hidden (2.4.7, 2.4.11 new in 2.2).** A `:focus-visible` style
   with at least 3:1 contrast against adjacent colours (1.4.11); never `outline: none`
   without a replacement. A sticky header, cookie banner or toast must not completely
   cover the focused element — add `scroll-padding-top` or move the overlay.
5. **Names, roles, states (1.1.1, 1.3.1, 4.1.2).** Every control has an accessible name
   that contains its visible label (2.5.3); icon-only buttons get `aria-label` or
   visually hidden text; decorative images `alt=""`; toggles expose `aria-pressed` or
   `aria-expanded`; errors link via `aria-describedby` and set `aria-invalid`.
6. **Dynamic changes (4.1.3).** Status messages (saved, 3 results, error) go to a live
   region that exists in the DOM **before** the text is inserted (`role="status"` polite,
   `role="alert"` only for urgent). Route changes in an SPA move focus to the new `h1` or
   announce the page title.
7. **Overlays.** On open move focus inside; keep it inside while modal (native `dialog`
   or `inert` on the background); Escape closes; on close return focus to the trigger.
8. **Pointer and target size (2.5.7, 2.5.8 new in 2.2).** Targets at least **24×24 CSS
   px** or spaced so a 24px circle does not overlap a neighbour (44×44 is the comfortable
   touch goal). Any drag interaction (reorder, slider, map pan) has a single-pointer
   alternative (buttons, click-to-place).
9. **Visual (1.4.1, 1.4.3, 1.4.11, 1.4.10, 1.4.12).** Text contrast 4.5:1 (3:1 at ≥ 24px
   or ≥ 18.66px bold); UI components and graphics 3:1; colour never the only signal;
   content reflows at 320 CSS px wide and survives WCAG text-spacing overrides.
10. **Motion (2.2.2; 2.3.3 is AAA but cheap).** Wrap non-essential animation in
    `@media (prefers-reduced-motion: no-preference)`; anything that moves for more than 5s
    can be paused.
11. **Forms and auth (3.3.1, 3.3.3, 3.3.7, 3.3.8 new in 2.2).** Errors identified in text
    with a fix suggestion; do not make users re-enter data they already gave in the flow;
    allow paste and password managers in credential fields, no puzzle-only CAPTCHA.
12. **Verify.** Run the repo's checks, then do the manual pass (below). Evidence each AC
    with the `record-evidence` skill (`test_output` for the axe, component and keyboard
    tests), then stop.

## Verification commands

Use the tools the repo already has; installs are hook-blocked, so a missing tool is
named in the evidence, not installed.

- Lint: `eslint-plugin-jsx-a11y` (React) / `eslint-plugin-vuejs-accessibility` if the repo
  has them — zero new warnings.
- Component tests: query by role and name (`getByRole('button', { name: /save/i })`); a
  test that cannot find the control by role has found a defect. `jest-axe`/`vitest-axe`
  `toHaveNoViolations()` on each new state (open, error, empty).
- E2E: `@axe-core/playwright` —
  `new AxeBuilder({ page }).withTags(['wcag2a','wcag2aa','wcag21a','wcag21aa','wcag22aa']).analyze()`
  with zero violations on the changed page.
- Keyboard walk as a test (automation finds only part of WCAG, and you have no mouse or
  screen reader to drive by hand): complete the AC's task with `userEvent.tab()` /
  `userEvent.keyboard` or Playwright `page.keyboard.press`, asserting focus
  (`toHaveFocus()`) at each step; render at 320 px width and with
  `emulateMedia({ reducedMotion: 'reduce' })` where Playwright exists. Anything you
  could not check (a screen-reader pass, a missing tool) is named in the evidence as
  unverified, never claimed.

## Done when

- Every AC task is completable by keyboard alone, with visible, unobscured focus.
- axe reports zero violations for the WCAG 2.2 A/AA tags on each new state (or the
  evidence says the repo has no axe tooling).
- Every new control is findable by role + accessible name in a test.
- Contrast, target size, reflow and reduced-motion checks above pass.

## Anti-patterns

- ARIA to repair the wrong element (`role="button"` on a `div` without key handling).
- `aria-label` that differs from the visible text; `title` as the only label.
- Placeholder as label; errors shown only in red; toasts as the only error channel.
- Live region inserted together with its message (nothing is announced).
- `tabindex="0"` sprinkled on non-interactive text; `autofocus` that skips context.
- An axe pass reported as "accessible" with no keyboard walk.

## Capture lore

This skill is one of the places durable, reusable knowledge naturally surfaces:
**An accessibility pattern or boundary this repo standardises on — a focus-management rule, a semantics convention, or an a11y check that must pass.** That kind of fact is *lore*. Capture it via the **lore-capture
protocol in your brief** (`CLAUDE.factory.md`, step 11 "Memory contribution"):
call the Memory MCP `suggest_lore` once at the close of your work — reusable
conventions, gotchas, decisions, and boundaries only, never per-ticket trivia.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.