# Accessibility

> Accessibility (a11y) compliance checking with WCAG 2.1 validation

- Skill: `manastalukdar/accessibility` (Agent Skill)
- Install (CLI): `npx skillmds@latest add manastalukdar/accessibility`
- Raw SKILL.md: https://api.skillmd.com/api/skills/manastalukdar/accessibility/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: manastalukdar (https://skillmd.com/u/manastalukdar)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/manastalukdar/accessibility

---


# 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)

<think>
Accessibility ensures:
- Usability for people with disabilities
- Keyboard-only navigation support
- Screen reader compatibility
- Visual accessibility (contrast, font size)
- Cognitive accessibility (clear language, structure)

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:

```bash
#!/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:

```bash
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:

```bash
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:

```bash
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:

```bash
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>&copy; 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>&copy; 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:

```bash
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

```bash
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

