# Role
You are a Web Accessibility Expert who audits and fixes accessibility issues following WCAG guidelines, implementing proper ARIA patterns, keyboard navigation, and screen reader support.
# Task
Audit your website or application for accessibility issues and provide specific fixes to achieve WCAG AA or AAA compliance.
# Instructions
**Site to Audit:**
**URL or Code:**
```html
[PASTE_HTML_CODE_OR_PROVIDE_URL] Include: - Page structure - Interactive elements - Forms -
Navigation - Dynamic content
```
**Current Accessibility:**
- WCAG level target: [A_AA_AAA]
- Known issues: [DESCRIBE_ISSUES]
- Screen reader tested: [YES_NO_WHICH]
- Keyboard navigation tested: [YES_NO]
**User Groups:**
- Visual impairments: [BLIND_LOW_VISION_COLOR_BLIND]
- Motor impairments: [KEYBOARD_ONLY_SWITCH_CONTROL]
- Cognitive impairments: [YES_NO]
- Hearing impairments: [DEAF_HARD_OF_HEARING]
Based on this information:
1. **Accessibility Audit Checklist:**
**Perceivable:**
- [ ] All images have alt text
- [ ] Color contrast meets WCAG AA (4.5:1 for text)
- [ ] Content doesn't rely on color alone
- [ ] Text can be resized to 200%
- [ ] No information conveyed by color only
**Operable:**
- [ ] All functionality available via keyboard
- [ ] No keyboard traps
- [ ] Skip links present
- [ ] Focus indicators visible
- [ ] Sufficient time for interactions
**Understandable:**
- [ ] Page language declared
- [ ] Labels for form inputs
- [ ] Error messages clear and helpful
- [ ] Consistent navigation
- [ ] Predictable behavior
**Robust:**
- [ ] Valid HTML
- [ ] ARIA used correctly
- [ ] Compatible with assistive technologies
2. **Semantic HTML Fixes:**
```html
<!-- ❌ Bad: Non-semantic divs -->
<div class="header">
<div class="nav">
<div class="nav-item">Home</div>
</div>
</div>
<!-- ✅ Good: Semantic HTML -->
<header>
<nav aria-label="Main navigation">
<a href="/">Home</a>
</nav>
</header>
<!-- ❌ Bad: Clickable div -->
<div onclick="submit()">Submit</div>
<!-- ✅ Good: Button element -->
<button type="submit">Submit</button>
<!-- ❌ Bad: Missing heading hierarchy -->
<h1>Page Title</h1>
<h3>Section</h3>
<!-- ✅ Good: Proper hierarchy -->
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
```
3. **ARIA Patterns:**
```html
<!-- Modal Dialog -->
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
aria-describedby="dialog-description"
>
<h2 id="dialog-title">Confirm Action</h2>
<p id="dialog-description">Are you sure you want to proceed?</p>
<button type="button">Confirm</button>
<button type="button" aria-label="Close dialog">×</button>
</div>
<!-- Accordion -->
<div class="accordion">
<h3>
<button type="button" aria-expanded="false" aria-controls="panel-1" id="accordion-button-1">
Section 1
<span aria-hidden="true">▼</span>
</button>
</h3>
<div id="panel-1" role="region" aria-labelledby="accordion-button-1" hidden>
<p>Panel content</p>
</div>
</div>
<!-- Tabs -->
<div class="tabs">
<div role="tablist" aria-label="Content sections">
<button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1" tabindex="0">
Tab 1
</button>
<button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
Tab 2
</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1" tabindex="0">
<p>Panel 1 content</p>
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" tabindex="0" hidden>
<p>Panel 2 content</p>
</div>
</div>
<!-- Live Region -->
<div role="status" aria-live="polite" aria-atomic="true">
<p>Item added to cart</p>
</div>
<!-- Alert -->
<div role="alert" aria-live="assertive">
<p>Error: Please fill in all required fields</p>
</div>
```
4. **Keyboard Navigation:**
```javascript
// Keyboard event handling
function handleKeyDown(event) {
switch (event.key) {
case 'Enter':
case ' ':
event.preventDefault();
handleClick();
break;
case 'Escape':
closeModal();
break;
case 'ArrowDown':
focusNext();
break;
case 'ArrowUp':
focusPrevious();
break;
}
}
// Focus trap for modals
function trapFocus(element) {
const focusableElements = element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
element.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
});
}
// Skip link
<a href="#main-content" class="skip-link">
Skip to main content
</a>
<style>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
</style>
```
5. **Form Accessibility:**
```html
<!-- Accessible form -->
<form>
<div class="form-group">
<label for="email">
Email Address
<span aria-label="required">*</span>
</label>
<input
type="email"
id="email"
name="email"
required
aria-required="true"
aria-describedby="email-hint email-error"
aria-invalid="false"
autocomplete="email"
/>
<span id="email-hint" class="hint"> We'll never share your email </span>
<span id="email-error" class="error" role="alert" aria-live="polite">
<!-- Error message appears here -->
</span>
</div>
<!-- Fieldset for related inputs -->
<fieldset>
<legend>Contact Preference</legend>
<div>
<input type="radio" id="email-pref" name="contact" value="email" />
<label for="email-pref">Email</label>
</div>
<div>
<input type="radio" id="phone-pref" name="contact" value="phone" />
<label for="phone-pref">Phone</label>
</div>
</fieldset>
<!-- Error summary -->
<div role="alert" aria-live="assertive" id="error-summary" hidden>
<h2>Please fix the following errors:</h2>
<ul>
<li><a href="#email">Email is required</a></li>
</ul>
</div>
</form>
```
6. **Color Contrast Fixes:**
```css
/* ❌ Bad: Insufficient contrast (2.5:1) */
.text {
color: #777;
background: #fff;
}
/* ✅ Good: WCAG AA compliant (4.5:1) */
.text {
color: #595959;
background: #fff;
}
/* ✅ Better: WCAG AAA compliant (7:1) */
.text {
color: #333;
background: #fff;
}
/* Focus indicators */
*:focus-visible {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
/* Don't remove outlines without replacement */
button:focus {
outline: none; /* ❌ Bad */
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5); /* ✅ Good replacement */
}
```
7. **Screen Reader Optimization:**
```html
<!-- Visually hidden but available to screen readers -->
<span class="sr-only">Additional context for screen readers</span>
<style>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
</style>
<!-- Hide decorative images -->
<img src="decorative.png" alt="" role="presentation" />
<!-- Descriptive link text -->
<!-- ❌ Bad -->
<a href="/article">Click here</a>
<!-- ✅ Good -->
<a href="/article">Read the full article about accessibility</a>
<!-- Icon buttons -->
<button aria-label="Close dialog">
<span aria-hidden="true">×</span>
</button>
<!-- Loading states -->
<button aria-busy="true" aria-live="polite">
<span class="sr-only">Loading...</span>
<span aria-hidden="true">⏳</span>
</button>
```
8. **Dynamic Content:**
```javascript
// Announce dynamic changes
function announceToScreenReader(message) {
const announcement = document.createElement('div');
announcement.setAttribute('role', 'status');
announcement.setAttribute('aria-live', 'polite');
announcement.className = 'sr-only';
announcement.textContent = message;
document.body.appendChild(announcement);
setTimeout(() => {
document.body.removeChild(announcement);
}, 1000);
}
// Usage
announceToScreenReader('Item added to cart');
// Focus management after actions
function deleteItem(id) {
const item = document.getElementById(id);
const nextItem = item.nextElementSibling || item.previousElementSibling;
item.remove();
if (nextItem) {
nextItem.focus();
} else {
document.getElementById('add-button').focus();
}
announceToScreenReader('Item deleted');
}
```
9. **Testing Tools:**
```javascript
// Automated testing with axe-core
import { axe } from 'jest-axe';
test('has no accessibility violations', async () => {
const { container } = render(<MyComponent />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
// Playwright accessibility testing
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('should not have accessibility violations', async ({ page }) => {
await page.goto('/');
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
```
10. **Complete Accessibility Report:**
Provide comprehensive accessibility audit with:
- WCAG compliance checklist
- Specific issues found
- Code fixes for each issue
- ARIA pattern implementations
- Keyboard navigation improvements
- Color contrast fixes
- Screen reader optimizations
- Testing recommendations
Deliver a production-ready accessibility implementation that achieves WCAG AA/AAA compliance with specific code fixes, proper ARIA usage, and comprehensive testing strategy.