{"kind":"AgentDefinition","metadata":{"namespace":"community","name":"a11y","version":"0.1.0"},"spec":{"agents_md":"---\napplyTo: '**'\ndescription: 'Comprehensive web accessibility standards based on WCAG 2.2 AA, with 38+ anti-patterns, legal enforcement context (EAA, ADA Title II), WAI-ARIA patterns, and framework-specific fixes for modern web frameworks and libraries.'\n---\n\n# Accessibility Standards\n\nComprehensive accessibility rules for web application development. Every anti-pattern includes a severity classification, detection method, WCAG 2.2 reference, and corrective code examples.\n\n**Severity levels:**\n\n- **CRITICAL** — Users cannot access content at all. Must be fixed before merge.\n- **IMPORTANT** — Significant barrier for assistive technology users. Fix in same sprint.\n- **SUGGESTION** — Improves usability for assistive technology. Plan for a future iteration.\n\n---\n\n## WCAG 2.2 Quick Reference (AA Level)\n\n### Perceivable\n\n| Criterion | Level | Summary |\n|-----------|-------|---------|\n| 1.1.1 Non-text Content | A | All non-text content has a text alternative. Decorative images use `alt=\"\"`. |\n| 1.2.1 Audio/Video-only | A | Provide transcript (audio) or text alternative (video). |\n| 1.2.2 Captions (Prerecorded) | A | All prerecorded video has synchronized captions. |\n| 1.3.1 Info and Relationships | A | Structure (headings, lists, tables, labels, landmarks) programmatically conveyed. |\n| 1.3.2 Meaningful Sequence | A | When the sequence that content is presented affects its meaning, the visual and programmatic ordering of content should align. |\n| 1.3.3 Sensory Characteristics | A | Instructions don't rely solely on shape, size, position, or sound. |\n| 1.3.4 Orientation | AA | Content is not restricted to single orientation unless essential. |\n| 1.3.5 Identify Input Purpose | AA | Input fields have `autocomplete` attributes when collecting information about the user. |\n| 1.4.1 Use of Color | A | Color is not the only means of conveying info. |\n| 1.4.3 Contrast (Minimum) | AA | Text: 4.5:1 normal, 3:1 large (18pt / 14pt bold). |\n| 1.4.4 Resize Text | AA | Text resizable to 200% without loss of content or functionality. |\n| 1.4.10 Reflow | AA | Sections of content can fit within 320px CSS width viewports without needing to scroll in two dimensions to read. |\n| 1.4.11 Non-text Contrast | AA | UI components and graphics: 3:1 against adjacent colors. |\n| 1.4.12 Text Spacing | AA | No loss of content or functionality with user-overridden line-height (1.5x), or specified paragraph spacing, letter spacing, and word spacing adjustments. |\n| 1.4.13 Content on Hover/Focus | AA | Popup content that appears on hover or focus is: dismissible, hoverable, persistent. |\n\n### Operable\n\n| Criterion | Level | Summary |\n|-----------|-------|---------|\n| 2.1.1 Keyboard | A | All functionality operable via keyboard. |\n| 2.1.2 No Keyboard Trap | A | User can navigate away from any component using keyboard. |\n| 2.2.1 Timing Adjustable | A | Time limits can be extended or disabled. |\n| 2.2.2 Pause, Stop, Hide | A | Auto-updating content can be paused. |\n| 2.3.1 Three Flashes | A | No content flashes more than 3 times per second. |\n| 2.4.1 Bypass Blocks | A | Skip link to bypass repeated navigation. |\n| 2.4.2 Page Titled | A | Pages have descriptive `\u003ctitle\u003e`. |\n| 2.4.3 Focus Order | A | Focus order preserves meaning and operability. |\n| 2.4.4 Link Purpose | A | Link purpose determinable from text or context. |\n| 2.4.6 Headings and Labels | AA | Headings and labels describe topic or purpose. |\n| 2.4.7 Focus Visible | AA | Keyboard focus indicator is visible. |\n| 2.4.11 Focus Not Obscured | AA | Focused element not entirely hidden by other overlaying elements (such as sticky headers or footers). *(New in 2.2)* |\n| 2.5.1 Pointer Gestures | A | Multi-point gestures have single-pointer alternative. |\n| 2.5.2 Pointer Cancellation | A | Activation on up-event, unless activation can be aborted, reversed, or down-event activation is essential. |\n| 2.5.3 Label in Name | A | Accessible name contains the label text as it is visually presented. |\n| 2.5.4 Motion Actuation | A | Device motion has UI alternative and can be disabled. |\n| 2.5.7 Dragging Movements | AA | Drag-and-drop has click/tap alternative. *(New in 2.2)* |\n| 2.5.8 Target Size (Minimum) | AA | Interactive controls have a target size, or spacing of at least 24x24 CSS px. *(New in 2.2)* |\n\n### Understandable\n\n| Criterion | Level | Summary |\n|-----------|-------|---------|\n| 3.1.1 Language of Page | A | `\u003chtml lang=\"...\"\u003e` set correctly. |\n| 3.1.2 Language of Parts | AA | Content in different language marked with `lang` attribute. |\n| 3.2.1 On Focus | A | Focus doesn't trigger unexpected context change. |\n| 3.2.2 On Input | A | Changing input doesn't auto-trigger unexpected context change. |\n| 3.2.6 Consistent Help | A | Help mechanisms in same relative order across pages. *(New in 2.2)* |\n| 3.3.1 Error Identification | A | Errors described to user in text. |\n| 3.3.2 Labels or Instructions | A | Labels or instructions provided for user input. |\n| 3.3.3 Error Suggestion | AA | Suggest corrections for detected errors. |\n| 3.3.4 Error Prevention | AA | Submissions are reversible, checked, or confirmed. |\n| 3.3.7 Redundant Entry | A | Don't re-ask for info already provided in same process. *(New in 2.2)* |\n| 3.3.8 Accessible Authentication (Minimum) | AA | No cognitive function test (puzzle CAPTCHA). Allow paste and autofill. *(New in 2.2)* |\n\n### Robust\n\n| Criterion | Level | Summary |\n|-----------|-------|---------|\n| 4.1.2 Name, Role, Value | A | All UI components have accessible name, role, and state. |\n| 4.1.3 Status Messages | AA | Status messages announced by screen readers without receiving focus. |\n\n\u003e **Note:** 4.1.1 Parsing is obsolete in WCAG 2.2 (always satisfied). Issues it covered are now addressed by 1.3.1 and 4.1.2.\n\n\u003e **New AAA criteria in 2.2** (not required for AA, but recommended): 2.4.12 Focus Not Obscured (Enhanced), 2.4.13 Focus Appearance, 3.3.9 Accessible Authentication (Enhanced).\n\n\u003e **Looking ahead:** WCAG 3.0 (W3C Accessibility Guidelines) is in Working Draft (March 2026). It replaces pass/fail with Bronze/Silver/Gold conformance and \"Outcomes\" instead of \"Success Criteria.\" It is NOT yet a standard — continue targeting WCAG 2.2 AA.\n\n## Legal Enforcement Context (2026)\n\n- **European Accessibility Act (EAA)**: Enforced since June 2025 across all 27 EU member states. Applies to digital products and services. Fines up to EUR 3 million. References EN 301 549 (maps to WCAG 2.1 AA).\n- **ADA Title II (US)**: Digital accessibility rule effective April 2026 for state/local governments serving 50,000+ people (April 2027 for smaller entities). Requires WCAG 2.1 AA.\n- **Section 508 (US Federal)**: References WCAG 2.0 AA (refresh to 2.1/2.2 expected).\n\n**Target**: WCAG 2.2 AA covers all current legal requirements (superset of 2.1 AA and 2.0 AA).\n\n---\n\n## Five Rules of ARIA\n\n1. **Prefer native HTML** — Use `\u003cbutton\u003e` not `\u003cdiv role=\"button\"\u003e`. Native elements have built-in keyboard, focus, and semantics.\n2. **Don't change native semantics where prohibited** — Don't add `role=\"heading\"` to a `\u003cbutton\u003e`. Use the correct element.\n3. **All ARIA controls must be keyboard operable** — If `role=\"button\"`, handle Enter and Space key events.\n4. **Don't use `aria-hidden=\"true\"` on focusable elements** — Hidden from assistive tech but still focusable creates a \"ghost\" element.\n5. **All interactive elements need an accessible name** — Via label, `aria-label`, `aria-labelledby`, or visible text content.\n\n---\n\n## Semantic HTML Anti-Patterns (S1-S8)\n\n### S1: Missing `lang` Attribute on `\u003chtml\u003e`\n\n- **Severity**: CRITICAL\n- **Detection**: `\u003chtml` without `lang=`\n- **WCAG**: 3.1.1 (A)\n\n```html\n\u003c!-- BAD --\u003e\n\u003chtml\u003e\n\n\u003c!-- GOOD --\u003e\n\u003chtml lang=\"en\"\u003e\n```\n\nNext.js: Set in `app/layout.tsx`. Angular: Set in `src/index.html`. Vue/Nuxt: Set in `app.vue` or `nuxt.config`.\n\n### S2: Multiple `\u003ch1\u003e` Per Page\n\n- **Severity**: SUGGESTION\n- **Detection**: Multiple `\u003ch1\u003e` elements that make the page heading structure unclear\n- **WCAG**: Best practice (supports 1.3.1)\n\nPrefer one `\u003ch1\u003e` per page representing the main topic. Use `\u003ch2\u003e` for sections. Multiple `\u003ch1\u003e` elements are not a strict WCAG violation but can confuse screen reader navigation.\n\n### S3: Heading Level Gaps\n\n- **Severity**: IMPORTANT\n- **Detection**: `\u003ch1\u003e` followed by `\u003ch3\u003e` (skipping `\u003ch2\u003e`)\n- **WCAG**: 1.3.1 (A)\n\nMaintain logical nesting: `h1 \u003e h2 \u003e h3 \u003e h4`. Style headings with CSS, not by choosing a different heading level.\n\n### S4: Div Soup — No Landmark Elements\n\n- **Severity**: IMPORTANT\n- **Detection**: Pages using only `\u003cdiv\u003e` without `\u003cnav\u003e`, `\u003cmain\u003e`, `\u003cheader\u003e`, `\u003cfooter\u003e`\n- **WCAG**: Best practice (supports 1.3.1, 2.4.1)\n\n```html\n\u003c!-- GOOD --\u003e\n\u003cheader\u003e...\u003c/header\u003e\n\u003cnav aria-label=\"Main\"\u003e...\u003c/nav\u003e\n\u003cmain\u003e...\u003c/main\u003e\n\u003cfooter\u003e...\u003c/footer\u003e\n```\n\n### S5: Layout Tables Without `role=\"presentation\"`\n\n- **Severity**: IMPORTANT\n- **Detection**: `\u003ctable\u003e` without `\u003cth\u003e`, `\u003ccaption\u003e`, or `role=\"presentation\"`\n- **WCAG**: 1.3.1 (A)\n\nUse CSS Grid/Flexbox for layout. If table must be used for layout, add `role=\"presentation\"`.\n\n### S6: Data Tables Without Headers\n\n- **Severity**: CRITICAL\n- **Detection**: `\u003ctable\u003e` with data rows but no `\u003cth\u003e` elements\n- **WCAG**: 1.3.1 (A)\n\n```html\n\u003c!-- GOOD --\u003e\n\u003ctable\u003e\n  \u003ccaption\u003eUser list\u003c/caption\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\u003cth scope=\"col\"\u003eName\u003c/th\u003e\u003cth scope=\"col\"\u003eEmail\u003c/th\u003e\u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\u003ctd\u003eAlice\u003c/td\u003e\u003ctd\u003ealice@example.com\u003c/td\u003e\u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n```\n\n### S7: Non-Descriptive Link Text\n\n- **Severity**: IMPORTANT\n- **Detection**: `\u003eclick here\u003c|\u003eread more\u003c|\u003elearn more\u003c|\u003ehere\u003c|\u003emore\u003c|\u003elink\u003c`\n- **WCAG**: 2.4.4 (A)\n\n```html\n\u003c!-- BAD --\u003e\n\u003ca href=\"/pricing\"\u003eClick here\u003c/a\u003e\n\n\u003c!-- GOOD --\u003e\n\u003ca href=\"/pricing\"\u003eView pricing plans\u003c/a\u003e\n```\n\n### S8: Interactive Elements Without Semantic HTML\n\n- **Severity**: CRITICAL\n- **Detection**: `\u003cdiv.*(?:onClick|@click|\\(click\\))`\n- **WCAG**: 4.1.2 (A)\n\n```tsx\n// BAD — not focusable, no role, no keyboard support\n\u003cdiv onClick={handleClick}\u003eSubmit\u003c/div\u003e\n\n// GOOD\n\u003cbutton onClick={handleClick}\u003eSubmit\u003c/button\u003e\n```\n\n---\n\n## ARIA Anti-Patterns (A1-A8)\n\n### A1: Redundant ARIA on Native Elements\n\n- **Severity**: SUGGESTION\n- **Detection**: `\u003cbutton.*role=\"button\"|\u003cnav.*role=\"navigation\"|\u003ca.*role=\"link\"`\n- **WCAG**: ARIA Rule 2\n\nRemove redundant ARIA. `\u003cbutton\u003e` already has `role=\"button\"`.\n\n### A2: `aria-hidden=\"true\"` on Focusable Element\n\n- **Severity**: CRITICAL\n- **Detection**: `aria-hidden=\"true\"` on focusable elements (button, input, a, [tabindex])\n- **WCAG**: ARIA Rule 4\n\nDo not leave focusable elements inside `aria-hidden=\"true\"` content or use `aria-hidden=\"true\"` on focusable elements. Rather, for native controls that support it, such as `\u003cbutton\u003e` or `\u003cinput\u003e`, use `disabled` if disabling the control is the intended behavior. For `\u003ca\u003e` elements or arbitrary elements made focusable with `[tabindex]`, remove focusability instead (for example, remove `href` or `tabindex`). If the content should be completely non-interactive and hidden from assistive technology, use `inert`, `hidden`, or remove it from the DOM.\n\n### A3: Missing Required ARIA Properties\n\n- **Severity**: CRITICAL\n- **Detection**: `role=\"tab\"` without `aria-selected`, `role=\"checkbox\"` without `aria-checked`\n- **WCAG**: 4.1.2 (A)\n\nRequired per role: `tab` needs `aria-selected`; `combobox` needs `aria-expanded`/`aria-controls`; `slider` needs `aria-valuemin`/`aria-valuemax`/`aria-valuenow`; `checkbox` needs `aria-checked`.\n\n### A4: Invalid ARIA Role Values\n\n- **Severity**: CRITICAL\n- **Detection**: `role=\"[^\"]*\"` with non-existent values\n- **WCAG**: 4.1.2 (A)\n\nInvalid roles are ignored by assistive technology. Common mistakes: `role=\"input\"`, `role=\"text\"`, misspellings.\n\n### A5: ARIA Where Native HTML Works\n\n- **Severity**: IMPORTANT\n- **Detection**: `role=\"button\"` on `\u003cdiv\u003e`, `role=\"checkbox\"` on `\u003cdiv\u003e`\n- **WCAG**: ARIA Rule 1\n\n```html\n\u003c!-- BAD — requires manual keyboard, focus, and state management --\u003e\n\u003cdiv role=\"checkbox\" aria-checked=\"false\" tabindex=\"0\"\u003eAccept terms\u003c/div\u003e\n\n\u003c!-- GOOD — all behavior built-in --\u003e\n\u003clabel\u003e\u003cinput type=\"checkbox\" /\u003e Accept terms\u003c/label\u003e\n```\n\n### A6: Missing `aria-label` on Icon-Only Buttons\n\n- **Severity**: CRITICAL\n- **Detection**: `\u003cbutton` with SVG/icon child and no text or `aria-label`\n- **WCAG**: 4.1.2 (A)\n\n```html\n\u003c!-- GOOD --\u003e\n\u003cbutton aria-label=\"Close dialog\"\u003e\u003csvg aria-hidden=\"true\"\u003e...\u003c/svg\u003e\u003c/button\u003e\n```\n\n### A7: `role=\"presentation\"` on Focusable Elements\n\n- **Severity**: IMPORTANT\n- **Detection**: `role=\"presentation\"` on interactive elements\n- **WCAG**: ARIA Rule 4\n\nBrowsers will ignore the presentation role on focusable elements.\n\n### A8: Missing Live Region for Dynamic Content\n\n- **Severity**: IMPORTANT\n- **Detection**: Toast/notification components without `role=\"alert\"`, `role=\"status\"`, or `aria-live`\n- **WCAG**: 4.1.3 (AA)\n\n```html\n\u003c!-- GOOD — content announced when content is injected into a preexisting live region element in the DOM --\u003e\n\u003cdiv role=\"status\" aria-live=\"polite\"\u003eItem saved successfully\u003c/div\u003e\n\u003c!-- Use role=\"alert\" (assertive) for errors --\u003e\n\u003cdiv role=\"alert\"\u003eFailed to save. Please try again.\u003c/div\u003e\n```\n\n---\n\n## Keyboard and Focus Anti-Patterns (K1-K7)\n\n### K1: `onClick` Without `onKeyDown` on Non-Native Elements\n\n- **Severity**: CRITICAL\n- **Detection**: `(?:onClick|@click|\\(click\\))` on `\u003cdiv\u003e` or `\u003cspan\u003e` without keyboard handler\n- **WCAG**: 2.1.1 (A)\n\nUse `\u003cbutton\u003e` instead. If a div is required: add `role=\"button\"`, `tabIndex={0}`, handle Enter and Space key activation.\n\n### K2: Positive `tabindex` Values\n\n- **Severity**: CRITICAL\n- **Detection**: `(?:tabindex=\"[1-9]\\d*\"|tabIndex=\\{[1-9]\\d*\\})`\n- **WCAG**: 2.4.3 (A)\n\nOnly use `tabindex=\"0\"` (add to tab order) and `tabindex=\"-1\"` (programmatic focus only).\n\n### K3: Focus Trap Without Escape\n\n- **Severity**: CRITICAL\n- **Detection**: Modal/overlay without Escape key handler or focus trapping\n- **WCAG**: 2.1.2 (A)\n\nUse native `\u003cdialog\u003e` with `showModal()` — it prevents keyboard focus from moving to the inert non-dialog content. Additionally, it has built in Escape key to dismiss, and focus will automatically return to the invoking element (if available). If a custom modal dialog implementation is needed: trap Tab within the dialog or use the `inert` attribute for non-dialog content (do not use `inert` on an element that contains the dialog), dismiss on Escape (unless user confirmation of an action is essential), return focus to the trigger element on close, or to best logical location if triggering element is no longer present upon dismissal.\n\n### K4: Missing Skip Link\n\n- **Severity**: IMPORTANT\n- **Detection**: No skip link as first focusable element\n- **WCAG**: 2.4.1 (A)\n\n```html\n\u003ca href=\"#main-content\" class=\"skip-link\"\u003eSkip to main content\u003c/a\u003e\n\u003cnav\u003e...\u003c/nav\u003e\n\u003cmain id=\"main-content\" tabindex=\"-1\"\u003e...\u003c/main\u003e\n```\n\n```css\n.skip-link { position: absolute; top: -40px; left: 0; padding: 8px 16px; background: #000; color: #fff; z-index: 100; }\n.skip-link:focus { top: 0; }\n```\n\n### K5: `outline: none` Without Replacement\n\n- **Severity**: CRITICAL\n- **Detection**: `outline:\\s*none|outline:\\s*0\\b` without `:focus-visible` replacement\n- **WCAG**: 2.4.7 (AA)\n\n```css\n/* GOOD */\nbutton:focus-visible { outline: 2px solid #005fcc; outline-offset: 2px; }\n```\n\n### K6: Mouse-Only Interactions\n\n- **Severity**: IMPORTANT\n- **Detection**: `onMouseOver|onMouseEnter|@mouseenter` without keyboard equivalent\n- **WCAG**: 2.1.1 (A)\n\nPair hover with focus events. Use `onFocus`/`onBlur` alongside `onMouseEnter`/`onMouseLeave`.\n\n### K7: Focus Not Returned After Custom Modal Close\n\n- **Severity**: IMPORTANT\n- **Detection**: Custom modal dialog close without restoring focus to trigger\n- **WCAG**: 2.4.3 (A)\n\nStore reference to trigger element or to best logical location if triggering element is no longer present upon close. On modal close, call `triggerElement.focus()`.\n\n---\n\n## Form Anti-Patterns (F1-F6)\n\n### F1: Input Without Associated Label\n\n- **Severity**: CRITICAL\n- **Detection**: `\u003cinput|\u003cselect|\u003ctextarea` without `\u003clabel\u003e`, `aria-label`, or `aria-labelledby`\n- **WCAG**: 1.3.1 (A), 3.3.2 (A)\n\n```html\n\u003c!-- GOOD --\u003e\n\u003clabel for=\"email\"\u003eEmail address\u003c/label\u003e\n\u003cinput id=\"email\" type=\"email\" placeholder=\"you@example.com\" /\u003e\n```\n\n### F2: Error Messages Not Linked to Input\n\n- **Severity**: CRITICAL\n- **Detection**: Error elements near inputs without `aria-describedby`\n- **WCAG**: 3.3.1 (A)\n\n```html\n\u003cinput id=\"email\" type=\"email\" aria-describedby=\"email-error\" aria-invalid=\"true\" /\u003e\n\u003cspan id=\"email-error\" class=\"error\"\u003eInvalid email format\u003c/span\u003e\n```\n\n### F3: Required Field Indicated Only by Color or `*`\n\n- **Severity**: IMPORTANT\n- **Detection**: `required` or `*` without `aria-required=\"true\"` or HTML `required`\n- **WCAG**: 3.3.2 (A), 1.4.1 (A)\n\n```html\n\u003clabel for=\"name\"\u003eName \u003cspan aria-hidden=\"true\"\u003e*\u003c/span\u003e\u003c/label\u003e\n\u003cinput id=\"name\" type=\"text\" required /\u003e\n\u003cp class=\"form-note\"\u003eFields marked * are required\u003c/p\u003e\n```\n\n### F4: No Error Summary or Focus on First Error\n\n- **Severity**: IMPORTANT\n- **Detection**: Form submit handler without focus management on validation failure\n- **WCAG**: 3.3.1 (A)\n\nOn submit failure, focus the first invalid field or show and focus an error summary.\n\n### F5: CAPTCHA Without Accessible Alternative\n\n- **Severity**: IMPORTANT\n- **Detection**: Puzzle/image CAPTCHAs without fallback; password fields with `autocomplete='off'` or paste-blocking JavaScript\n- **WCAG**: 3.3.8 (AA)\n\nUse reCAPTCHA v3 (invisible), hCaptcha accessibility mode, or alternative authentication. Never block paste or autofill on password fields — this violates WCAG 3.3.8.\n\n### F6: Placeholder as Label\n\n- **Severity**: IMPORTANT\n- **Detection**: `placeholder=` without accompanying `\u003clabel\u003e`, `aria-label`, or `aria-labelledby`\n- **WCAG**: 3.3.2 (A)\n\nAlways pair placeholder with a visible `\u003clabel\u003e`. Placeholder is a hint, not a label.\n\n---\n\n## Visual and Color Anti-Patterns (V1-V5)\n\n### V1: Insufficient Text Contrast\n\n- **Severity**: CRITICAL\n- **Detection**: Text color combinations below 4.5:1 (normal) or 3:1 (large)\n- **WCAG**: 1.4.3 (AA)\n\n```css\n/* BAD — #999 on #fff is ~2.5:1 */\n.text { color: #999; background: #fff; }\n\n/* GOOD — #595959 on #fff is 7.0:1 */\n.text { color: #595959; background: #fff; }\n```\n\n### V2: Information Conveyed by Color Alone\n\n- **Severity**: CRITICAL\n- **Detection**: Error/success states distinguished only by color\n- **WCAG**: 1.4.1 (A)\n\nAdd a secondary indicator: icon, text, pattern, underline, or border.\n\n### V3: Fixed Font Sizes Preventing Resize\n\n- **Severity**: IMPORTANT\n- **Detection**: `font-size:\\s*[0-9]*px` (excluding root/base)\n- **WCAG**: 1.4.4 (AA)\n\nUse `rem` or `em` for font sizes. Base font can be `px`, but content fonts should be relative.\n\n### V4: Content Not Reflowing at 320px\n\n- **Severity**: IMPORTANT\n- **Detection**: Fixed-width containers, horizontal scroll at narrow widths\n- **WCAG**: 1.4.10 (AA)\n\nUse responsive layouts (Grid, Flexbox). Test at 320px CSS width. Avoid fixed-width containers.\n\n### V5: Animation Without `prefers-reduced-motion`\n\n- **Severity**: SUGGESTION\n- **Detection**: `animation:|transition:` without `prefers-reduced-motion` media query\n- **WCAG**: 2.3.3 (AAA)\n\nBest practice and AAA enhancement. Gate non-essential animations behind `prefers-reduced-motion` so users who request less motion are not forced to experience interaction-triggered effects.\n\n```css\n@media (prefers-reduced-motion: no-preference) {\n  .card { transition: transform 0.3s ease; }\n  .card:hover { transform: scale(1.05); }\n}\n```\n\n---\n\n## Media Anti-Patterns (D1-D4)\n\n### D1: Informational Image Without Alt Text\n\n- **Severity**: CRITICAL\n- **Detection**: `\u003cimg|\u003cImage` without `alt=` attribute\n- **WCAG**: 1.1.1 (A)\n\nAlt text decision tree: decorative = `alt=\"\"`; contains text = include that text; functional = describe action; informational = describe content.\n\n### D2: Decorative Image with Non-Empty Alt\n\n- **Severity**: SUGGESTION\n- **Detection**: Decorative images with meaningful alt text\n- **WCAG**: 1.1.1 (A)\n\nUse `alt=\"\"` for decorative images. Add `aria-hidden=\"true\"` for decorative SVGs.\n\n### D3: Video Without Captions\n\n- **Severity**: CRITICAL\n- **Detection**: `\u003cvideo` without `\u003ctrack kind=\"captions\"\u003e`\n- **WCAG**: 1.2.2 (A)\n\n```html\n\u003cvideo src=\"/tutorial.mp4\" controls\u003e\n  \u003ctrack kind=\"captions\" src=\"/tutorial-en.vtt\" srclang=\"en\" label=\"English\" default /\u003e\n\u003c/video\u003e\n```\n\n### D4: Audio/Video Autoplay\n\n- **Severity**: IMPORTANT\n- **Detection**: `autoplay` attribute without `muted`\n- **WCAG**: 1.4.2 (A)\n\nNever autoplay audio. If video autoplays, start muted with controls.\n\n---\n\n## Framework-Specific: React / Next.js (RX1-RX4)\n\n### RX1: Missing `htmlFor` on `\u003clabel\u003e`\n\n- **Severity**: IMPORTANT\n- **Detection**: `\u003clabel.*for=\"` in JSX (should be `htmlFor`)\n- **WCAG**: 1.3.1 (A), 3.3.2 (A)\n\n### RX2: SPA Route Change Without Focus Management\n\n- **Severity**: IMPORTANT\n- **Detection**: Navigation without focus management or live region\n- **WCAG**: 4.1.3 (AA)\n\nAfter route change, focus the main heading or announce the new page title via a live region. Next.js includes a built-in route announcer (since v13) that reads `document.title`, then `\u003ch1\u003e`, then pathname. Ensure every page has a unique `\u003ctitle\u003e`.\n\n### RX3: Fragment Root Causing Focus Loss on Re-render\n\n- **Severity**: SUGGESTION\n- **Detection**: `\u003c\u003e...\u003c/\u003e` root with conditional rendering causing DOM restructuring\n- **WCAG**: 2.4.3 (A)\n\nUse `key` prop to preserve DOM identity, or manually restore focus with `useRef` + `useEffect`.\n\n### RX4: Injected HTML Without ARIA Consideration\n\n- **Severity**: IMPORTANT\n- **Detection**: Rich text rendering without accessibility validation\n- **WCAG**: 1.3.1 (A)\n\nSanitize and validate injected HTML for heading hierarchy, alt text, and ARIA structure.\n\n---\n\n## Framework-Specific: Angular (NG1-NG4)\n\n### NG1: `(click)` on `\u003cdiv\u003e` Without Role and Keyboard Support\n\n- **Severity**: CRITICAL\n- **Detection**: `(click)` on `\u003cdiv\u003e` or `\u003cspan\u003e` without `role=`, `tabindex`, `(keydown)`\n- **WCAG**: 2.1.1 (A), 4.1.2 (A)\n\nUse `\u003cbutton\u003e`. If div required: add `role=\"button\"`, `tabindex=\"0\"`, `(keydown.enter)`, `(keydown.space)`.\n\n### NG2: Missing `cdkTrapFocus` in Modal Components\n\n- **Severity**: IMPORTANT\n- **Detection**: Modal components without `cdkTrapFocus`\n- **WCAG**: 2.1.2 (A)\n\n```html\n\u003cdiv class=\"modal\" cdkTrapFocus [cdkTrapFocusAutoCapture]=\"true\"\u003e...\u003c/div\u003e\n```\n\nAngular CDK's `Dialog` service handles focus trapping and restoration automatically.\n\n### NG3: Route Change Without LiveAnnouncer\n\n- **Severity**: IMPORTANT\n- **Detection**: Angular Router navigation without `LiveAnnouncer`\n- **WCAG**: 4.1.3 (AA)\n\n```typescript\nrouter.events.pipe(filter(e =\u003e e instanceof NavigationEnd)).subscribe(() =\u003e {\n  liveAnnouncer.announce(titleService.getTitle(), 'polite');\n});\n```\n\n### NG4: Template-Driven Forms Without Accessible Validation\n\n- **Severity**: IMPORTANT\n- **Detection**: Forms showing errors without `[attr.aria-invalid]` or `[attr.aria-describedby]`\n- **WCAG**: 3.3.1 (A), 3.3.3 (AA)\n\nBind `[attr.aria-invalid]` and `[attr.aria-describedby]` to form control state.\n\n---\n\n## Framework-Specific: Vue (VU1-VU3)\n\n### VU1: `@click` on Non-Interactive Element Without Role and Keyboard\n\n- **Severity**: CRITICAL\n- **Detection**: `@click` on `\u003cdiv\u003e` or `\u003cspan\u003e` without `role=`, `tabindex`, `@keydown`\n- **WCAG**: 2.1.1 (A), 4.1.2 (A)\n\nUse `\u003cbutton\u003e`. Or add `role=\"button\"`, `tabindex=\"0\"`, `@keydown.enter`, `@keydown.space.prevent`.\n\n### VU2: `v-if` Toggle Without Focus Management\n\n- **Severity**: IMPORTANT\n- **Detection**: `v-if` toggling without managing focus via `nextTick`\n- **WCAG**: 2.4.3 (A)\n\n```vue\n\u003cscript setup\u003e\nimport { ref, watch, nextTick } from 'vue';\nconst showPanel = ref(false);\nconst panel = ref(null);\nwatch(showPanel, async (val) =\u003e {\n  if (val) { await nextTick(); panel.value?.focus(); }\n});\n\u003c/script\u003e\n```\n\n### VU3: `v-html` Injecting Content Without Accessible Structure\n\n- **Severity**: IMPORTANT\n- **Detection**: `v-html` rendering user or CMS content\n- **WCAG**: 1.3.1 (A)\n\nSanitize and validate HTML for heading hierarchy, alt text, and ARIA structure before injection.\n\n---\n\n## Keyboard Interaction Reference\n\n| Key | Expected Behavior |\n|-----|-------------------|\n| `Tab` | Move focus to next focusable element in DOM order |\n| `Shift+Tab` | Move focus to previous focusable element |\n| `Enter` | Activate buttons and links |\n| `Space` | Activate buttons, toggle checkboxes, select radio buttons |\n| `Escape` | Close modals, dialogs, popovers, dropdowns |\n| `Arrow Up/Down` | Navigate within menus, listboxes, radio groups, tabs |\n| `Arrow Left/Right` | Navigate within tab bars, sliders, radio groups |\n| `Home` | Move to first item in list, menu, or tab bar |\n| `End` | Move to last item in list, menu, or tab bar |\n\n### Widget-Specific Patterns\n\n| Widget | Tab enters | Internal nav | Activate | Exit |\n|--------|-----------|-------------|----------|------|\n| Tab bar | Focus active tab | Arrow Left/Right | automatic or Enter | Tab out |\n| Menu | Focus first item | Arrow Up/Down | Enter | Escape |\n| Dialog | Focus first element | Tab cycles within | Enter on buttons | Escape |\n| Combobox | Focus input | Arrow Up/Down | Enter selects | Escape closes |\n| Tree view | Focus first node | Arrow keys | Enter/Space | Tab out |\n\n---\n\n## Color Contrast Quick Reference\n\n### Text Contrast (WCAG 1.4.3 AA)\n\n| Text Type | Minimum Ratio |\n|-----------|--------------|\n| Normal text (\u003c 18pt / \u003c 14pt bold) | 4.5:1 |\n| Large text (\u003e= 18pt / \u003e= 14pt bold) | 3:1 |\n| Incidental (disabled, decorative) | No requirement |\n\n### Non-Text Contrast (WCAG 1.4.11 AA)\n\n| Element | Minimum Ratio |\n|---------|--------------|\n| UI components (borders, icons) | 3:1 against adjacent |\n| Graphical objects | 3:1 against adjacent |\n| Focus indicators | 3:1 against background |\n\n---\n\n## Accessibility Checklist (POUR)\n\n### Perceivable\n- [ ] All images have appropriate alt text (descriptive or empty for decorative)\n- [ ] Videos have synchronized captions\n- [ ] Page uses semantic landmarks: `\u003cheader\u003e`, `\u003cnav\u003e`, `\u003cmain\u003e`, `\u003cfooter\u003e`\n- [ ] Headings follow logical hierarchy (h1 \u003e h2 \u003e h3, no gaps)\n- [ ] Text contrast meets 4.5:1 (normal) / 3:1 (large)\n- [ ] UI component contrast meets 3:1\n- [ ] Information not conveyed by color alone\n- [ ] Content reflows at 320px without horizontal scroll\n- [ ] `\u003chtml lang=\"...\"\u003e` is set correctly\n- [ ] Text resizable to 200% without loss of content\n\n### Operable\n- [ ] All functionality accessible via keyboard\n- [ ] No keyboard traps (Escape closes overlays)\n- [ ] Skip link provided as first focusable element\n- [ ] Focus indicator visible on all interactive elements\n- [ ] Focus order matches visual order\n- [ ] Focus not obscured by sticky headers/footers\n- [ ] Focus returned to trigger after modal close\n- [ ] Touch targets at least 24x24 CSS px\n- [ ] Animations respect `prefers-reduced-motion`\n- [ ] No content flashes more than 3 times per second\n\n### Understandable\n- [ ] All form inputs have associated `\u003clabel\u003e` or `aria-label`\n- [ ] Error messages linked to inputs via `aria-describedby`\n- [ ] Required fields indicated with `required` or `aria-required`\n- [ ] Error summary or focus-on-first-error on submit failure\n- [ ] No unexpected context changes on focus or input\n\n### Robust\n- [ ] All interactive elements have accessible name, role, and state\n- [ ] ARIA roles have required properties\n- [ ] No `aria-hidden=\"true\"` on focusable elements\n- [ ] Dynamic content announced via live regions\n- [ ] SPA route changes announced to screen readers\n- [ ] No redundant ARIA on native HTML elements\n","description":"Comprehensive web accessibility standards based on WCAG 2.2 AA, with 38+ anti-patterns, legal enforcement context (EAA, ADA Title II), WAI-ARIA patterns, and framework-specific fixes for modern web frameworks and libraries.","import":{"commit_sha":"541b7819d8c3545c6df122491af4fa1eae415779","imported_at":"2026-05-18T20:05:35Z","license_text":"MIT License\n\nCopyright GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","owner":"github","repo":"github/awesome-copilot","source_url":"https://github.com/github/awesome-copilot/blob/541b7819d8c3545c6df122491af4fa1eae415779/instructions/a11y.instructions.md"},"manifest":{}},"content_hash":[74,3,192,205,176,76,185,42,43,80,218,248,207,174,78,248,245,203,36,126,42,49,105,143,203,91,71,25,144,205,70,158],"trust_level":"unsigned","yanked":false}
