Purpose
Use these rules when an AI coding assistant creates or edits frontend UI. The
goal is to stop generated markup, styling, copy, and component wiring from
shipping inaccessible behavior just because the page looks correct in a visual
diff.
This is a review policy, not a full WCAG tutorial and not an automated scanner.
It tells reviewers what evidence must exist before an AI-generated UI change is
safe to merge.
Review Inputs
Collect enough context to know what changed.
- Changed surfaces. Components, forms, navigation, dialogs, menus,
popovers, route transitions, tables, charts, empty states, errors, loading
states, responsive layouts, and icons.
- Interaction paths. Mouse, keyboard, touch, screen reader, zoom,
high-contrast, reduced-motion, and narrow viewport use.
- State inventory. Default, hover, focus, active, disabled, invalid,
expanded, selected, loading, success, warning, error, and empty states.
- Evidence path. Unit or component tests, Storybook states, Playwright
flows, axe results, manual keyboard checks, screenshots, and design tokens.
- Privacy boundary. Whether test evidence can expose user content, private
route names, accessible names, screenshots, traces, or browser state.
If the PR cannot name the changed surfaces and interaction paths, require that
inventory before reviewing style or visual polish.
Semantic Markup Rules
- Prefer native controls such as
button, a, input, select, textarea,
label, fieldset, legend, details, and heading elements before custom
ARIA widgets.
- Do not turn buttons, links, tabs, menus, checkboxes, or dialogs into generic
containers unless the full keyboard and ARIA pattern is implemented.
- Keep accessible names stable when generated copy changes visible labels,
icons, tooltips, placeholders, or
aria-label values.
- Preserve heading order, landmarks, form labels, error descriptions, table
headers, image alternatives, and live-region meaning when refactoring markup.
- Remove unnecessary ARIA when native semantics already provide the role,
state, name, and keyboard behavior.
ARIA can repair some custom patterns, but it can also create a false role or
state that conflicts with actual behavior. Review the rendered interaction, not
only the JSX or template.
Keyboard And Focus Rules
- Every changed interactive element must be reachable and operable by keyboard.
- Focus indicators must remain visible after generated CSS, reset rules,
animations, or design-token changes.
- Opening dialogs, menus, popovers, drawers, and route overlays must move focus
predictably and restore focus when closed.
- Tab order should follow the visual and logical reading order without hidden
traps, skipped controls, or offscreen focus targets.
- Escape, Enter, Space, arrow keys, and Tab behavior must match the widget
pattern being used.
- Disabled, loading, and pending states must not strand keyboard users or hide
the control's purpose.
Do not accept a screenshot as evidence for keyboard accessibility. The reviewer
needs a manual check, a browser test, or a component story that exercises the
interaction.
Visual And Content Rules
- Text must remain readable at responsive breakpoints, zoomed views, and long
localized strings.
- Color must not be the only way to convey errors, success, selection, or
required fields.
- Contrast, focus ring visibility, target size, spacing, and touch affordances
must survive generated theme or CSS changes.
- Motion, auto-advance, skeletons, shimmer effects, and animated transitions
should respect reduced-motion expectations and avoid interfering with input.
- Error messages should be connected to the field or region they describe, and
users should know how to recover.
- Icons, images, charts, and generated decorative assets need intentional text
alternatives or explicit decorative treatment.
Generated UI often changes copy and visual hierarchy while leaving tests green.
Review content meaning as part of accessibility, not as a separate polish pass.
Evidence Rules
Automated checks are useful, but they are not the whole review.
- Run an automated accessibility scan for changed pages, stories, or key flows
when the project has one.
- Use Playwright, Storybook, component tests, or a local browser to cover the
changed interactive states.
- Pair axe results with manual keyboard checks for dialogs, menus, custom
selects, tabs, drag-and-drop, form validation, and route transitions.
- Tie evidence to the latest commit after rebases, force pushes, generated CSS,
snapshot updates, or final copy edits.
- Record skipped checks and explain whether the gap is accepted by a frontend
owner, design-system owner, or accessibility reviewer.
Passing axe output does not guarantee that the UI is usable. It is evidence for
known rule classes, not a replacement for user-flow review.
Merge Blockers
Block merge until resolved when:
- generated markup replaces a native control with a custom widget that lacks
keyboard behavior or the required role, state, and name;
- labels, accessible names, alt text, error descriptions, headings, landmarks,
table headers, or live announcements are missing or stale;
- focus order, focus visibility, focus restoration, or Escape/Enter/Space
behavior is broken for changed interactions;
- color, animation, icon-only UI, placeholder text, or visual placement is the
only way to understand the state;
- automated scan output is treated as complete proof for a custom interaction;
- screenshots, traces, DOM snapshots, or accessibility trees expose private
user content or internal routes in public review comments;
- accessibility evidence ran before the latest generated UI, CSS, copy, or
snapshot update.
Review Checklist
- {"task": "Surfaces mapped", "description": "The PR identifies changed components, routes, states, and interaction paths"}
- {"task": "Semantics preserved", "description": "Native controls, headings, labels, names, roles, states, and descriptions remain correct"}
- {"task": "Keyboard works", "description": "Changed interactions are reachable, operable, visibly focused, and have predictable focus movement"}
- {"task": "Visual cues accessible", "description": "Contrast, motion, target size, responsive text, and non-color cues are reviewed"}
- {"task": "Evidence current", "description": "Automated scans and manual or browser checks ran after the latest UI change"}
- {"task": "Privacy safe", "description": "Screenshots, traces, DOM output, and examples avoid private user data and internal details"}
AI Review Rules
AI assistants can help find accessibility risk, but they should show their
evidence.
- Ask the assistant to list changed UI surfaces before asking for a verdict.
- Require selectors, component names, routes, or stories for every accessibility
concern.
- Have the assistant separate confirmed issues from checks it could not run.
- Do not let the assistant claim screen-reader compatibility from source code
alone.
- Re-run review after generated markup, styling, copy, snapshot, or test
changes.
Troubleshooting
- The UI looks fine but keyboard fails: restore native controls or implement
the documented widget pattern, then retest focus order and key behavior.
- A scan passes but users cannot complete the flow: add manual keyboard
and state coverage for the actual interaction.
- The design uses icon-only controls: provide visible or accessible names
and test that tooltips are not the only label path.
- A modal traps focus forever: ensure close controls, Escape behavior, and
focus restoration are covered by test or manual review.
- Evidence contains private data: rerun with synthetic data or redact the
public artifact before posting.
Duplicate And History Check
Checked existing rules, hooks, statuslines, guides, collections, skills, open
PRs, and closed PRs for WCAG accessibility auditor, frontend accessibility
workflow, accessibility checker hook, accessibility-first statusline, keyboard
focus rules, ARIA guidance, axe testing, Playwright accessibility testing, and
AI-generated UI review.
Adjacent content includes the WCAG accessibility auditor rule, the accessibility
checker hook, the frontend QA accessibility collection, and the accessibility
statusline. This entry is distinct because it is a portable pre-merge review
policy for AI-generated frontend diffs: it decides what changed surfaces,
keyboard/focus evidence, semantic HTML review, automated-scan limits, and
privacy-safe evidence must exist before a generated UI change can merge.
No prior closed PR for Closes #735 was found during the duplicate/history
check.
Automated Scan Reference
The Playwright accessibility-testing guide pairs Playwright with the @axe-core/playwright package. A baseline scan asserts that no automatically detectable violations exist for a page:
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('should not have any automatically detectable accessibility issues', async ({ page }) => {
await page.goto('https://your-site.com/');
const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
To scan only the rules tagged for WCAG A and AA success criteria, constrain the run with withTags:
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
AxeBuilder exposes the configuration the guide documents:
AxeBuilder method |
What it does (per the Playwright guide) |
.analyze() |
Run the accessibility scan against the page. |
.include() |
Constrain an accessibility scan to only run against one specific part of a page. |
.exclude() |
Exclude part of a page from being scanned until you're able to fix the issues. |
.withTags() |
Run only those rules tagged as corresponding to specific WCAG success criteria. |
.disableRules() |
Temporarily disable individual rules until you're able to fix the issues. |
Sources