feat: implement Dynamic Type with Apple text styles

Replace all custom Theme.FontSize values and hardcoded font sizes with
Apple's built-in text styles (.largeTitle, .title2, .headline, .body,
.subheadline, .caption, .caption2) to support accessibility scaling.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-11 10:23:16 -06:00
parent 8affa3ce0d
commit 2d48f1411a
16 changed files with 273 additions and 284 deletions

View File

@@ -124,23 +124,23 @@ struct TripDetailView: View {
.animation(.easeInOut(duration: 0.3), value: exportProgress?.percentComplete)
Image(systemName: "doc.fill")
.font(.system(size: 24))
.font(.title2)
.foregroundStyle(Theme.warmOrange)
}
VStack(spacing: Theme.Spacing.xs) {
Text("Creating PDF")
.font(.system(size: Theme.FontSize.cardTitle, weight: .semibold))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text(exportProgress?.currentStep ?? "Preparing...")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
.multilineTextAlignment(.center)
if let progress = exportProgress {
Text("\(Int(progress.percentComplete * 100))%")
.font(.system(size: Theme.FontSize.micro, weight: .medium, design: .monospaced))
.font(.caption.monospaced())
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -177,7 +177,7 @@ struct TripDetailView: View {
toggleSaved()
} label: {
Image(systemName: isSaved ? "heart.fill" : "heart")
.font(.system(size: 22, weight: .medium))
.font(.title3)
.foregroundStyle(isSaved ? .red : .white)
.padding(12)
.background(.ultraThinMaterial)
@@ -214,7 +214,7 @@ struct TripDetailView: View {
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
// Date range
Text(trip.formattedDateRange)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
// Route preview
@@ -226,9 +226,9 @@ struct TripDetailView: View {
ForEach(Array(trip.uniqueSports), id: \.self) { sport in
HStack(spacing: 4) {
Image(systemName: sport.iconName)
.font(.system(size: 10))
.font(.caption2)
Text(sport.rawValue)
.font(.system(size: 11, weight: .medium))
.font(.caption2)
}
.padding(.horizontal, 10)
.padding(.vertical, 5)
@@ -261,11 +261,11 @@ struct TripDetailView: View {
VStack(spacing: Theme.Spacing.md) {
HStack {
Text("Trip Score")
.font(.system(size: Theme.FontSize.cardTitle, weight: .semibold))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
Text(score.scoreGrade)
.font(.system(size: 32, weight: .bold, design: .rounded))
.font(.largeTitle)
.foregroundStyle(Theme.warmOrange)
.glowEffect(color: Theme.warmOrange, radius: 8)
}
@@ -283,10 +283,10 @@ struct TripDetailView: View {
private func scoreItem(label: String, value: Double, color: Color) -> some View {
VStack(spacing: 4) {
Text(String(format: "%.0f", value))
.font(.system(size: Theme.FontSize.cardTitle, weight: .bold))
.font(.headline)
.foregroundStyle(color)
Text(label)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -296,7 +296,7 @@ struct TripDetailView: View {
private var itinerarySection: some View {
VStack(alignment: .leading, spacing: Theme.Spacing.md) {
Text("Itinerary")
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
ForEach(Array(itinerarySections.enumerated()), id: \.offset) { index, section in
@@ -604,11 +604,11 @@ struct DaySection: View {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text("Day \(dayNumber)")
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text(formattedDate)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
@@ -626,7 +626,7 @@ struct DaySection: View {
// City label
if let city = gameCity {
Label(city, systemImage: "mappin")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
@@ -656,20 +656,20 @@ struct GameRow: View {
// Sport icon and name
HStack(spacing: 3) {
Image(systemName: game.game.sport.iconName)
.font(.system(size: 10))
.font(.caption2)
Text(game.game.sport.rawValue)
.font(.system(size: 10, weight: .medium))
.font(.caption2)
}
.foregroundStyle(game.game.sport.themeColor)
// Matchup
HStack(spacing: 4) {
Text(game.awayTeam.abbreviation)
.font(.system(size: Theme.FontSize.body, weight: .bold))
.font(.body)
Text("@")
.foregroundStyle(Theme.textMuted(colorScheme))
Text(game.homeTeam.abbreviation)
.font(.system(size: Theme.FontSize.body, weight: .bold))
.font(.body)
}
.foregroundStyle(Theme.textPrimary(colorScheme))
}
@@ -677,9 +677,9 @@ struct GameRow: View {
// Stadium
HStack(spacing: 4) {
Image(systemName: "building.2")
.font(.system(size: 10))
.font(.caption2)
Text(game.stadium.name)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
}
.foregroundStyle(Theme.textSecondary(colorScheme))
}
@@ -688,7 +688,7 @@ struct GameRow: View {
// Time
Text(game.game.gameTime)
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
.font(.subheadline)
.foregroundStyle(Theme.warmOrange)
}
.padding(Theme.Spacing.sm)
@@ -731,11 +731,11 @@ struct TravelSection: View {
VStack(alignment: .leading, spacing: 2) {
Text("Travel")
.font(.system(size: Theme.FontSize.micro, weight: .semibold))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
Text("\(segment.fromLocation.name)\(segment.toLocation.name)")
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
@@ -743,10 +743,10 @@ struct TravelSection: View {
VStack(alignment: .trailing, spacing: 2) {
Text(segment.formattedDistance)
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text(segment.formattedDuration)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
}
@@ -765,16 +765,16 @@ struct TravelSection: View {
HStack(spacing: Theme.Spacing.sm) {
Image(systemName: "bolt.fill")
.foregroundStyle(.green)
.font(.system(size: 12))
.font(.caption)
Text("\(segment.evChargingStops.count) EV Charger\(segment.evChargingStops.count > 1 ? "s" : "") Along Route")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
Image(systemName: showEVChargers ? "chevron.up" : "chevron.down")
.font(.system(size: 12, weight: .semibold))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
.padding(.horizontal, Theme.Spacing.md)
@@ -834,7 +834,7 @@ struct EVChargerRow: View {
VStack(alignment: .leading, spacing: 2) {
HStack(spacing: Theme.Spacing.xs) {
Text(charger.name)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
.lineLimit(1)
@@ -844,7 +844,7 @@ struct EVChargerRow: View {
HStack(spacing: Theme.Spacing.xs) {
if let address = charger.location.address {
Text(address)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
@@ -852,7 +852,7 @@ struct EVChargerRow: View {
.foregroundStyle(Theme.textMuted(colorScheme))
Text("~\(charger.formattedChargeTime) charge")
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
}
@@ -866,7 +866,7 @@ struct EVChargerRow: View {
private var chargerTypeBadge: some View {
let (text, color) = chargerTypeInfo
Text(text)
.font(.system(size: 9, weight: .semibold))
.font(.caption2)
.foregroundStyle(color)
.padding(.horizontal, 6)
.padding(.vertical, 2)