` -- not keyboard accessible |
+| 4 | 3.1.1 | Minor | `` missing `lang` attribute (check `layout.tsx`) |
+
+```tsx
+// AFTER: src/app/page.tsx
+import type { Metadata } from 'next';
+import Link from 'next/link';
+
+export const metadata: Metadata = {
+ title: 'Acme - Build Better Products',
+ description: 'Build better products with the Acme platform.',
+};
+
+export default function Home() {
+ return (
+
+ Welcome to Acme
+
+ Build better products with our platform.
+
+
+ Get Started
+
+
+ );
+}
+```
+
+```tsx
+// Also fix: src/app/layout.tsx
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+```
+
+## Example 4: Svelte Component Audit
+
+```svelte
+
+
+
+{#each items as item, i}
+
+ {#if openIndex === i}
+
{item.body}
+ {/if}
+{/each}
+```
+
+**Violations detected:**
+
+| # | WCAG | Severity | Issue |
+|---|------|----------|-------|
+| 1 | 4.1.2 | Critical | Accordion missing ARIA roles and properties |
+| 2 | 2.1.1 | Critical | Headers not keyboard accessible |
+| 3 | 2.5.8 | Minor | Click targets may be smaller than 24x24px (NEW in WCAG 2.2) |
+
+```svelte
+
+
+
+
+ {#each items as item, i}
+
+
+
+
+ {item.body}
+
+ {/each}
+
+
+
+```
diff --git a/engineering-team/a11y-audit/references/framework-a11y-patterns.md b/engineering-team/a11y-audit/references/framework-a11y-patterns.md
index cda2732..5e811e8 100644
--- a/engineering-team/a11y-audit/references/framework-a11y-patterns.md
+++ b/engineering-team/a11y-audit/references/framework-a11y-patterns.md
@@ -321,3 +321,341 @@ export class MyComponent {
border-width: 0;
}
```
+
+## Fix Patterns Catalog
+
+### React / Next.js Fix Patterns
+
+#### Missing Alt Text (1.1.1)
+
+```tsx
+// BEFORE
+

+
+// AFTER - Informational image
+

+
+// AFTER - Decorative image
+

+```
+
+#### Non-Interactive Element with Click Handler (2.1.1)
+
+```tsx
+// BEFORE
+
Click me
+
+// AFTER - If it navigates
+
Click me
+
+// AFTER - If it performs an action
+
+```
+
+#### Missing Focus Management in Modals (2.4.3)
+
+```tsx
+// BEFORE
+function Modal({ isOpen, onClose, children }) {
+ if (!isOpen) return null;
+ return
{children}
;
+}
+
+// AFTER
+import { useEffect, useRef } from 'react';
+
+function Modal({ isOpen, onClose, children, title }) {
+ const modalRef = useRef(null);
+ const previousFocus = useRef(null);
+
+ useEffect(() => {
+ if (isOpen) {
+ previousFocus.current = document.activeElement;
+ modalRef.current?.focus();
+ } else {
+ previousFocus.current?.focus();
+ }
+ }, [isOpen]);
+
+ useEffect(() => {
+ if (!isOpen) return;
+ const handleKeydown = (e) => {
+ if (e.key === 'Escape') onClose();
+ if (e.key === 'Tab') {
+ const focusable = modalRef.current?.querySelectorAll(
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
+ );
+ if (!focusable?.length) return;
+ const first = focusable[0];
+ const last = focusable[focusable.length - 1];
+ if (e.shiftKey && document.activeElement === first) {
+ e.preventDefault();
+ last.focus();
+ } else if (!e.shiftKey && document.activeElement === last) {
+ e.preventDefault();
+ first.focus();
+ }
+ }
+ };
+ document.addEventListener('keydown', handleKeydown);
+ return () => document.removeEventListener('keydown', handleKeydown);
+ }, [isOpen, onClose]);
+
+ if (!isOpen) return null;
+
+ return (
+
+
e.stopPropagation()}
+ >
+
+ {children}
+
+
+ );
+}
+```
+
+#### Focus Appearance (2.4.11 -- NEW in WCAG 2.2)
+
+```css
+/* BEFORE */
+button:focus {
+ outline: none; /* Removes default focus indicator */
+}
+
+/* AFTER - Meets WCAG 2.2 Focus Appearance */
+button:focus-visible {
+ outline: 2px solid #005fcc;
+ outline-offset: 2px;
+}
+```
+
+```tsx
+// Tailwind CSS pattern
+
+```
+
+### Vue Fix Patterns
+
+#### Missing Form Labels (1.3.1)
+
+```vue
+
+
+
+
+
+
+```
+
+#### Dynamic Content Without Live Region (4.1.3)
+
+```vue
+
+
{{ statusMessage }}
+
+
+
+```
+
+#### Vue Router Navigation Announcements (2.4.2)
+
+```typescript
+// router/index.ts
+router.afterEach((to) => {
+ const title = to.meta.title || 'Page';
+ document.title = `${title} | My App`;
+
+ // Announce route change to screen readers
+ const announcer = document.getElementById('route-announcer');
+ if (announcer) {
+ announcer.textContent = `Navigated to ${title}`;
+ }
+});
+```
+
+```vue
+
+
+```
+
+### Angular Fix Patterns
+
+#### Missing ARIA on Custom Components (4.1.2)
+
+```typescript
+// BEFORE
+@Component({
+ selector: 'app-dropdown',
+ template: `
+
{{ selected }}
+
+ `
+})
+
+// AFTER
+@Component({
+ selector: 'app-dropdown',
+ template: `
+
+
+ `
+})
+```
+
+#### Angular CDK A11y Module Integration
+
+```typescript
+// Use Angular CDK for focus trap in dialogs
+import { A11yModule } from '@angular/cdk/a11y';
+
+@Component({
+ template: `
+
+
Edit Profile
+
+
+ `
+})
+```
+
+### Svelte Fix Patterns
+
+#### Accessible Announcements (4.1.3)
+
+```svelte
+
+{#if message}
+
{message}
+{/if}
+
+
+
+ {#if message}
+
{message}
+ {/if}
+
+
+ {#if message}
+
{message}
+ {/if}
+
+```
+
+#### SvelteKit Page Titles (2.4.2)
+
+```svelte
+
+
+ Dashboard | My App
+
+```
+
+### Plain HTML Fix Patterns
+
+#### Skip Navigation Link (2.4.1)
+
+```html
+
+
+
+
+
+
+
+
+
Skip to main content
+
+
+
+```
+
+```css
+.skip-link {
+ position: absolute;
+ top: -40px;
+ left: 0;
+ padding: 8px 16px;
+ background: #005fcc;
+ color: #fff;
+ z-index: 1000;
+ transition: top 0.2s;
+}
+.skip-link:focus {
+ top: 0;
+}
+```
+
+#### Accessible Data Table (1.3.1)
+
+```html
+
+
+ | Name | Email | Role |
+ | Alice | alice@co.com | Admin |
+
+
+
+
+ List of team members and their roles
+
+
+ | Name |
+ Email |
+ Role |
+
+
+
+
+ | Alice |
+ alice@co.com |
+ Admin |
+
+
+
+```
diff --git a/engineering-team/a11y-audit/references/testing-checklist.md b/engineering-team/a11y-audit/references/testing-checklist.md
new file mode 100644
index 0000000..12609b9
--- /dev/null
+++ b/engineering-team/a11y-audit/references/testing-checklist.md
@@ -0,0 +1,41 @@
+# Accessibility Testing Checklist
+
+Use this checklist after applying fixes to verify accessibility manually.
+
+## Keyboard Navigation
+- [ ] All interactive elements reachable via Tab key
+- [ ] Tab order follows visual/logical reading order
+- [ ] Focus indicator visible on every focusable element (2px+ outline)
+- [ ] Modals trap focus and return focus on close
+- [ ] Escape key closes modals, dropdowns, and popups
+- [ ] Arrow keys navigate within composite widgets (tabs, menus, listboxes)
+- [ ] No keyboard traps (user can always Tab away)
+
+## Screen Reader
+- [ ] All images have appropriate alt text (or `alt=""` for decorative)
+- [ ] Headings create logical document outline (h1 -> h2 -> h3)
+- [ ] Form inputs have associated labels
+- [ ] Error messages announced via `aria-live` or `role="alert"`
+- [ ] Page title updates on navigation (SPA)
+- [ ] Dynamic content changes announced appropriately
+
+## Visual
+- [ ] Text contrast meets 4.5:1 for normal text, 3:1 for large text
+- [ ] UI component contrast meets 3:1 against background
+- [ ] Content reflows without horizontal scrolling at 320px width
+- [ ] Text resizable to 200% without loss of content
+- [ ] No information conveyed by color alone
+- [ ] Focus indicators meet 2.4.11 Focus Appearance criteria
+
+## Motion and Media
+- [ ] Animations respect `prefers-reduced-motion`
+- [ ] No auto-playing media with audio
+- [ ] No content flashing more than 3 times per second
+- [ ] Video has captions; audio has transcripts
+
+## Forms
+- [ ] All inputs have visible labels
+- [ ] Required fields indicated (not by color alone)
+- [ ] Error messages specific and associated with input via `aria-describedby`
+- [ ] Autocomplete attributes present on common fields (name, email, etc.)
+- [ ] No CAPTCHA without alternative method (WCAG 2.2 3.3.8)
diff --git a/engineering-team/a11y-audit/references/wcag-22-new-criteria.md b/engineering-team/a11y-audit/references/wcag-22-new-criteria.md
new file mode 100644
index 0000000..b1841a8
--- /dev/null
+++ b/engineering-team/a11y-audit/references/wcag-22-new-criteria.md
@@ -0,0 +1,79 @@
+# WCAG 2.2 New Success Criteria Reference
+
+These criteria were added in WCAG 2.2 and are commonly missed.
+
+## 2.4.11 Focus Appearance (Level AA)
+
+The focus indicator must have a minimum area of a 2px perimeter around the component and a contrast ratio of at least 3:1 against adjacent colors.
+
+**Pattern:**
+```css
+:focus-visible {
+ outline: 2px solid #005fcc;
+ outline-offset: 2px;
+}
+```
+
+## 2.5.7 Dragging Movements (Level AA)
+
+Any functionality that uses dragging must have a single-pointer alternative (click, tap).
+
+**Pattern:**
+```tsx
+// Sortable list: support both drag and button-based reorder
+
+ {item.name}
+
+
+
+```
+
+## 2.5.8 Target Size (Level AA)
+
+Interactive targets must be at least 24x24 CSS pixels, with exceptions for inline text links and elements where the spacing provides equivalent clearance.
+
+**Pattern:**
+```css
+button, a, input, select, textarea {
+ min-height: 24px;
+ min-width: 24px;
+}
+
+/* Recommended: 44x44px for touch targets */
+@media (pointer: coarse) {
+ button, a, input[type="checkbox"], input[type="radio"] {
+ min-height: 44px;
+ min-width: 44px;
+ }
+}
+```
+
+## 3.3.7 Redundant Entry (Level A)
+
+Information previously entered by the user must be auto-populated or available for selection when needed again in the same process.
+
+**Pattern:**
+```tsx
+// Multi-step form: persist data across steps
+const [formData, setFormData] = useState({});
+
+// Step 2 pre-fills shipping address from billing
+
+```
+
+## 3.3.8 Accessible Authentication (Level AA)
+
+Authentication must not require cognitive function tests (e.g., remembering a password, solving a puzzle) unless an alternative is provided.
+
+**Pattern:**
+- Support password managers (`autocomplete="current-password"`)
+- Offer passkey / biometric authentication
+- Allow copy-paste in password fields (never block paste)
+- Provide email/SMS OTP as alternative to CAPTCHA