Skip to content
Development
Skill

/a11y-fixes

Resolves axe-core accessibility violations reported by a story under Vitest (addon-a11y), Playwright (.playwright/a11y.ts), or the code-audit-frontend agent's a11y bucket, with fix patterns for the common violations and a fix-then-verify loop for the rest. Trigger on any axe

From plugin
gaia
2619 skills18 agents12 commands

Claude Code

$ npx -y skills add gaia-harness/gaia --skill a11y-fixes --agent claude-code

SKILL.md

a11y-fixes.SKILL.md
name: a11y-fixes
description: Resolves axe-core accessibility violations reported by a story under Vitest (addon-a11y), Playwright (.playwright/a11y.ts), or the code-audit-frontend agent's a11y bucket, with fix patterns for the common violations and a fix-then-verify loop for the rest. Trigger on any axe rule id appearing in test output.
model: haiku

Accessibility Fix Patterns

Contents: Fix-then-verify loop; color-contrast; label; label-title-only; image-alt; button-name; link-name; region / landmark-one-main; heading-order; aria-allowed-attr; aria-required-attr; aria-required-children / aria-required-parent; aria-valid-attr-value; focus-trap (focus management); tabindex; html-has-lang; document-title; duplicate-id; listitem; definition-list; Upstream shadcn and Base UI findings.

Violations come from addon-a11y, which axe-checks every story under Vitest (configured in `.storybook/a11y.ts`), `.playwright/a11y.ts` (Playwright), or the `code-audit-frontend` agent's a11y bucket. General a11y guidance lives in `apps/web/.claude/rules/accessibility.md`.

Fix-then-verify loop

1. Fix one violation with the pattern below; for a rule id not listed here, use the violation's `help` and `failureSummary` from the axe output. 2. Re-run the reporter that flagged it: Vitest `pnpm test --run --project storybook <story-file>` (`--project storybook-dark` for a dark-theme violation), Playwright `pnpm pw <spec-file>`. 3. Some rules are not checked by the story run. addon-a11y runs the WCAG 2.0/2.1 A and AA tags and disables `region` (a story renders a fragment outside the page landmarks), so `landmark-one-main` and `page-has-heading-one` never fire there: assert the page landmark and heading in a `.test()` on the page story instead (exactly one `main`, one level-1 heading; `wiki/decisions/Stories as Tests.md`). 4. Repeat until the reporter shows no violations.

color-contrast

WCAG AA requires 4.5:1 for normal text, 3:1 for large text. Use the theme role tokens (see `apps/web/.claude/rules/tailwind.md`) instead of raw palette colors: each token carries a light and a dark value that pass in both themes. Pair a surface token with its foreground (`bg-primary` with `text-primary-foreground`).

// BAD, fails contrast in dark mode, raw colors
<p className="text-gray-400 bg-white">Status</p>

// GOOD, role tokens, contrast-safe in both themes
<p className="bg-background text-foreground">Status</p>

If a token itself fails (the placeholder values are a neutral default), fix the value in `app/styles/theme.css` and keep it an accessibility fix, per `apps/web/.claude/rules/design-baseline.md`.

label

Form inputs need an associated label. Put the control in `ConformField` rather than a bare `<label>`: it links the label and the errors. See `apps/web/.claude/skills/react-code/references/conform-forms.md` for each control type.

// BAD, bare input with no label association
<input type="text" name="email" />

// GOOD, ConformField wires the label to the ui control
<ConformField field={fields.email} label={t('email')}>
  {(controlOptions) => (
    <Input {...getInputProps(fields.email, {...controlOptions, type: 'email'})} />
  )}
</ConformField>

label-title-only

`title` attributes are not labels, screen readers and mobile devices ignore them. Use `aria-label` or a real `<label>`.

// BAD, title is not an accessible name
<input type="text" title="Search" />

// GOOD, aria-label provides the accessible name
<input type="text" aria-label={t('search')} />

image-alt

Every `<img>` needs `alt`. Content images describe the image; decorative images use `alt=""`. See `apps/web/.claude/rules/accessibility.md`.

