7.8 KiB
7.8 KiB
WCAG 2.1 AA Accessibility Implementation Design
Overview
Implement WCAG 2.1 AA compliance app-wide, ensuring accessibility for users with visual impairments, motor disabilities, and those using assistive technologies like VoiceOver.
Scope
In scope:
- Color contrast certification for Monochrome theme (light + dark modes)
- VoiceOver support for all view files - accessibility labels, hints, and traits
- Theme picker update - accessible badge on Monochrome
- Touch targets - minimum 44x44pt for all interactive elements
- Focus management - logical focus order, visible focus indicators
- Developer guidelines - patterns and rules for future development
Out of scope:
- Certifying the other 6 themes (kept as "visual preference" options)
- PDF export accessibility (separate concern, fixed dimensions)
- Motion reduction (iOS handles via system setting)
Decisions
- Default theme: Monochrome is the guaranteed-compliant theme
- Other themes: Kept unchanged, no warnings - only Monochrome shows "✓ Accessible" badge
- Color changes: Fix Monochrome palette for all users (no conditional/toggle)
- Coverage: Full accessibility across all screens
Monochrome Theme Color Fixes
Current Values
| Role | Dark Mode | Light Mode |
|---|---|---|
| Text muted | #707070 |
#707070 |
| Primary accent | #808080 |
#808080 |
| Background 1 | #121212 |
#FAFAFA |
| Card background | #1C1C1C |
#FFFFFF |
Issues
- Text muted (
#707070) on dark card (#1C1C1C) = ~3.5:1 (needs 4.5:1)
Fixes
| Role | Dark Mode | Light Mode |
|---|---|---|
| Text muted | #707070 → #8A8A8A |
#707070 → #595959 |
New contrast ratios:
- Dark:
#8A8A8Aon#1C1C1C= ~5.1:1 ✓ - Light:
#595959on#FFFFFF= ~7:1 ✓
VoiceOver Support Strategy
Element Labeling
| Element Type | Modifier | Example |
|---|---|---|
| Buttons | .accessibilityLabel() |
"Create trip" |
| Icons with meaning | .accessibilityLabel() |
"Baseball" for MLB icon |
| Decorative images | .accessibilityHidden(true) |
Background gradients |
| Status indicators | .accessibilityLabel() + .accessibilityValue() |
"Progress: 12 of 30 stadiums visited" |
| Toggle/Picker | .accessibilityLabel() + .accessibilityHint() |
Label: "Theme", Hint: "Double tap to change" |
| Cards (tappable) | .accessibilityElement(children: .combine) |
Combines child text into single announcement |
| Lists | .accessibilityLabel() per row |
"Yankees at Red Sox, April 15, 7:05 PM" |
Naming Conventions
- Labels describe what it IS: "Create trip button"
- Hints describe what happens: "Opens trip planner"
- Values describe current state: "Selected" or "3 of 5"
Files to Modify (20 view files)
Home:
HomeView.swift- Tab bar, suggested trip cards, quick actionsSuggestedTripCard.swift- Card accessibilityLoadingTripsView.swift- Loading state announcements
Trip:
TripCreationView.swift- Sport picker, date picker, region selector, mode selectorTripDetailView.swift- Timeline items, game cards, travel segments, export buttonRegionMapSelector.swift- Region selectionTimelineItemView.swift- Timeline accessibility
Schedule:
ScheduleListView.swift- Game list rows, sport filter, date navigation
Progress:
ProgressTabView.swift- Tab navigationProgressMapView.swift- Stadium map, markersVisitDetailView.swift- Visit informationStadiumVisitSheet.swift- Stadium detailsAchievementsListView.swift- Achievement badgesGameMatchConfirmationView.swift- Confirmation dialogPhotoImportView.swift- Photo selection
Settings:
SettingsView.swift- All rows, theme picker with accessible badge
Theme:
ViewModifiers.swift- Reusable component accessibilityAnimatedComponents.swift- Animated element accessibilitySportSelectorGrid.swift- Sport grid accessibility
App:
SportsTimeApp.swift- Root accessibility configuration
Theme Picker UI
Update SettingsView.swift theme section:
- Monochrome shows "✓ Accessible" badge beneath theme name
- Other 6 themes display normally with no indicators
┌─────────────────────────────────────┐
│ Theme │
├─────────────────────────────────────┤
│ [███] Monochrome │
│ ✓ Accessible │
├─────────────────────────────────────┤
│ [███] Teal │
├─────────────────────────────────────┤
│ [███] Orbit │
└─────────────────────────────────────┘
Touch Targets
Minimum 44x44pt hit area for all interactive elements.
Areas to audit:
- Icon-only buttons (export, share, close)
- Inline links
- Compact list rows
Fix pattern:
Button(action: { }) {
Image(systemName: "square.and.arrow.up")
}
.frame(minWidth: 44, minHeight: 44)
Focus Management
SwiftUI defaults are sufficient. Ensure:
- Logical tab order (top-to-bottom, left-to-right)
- Focus indicators visible (system default rings)
- Modal dismissal returns focus to trigger
- No keyboard traps
Developer Guidelines
Create docs/ACCESSIBILITY.md with:
Color Rules
- Monochrome theme: verify 4.5:1 contrast for text, 3:1 for UI components
- Don't rely solely on color to convey meaning (add icons, text, or patterns)
Accessibility Label Rules
- Every
Buttonneeds.accessibilityLabel()unless text is self-descriptive - Every meaningful
Imageneeds.accessibilityLabel() - Decorative images get
.accessibilityHidden(true) - Group related elements with
.accessibilityElement(children: .combine)
Touch Target Rules
- All tappable elements minimum 44x44pt hit area
- Use
.contentShape(Rectangle())to expand hit areas when needed
Testing Requirements
- Enable VoiceOver and navigate every new screen before PR
- Test with Dynamic Type at Accessibility XXL
- Test Monochrome theme in both light and dark modes
PR Checklist
## Accessibility
- [ ] All new buttons have accessibility labels
- [ ] Images labeled or hidden appropriately
- [ ] Touch targets are 44x44pt minimum
- [ ] Tested with VoiceOver
- [ ] Tested with large Dynamic Type
Testing Strategy
Manual Testing with Accessibility Inspector
- Xcode → Open Developer Tool → Accessibility Inspector
- Audit each screen for contrast warnings and missing labels
VoiceOver Testing (per screen)
- Enable VoiceOver (Settings → Accessibility → VoiceOver)
- Navigate entire screen with swipe gestures
- Verify all interactive elements are announced correctly
- Verify logical reading order
- Test activation (double-tap) of buttons and controls
Dynamic Type Testing
- Settings → Accessibility → Display & Text Size → Larger Text
- Test at default, Large, and Accessibility XXL sizes
- Check for truncation, overlap, or layout breaks
Contrast Validation
- Use WebAIM Contrast Checker or similar tool
- Validate all Monochrome text/background combinations
- Document final contrast ratios in ACCESSIBILITY.md
Implementation Order
- Fix Monochrome theme colors in
Theme.swift - Update
SettingsView.swifttheme picker with accessible badge - Add accessibility labels to all 20 view files (alphabetically by feature)
- Audit touch targets and fix undersized elements
- Create
docs/ACCESSIBILITY.mdwith guidelines - Full VoiceOver testing pass
- Full contrast validation pass