Accessibility
Professional development studio for Claude Code CLI
npx -y skills add manastalukdar/ai-devstudio --skill accessibilityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Accessibility (a11y) compliance checking with WCAG 2.1 validation
SKILL.md
36.3 KB, as published. Nobody here has run it
Accessibility (a11y) Compliance Checking
I'll analyze your web application for accessibility issues, validate WCAG 2.1 compliance, check ARIA attributes, verify keyboard navigation, validate color contrast, and generate comprehensive accessibility reports.
Compliance Standards:
- WCAG 2.1 Level A (minimum compliance)
- WCAG 2.1 Level AA (target standard)
- WCAG 2.1 Level AAA (enhanced accessibility)
- Section 508 (US federal requirements)
- EN 301 549 (EU accessibility requirements)
Frameworks Supported:
- React (React Testing Library, jest-axe)
- Vue.js (Vue Test Utils, vue-axe)
- Angular (Angular testing utilities)
- HTML/CSS (static analysis)
- Next.js, Gatsby, Nuxt.js
Arguments: $ARGUMENTS - optional:
- URL to audit (defaults to http://localhost:3000)
- WCAG level (A, AA, AAA - defaults to AA)
- Focus area: --images, --forms, --keyboard, --contrast, --aria (for targeted analysis)
Common a11y issues:
- Missing alt text on images
- Insufficient color contrast
- Missing ARIA labels
- Non-semantic HTML
- Keyboard navigation barriers
- Missing form labels
- Poor heading hierarchy </think>
Phase 1: Framework & Tool Detection
First, I'll detect your framework and set up accessibility testing tools:
#!/bin/bash
# Accessibility Analysis - Framework Detection & Tool Setup
echo "=== Accessibility (a11y) Compliance Checking ==="
echo ""
# Create accessibility directory
mkdir -p .claude/accessibility
A11Y_DIR=".claude/accessibility"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
REPORT="$A11Y_DIR/a11y-report-$TIMESTAMP.md"
WCAG_LEVEL="${1:-AA}" # Default to WCAG 2.1 Level AA
TARGET_URL="${2:-http://localhost:3000}"
echo "Configuration:"
echo " WCAG Level: $WCAG_LEVEL"
echo " Target URL: $TARGET_URL"
echo " Analysis directory: $A11Y_DIR"
echo ""
detect_framework() {
echo "Detecting framework..."
echo ""
local framework=""
if [ ! -f "package.json" ]; then
echo "⚠️ No package.json found"
echo " This skill works best with JavaScript/TypeScript projects"
echo " Will perform generic HTML analysis"
framework="generic"
else
# React detection
if grep -q '"react"' package.json; then
framework="react"
echo "✓ React detected"
# Check for React Testing Library
if grep -q '@testing-library/react' package.json; then
echo " - React Testing Library available"
fi
# Check for jest-axe
if grep -q 'jest-axe' package.json; then
echo " - jest-axe available"
fi
# Vue detection
elif grep -q '"vue"' package.json; then
framework="vue"
echo "✓ Vue.js detected"
# Angular detection
elif grep -q '"@angular' package.json; then
framework="angular"
echo "✓ Angular detected"
# Next.js detection
elif grep -q '"next"' package.json; then
framework="next"
echo "✓ Next.js detected"
else
framework="generic"
echo "✓ Generic web project detected"
fi
fi
echo "$framework"
}
FRAMEWORK=$(detect_framework)
echo ""
Phase 2: Install Accessibility Tools
I'll install axe-core and related accessibility testing tools:
echo "=== Installing Accessibility Testing Tools ==="
echo ""
install_axe_tools() {
echo "Setting up axe-core (accessibility engine)..."
# Check for axe-core CLI
if ! command -v axe >/dev/null 2>&1 && ! npm list -g @axe-core/cli >/dev/null 2>&1; then
echo "Installing @axe-core/cli..."
npm install -g @axe-core/cli 2>/dev/null || npm install --save-dev @axe-core/cli
if [ $? -eq 0 ]; then
echo "✓ axe-core CLI installed"
else
echo "⚠️ Failed to install axe-core CLI"
return 1
fi
else
echo "✓ axe-core CLI already installed"
fi
# Framework-specific setup
case "$FRAMEWORK" in
react)
if ! grep -q 'jest-axe' package.json 2>/dev/null; then
echo ""
echo "Installing React accessibility tools..."
npm install --save-dev jest-axe @testing-library/react @testing-library/jest-dom 2>/dev/null
echo "✓ jest-axe and React Testing Library installed"
fi
;;
vue)
if ! grep -q 'vue-axe' package.json 2>/dev/null; then
echo ""
echo "Installing Vue accessibility tools..."
npm install --save-dev vue-axe 2>/dev/null
echo "✓ vue-axe installed"
fi
;;
esac
# Install pa11y for additional testing
if ! command -v pa11y >/dev/null 2>&1 && ! npm list -g pa11y >/dev/null 2>&1; then
echo ""
echo "Installing pa11y (accessibility testing tool)..."
npm install -g pa11y 2>/dev/null || echo "⚠️ pa11y installation skipped"
else
echo "✓ pa11y available"
fi
return 0
}
install_axe_tools
echo ""
Phase 3: Run Accessibility Audits
I'll run comprehensive accessibility tests:
echo "=== Running Accessibility Audits ==="
echo ""
# Check if server is accessible
check_server() {
echo "Checking if server is accessible at $TARGET_URL..."
if curl -s --head "$TARGET_URL" >/dev/null 2>&1; then
echo "✓ Server is accessible"
return 0
else
echo "⚠️ Server not accessible at $TARGET_URL"
echo ""
echo "Please start your development server:"
echo " npm start"
echo " npm run dev"
echo ""
echo "Or specify a different URL:"
echo " /accessibility $WCAG_LEVEL https://your-site.com"
return 1
fi
}
run_axe_analysis() {
echo "Running axe-core analysis (WCAG $WCAG_LEVEL)..."
echo ""
# Map WCAG level to axe tags
local axe_tags=""
case "$WCAG_LEVEL" in
A|a)
axe_tags="wcag2a,wcag21a"
;;
AA|aa)
axe_tags="wcag2a,wcag2aa,wcag21a,wcag21aa"
;;
AAA|aaa)
axe_tags="wcag2a,wcag2aa,wcag2aaa,wcag21a,wcag21aa,wcag21aaa"
;;
*)
axe_tags="wcag2a,wcag2aa,wcag21a,wcag21aa"
;;
esac
# Run axe-core
if command -v axe >/dev/null 2>&1 || npx axe --help >/dev/null 2>&1; then
npx axe "$TARGET_URL" \
--tags "$axe_tags" \
--save "$A11Y_DIR/axe-results.json" \
--stdout \
2>&1 | tee "$A11Y_DIR/axe-output.txt"
if [ $? -eq 0 ]; then
echo ""
echo "✓ axe-core analysis complete"
# Parse results
if [ -f "$A11Y_DIR/axe-results.json" ]; then
# Count violations by impact
CRITICAL=$(jq '[.violations[] | select(.impact=="critical")] | length' "$A11Y_DIR/axe-results.json" 2>/dev/null || echo "0")
SERIOUS=$(jq '[.violations[] | select(.impact=="serious")] | length' "$A11Y_DIR/axe-results.json" 2>/dev/null || echo "0")
MODERATE=$(jq '[.violations[] | select(.impact=="moderate")] | length' "$A11Y_DIR/axe-results.json" 2>/dev/null || echo "0")
MINOR=$(jq '[.violations[] | select(.impact=="minor")] | length' "$A11Y_DIR/axe-results.json" 2>/dev/null || echo "0")
echo ""
echo "Violations by Impact:"
echo " Critical: $CRITICAL"
echo " Serious: $SERIOUS"
echo " Moderate: $MODERATE"
echo " Minor: $MINOR"
echo " Total: $((CRITICAL + SERIOUS + MODERATE + MINOR))"
fi
else
echo "❌ axe-core analysis failed"
return 1
fi
else
echo "⚠️ axe-core CLI not available"
return 1
fi
return 0
}
run_pa11y_analysis() {
if command -v pa11y >/dev/null 2>&1; then
echo ""
echo "Running pa11y analysis..."
# Map WCAG level to pa11y standard
local pa11y_standard=""
case "$WCAG_LEVEL" in
A|a) pa11y_standard="WCAG2A" ;;
AA|aa) pa11y_standard="WCAG2AA" ;;
AAA|aaa) pa11y_standard="WCAG2AAA" ;;
*) pa11y_standard="WCAG2AA" ;;
esac
pa11y "$TARGET_URL" \
--standard "$pa11y_standard" \
--reporter json \
> "$A11Y_DIR/pa11y-results.json" 2>&1
if [ $? -eq 0 ]; then
echo "✓ pa11y analysis complete"
# Count issues
PA11Y_ISSUES=$(jq 'length' "$A11Y_DIR/pa11y-results.json" 2>/dev/null || echo "0")
echo " Issues found: $PA11Y_ISSUES"
else
echo "⚠️ pa11y analysis had issues (may still have results)"
fi
fi
}
# Run audits
if check_server; then
run_axe_analysis
run_pa11y_analysis
else
echo "Skipping live server analysis"
echo "Will provide general accessibility guidance"
fi
echo ""
Phase 4: Analyze Common Accessibility Issues
I'll check for common accessibility problems in the codebase:
echo "=== Analyzing Common Accessibility Issues ==="
echo ""
analyze_static_html() {
echo "Checking HTML/JSX/Vue files for common issues..."
echo ""
# Find source files
SOURCE_PATTERNS="-name '*.html' -o -name '*.jsx' -o -name '*.tsx' -o -name '*.vue'"
# Check for images without alt text
echo "1. Checking for images without alt attributes..."
MISSING_ALT=$(find . -type f \( $SOURCE_PATTERNS \) \
-not -path "*/node_modules/*" \
-not -path "*/dist/*" \
-not -path "*/build/*" \
-exec grep -l '<img[^>]*>' {} \; 2>/dev/null | \
xargs grep -h '<img[^>]*>' 2>/dev/null | \
grep -v 'alt=' | wc -l)
if [ "$MISSING_ALT" -gt 0 ]; then
echo " ❌ Found $MISSING_ALT images potentially missing alt text"
echo " Search: grep -r '<img' --include='*.jsx' --include='*.tsx' | grep -v 'alt='"
else
echo " ✓ All img tags appear to have alt attributes"
fi
# Check for buttons without accessible text
echo ""
echo "2. Checking for buttons without accessible text..."
BUTTON_ISSUES=$(find . -type f \( $SOURCE_PATTERNS \) \
-not -path "*/node_modules/*" \
-exec grep -l '<button[^>]*>\\s*<' {} \; 2>/dev/null | wc -l)
if [ "$BUTTON_ISSUES" -gt 0 ]; then
echo " ⚠️ Found $BUTTON_ISSUES potential buttons with icon-only content"
echo " These may need aria-label or sr-only text"
else
echo " ✓ Buttons appear to have text content"
fi
# Check for form inputs without labels
echo ""
echo "3. Checking for form inputs without labels..."
UNLABELED_INPUTS=$(find . -type f \( $SOURCE_PATTERNS \) \
-not -path "*/node_modules/*" \
-exec grep -h '<input[^>]*>' {} \; 2>/dev/null | \
grep -v 'id=' | wc -l)
if [ "$UNLABELED_INPUTS" -gt 0 ]; then
echo " ⚠️ Found $UNLABELED_INPUTS inputs potentially without associated labels"
else
echo " ✓ Inputs appear to have proper labeling"
fi
# Check for proper heading hierarchy
echo ""
echo "4. Checking heading hierarchy..."
echo " Run this command to review headings:"
echo " grep -rh '<h[1-6]' --include='*.jsx' --include='*.tsx' --include='*.html' | head -20"
# Check for semantic HTML
echo ""
echo "5. Checking for semantic HTML usage..."
SEMANTIC_COUNT=$(find . -type f \( $SOURCE_PATTERNS \) \
-not -path "*/node_modules/*" \
-exec grep -l '<main\|<nav\|<header\|<footer\|<article\|<section' {} \; 2>/dev/null | wc -l)
if [ "$SEMANTIC_COUNT" -gt 0 ]; then
echo " ✓ Found semantic HTML elements in $SEMANTIC_COUNT files"
else
echo " ⚠️ Few or no semantic HTML elements found"
echo " Consider using <main>, <nav>, <header>, <footer>, <article>, <section>"
fi
# Check for ARIA usage
echo ""
echo "6. Checking ARIA attribute usage..."
ARIA_COUNT=$(find . -type f \( $SOURCE_PATTERNS \) \
-not -path "*/node_modules/*" \
-exec grep -l 'aria-' {} \; 2>/dev/null | wc -l)
echo " Found ARIA attributes in $ARIA_COUNT files"
# Check for color contrast issues in CSS
echo ""
echo "7. Checking for potential color contrast issues..."
echo " Common low-contrast colors (review manually):"
grep -rh "color.*#[cdefCDEF]" --include="*.css" --include="*.scss" \
--exclude-dir=node_modules . 2>/dev/null | head -5 | sed 's/^/ /'
echo ""
}
analyze_static_html > "$A11Y_DIR/static-analysis.txt"
cat "$A11Y_DIR/static-analysis.txt"
Phase 5: Generate Accessibility Fixes
I'll generate specific fix implementations:
echo ""
echo "=== Generating Accessibility Fixes ==="
echo ""
FIXES_DIR="$A11Y_DIR/fixes"
mkdir -p "$FIXES_DIR"
# Fix 1: Image Accessibility
cat > "$FIXES_DIR/01-image-accessibility.jsx" << 'IMAGES'
// ============================================
// Image Accessibility Fixes
// ============================================
// ❌ BAD: No alt text
<img src="product.jpg" />
// ❌ BAD: Generic alt text
<img src="product.jpg" alt="image" />
// ✅ GOOD: Descriptive alt text
<img src="product.jpg" alt="Blue cotton t-shirt with company logo" />
// ✅ GOOD: Decorative image (empty alt)
<img src="decorative-border.png" alt="" role="presentation" />
// ✅ GOOD: Complex image with detailed description
<figure>
<img
src="sales-chart.png"
alt="Bar chart showing monthly sales from January to December"
aria-describedby="chart-description"
/>
<figcaption id="chart-description">
Sales increased from $10k in January to $50k in December,
with the highest growth occurring in Q4.
</figcaption>
</figure>
// React: Dynamic images
export const AccessibleImage = ({ src, alt, isDecorative = false }) => {
if (isDecorative) {
return <img src={src} alt="" role="presentation" />;
}
return <img src={src} alt={alt} />;
};
IMAGES
# Fix 2: Form Accessibility
cat > "$FIXES_DIR/02-form-accessibility.jsx" << 'FORMS'
// ============================================
// Form Accessibility Fixes
// ============================================
// ❌ BAD: Input without label
<input type="text" placeholder="Enter email" />
// ✅ GOOD: Properly labeled input
<div>
<label htmlFor="email">Email Address</label>
<input type="email" id="email" name="email" required />
</div>
// ✅ GOOD: Label with input inside (implicit)
<label>
Email Address
<input type="email" name="email" required />
</label>
// ✅ GOOD: ARIA label when visual label is hidden
<input
type="search"
aria-label="Search products"
placeholder="Search..."
/>
// ✅ GOOD: Error messages
<div>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
aria-invalid="true"
aria-describedby="password-error"
/>
<span id="password-error" role="alert">
Password must be at least 8 characters
</span>
</div>
// React: Accessible form component
export const AccessibleForm = () => {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email">
Email Address <span aria-label="required">*</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
aria-required="true"
aria-invalid={!!error}
aria-describedby={error ? "email-error" : undefined}
/>
{error && (
<span id="email-error" role="alert" className="error">
{error}
</span>
)}
</div>
<button type="submit">Submit</button>
</form>
);
};
FORMS
# Fix 3: Button Accessibility
cat > "$FIXES_DIR/03-button-accessibility.jsx" << 'BUTTONS'
// ============================================
// Button Accessibility Fixes
// ============================================
// ❌ BAD: Div acting as button
<div onClick={handleClick}>Click me</div>
// ❌ BAD: Button with only icon, no accessible text
<button onClick={handleDelete}>
<TrashIcon />
</button>
// ✅ GOOD: Proper button with text
<button onClick={handleClick}>Click me</button>
// ✅ GOOD: Icon button with aria-label
<button onClick={handleDelete} aria-label="Delete item">
<TrashIcon />
</button>
// ✅ GOOD: Icon button with visually hidden text
<button onClick={handleDelete}>
<TrashIcon aria-hidden="true" />
<span className="sr-only">Delete item</span>
</button>
// CSS for screen-reader only text
.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;
}
// ✅ GOOD: Loading state
<button disabled={isLoading} aria-busy={isLoading}>
{isLoading ? 'Saving...' : 'Save'}
</button>
// React: Accessible icon button component
export const IconButton = ({ icon: Icon, label, onClick, ...props }) => {
return (
<button onClick={onClick} aria-label={label} {...props}>
<Icon aria-hidden="true" />
<span className="sr-only">{label}</span>
</button>
);
};
BUTTONS
# Fix 4: Keyboard Navigation
cat > "$FIXES_DIR/04-keyboard-navigation.jsx" << 'KEYBOARD'
// ============================================
// Keyboard Navigation Fixes
// ============================================
// ❌ BAD: Clickable div without keyboard support
<div onClick={handleClick}>Click me</div>
// ✅ GOOD: Proper button (keyboard accessible by default)
<button onClick={handleClick}>Click me</button>
// ✅ GOOD: If you must use div, add keyboard support
<div
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick();
}
}}
>
Click me
</div>
// ✅ GOOD: Skip to main content link
<a href="#main-content" className="skip-link">
Skip to main content
</a>
// CSS for skip link (visible on focus)
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: white;
padding: 8px;
text-decoration: none;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
// ✅ GOOD: Focus management in modals
export const Modal = ({ isOpen, onClose, children }) => {
const modalRef = useRef(null);
useEffect(() => {
if (isOpen) {
// Focus first focusable element
const focusable = modalRef.current?.querySelector(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
focusable?.focus();
}
}, [isOpen]);
return isOpen ? (
<div
ref={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<h2 id="modal-title">Modal Title</h2>
{children}
<button onClick={onClose}>Close</button>
</div>
) : null;
};
// ✅ GOOD: Roving tabindex for custom widgets
export const RadioGroup = ({ options, value, onChange }) => {
const [focusedIndex, setFocusedIndex] = useState(0);
const handleKeyDown = (e, index) => {
switch (e.key) {
case 'ArrowDown':
case 'ArrowRight':
e.preventDefault();
const nextIndex = (index + 1) % options.length;
setFocusedIndex(nextIndex);
onChange(options[nextIndex].value);
break;
case 'ArrowUp':
case 'ArrowLeft':
e.preventDefault();
const prevIndex = (index - 1 + options.length) % options.length;
setFocusedIndex(prevIndex);
onChange(options[prevIndex].value);
break;
}
};
return (
<div role="radiogroup">
{options.map((option, index) => (
<div key={option.value}>
<input
type="radio"
id={option.value}
checked={value === option.value}
onChange={() => onChange(option.value)}
onKeyDown={(e) => handleKeyDown(e, index)}
tabIndex={index === focusedIndex ? 0 : -1}
/>
<label htmlFor={option.value}>{option.label}</label>
</div>
))}
</div>
);
};
KEYBOARD
# Fix 5: Color Contrast
cat > "$FIXES_DIR/05-color-contrast.css" << 'CONTRAST'
/* ============================================ */
/* Color Contrast Fixes (WCAG 2.1 Level AA) */
/* ============================================ */
/* ❌ BAD: Insufficient contrast (2.5:1) */
.bad-contrast {
color: #999;
background: #fff;
}
/* ✅ GOOD: Sufficient contrast for normal text (4.5:1) */
.good-contrast {
color: #595959;
background: #fff;
}
/* ✅ GOOD: Sufficient contrast for large text (3:1) */
.large-text-contrast {
font-size: 18px;
font-weight: bold;
color: #767676;
background: #fff;
}
/* Common color combinations with sufficient contrast */
:root {
/* White backgrounds */
--text-on-white: #212529; /* 16.4:1 - AAA */
--text-on-white-aa: #595959; /* 4.5:1 - AA */
/* Dark backgrounds */
--text-on-dark: #ffffff; /* 21:1 - AAA */
--text-on-dark-aa: #b3b3b3; /* 4.5:1 - AA */
/* Primary colors */
--primary: #0d6efd;
--primary-text: #ffffff; /* 4.5:1 on primary */
/* Success colors */
--success: #198754;
--success-text: #ffffff; /* 4.5:1 on success */
/* Error colors */
--error: #dc3545;
--error-text: #ffffff; /* 5.6:1 on error */
/* Warning colors */
--warning: #ffc107;
--warning-text: #000000; /* 10.4:1 on warning */
}
/* Focus indicators */
:focus-visible {
outline: 2px solid #0d6efd;
outline-offset: 2px;
}
/* Don't rely on color alone */
.status-error {
color: var(--error);
/* Add icon or text indicator */
}
.status-error::before {
content: "⚠️ ";
/* Icon provides additional visual cue */
}
CONTRAST
# Fix 6: Semantic HTML & ARIA
cat > "$FIXES_DIR/06-semantic-html-aria.jsx" << 'SEMANTIC'
// ============================================
// Semantic HTML & ARIA Best Practices
// ============================================
// ❌ BAD: Non-semantic divs
<div className="header">
<div className="nav">
<div className="nav-item">Home</div>
</div>
</div>
<div className="main">
<div className="article">
<div className="title">Article Title</div>
<div className="content">...</div>
</div>
</div>
// ✅ GOOD: Semantic HTML
<header>
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
<main id="main-content">
<article>
<h1>Article Title</h1>
<p>Article content...</p>
</article>
</main>
<footer>
<p>© 2024 Company Name</p>
</footer>
// ✅ GOOD: Proper heading hierarchy
<main>
<h1>Page Title</h1>
<section>
<h2>Section Title</h2>
<h3>Subsection</h3>
</section>
<section>
<h2>Another Section</h2>
</section>
</main>
// ✅ GOOD: ARIA landmarks
<div role="banner">
<h1>Site Header</h1>
</div>
<div role="main">
<h1>Main Content</h1>
</div>
<div role="navigation" aria-label="Footer navigation">
<nav>...</nav>
</div>
// ✅ GOOD: ARIA live regions for dynamic content
<div role="status" aria-live="polite" aria-atomic="true">
Item added to cart
</div>
<div role="alert" aria-live="assertive">
Error: Form validation failed
</div>
// React: Accessible page structure
export const PageLayout = ({ children }) => {
return (
<>
<a href="#main-content" className="skip-link">
Skip to main content
</a>
<header role="banner">
<nav aria-label="Main navigation">
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
</ul>
</nav>
</header>
<main id="main-content" role="main" tabIndex={-1}>
{children}
</main>
<footer role="contentinfo">
<p>© 2024 Company</p>
</footer>
</>
);
};
// ✅ GOOD: Accessible data tables
<table>
<caption>Monthly Sales Report</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Revenue</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">January</th>
<td>$10,000</td>
</tr>
</tbody>
</table>
SEMANTIC
echo "✓ Accessibility fix templates created in $FIXES_DIR"
Phase 6: Generate Accessibility Report
I'll create a comprehensive accessibility compliance report:
echo ""
echo "=== Generating Accessibility Report ==="
echo ""
# Calculate total violations
TOTAL_VIOLATIONS=0
if [ -f "$A11Y_DIR/axe-results.json" ]; then
TOTAL_VIOLATIONS=$(jq '.violations | length' "$A11Y_DIR/axe-results.json" 2>/dev/null || echo "0")
fi
cat > "$REPORT" << EOF
# Accessibility (a11y) Compliance Report
**Generated:** $(date)
**WCAG Level:** $WCAG_LEVEL
**Target URL:** $TARGET_URL
**Framework:** $FRAMEWORK
**Total Violations:** $TOTAL_VIOLATIONS
---
## Executive Summary
Web accessibility ensures your application is usable by everyone, including people with disabilities. This report analyzes compliance with WCAG 2.1 Level $WCAG_LEVEL standards.
**WCAG 2.1 Levels:**
- **Level A:** Minimum compliance (essential)
- **Level AA:** Target standard (recommended)
- **Level AAA:** Enhanced accessibility (ideal)
---
## Audit Results
EOF
if [ -f "$A11Y_DIR/axe-results.json" ] && [ "$TOTAL_VIOLATIONS" -gt 0 ]; then
cat >> "$REPORT" << EOF
### Violations by Impact
| Impact | Count | Priority |
|--------|-------|----------|
| Critical | $CRITICAL | 🔴 Fix immediately |
| Serious | $SERIOUS | 🟠 Fix within 1 week |
| Moderate | $MODERATE | 🟡 Fix within 2 weeks |
| Minor | $MINOR | 🟢 Fix when possible |
### Top Violations
EOF
# Extract top 10 violations
jq -r '.violations[:10] | .[] |
"#### \(.help)\n\n" +
"- **Impact:** \(.impact)\n" +
"- **WCAG:** \(.tags | join(", "))\n" +
"- **Description:** \(.description)\n" +
"- **Affects:** \(.nodes | length) element(s)\n" +
"- **Fix:** \(.helpUrl)\n"' \
"$A11Y_DIR/axe-results.json" 2>/dev/null >> "$REPORT"
cat >> "$REPORT" << EOF
**Full axe-core results:** \`cat $A11Y_DIR/axe-results.json\`
EOF
else
cat >> "$REPORT" << EOF
✓ No automated violations detected by axe-core.
However, automated tools can only catch ~30-40% of accessibility issues.
Manual testing is still required for:
- Keyboard navigation
- Screen reader compatibility
- Cognitive accessibility
- Content clarity
EOF
fi
# Add pa11y results if available
if [ -f "$A11Y_DIR/pa11y-results.json" ]; then
PA11Y_COUNT=$(jq 'length' "$A11Y_DIR/pa11y-results.json" 2>/dev/null || echo "0")
if [ "$PA11Y_COUNT" -gt 0 ]; then
cat >> "$REPORT" << EOF
### pa11y Additional Issues
Found $PA11Y_COUNT additional issues. See: \`$A11Y_DIR/pa11y-results.json\`
EOF
fi
fi
cat >> "$REPORT" << EOF
---
## Static Code Analysis
See detailed static analysis: \`$A11Y_DIR/static-analysis.txt\`
**Common Issues Found:**
- Images without alt text
- Forms without labels
- Buttons without accessible text
- Missing semantic HTML
- Potential contrast issues
---
## Priority Fix Checklist
### 🔴 Critical (Fix Immediately)
- [ ] Add alt text to all images
- [ ] Ensure all forms have labels
- [ ] Fix color contrast issues (minimum 4.5:1)
- [ ] Add ARIA labels to icon-only buttons
- [ ] Ensure keyboard navigation works
### 🟠 High Priority (Fix Within 1 Week)
- [ ] Implement proper heading hierarchy (h1-h6)
- [ ] Add skip to main content link
- [ ] Ensure focus indicators are visible
- [ ] Add ARIA live regions for dynamic content
- [ ] Fix semantic HTML structure
### 🟡 Medium Priority (Fix Within 2 Weeks)
- [ ] Add ARIA landmarks
- [ ] Improve screen reader support
- [ ] Document keyboard shortcuts
- [ ] Add accessible error messages
- [ ] Implement roving tabindex for custom widgets
### 🟢 Low Priority (Ongoing)
- [ ] Enhance color contrast to AAA level (7:1)
- [ ] Add descriptions for complex images
- [ ] Improve content readability
- [ ] Add accessibility statement page
- [ ] Conduct user testing with assistive tech
---
## Implementation Files
Generated fix templates in \`$FIXES_DIR\`:
1. **Image Accessibility** - \`01-image-accessibility.jsx\`
2. **Form Accessibility** - \`02-form-accessibility.jsx\`
3. **Button Accessibility** - \`03-button-accessibility.jsx\`
4. **Keyboard Navigation** - \`04-keyboard-navigation.jsx\`
5. **Color Contrast** - \`05-color-contrast.css\`
6. **Semantic HTML & ARIA** - \`06-semantic-html-aria.jsx\`
### Usage
1. Review each fix file for examples
2. Apply fixes to your codebase
3. Test with keyboard navigation
4. Test with screen reader
5. Re-run accessibility audit
---
## Testing Checklist
### Automated Testing
\`\`\`bash
# Run axe-core audit
npx axe $TARGET_URL --tags wcag2a,wcag2aa,wcag21a,wcag21aa
# Run pa11y audit
pa11y $TARGET_URL --standard WCAG2AA
# Add to CI/CD
npm test -- --coverage --a11y
\`\`\`
### Manual Testing
#### Keyboard Navigation
- [ ] Tab through all interactive elements
- [ ] Verify focus indicators are visible
- [ ] Test all functionality without mouse
- [ ] Verify skip to main content link
- [ ] Check modal focus trapping
#### Screen Reader Testing
- [ ] Test with NVDA (Windows) or VoiceOver (Mac)
- [ ] Verify all content is announced
- [ ] Check ARIA labels are correct
- [ ] Verify form error messages
- [ ] Test dynamic content updates
#### Visual Testing
- [ ] Check color contrast (use WebAIM contrast checker)
- [ ] Verify text is readable at 200% zoom
- [ ] Test with high contrast mode
- [ ] Verify focus indicators
- [ ] Check for text alternatives
#### Cognitive Accessibility
- [ ] Ensure clear, simple language
- [ ] Verify consistent navigation
- [ ] Check error message clarity
- [ ] Test with browser reader mode
---
## Continuous Monitoring
### Add to CI/CD Pipeline
#### Jest + jest-axe (React)
\`\`\`javascript
// __tests__/accessibility.test.js
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import App from '../App';
expect.extend(toHaveNoViolations);
test('should not have accessibility violations', async () => {
const { container } = render(<App />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
\`\`\`
#### GitHub Actions
\`\`\`yaml
name: Accessibility
on: [push, pull_request]
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run accessibility tests
run: |
npm install
npm test -- --coverage --a11y
- name: Run axe-core
run: |
npm start &
npx wait-on http://localhost:3000
npx axe http://localhost:3000 --exit
\`\`\`
---
## Integration Points
- **\`/lighthouse\`** - Includes accessibility scoring
- **\`/test\`** - Run a11y tests in test suite
- **\`/review\`** - Include accessibility in code reviews
- **\`/ci-setup\`** - Add a11y checks to CI/CD
---
## Resources
### Standards & Guidelines
- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/)
- [Section 508](https://www.section508.gov/)
- [ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/)
### Testing Tools
- [axe DevTools](https://www.deque.com/axe/devtools/)
- [WAVE Browser Extension](https://wave.webaim.org/extension/)
- [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/)
- [Pa11y](https://pa11y.org/)
### Screen Readers
- [NVDA](https://www.nvaccess.org/) (Windows, free)
- [JAWS](https://www.freedomscientific.com/products/software/jaws/) (Windows)
- [VoiceOver](https://www.apple.com/accessibility/voiceover/) (Mac/iOS, built-in)
- [TalkBack](https://support.google.com/accessibility/android/answer/6283677) (Android)
### Learning Resources
- [MDN Web Accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility)
- [A11y Project](https://www.a11yproject.com/)
- [WebAIM Articles](https://webaim.org/articles/)
---
**Report generated at:** $(date)
**Next Steps:**
1. Review and prioritize violations
2. Apply fixes from templates
3. Test with keyboard and screen reader
4. Re-run accessibility audit
5. Add automated tests to CI/CD
EOF
echo "✓ Accessibility report generated: $REPORT"
Summary
echo ""
echo "=== ✓ Accessibility Analysis Complete ==="
echo ""
echo "📊 Analysis Results:"
if [ -f "$A11Y_DIR/axe-results.json" ]; then
echo " Total violations: $TOTAL_VIOLATIONS"
echo " Critical: $CRITICAL"
echo " Serious: $SERIOUS"
echo " Moderate: $MODERATE"
echo " Minor: $MINOR"
echo " WCAG Level: $WCAG_LEVEL"
else
echo " Static analysis complete"
echo " Start dev server for live audit"
fi
echo ""
echo "📁 Generated Files:"
echo " - Main Report: $REPORT"
[ -f "$A11Y_DIR/axe-results.json" ] && echo " - axe Results: $A11Y_DIR/axe-results.json"
[ -f "$A11Y_DIR/pa11y-results.json" ] && echo " - pa11y Results: $A11Y_DIR/pa11y-results.json"
echo " - Fix Templates: $FIXES_DIR/"
echo " - Static Analysis: $A11Y_DIR/static-analysis.txt"
echo ""
echo "🎯 Priority Actions:"
if [ "$TOTAL_VIOLATIONS" -gt 0 ]; then
echo " 1. Fix critical violations ($CRITICAL found)"
echo " 2. Add alt text to images"
echo " 3. Ensure form labels"
echo " 4. Verify keyboard navigation"
echo " 5. Test with screen reader"
else
echo " ✓ No automated violations found"
echo " Complete manual testing checklist"
fi
echo ""
echo "💡 Quick Wins:"
echo " - Add alt text to all images"
echo " - Label all form inputs"
echo " - Use semantic HTML (header, main, nav)"
echo " - Add skip to main content link"
echo " - Verify color contrast (4.5:1 minimum)"
echo ""
echo "🧪 Testing Recommendations:"
echo " - Test keyboard navigation (Tab key)"
echo " - Test with screen reader (NVDA/VoiceOver)"
echo " - Verify at 200% zoom"
echo " - Add jest-axe to test suite"
echo ""
echo "🔗 Integration Points:"
echo " - /lighthouse - Includes a11y scoring"
echo " - /test - Run a11y tests"
echo " - /ci-setup - Add to CI/CD"
echo ""
echo "View report: cat $REPORT"
echo "View fixes: ls $FIXES_DIR/"
Safety Guarantees
What I'll NEVER do:
- Disable accessibility features
- Remove semantic HTML
- Skip WCAG compliance
- Make content inaccessible
- Add AI attribution to commits
What I WILL do:
- Identify accessibility violations
- Provide WCAG-compliant fixes
- Suggest semantic HTML improvements
- Recommend proper ARIA usage
- Enable keyboard navigation
- Support screen reader compatibility
Credits
This skill is based on:
- WCAG 2.1 - Web Content Accessibility Guidelines (W3C)
- axe-core - Accessibility testing engine (Deque Systems)
- pa11y - Automated accessibility testing
- ARIA Authoring Practices - W3C accessibility patterns
- Section 508 - US federal accessibility standards
- WebAIM - Web accessibility resources and tools
Token Optimization
Expected range: 1,200–2,000 tokens (initial), 300 tokens (cache hit or early exit)
Caching: Caches framework detection and tool availability in .claude/cache/accessibility/. Invalidated when package.json changes.
Early exit: Returns immediately if the server is not accessible at the target URL, or if no relevant files changed.
Patterns used: Grep-before-Read, early exit, git diff scope default, template-based generation, caching, progressive disclosure