#!/bin/bash # Frontend Designer - Accessibility Audit # Comprehensive WCAG 2.1 AA compliance checker set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' MAGENTA='\033[0;35m' NC='\033[0m' # No Color # Counters PASS_COUNT=0 FAIL_COUNT=0 WARNING_COUNT=0 # Helper functions print_success() { echo -e "${GREEN}✓ PASS${NC} $1" ((PASS_COUNT++)) } print_error() { echo -e "${RED}✗ FAIL${NC} $1" ((FAIL_COUNT++)) } print_warning() { echo -e "${YELLOW}⚠ WARN${NC} $1" ((WARNING_COUNT++)) } print_info() { echo -e "${BLUE}ℹ INFO${NC} $1" } print_section() { echo "" echo -e "${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${MAGENTA}$1${NC}" echo -e "${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" } # Banner echo "" echo "╔════════════════════════════════════════════════════════════╗" echo "║ ║" echo "║ Frontend Designer - Accessibility Audit ║" echo "║ WCAG 2.1 AA Compliance ║" echo "║ ║" echo "╚════════════════════════════════════════════════════════════╝" echo "" # Get target if [ -z "$1" ]; then print_info "Usage: $0 " print_info "Example: $0 index.html" print_info "Example: $0 src/components/" exit 1 fi TARGET="$1" # Check if target exists if [ ! -e "$TARGET" ]; then print_error "Target not found: $TARGET" exit 1 fi # Section 1: HTML Structure print_section "1. HTML STRUCTURE & SEMANTICS" check_html_lang() { if grep -q ']*\slang=' "$1"; then print_success "HTML lang attribute present" else print_error "Missing lang attribute on " echo " Fix: " fi } check_page_title() { if grep -q '' "$1"; then print_success "Page title present" else print_error "Missing <title> element" echo " Fix: Add <title>Page Title" fi } check_main_landmark() { if grep -q ' landmark found" echo " Tip: Use
for primary content" fi } check_heading_structure() { if grep -q ' for main page heading" fi } check_semantic_html() { local semantic_tags=("nav" "header" "footer" "article" "section" "aside") local found=false for tag in "${semantic_tags[@]}"; do if grep -q "<$tag" "$1"; then found=true break fi done if [ "$found" = true ]; then print_success "Semantic HTML elements used" else print_warning "Consider using semantic HTML (nav, header, footer, etc.)" fi } # Section 2: Images & Media print_section "2. IMAGES & MEDIA" check_img_alt() { local img_count=$(grep -o '" fi fi } # Section 3: Forms print_section "3. FORMS & INPUTS" check_form_labels() { local input_count=$(grep -o ' or aria-label" fi # Check for required fields if grep -q 'required' "$1"; then if grep -q 'aria-required="true"' "$1"; then print_success "Required fields marked with aria-required" else print_warning "Consider adding aria-required=\"true\" to required fields" fi fi fi } check_error_messages() { if grep -q 'aria-describedby' "$1"; then print_success "Error messages linked with aria-describedby" elif grep -q 'error' "$1"; then print_warning "Error handling present, verify aria-describedby usage" fi } # Section 4: Interactive Elements print_section "4. INTERACTIVE ELEMENTS" check_button_text() { # Check for empty buttons if grep -q ']*>' "$1"; then print_error "Empty button found" echo " Fix: Add text or aria-label to button" else print_success "No empty buttons found" fi } check_link_text() { # Check for generic link text if grep -qi 'click here\|read more\|more' "$1"; then print_warning "Generic link text found (click here, read more)" echo " Tip: Use descriptive link text" else print_success "No generic link text detected" fi } check_skip_links() { if grep -q 'skip.*content\|skip.*navigation' "$1"; then print_success "Skip navigation link present" else print_warning "No skip navigation link found" echo " Tip: Add skip link for keyboard users" echo " Skip to content" fi } # Section 5: ARIA print_section "5. ARIA ATTRIBUTES" check_aria_roles() { if grep -q 'role=' "$1"; then print_success "ARIA roles found" # Check for button roles on non-button elements if grep -q ']*role="button"' "$1" || grep -q ']*role="button"' "$1"; then if grep -q 'tabindex=' "$1"; then print_success "Custom buttons have tabindex" else print_error "role=\"button\" without tabindex" echo " Fix: Add tabindex=\"0\" to custom buttons" fi fi fi } check_aria_labels() { if grep -q 'aria-label=' "$1"; then print_success "ARIA labels used for context" fi # Check for redundant aria-label if grep -q ']*aria-label.*>[^<]*' "$1"; then print_warning "Possible redundant aria-label on button with text" echo " Tip: Use aria-label when button has no visible text" fi } check_aria_live() { if grep -q 'aria-live' "$1"; then print_success "Live regions defined" fi } # Section 6: Keyboard Navigation print_section "6. KEYBOARD NAVIGATION" check_tabindex() { # Check for positive tabindex if grep -q 'tabindex="[1-9]' "$1"; then print_error "Positive tabindex values found" echo " Fix: Use tabindex=\"0\" or \"-1\" only" echo " Positive values disrupt natural tab order" else print_success "No positive tabindex values (good)" fi } check_focus_indicators() { # This would need CSS analysis print_info "Manual check: Verify focus indicators are visible" echo " Test: Tab through page, ensure focus is visible" echo " CSS: :focus-visible { outline: 2px solid; }" } # Section 7: Color & Contrast print_section "7. COLOR & CONTRAST" print_info "Manual checks required for color/contrast:" echo "" echo " Required contrast ratios (WCAG AA):" echo " ✓ Normal text: 4.5:1" echo " ✓ Large text (18pt+): 3:1" echo " ✓ UI components: 3:1" echo "" echo " Tools for testing:" echo " - Chrome DevTools (Lighthouse)" echo " - WebAIM Contrast Checker" echo " - axe DevTools" echo "" check_color_only() { if grep -qi 'color:.*red\|color:.*green' "$1"; then print_warning "Color usage detected - ensure not used as only indicator" echo " Tip: Don't rely on color alone (add icons, text, patterns)" fi } # Section 8: Responsive & Mobile print_section "8. RESPONSIVE & MOBILE" check_viewport() { if grep -q 'viewport' "$1"; then print_success "Viewport meta tag present" else print_error "Missing viewport meta tag" echo " Fix: " fi } check_touch_targets() { print_info "Manual check: Touch targets minimum 44x44px" echo " Test: Verify buttons/links meet minimum size" echo " CSS: min-height: 44px; min-width: 44px;" } # Section 9: Content print_section "9. CONTENT & READABILITY" check_lang_changes() { if grep -q '\slang=' "$1"; then local lang_count=$(grep -o '\slang=' "$1" | wc -l) if [ "$lang_count" -gt 1 ]; then print_success "Language changes marked ($lang_count instances)" fi fi } check_abbreviations() { if grep -q ' element" fi } # Section 10: Motion & Animation print_section "10. MOTION & ANIMATIONS" print_info "Manual check: Respect prefers-reduced-motion" echo "" echo " CSS:" echo " @media (prefers-reduced-motion: reduce) {" echo " * { animation: none !important; }" echo " }" echo "" # Run checks on files if [ -f "$TARGET" ]; then # Single file check_html_lang "$TARGET" check_page_title "$TARGET" check_main_landmark "$TARGET" check_heading_structure "$TARGET" check_semantic_html "$TARGET" check_img_alt "$TARGET" check_video_captions "$TARGET" check_form_labels "$TARGET" check_error_messages "$TARGET" check_button_text "$TARGET" check_link_text "$TARGET" check_skip_links "$TARGET" check_aria_roles "$TARGET" check_aria_labels "$TARGET" check_aria_live "$TARGET" check_tabindex "$TARGET" check_focus_indicators "$TARGET" check_color_only "$TARGET" check_viewport "$TARGET" check_touch_targets "$TARGET" check_lang_changes "$TARGET" check_abbreviations "$TARGET" elif [ -d "$TARGET" ]; then # Directory - find HTML files html_files=$(find "$TARGET" -name "*.html" -o -name "*.htm") if [ -z "$html_files" ]; then print_error "No HTML files found in $TARGET" exit 1 fi for file in $html_files; do print_info "Checking: $file" check_html_lang "$file" check_page_title "$file" check_main_landmark "$file" check_heading_structure "$file" check_img_alt "$file" check_form_labels "$file" echo "" done fi # Summary echo "" echo "╔════════════════════════════════════════════════════════════╗" echo "║ Audit Summary ║" echo "╚════════════════════════════════════════════════════════════╝" echo "" echo -e "${GREEN}✓ Passed: $PASS_COUNT${NC}" echo -e "${RED}✗ Failed: $FAIL_COUNT${NC}" echo -e "${YELLOW}⚠ Warnings: $WARNING_COUNT${NC}" echo "" # Calculate score TOTAL=$((PASS_COUNT + FAIL_COUNT)) if [ $TOTAL -gt 0 ]; then SCORE=$(( (PASS_COUNT * 100) / TOTAL )) echo "Score: $SCORE%" echo "" if [ $SCORE -ge 90 ]; then echo -e "${GREEN}Excellent! Your site is highly accessible.${NC}" elif [ $SCORE -ge 70 ]; then echo -e "${YELLOW}Good, but needs improvements.${NC}" else echo -e "${RED}Needs significant accessibility improvements.${NC}" fi fi echo "" print_info "Additional Testing Recommended:" echo " 1. Screen reader testing (NVDA, JAWS, VoiceOver)" echo " 2. Keyboard-only navigation" echo " 3. Automated tools (axe, Lighthouse, WAVE)" echo " 4. Color contrast analyzer" echo " 5. Real user testing with assistive technologies" echo "" print_info "Resources:" echo " - WCAG 2.1: https://www.w3.org/WAI/WCAG21/quickref/" echo " - WebAIM: https://webaim.org/" echo " - a11y Project: https://www.a11yproject.com/" echo "" # Exit code based on failures if [ $FAIL_COUNT -gt 0 ]; then exit 1 else exit 0 fi