Files
Sportstime/docs/plans/2026-01-11-wcag-accessibility-design.md
Trey t e7fb3cfbbe docs: add WCAG 2.1 AA accessibility design
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 11:14:40 -06:00

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

  1. Default theme: Monochrome is the guaranteed-compliant theme
  2. Other themes: Kept unchanged, no warnings - only Monochrome shows "✓ Accessible" badge
  3. Color changes: Fix Monochrome palette for all users (no conditional/toggle)
  4. 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: #8A8A8A on #1C1C1C = ~5.1:1 ✓
  • Light: #595959 on #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 actions
  • SuggestedTripCard.swift - Card accessibility
  • LoadingTripsView.swift - Loading state announcements

Trip:

  • TripCreationView.swift - Sport picker, date picker, region selector, mode selector
  • TripDetailView.swift - Timeline items, game cards, travel segments, export button
  • RegionMapSelector.swift - Region selection
  • TimelineItemView.swift - Timeline accessibility

Schedule:

  • ScheduleListView.swift - Game list rows, sport filter, date navigation

Progress:

  • ProgressTabView.swift - Tab navigation
  • ProgressMapView.swift - Stadium map, markers
  • VisitDetailView.swift - Visit information
  • StadiumVisitSheet.swift - Stadium details
  • AchievementsListView.swift - Achievement badges
  • GameMatchConfirmationView.swift - Confirmation dialog
  • PhotoImportView.swift - Photo selection

Settings:

  • SettingsView.swift - All rows, theme picker with accessible badge

Theme:

  • ViewModifiers.swift - Reusable component accessibility
  • AnimatedComponents.swift - Animated element accessibility
  • SportSelectorGrid.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 Button needs .accessibilityLabel() unless text is self-descriptive
  • Every meaningful Image needs .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)

  1. Enable VoiceOver (Settings → Accessibility → VoiceOver)
  2. Navigate entire screen with swipe gestures
  3. Verify all interactive elements are announced correctly
  4. Verify logical reading order
  5. 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

  1. Fix Monochrome theme colors in Theme.swift
  2. Update SettingsView.swift theme picker with accessible badge
  3. Add accessibility labels to all 20 view files (alphabetically by feature)
  4. Audit touch targets and fix undersized elements
  5. Create docs/ACCESSIBILITY.md with guidelines
  6. Full VoiceOver testing pass
  7. Full contrast validation pass