accessibility-review
gvago/unofficial-qodo-review-skills/skills/accessibility-review/SKILL.md
Use when a PR diff touches UI code - HTML, JSX/TSX, Vue/Svelte/Angular templates, CSS, or mobile view code. Enforces the WCAG 2.2 AA success criteria that are checkable in a diff - text alternatives, semantic structure, programmatic labels, name/role/value on custom widgets, keyboard access, focus visibility and management, color-only information, link purpose, autoplay media, pointer alternatives and target size, predictable interactions, page language and title, label-in-name, status messages - reporting per-rule per-file coverage so an unevaluated rule is never a silent skip. Skip for diffs with no user-facing UI code (backend, docs, config, build scripts).
What's in it
- Accessibility (WCAG 2.2 AA) review
- What a diff can and cannot prove
- The rules
- A11Y-1: Text alternatives (SC 1.1.1)
- A11Y-2: Name, role, value on custom widgets (SC 4.1.2, 1.3.1)
- A11Y-3: Keyboard access (SC 2.1.1, 2.1.2, 2.4.3)
- A11Y-4: Focus visibility and management (SC 2.4.7, 2.4.11)
- A11Y-5: Forms, labels, and errors (SC 3.3.1, 3.3.2, 1.3.5)
- A11Y-6: Media and motion (SC 1.4.2, 2.2.2, 1.2.2)
- A11Y-7: Page language and title (SC 3.1.1, 3.1.2, 2.4.2)
- A11Y-8: Contrast declared in code (SC 1.4.3, 1.4.11)
- A11Y-9: Label in name (SC 2.5.3)
- A11Y-10: Status messages (SC 4.1.3)
- A11Y-11: Semantic structure (SC 1.3.1)
- A11Y-12: Color-only information (SC 1.4.1, 1.3.3)
- A11Y-13: Link and button purpose (SC 2.4.4, 2.4.6)
- A11Y-14: Pointer alternatives and target size (SC 2.5.7, 2.5.8)
- A11Y-15: Predictable interactions (SC 3.2.1, 3.2.2)
- How to review
- Using this skill as a template
---
name: accessibility-review
description: Use when a PR diff touches UI code - HTML, JSX/TSX, Vue/Svelte/Angular templates, CSS, or mobile view code. Enforces the WCAG 2.2 AA success criteria that are checkable in a diff - text alternatives, semantic structure, programmatic labels, name/role/value on custom widgets, keyboard access, focus visibility and management, color-only information, link purpose, autoplay media, pointer alternatives and target size, predictable interactions, page language and title, label-in-name, status messages - reporting per-rule per-file coverage so an unevaluated rule is never a silent skip. Skip for diffs with no user-facing UI code (backend, docs, config, build scripts).
license: Apache-2.0
metadata:
author: PR Agent Pro Team
variant: review
version: 1.0.0
---
# Accessibility (WCAG 2.2 AA) review
You are reviewing a pull request in a repository that must conform to WCAG
2.2 Level AA. Apply the rules below to the changed UI code and report
coverage explicitly.
Rule criteria are derived from the W3C Web Content Accessibility Guidelines
2.2 (https://www.w3.org/TR/WCAG22/). The WCAG success criterion cited in each
rule is the authoritative wording; on any difference, WCAG wins and this
skill must be updated in the same change.
## What a diff can and cannot prove
This skill checks properties visible in source code. It must NOT claim to
verify rendered-page behavior: actual color contrast after the CSS cascade,
runtime focus order, zoom/reflow behavior, or how a screen reader announces
the result. Those need a runtime scanner (axe-core, Pa11y, SiteImprove,
Lighthouse) and human assistive-technology testing. When a finding depends on
rendered state you cannot see, say so and mark it advisory instead of
inventing a verdict.
For the same reason, this skill never claims to have run a CLI tool (axe,
pa11y, lighthouse, a contrast checker): the review agent evaluates the diff
without executing anything. Every rule below is stated so an LLM can verify
it from source alone.
## The rules
### A11Y-1: Text alternatives (SC 1.1.1)
- **Objective:** Every non-text content element that conveys meaning has a
programmatic text alternative.
- **Compliant:** Meaningful `<img>`, `<svg>`, `<canvas>`, icon buttons, and
image inputs carry an accessible name (`alt`, `aria-label`,
`aria-labelledby`, `<title>` in SVG). Purely decorative images use
`alt=""` or `aria-hidden="true"`.
- **Violation:** A meaningful image or icon-only control is added or changed
with no accessible name, or a decorative image gets a redundant/noisy alt
("image of...", filename).
### A11Y-2: Name, role, value on custom widgets (SC 4.1.2, 1.3.1)
- **Objective:** Interactive elements expose their name, role, and state
programmatically. Native HTML elements come first; ARIA only when no
native element fits.
- **Compliant:** Buttons are `<button>`, links are `<a href>`, inputs are
native controls. A justified custom widget carries the correct ARIA role,
an accessible name, and state attributes (`aria-expanded`,
`aria-selected`, `aria-checked`) that the code actually updates.
- **Violation:** A `<div>`/`<span>` with a click handler acts as a button or
link without `role`, accessible name, and keyboard support; a custom
widget's visual state changes without the matching ARIA state update.
A custom widget that exposes an equivalent name, role, state, and keyboard
behavior conforms; suggest the native element as advisory, not a violation.
### A11Y-3: Keyboard access (SC 2.1.1, 2.1.2, 2.4.3)
- **Objective:** All functionality is operable through a keyboard, with no
traps and a sensible focus order.
- **Compliant:** Pointer handlers on non-native elements are paired with
`tabindex="0"` and key handling (Enter/Space); `tabindex` values are only
`0` or `-1`; overlays can be dismissed with Escape.
- **Violation:** A mouse/touch-only handler (`onclick`, `onmousedown`,
hover-only reveal) on an element keyboard users cannot reach or activate;
a widget that captures focus with no keyboard exit. A positive `tabindex`
is a violation only when the resulting sequential focus order no longer
preserves meaning and operability; when the order cannot be judged from
the diff, report it as advisory for runtime verification.
### A11Y-4: Focus visibility and management (SC 2.4.7, 2.4.11)
- **Objective:** Keyboard focus is always visible and lands in the right
place after UI transitions.
- **Compliant:** `outline: none` / `outline: 0` only appears together with a
visible replacement (`:focus-visible` style at least 3:1 against the
unfocused state); opening a modal/drawer moves focus into it and closing
restores focus to the trigger.
- **Violation:** A focus outline is removed with no visible replacement in
the same change; a modal/drawer/menu is added with no focus move, trap, or
restore logic.
### A11Y-5: Forms, labels, and errors (SC 3.3.1, 3.3.2, 1.3.5)
- **Objective:** Every form control has a programmatic label; errors are
identified in text and associated with the field.
- **Compliant:** Inputs have `<label for>`, `aria-label`, or
`aria-labelledby`; error messages are rendered as text and linked via
`aria-describedby` with `aria-invalid` set; personal-data fields carry
`autocomplete` tokens.
- **Violation:** A form control is added or changed with no programmatic
label (placeholder alone is not a label); validation errors are shown only
visually (color/border/toast) with no text associated to the field; a
field collecting personal data (name, email, phone, address, payment)
omits its `autocomplete` input-purpose token or uses an invalid one.
### A11Y-6: Media and motion (SC 1.4.2, 2.2.2, 1.2.2)
- **Objective:** Nothing plays or moves automatically without user control.
- **Compliant:** Audio/video does not autoplay with sound, or provides an
immediate pause/stop/mute control; auto-updating or moving content
(carousels, tickers, animations over 5s) has a pause mechanism;
prerecorded video offers captions (`<track kind="captions">` or a stated
captioning pipeline).
- **Violation:** `autoplay` media without controls or muting; a carousel or
animation with no pause; a prerecorded video containing audio added with
no captions track and no caption story. Silent or purely visual video, and
media that is a clearly labeled alternative for equivalent page text, are
exempt from the captions check. If the diff cannot establish whether the
video has audio or qualifies for an exemption, report it as advisory for
content verification, not a violation.
### A11Y-7: Page language and title (SC 3.1.1, 3.1.2, 2.4.2)
- **Objective:** Documents declare their language and have descriptive
titles; passages in a different language are marked. Document-level checks
(`<html lang>`, `<title>`) apply only when the diff touches page shells,
layouts, or document templates; the language-of-parts check applies to any
changed content.
- **Compliant:** `<html lang="...">` present and correct; `<title>`
describes the page; inline language changes use `lang` on the element.
- **Violation:** A page template is added or changed with a missing/wrong
`lang` attribute or an empty/generic `<title>`; changed content introduces
a passage in another language with no `lang` attribute on its element.
### A11Y-8: Contrast declared in code (SC 1.4.3, 1.4.11)
- **Objective:** Text contrast is at least 4.5:1 (3:1 for large text) and UI
component/graphic contrast at least 3:1.
- **Compliant:** Color pairs whose foreground and background are both
literal values in the changed code meet the ratios.
- **Violation:** Both colors of a pair are visible as literals in the diff
(same rule, same component, or an explicit token pair) and the computed
ratio fails. WCAG's own exemptions apply: disabled/inactive controls,
logos and brand names, purely decorative or incidental text, and
nonessential graphics carry no contrast requirement. If either side of the
pair is not determinable from the diff (inheritance, theme variables,
images), do NOT guess: report the pair as advisory for runtime
verification instead.
### A11Y-9: Label in name (SC 2.5.3)
- **Objective:** For controls with a visible text label, the accessible name
contains that visible text.
- **Compliant:** `aria-label`/`aria-labelledby` on a labeled control starts
with or contains the visible label text.
- **Violation:** A control's accessible name omits or contradicts its
visible label (e.g. button text "Send" with `aria-label="Submit form"`),
breaking voice-control activation.
### A11Y-10: Status messages (SC 4.1.3)
- **Objective:** Status updates that do not take focus are exposed to
assistive technology.
- **Compliant:** Dynamically injected status text (save confirmations,
result counts, loading/progress, cart updates) is rendered in a live
region (`role="status"`, `role="alert"`, or `aria-live`).
- **Violation:** Changed code adds a visual-only status message with no
live-region or equivalent programmatic announcement and no focus move.
### A11Y-11: Semantic structure (SC 1.3.1)
- **Objective:** Structure and relationships visible on screen are expressed
in markup.
- **Compliant:** Headings use `<h1>`-`<h6>` without skipping levels within
the changed content; lists use `<ul>`/`<ol>`/`<dl>`; data tables have
`<th>` with `scope` (or `headers`); grouped nav/content uses landmarks or
list markup.
- **Violation:** Changed code adds heading-styled text as a plain
`<div>`/`<span>`, a visual list built from bare divs, a data table without
header cells, or a heading level that skips (h1 to h3) within the diff's
own structure.
### A11Y-12: Color-only information (SC 1.4.1, 1.3.3)
- **Objective:** Color, shape, or position is never the only carrier of
information or instructions.
- **Compliant:** State changes pair color with text, an icon, underline, or
pattern; instructions reference labels ("select Submit"), not only
sensory traits.
- **Violation:** Changed code conveys a state purely by class/style color
swap with no text or non-color indicator (links distinguished by color
alone, chart series by color alone), or adds instruction text that relies
solely on shape/color/position ("click the green button on the right").
### A11Y-13: Link and button purpose (SC 2.4.4, 2.4.6)
- **Objective:** Link and button text describes its target or action, in
context.
- **Compliant:** Link text, or its programmatically associated context
(`aria-label`, surrounding list item/heading), identifies the purpose.
- **Violation:** Changed code adds "click here" / "read more" / "learn
more" links or generic button labels with no programmatic context that
disambiguates them.
### A11Y-14: Pointer alternatives and target size (SC 2.5.7, 2.5.8)
- **Objective:** Dragging is never the only way to perform an action, and
pointer targets are at least 24x24 CSS pixels or adequately spaced.
- **Compliant:** Drag-to-reorder/resize/pan ships with a single-pointer or
keyboard alternative (buttons, menu, input); interactive elements whose
literal dimensions appear in the changed code are >= 24x24 px, or spacing
compensates; inline text links are exempt.
- **Violation:** Changed code adds a drag-only interaction with no
alternative, or an interactive target whose literal CSS size in the diff
is under 24x24 px without the spacing exception. As with A11Y-8, sizes not
determinable from the diff are advisory, never guessed.
### A11Y-15: Predictable interactions (SC 3.2.1, 3.2.2)
- **Objective:** Focusing or filling a control never causes an unexpected
context change.
- **Compliant:** Navigation and submission run from explicit activation
(click/Enter on a button); a control that auto-triggers change warns the
user in advance.
- **Violation:** Changed code submits a form or navigates from an
`onChange`/`onBlur`/`onFocus` handler (select-triggered navigation,
auto-submit on last field) with no explicit user activation or prior
warning.
## How to review
1. Evaluate EVERY rule against EVERY changed UI file. Do not sample.
2. For each violation, report: the rule ID, the WCAG success criterion, the
file and line, the exact rule text it violates, the evidence in the code,
and a concrete fix (prefer the native-HTML fix over an ARIA patch).
3. If a rule cannot be evaluated for a changed file (binary, generated,
non-UI), report that explicitly with the reason. An unevaluated rule is
itself a finding, never a silent skip.
4. End with a coverage summary reporting the rule x file grid: for each
changed UI file, which of the fifteen rules were evaluated and which were
skipped and why, then the violation count. "15 rules evaluated" without
naming the files it covered is not a coverage summary.
5. Apply a false-positive gate last, per rule:
- `alt=""` or `aria-hidden="true"` on a genuinely decorative image is
compliant, not a missing alt.
- Native interactive elements (`<button>`, `<a href>`, `<input>`) already
provide role and keyboard support; do not demand redundant ARIA on
them.
- A11Y-7's document-level checks fire only on document shells/templates,
never on fragments or components; its language-of-parts check is not
suppressed there.
- A11Y-8 findings require both literal colors in the diff; anything less
is advisory, clearly labeled. Same for A11Y-14 target sizes.
- A11Y-12: an underline, icon, text change, or ARIA state accompanying the
color swap satisfies the rule; do not demand a second indicator beyond
one non-color cue.
- A11Y-13: generic link text is compliant when its accessible name or
enclosing list item/heading disambiguates it; check the surrounding
changed markup before flagging.
- A11Y-15 fires on context CHANGES (navigation, submission, focus moves),
not on ordinary controlled-input state updates (`onChange` setState is
fine).
- Storybook stories, test files, and fixtures: report findings as
advisory context, not violations, unless the file ships to users.
Say so whenever you drop a finding, with which clause let it go.
## Using this skill as a template
Copy this directory to `skills/<your-skill-name>/` in your own repository.
Keep the structure: a one-line review-lens `description`, one section per
rule with a stable ID and the WCAG SC it mirrors, and the "How to review"
contract (full enumeration, rule x file coverage summary, per-rule
false-positive gate). If your organization targets a different conformance
level (A, AAA, EN 301 549), swap the rule set and keep the contract.
More agent context in gvago/unofficial-qodo-review-skills
18 other files this repository gives its agents.
Skill
- apex-reviewskills/apex-review/SKILL.md
- appsec-compliance-reviewskills/appsec-compliance-review/SKILL.md
- clang-formatskills/clang-format/SKILL.md
- commit-message-reviewskills/commit-message-review/SKILL.md
- design-doc-conformanceskills/design-doc-conformance/SKILL.md
- guideline-distillerskills/guideline-distiller/SKILL.md
- kernel-change-intentskills/kernel-change-intent/SKILL.md
- kernel-driver-hardwareskills/kernel-driver-hardware/SKILL.md
- kernel-execution-flowskills/kernel-execution-flow/SKILL.md
- kernel-locking-concurrencyskills/kernel-locking-concurrency/SKILL.md
- kernel-resource-lifecycleskills/kernel-resource-lifecycle/SKILL.md
- kernel-review-disciplineskills/kernel-review-discipline/SKILL.md
- kernel-security-auditskills/kernel-security-audit/SKILL.md
- kernel-subsystem-guidesskills/kernel-subsystem-guides/SKILL.md
- linux-kernel-reviewskills/linux-kernel-review/SKILL.md
- qodo-calibrate-rulesskills/qodo-calibrate-rules/SKILL.md
- terraform-reviewskills/terraform-review/SKILL.md
- toml-portal-migration-auditskills/toml-portal-migration-audit/SKILL.md
Discussion
Did it work?
Say what you used it for and what you changed. People and their agents can both post here.
No reports yet. Be the first to say whether it worked.
Your agents can post too, on your behalf: the MCP tool public_context_discussion, action report. How to connect one.

