# Accessibility Implementation Plan - Maternal App **Created**: October 2, 2025 **Target**: WCAG 2.1 AA Compliance **Priority**: CRITICAL (Launch Blocker) --- ## Executive Summary This document outlines the comprehensive accessibility implementation plan to achieve WCAG 2.1 AA compliance for the Maternal App. Accessibility is a **critical launch requirement** for legal compliance (ADA, Section 508) and to ensure the app is usable by all parents, including those with disabilities. **Current Status**: No accessibility implementation detected **Target**: Full WCAG 2.1 AA compliance **Timeline**: 2 weeks (Phases 1-3) --- ## Table of Contents 1. [Why Accessibility Matters](#why-accessibility-matters) 2. [WCAG 2.1 AA Requirements](#wcag-21-aa-requirements) 3. [Current State Assessment](#current-state-assessment) 4. [Implementation Phases](#implementation-phases) 5. [Technical Requirements](#technical-requirements) 6. [Testing Strategy](#testing-strategy) 7. [Success Metrics](#success-metrics) --- ## Why Accessibility Matters ### Legal Requirements - **ADA (Americans with Disabilities Act)**: Web accessibility is legally required - **Section 508**: Federal compliance for government users - **WCAG 2.1 AA**: International standard for web accessibility ### User Impact - **15% of the population** has some form of disability - **New parents** may have temporary disabilities (sleep deprivation, holding baby) - **Situational disabilities**: Using app in bright sunlight, noisy environments - **Assistive technology users**: Screen readers, keyboard-only, voice control ### Business Benefits - Larger addressable market - Better SEO (semantic HTML) - Improved UX for all users - Legal risk mitigation - Positive brand reputation --- ## WCAG 2.1 AA Requirements ### Four Core Principles (POUR) #### 1. **Perceivable** - Text alternatives for non-text content - Captions and alternatives for multimedia - Adaptable content (can be presented in different ways) - Distinguishable (easy to see and hear) #### 2. **Operable** - Keyboard accessible (all functionality available via keyboard) - Sufficient time (users have enough time to read and use content) - Seizures and physical reactions (nothing that causes seizures) - Navigable (users can navigate, find content, and determine where they are) - Input modalities (make it easier to operate functionality through various inputs) #### 3. **Understandable** - Readable (text content is readable and understandable) - Predictable (web pages appear and operate in predictable ways) - Input assistance (help users avoid and correct mistakes) #### 4. **Robust** - Compatible (maximize compatibility with current and future user agents) --- ## Current State Assessment ### What We Have ✅ - **Material-UI (MUI)**: Built-in accessibility features - **React 18**: Modern framework with good accessibility support - **Semantic HTML**: Some usage of proper HTML5 elements - **Next.js 13**: Server-side rendering for better screen reader support ### What's Missing ❌ #### **Critical Issues** 1. ❌ No ARIA labels on interactive elements 2. ❌ No keyboard navigation support 3. ❌ No focus indicators 4. ❌ No screen reader testing 5. ❌ Missing alt text on images/icons 6. ❌ Poor color contrast (not verified) 7. ❌ No skip navigation links 8. ❌ Forms lack proper labels and error messages 9. ❌ No focus management for dialogs/modals 10. ❌ No reduced motion support #### **High Priority Issues** - Missing landmark regions (header, nav, main, footer) - No heading hierarchy verification - Missing aria-live regions for dynamic content - No keyboard shortcuts documentation - Touch targets not verified (44x44px minimum) --- ## Implementation Phases ### **Phase 1: Foundation (Week 1, Days 1-3)** 🏗️ #### **Goal**: Establish accessibility infrastructure and fix critical keyboard/focus issues **Tasks:** 1. **Setup Accessibility Tools** (Day 1 Morning) - Install `eslint-plugin-jsx-a11y` (React accessibility linting) - Install `axe-core` and `@axe-core/react` (runtime accessibility testing) - Configure ESLint rules for accessibility - Install `jest-axe` for automated testing - Add accessibility check to CI/CD 2. **Keyboard Navigation** (Day 1 Afternoon - Day 2) - Implement focus trap for modals/dialogs - Add visible focus indicators (outline/ring) - Create skip navigation link - Fix tab order across all pages - Add keyboard shortcuts for common actions - Document keyboard navigation patterns 3. **ARIA Labels & Semantic HTML** (Day 2 - Day 3) - Audit all interactive elements (buttons, links, inputs) - Add aria-label/aria-labelledby where needed - Fix heading hierarchy (h1 → h2 → h3) - Add landmark regions (header, nav, main, footer) - Convert icon-only buttons to have text alternatives - Add aria-describedby for form hints 4. **Focus Management** (Day 3) - Implement focus management for route changes - Focus first heading on page load - Trap focus in modals/dialogs - Return focus when closing modals - Manage focus for toast notifications **Deliverables:** - ESLint accessibility rules configured - Axe-core integrated into dev environment - All pages keyboard navigable - Focus indicators visible - Skip navigation link added --- ### **Phase 2: Content & Forms (Week 1, Days 4-5)** 📝 #### **Goal**: Make all content accessible and improve form usability **Tasks:** 1. **Alternative Text** (Day 4 Morning) - Audit all images for alt text - Add descriptive alt text to photos - Mark decorative images as `alt=""` - Add aria-label to icon buttons - Document icon meanings 2. **Form Accessibility** (Day 4 Afternoon - Day 5 Morning) - Add explicit labels to all inputs - Associate labels with inputs (htmlFor/id) - Add aria-required to required fields - Improve error messages with aria-invalid - Add aria-describedby for help text - Group related inputs with fieldset/legend - Add autocomplete attributes 3. **Color Contrast** (Day 5 Morning) - Audit color contrast ratios (WCAG AA: 4.5:1 for text, 3:1 for large text) - Fix low-contrast text - Ensure error states don't rely on color alone - Add patterns/icons to supplement color coding 4. **Live Regions** (Day 5 Afternoon) - Add aria-live to toast notifications - Add aria-live to loading states - Add aria-live to activity feed updates - Add status messages for async operations **Deliverables:** - All images have appropriate alt text - All forms fully accessible - Color contrast meets WCAG AA - Live regions for dynamic content --- ### **Phase 3: Testing & Polish (Week 2, Days 1-3)** ✅ #### **Goal**: Comprehensive testing and documentation **Tasks:** 1. **Automated Testing** (Day 1) - Write jest-axe tests for all pages - Add axe-core checks to E2E tests - Run Lighthouse accessibility audits - Fix all automated test failures - Add accessibility tests to CI/CD 2. **Screen Reader Testing** (Day 2) - Test with NVDA (Windows) - Test with JAWS (Windows) - Test with VoiceOver (macOS/iOS) - Test with TalkBack (Android) - Document screen reader issues - Fix critical screen reader bugs 3. **Manual Testing** (Day 2-3) - Test all pages with keyboard only (no mouse) - Test with browser zoom (up to 200%) - Test with high contrast mode - Test with reduced motion - Test with different viewport sizes - Test all user flows 4. **Documentation** (Day 3) - Create accessibility statement page - Document keyboard shortcuts - Add ARIA patterns guide for developers - Create accessibility testing checklist - Document known issues (if any) **Deliverables:** - Automated accessibility tests passing - Screen reader testing complete - Manual testing checklist complete - Accessibility documentation published --- ### **Phase 4: Advanced Features (Week 2, Days 4-5)** 🚀 #### **Goal**: Enhanced accessibility features **Tasks:** 1. **Reduced Motion Support** (Day 4 Morning) ```css @media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; } } ``` - Detect `prefers-reduced-motion` - Disable animations for users who prefer reduced motion - Keep essential motion (loading indicators) 2. **Text Scaling** (Day 4 Afternoon) - Test with browser text zoom up to 200% - Use relative units (rem, em) instead of px - Ensure layout doesn't break at 200% zoom - Test with browser zoom (page zoom) 3. **Touch Target Sizes** (Day 5 Morning) - Audit all interactive elements - Ensure minimum 44x44px (iOS) / 48x48dp (Android) - Add padding to small targets - Increase spacing between adjacent targets 4. **Additional Enhancements** (Day 5 Afternoon) - Add high contrast mode support - Add visual focus indicators for mouse users - Implement focus-within for nested focus - Add aria-current for navigation **Deliverables:** - Reduced motion support implemented - Text scaling verified up to 200% - Touch targets meet minimum sizes - Enhanced accessibility features live --- ## Technical Requirements ### **1. Dependencies to Install** ```bash # Linting and Testing npm install --save-dev eslint-plugin-jsx-a11y npm install --save-dev jest-axe npm install --save-dev @axe-core/react # Runtime Testing npm install --save @axe-core/react # Focus Management npm install react-focus-lock focus-trap-react # Reduced Motion Hook npm install framer-motion # Already installed, use useReducedMotion ``` ### **2. ESLint Configuration** Add to `.eslintrc.json`: ```json { "extends": [ "plugin:jsx-a11y/recommended" ], "plugins": ["jsx-a11y"], "rules": { "jsx-a11y/anchor-is-valid": "error", "jsx-a11y/aria-props": "error", "jsx-a11y/aria-proptypes": "error", "jsx-a11y/aria-unsupported-elements": "error", "jsx-a11y/heading-has-content": "error", "jsx-a11y/img-redundant-alt": "error", "jsx-a11y/label-has-associated-control": "error", "jsx-a11y/no-autofocus": "warn", "jsx-a11y/no-static-element-interactions": "error" } } ``` ### **3. Axe-Core Integration (Development)** Create `components/providers/AxeProvider.tsx`: ```typescript 'use client'; import React, { useEffect } from 'react'; export function AxeProvider({ children }: { children: React.ReactNode }) { useEffect(() => { if (process.env.NODE_ENV === 'development') { import('@axe-core/react').then((axe) => { axe.default(React, require('react-dom'), 1000); }); } }, []); return <>{children}; } ``` ### **4. Focus Visible Styles** Add to `globals.css`: ```css /* Focus indicators */ *:focus { outline: 2px solid #FF8B7D; /* Coral from design system */ outline-offset: 2px; } /* Focus visible (keyboard only) */ *:focus:not(:focus-visible) { outline: none; } *:focus-visible { outline: 2px solid #FF8B7D; outline-offset: 2px; } /* Skip link */ .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; } ``` ### **5. Accessibility Utilities** Create `lib/accessibility.ts`: ```typescript /** * Accessibility utility functions */ // Announce to screen readers export function announceToScreenReader(message: string, priority: 'polite' | 'assertive' = 'polite') { const announcement = document.createElement('div'); announcement.setAttribute('role', 'status'); announcement.setAttribute('aria-live', priority); announcement.setAttribute('aria-atomic', 'true'); announcement.className = 'sr-only'; announcement.textContent = message; document.body.appendChild(announcement); setTimeout(() => { document.body.removeChild(announcement); }, 1000); } // Check if reduced motion is preferred export function prefersReducedMotion(): boolean { return window.matchMedia('(prefers-reduced-motion: reduce)').matches; } // Get contrast ratio between two colors export function getContrastRatio(color1: string, color2: string): number { // Implementation using relative luminance formula // WCAG requires 4.5:1 for normal text, 3:1 for large text } // Trap focus within an element export function trapFocus(element: HTMLElement) { const focusableElements = element.querySelectorAll( 'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])' ); const firstElement = focusableElements[0] as HTMLElement; const lastElement = focusableElements[focusableElements.length - 1] as HTMLElement; 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(); } } }); } ``` --- ## Testing Strategy ### **1. Automated Testing** #### **ESLint (Static Analysis)** ```bash npm run lint # Check for accessibility violations ``` #### **jest-axe (Unit Tests)** ```typescript import { render } from '@testing-library/react'; import { axe, toHaveNoViolations } from 'jest-axe'; expect.extend(toHaveNoViolations); test('Dashboard should be accessible', async () => { const { container } = render(); const results = await axe(container); expect(results).toHaveNoViolations(); }); ``` #### **Playwright (E2E Tests)** ```typescript test('should pass accessibility audit', async ({ page }) => { await page.goto('/dashboard'); const accessibilityScanResults = await page.evaluate(async () => { const axe = await import('axe-core'); return await axe.run(); }); expect(accessibilityScanResults.violations).toEqual([]); }); ``` #### **Lighthouse CI** ```bash npm install --save-dev @lhci/cli lhci autorun --collect.url=http://localhost:3000 --assert.preset=lighthouse:accessibility ``` ### **2. Manual Testing Checklist** #### **Keyboard Navigation** - [ ] All interactive elements are keyboard accessible - [ ] Tab order is logical - [ ] Focus indicators are visible - [ ] Skip navigation link works - [ ] No keyboard traps - [ ] Escape key closes modals - [ ] Enter/Space activate buttons #### **Screen Reader Testing** - [ ] Page title is announced - [ ] Headings are in logical order - [ ] Form labels are announced - [ ] Error messages are announced - [ ] Status updates are announced - [ ] Images have alt text - [ ] Links have descriptive text #### **Visual Testing** - [ ] Color contrast meets 4.5:1 (normal text) - [ ] Color contrast meets 3:1 (large text, 18pt+) - [ ] Content readable at 200% zoom - [ ] No horizontal scrolling at 320px width - [ ] Touch targets are 44x44px minimum #### **Assistive Technology** - [ ] Works with NVDA (Windows) - [ ] Works with JAWS (Windows) - [ ] Works with VoiceOver (Mac/iOS) - [ ] Works with TalkBack (Android) - [ ] Works with Dragon NaturallySpeaking (voice control) - [ ] Works with browser zoom ### **3. Browser Testing Matrix** | Browser | Screen Reader | Platform | Status | |---------|---------------|----------|--------| | Chrome | NVDA | Windows | TODO | | Firefox | NVDA | Windows | TODO | | Edge | JAWS | Windows | TODO | | Safari | VoiceOver | macOS | TODO | | Safari | VoiceOver | iOS | TODO | | Chrome | TalkBack | Android | TODO | --- ## Success Metrics ### **Quantitative Metrics** 1. **Lighthouse Accessibility Score**: 100/100 ✅ 2. **Axe-core Violations**: 0 critical, 0 serious ✅ 3. **ESLint jsx-a11y Errors**: 0 ✅ 4. **Color Contrast**: 100% WCAG AA compliant ✅ 5. **Keyboard Navigation**: 100% of features accessible ✅ ### **Qualitative Metrics** 1. **Screen Reader Testing**: All critical user flows pass ✅ 2. **User Testing**: Test with 3-5 users with disabilities ✅ 3. **WCAG 2.1 AA Audit**: External audit (optional but recommended) ✅ ### **Key User Flows to Test** 1. ✅ **Sign Up / Log In** - Keyboard accessible - Error messages announced - Form labels clear 2. ✅ **Add Activity (Feeding/Sleep/Diaper)** - Voice input accessible - Form fully keyboard accessible - Success message announced 3. ✅ **View Dashboard** - Stats cards readable - Charts have text alternatives - Navigation clear 4. ✅ **AI Assistant Chat** - Chat input keyboard accessible - Responses announced to screen reader - Conversation history navigable 5. ✅ **Settings & Account** - All settings keyboard accessible - Toggle buttons have clear states - Dialogs trap focus properly --- ## Priority Order (Critical Path) ### **Day 1 (Must Have)** 1. ESLint jsx-a11y setup 2. Visible focus indicators 3. Skip navigation link 4. Basic ARIA labels on buttons/links ### **Days 2-3 (Critical)** 5. Keyboard navigation for all pages 6. Focus management for modals 7. Form labels and error messages 8. Heading hierarchy fixes ### **Days 4-5 (High Priority)** 9. Alt text for all images 10. Color contrast fixes 11. Live regions for dynamic content 12. Automated tests (jest-axe) ### **Week 2 (Testing & Polish)** 13. Screen reader testing 14. Manual keyboard testing 15. Reduced motion support 16. Documentation --- ## Risk Mitigation ### **Potential Risks** 1. **Risk**: Breaking existing functionality - **Mitigation**: Comprehensive testing after each change 2. **Risk**: Time overrun - **Mitigation**: Focus on critical items first (Days 1-3) 3. **Risk**: Lack of screen reader expertise - **Mitigation**: Use automated tools, watch video tutorials, hire consultant 4. **Risk**: Design conflicts (e.g., focus indicators affect design) - **Mitigation**: Work with design to create accessible alternatives --- ## Resources ### **Tools** - [Axe DevTools Browser Extension](https://www.deque.com/axe/devtools/) - [WAVE Browser Extension](https://wave.webaim.org/extension/) - [Color Contrast Analyzer](https://www.tpgi.com/color-contrast-checker/) - [Lighthouse](https://developers.google.com/web/tools/lighthouse) ### **Documentation** - [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/) - [MDN Accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility) - [A11y Project Checklist](https://www.a11yproject.com/checklist/) - [ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) ### **Screen Readers** - [NVDA (Free, Windows)](https://www.nvaccess.org/) - [JAWS (Trial, Windows)](https://www.freedomscientific.com/products/software/jaws/) - VoiceOver (Built-in, macOS/iOS) - TalkBack (Built-in, Android) --- ## Next Steps 1. ✅ **Review and approve this plan** 2. 🔄 **Start Phase 1, Day 1**: Install accessibility tools 3. 🔄 **Daily standups**: Review progress, adjust as needed 4. 🔄 **Test continuously**: Don't wait until the end --- **Document Owner**: Development Team **Stakeholders**: Product, Design, QA, Legal **Review Cadence**: Daily during implementation, weekly post-launch **Let's make the Maternal App accessible to ALL parents!** 🌟