docs: add Dynamic Type implementation design

Replace all custom font sizes with Apple's built-in text styles
for accessibility compliance. Remove Theme.FontSize enum entirely.
Export files keep fixed sizes for consistent PDF output.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-11 10:04:13 -06:00
parent 3aef39adba
commit 8affa3ce0d

View File

@@ -0,0 +1,206 @@
# 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