// BAD, no alt attribute
<img src="/logo.png" />

// GOOD, content image
<img src="/logo.png" alt={t('companyLogo')} />

// GOOD, decorative image, hidden from AT
<img src="/divider.svg" alt="" />

button-name

Buttons need an accessible name. Visible text is fine; icon-only buttons need `aria-label`.

// BAD, icon-only button with no name
<button onClick={onClose}>
  <CloseIcon />
</button>

// GOOD, aria-label supplies the name
<button aria-label={t('close')} onClick={onClose}>
  <CloseIcon />
</button>

// GOOD, visible text, no aria-label needed
<button onClick={onSave}>{t('save')}</button>

link-name

Same pattern as `button-name`, anchors need an accessible name.

// BAD, icon-only link
<Link to="/settings"><GearIcon /></Link>

// GOOD, aria-label on the link
<Link to="/settings" aria-label={t('settings')}>
  <GearIcon />
</Link>

region / landmark-one-main

Page content must live inside a landmark, and there must be exactly one `<main>`. The Layout component owns the `<main>` landmark, page components render inside it and should not add their own.

// BAD, page component wraps itself in <main>, duplicating Layout's
const Page = () => (
  <main>
    <h1>{t('title')}</h1>
  </main>
);

// GOOD, page renders into Layout's <main>
const Page = () => (
  <>
    <h1>{t('title')}</h1>
  </>
);

heading-order

One `<h1>` per page. Levels do not skip, `h2 → h4` is a violation.

// BAD, skips h3
<h2>{t('section')}</h2>
<h4>{t('subsection')}</h4>

// GOOD, sequential levels
<h2>{t('section')}</h2>
<h3>{t('subsection')}</h3>

aria-allowed-attr

ARIA attributes are role-scoped. Look up the role; only attrs in its allowed list are valid. Common case: `aria-checked` only on `role="checkbox"`, `role="radio"`, `role="menuitemcheckbox"`, `role="menuitemradio"`, `role="switch"`, `role="treeitem"`.

// BAD, aria-checked is not allowed on a button
<button aria-checked={selected}>{t('toggle')}</button>

// GOOD, aria-pressed for buttons, aria-checked for checkbox role
<button aria-pressed={selected}>{t('toggle')}</button>

aria-required-attr

Some roles require companion attrs. `role="combobox"` requires `aria-expanded` and `aria-controls`. On a disclosure button axe does not require `aria-contro

Read more
@gaia-react
Ships withgaia

Claude is raw power. GAIA is order and focus. A serious harness for Claude Code. It keeps Claude-shipped code production-grade as your team scales. The React frontend is handled. You build the rest of your app on top.

2619 skills18 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
26
Star history of gaia-react/gaia on GitHub, a chart by star-history.com

Other skills on gaia.

file-tech-debt

file-tech-debt

Files a new tech-debt GitHub issue for an out-of-scope code-review finding. Builds the dedup key, skips an open or…

@gaia-react@gaia-reactInstall Skill
gaia-wiki

gaia-wiki

Runs GAIA wiki maintenance (sync, consolidate, lint with fixes) in one command and lands it as one commit and PR. Trigger on…

@gaia-react@gaia-reactInstall Skill
naming-conventions

naming-conventions

Applies descriptive, self-documenting naming in any language (TypeScript, bash, workflow YAML, Dockerfiles). Use this skill…

@gaia-react@gaia-reactInstall Skill
release-notes

release-notes

Maintainer-only. Rewrites a version's GAIA CHANGELOG entries as plain-language public release notes for the marketing site…

@gaia-react@gaia-reactInstall Skill
tdd

tdd

Runs test-driven development with a red-green-refactor loop, an opt-in tool for logic where test-first helps. Use when the user…

@gaia-react@gaia-reactInstall Skill
update-deps

update-deps

Remediates dependencies in place of Dependabot version and security updates. Discovers outdated packages and open security…

@gaia-react@gaia-reactInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc11d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about a11y-fixes.

Which coding agents can use a11y-fixes?
a11y-fixes installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.