# Dynamic Type Implementation Design

## Overview

Implement Dynamic Type app-wide by replacing all custom font sizes with Apple's built-in text styles. This ensures accessibility compliance and automatic scaling based on user preferences.

## Scope

**What changes:**
- Remove `Theme.FontSize` enum entirely
- Replace all `.font(.system(size:))` with Apple text styles
- Update 20 UI files

**What stays fixed:**
- PDF export files (ProgressCardGenerator, PDFGenerator, MapSnapshotService)
- These need predictable dimensions for consistent output

## Text Style Mapping

### Apple Text Styles Reference

| Style | Default Size | Use Case |
|-------|--------------|----------|
| `.largeTitle` | 34pt | Screen titles, hero text |
| `.title` | 28pt | Primary headings |
| `.title2` | 22pt | Section headers |
| `.title3` | 20pt | Subsection headers |
| `.headline` | 17pt semibold | Card titles, emphasis |
| `.body` | 17pt | Primary content |
| `.callout` | 16pt | Secondary content |
| `.subheadline` | 15pt | Supporting text |
| `.footnote` | 13pt | Tertiary info |
| `.caption` | 12pt | Labels, metadata |
| `.caption2` | 11pt | Fine print |

### Current → Apple Mapping

| Current Usage | Current Size | Apple Style |
|---------------|--------------|-------------|
| `Theme.FontSize.heroTitle` | 34 | `.largeTitle` |
| `Theme.FontSize.sectionTitle` | 24 | `.title2` |
| `Theme.FontSize.cardTitle` | 18 | `.headline` |
| `Theme.FontSize.body` | 16 | `.body` |
| `Theme.FontSize.caption` | 14 | `.subheadline` |
| `Theme.FontSize.micro` | 12 | `.caption` |

## Files to Modify

### UI Files (Dynamic Type)

```
SportsTime/Core/Theme/Theme.swift              # Remove FontSize enum
SportsTime/Core/Theme/ViewModifiers.swift      # Update BadgeStyle, SectionHeaderStyle
SportsTime/Core/Theme/AnimatedComponents.swift # Update component fonts
SportsTime/Features/Home/Views/HomeView.swift
SportsTime/Features/Home/Views/SuggestedTripCard.swift
SportsTime/Features/Home/Views/LoadingTripsView.swift
SportsTime/Features/Trip/Views/TripCreationView.swift
SportsTime/Features/Trip/Views/TripDetailView.swift
SportsTime/Features/Trip/Views/RegionMapSelector.swift
SportsTime/Features/Trip/Views/TimelineItemView.swift
SportsTime/Features/Schedule/Views/ScheduleListView.swift
SportsTime/Features/Settings/Views/SettingsView.swift
SportsTime/Features/Progress/Views/ProgressTabView.swift
SportsTime/Features/Progress/Views/ProgressMapView.swift
SportsTime/Features/Progress/Views/VisitDetailView.swift
SportsTime/Features/Progress/Views/StadiumVisitSheet.swift
SportsTime/Features/Progress/Views/AchievementsListView.swift
SportsTime/Features/Progress/Views/GameMatchConfirmationView.swift
SportsTime/Features/Progress/Views/PhotoImportView.swift
SportsTime/SportsTimeApp.swift
```

### Export Files (Keep Fixed)

```
SportsTime/Export/PDFGenerator.swift
SportsTime/Export/Services/ProgressCardGenerator.swift
SportsTime/Export/Services/MapSnapshotService.swift
```

## Replacement Patterns

### Before → After Examples

```swift
// Hero titles
// Before:
.font(.system(size: Theme.FontSize.heroTitle, weight: .bold))
// After:
.font(.largeTitle)

// Section headers
// Before:
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
// After:
.font(.title2)

// Card titles
// Before:
.font(.system(size: Theme.FontSize.cardTitle, weight: .semibold))
// After:
.font(.headline)

// Body text
// Before:
.font(.system(size: Theme.FontSize.body))
// After:
.font(.body)

// Captions
// Before:
.font(.system(size: Theme.FontSize.caption))
// After:
.font(.subheadline)

// Micro/labels
// Before:
.font(.system(size: Theme.FontSize.micro, weight: .medium))
// After:
.font(.caption)
```

### ViewModifiers.swift Updates

```swift
// BadgeStyle
// Before:
.font(.system(size: Theme.FontSize.micro, weight: .semibold))
// After:
.font(.caption)

// SectionHeaderStyle
// Before:
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
// After:
.font(.title2)
```

### Decision Rule for Ambiguous Cases

When a hardcoded size doesn't map cleanly:
- Primary content → `.body`
- Secondary/supporting → `.subheadline`
- Labels/metadata → `.caption`
- Emphasis within body → `.headline`

## Implementation Plan

### Order

1. **Theme.swift** — Remove `FontSize` enum
2. **ViewModifiers.swift** — Update `BadgeStyle` and `SectionHeaderStyle`
3. **AnimatedComponents.swift** — Update component fonts
4. **Feature views** — Update all 16 view files
5. **SportsTimeApp.swift** — Update any root-level fonts
6. **Build & fix** — Compiler catches remaining `Theme.FontSize` references

## Testing Strategy

### Test Dynamic Type in Simulator

Settings → Accessibility → Display & Text Size → Larger Text

Or via Xcode scheme arguments:

```
-UIPreferredContentSizeCategoryName UICTContentSizeCategoryAccessibilityExtraExtraExtraLarge
```

### Test Cases

| Size Category | What to Check |
|---------------|---------------|
| Default | App looks normal, similar to before |
| Large | Text scales up, layouts don't break |
| Accessibility XXL | Text very large, no truncation/overlap |
| Extra Small | Text scales down, still readable |

### Key Screens to Test

- Home (hero title, trip cards)
- Trip Creation (form labels, buttons)
- Trip Detail (timeline, game info)
- Schedule (list items, headers)
- Progress (stats, achievements)
- Settings (all rows)

### Potential Layout Issues

Watch for:
- Text truncation in fixed-width containers
- Overlapping elements at large sizes
- Buttons too small to tap at large sizes

Fix with:
- `.lineLimit(nil)` for multi-line text
- `.minimumScaleFactor(0.8)` for tight spaces
- `ScrollView` for content that might overflow

## Future Development Guidelines

After this migration:
- **Always use Apple text styles** (`.body`, `.headline`, `.caption`, etc.)
- **Never use `.system(size:)`** in UI code (exception: Export files)
- **Test with Dynamic Type** enabled before merging UI changes
