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:
@@ -129,11 +129,11 @@ struct HomeView: View {
|
||||
VStack(spacing: Theme.Spacing.lg) {
|
||||
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
|
||||
Text("Adventure Awaits")
|
||||
.font(.system(size: Theme.FontSize.heroTitle, weight: .bold, design: .rounded))
|
||||
.font(.largeTitle)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
|
||||
Text("Plan your ultimate sports road trip. Visit stadiums, catch games, and create unforgettable memories.")
|
||||
.font(.system(size: Theme.FontSize.body))
|
||||
.font(.body)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
@@ -174,7 +174,7 @@ struct HomeView: View {
|
||||
|
||||
return VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
|
||||
Text("Quick Start")
|
||||
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
|
||||
.font(.title2)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
|
||||
VStack(spacing: Theme.Spacing.md) {
|
||||
@@ -217,7 +217,7 @@ struct HomeView: View {
|
||||
// Header with refresh button
|
||||
HStack {
|
||||
Text("Featured Trips")
|
||||
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
|
||||
.font(.title2)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
|
||||
Spacer()
|
||||
@@ -228,7 +228,7 @@ struct HomeView: View {
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "arrow.clockwise")
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
}
|
||||
}
|
||||
@@ -241,9 +241,9 @@ struct HomeView: View {
|
||||
// Region header
|
||||
HStack(spacing: Theme.Spacing.xs) {
|
||||
Image(systemName: regionGroup.region.iconName)
|
||||
.font(.system(size: 12))
|
||||
.font(.caption)
|
||||
Text(regionGroup.region.shortName)
|
||||
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
|
||||
.font(.subheadline)
|
||||
}
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
|
||||
@@ -268,14 +268,14 @@ struct HomeView: View {
|
||||
// Error state
|
||||
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
|
||||
Text("Featured Trips")
|
||||
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
|
||||
.font(.title2)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
|
||||
HStack {
|
||||
Image(systemName: "exclamationmark.triangle")
|
||||
.foregroundStyle(.orange)
|
||||
Text(error)
|
||||
.font(.system(size: Theme.FontSize.caption))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
|
||||
Spacer()
|
||||
@@ -285,7 +285,7 @@ struct HomeView: View {
|
||||
await suggestedTripsGenerator.generateTrips()
|
||||
}
|
||||
}
|
||||
.font(.system(size: Theme.FontSize.caption, weight: .medium))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
}
|
||||
.padding(Theme.Spacing.md)
|
||||
@@ -301,7 +301,7 @@ struct HomeView: View {
|
||||
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
|
||||
HStack {
|
||||
Text("Recent Trips")
|
||||
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
|
||||
.font(.title2)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
Spacer()
|
||||
Button {
|
||||
@@ -312,7 +312,7 @@ struct HomeView: View {
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.caption)
|
||||
}
|
||||
.font(.system(size: Theme.FontSize.caption, weight: .medium))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
}
|
||||
}
|
||||
@@ -331,7 +331,7 @@ struct HomeView: View {
|
||||
private var tipsSection: some View {
|
||||
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
|
||||
Text("Planning Tips")
|
||||
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
|
||||
.font(.title2)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
|
||||
VStack(spacing: Theme.Spacing.xs) {
|
||||
@@ -367,12 +367,12 @@ struct QuickSportButton: View {
|
||||
.frame(width: 48, height: 48)
|
||||
|
||||
Image(systemName: sport.iconName)
|
||||
.font(.system(size: 20))
|
||||
.font(.title3)
|
||||
.foregroundStyle(sport.themeColor)
|
||||
}
|
||||
|
||||
Text(sport.rawValue)
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.font(.caption2)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
@@ -413,11 +413,11 @@ struct SavedTripCard: View {
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(trip.name)
|
||||
.font(.system(size: Theme.FontSize.body, weight: .semibold))
|
||||
.font(.body)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
|
||||
Text(trip.formattedDateRange)
|
||||
.font(.system(size: Theme.FontSize.caption))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
|
||||
HStack(spacing: Theme.Spacing.sm) {
|
||||
@@ -432,7 +432,7 @@ struct SavedTripCard: View {
|
||||
Text("\(trip.totalGames) games")
|
||||
}
|
||||
}
|
||||
.font(.system(size: Theme.FontSize.micro))
|
||||
.font(.caption)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
}
|
||||
|
||||
@@ -468,16 +468,16 @@ struct TipRow: View {
|
||||
.frame(width: 36, height: 36)
|
||||
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 14))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.routeGold)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(title)
|
||||
.font(.system(size: Theme.FontSize.caption, weight: .medium))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
Text(subtitle)
|
||||
.font(.system(size: Theme.FontSize.micro))
|
||||
.font(.caption)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
}
|
||||
|
||||
@@ -500,7 +500,7 @@ struct SavedTripsListView: View {
|
||||
.frame(height: 100)
|
||||
|
||||
Image(systemName: "suitcase")
|
||||
.font(.system(size: 60))
|
||||
.font(.largeTitle)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Text("No Saved Trips")
|
||||
@@ -558,11 +558,11 @@ struct SavedTripListRow: View {
|
||||
|
||||
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
|
||||
Text(trip.name)
|
||||
.font(.system(size: Theme.FontSize.cardTitle, weight: .semibold))
|
||||
.font(.headline)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
|
||||
Text(trip.formattedDateRange)
|
||||
.font(.system(size: Theme.FontSize.caption))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
|
||||
// Route preview strip
|
||||
|
||||
@@ -17,7 +17,7 @@ struct LoadingTripsView: View {
|
||||
// Header
|
||||
HStack {
|
||||
Text("Featured Trips")
|
||||
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
|
||||
.font(.title2)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
Spacer()
|
||||
}
|
||||
@@ -27,7 +27,7 @@ struct LoadingTripsView: View {
|
||||
LoadingDots()
|
||||
|
||||
Text(message)
|
||||
.font(.system(size: Theme.FontSize.body))
|
||||
.font(.body)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
.lineLimit(2)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
@@ -17,7 +17,7 @@ struct SuggestedTripCard: View {
|
||||
HStack {
|
||||
// Region badge
|
||||
Text(suggestedTrip.region.shortName)
|
||||
.font(.system(size: Theme.FontSize.micro, weight: .semibold))
|
||||
.font(.caption)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, Theme.Spacing.xs)
|
||||
.padding(.vertical, 4)
|
||||
@@ -30,7 +30,7 @@ struct SuggestedTripCard: View {
|
||||
HStack(spacing: 4) {
|
||||
ForEach(suggestedTrip.displaySports, id: \.self) { sport in
|
||||
Image(systemName: sport.iconName)
|
||||
.font(.system(size: 12))
|
||||
.font(.caption)
|
||||
.foregroundStyle(sport.themeColor)
|
||||
}
|
||||
}
|
||||
@@ -58,12 +58,12 @@ struct SuggestedTripCard: View {
|
||||
Image(systemName: "mappin")
|
||||
}
|
||||
}
|
||||
.font(.system(size: 11))
|
||||
.font(.caption2)
|
||||
.foregroundStyle(Theme.textSecondary(colorScheme))
|
||||
|
||||
// Date range
|
||||
Text(suggestedTrip.trip.formattedDateRange)
|
||||
.font(.system(size: Theme.FontSize.micro))
|
||||
.font(.caption)
|
||||
.foregroundStyle(Theme.textMuted(colorScheme))
|
||||
}
|
||||
.padding(Theme.Spacing.md)
|
||||
@@ -86,16 +86,16 @@ struct SuggestedTripCard: View {
|
||||
// Start → End display
|
||||
HStack(spacing: 6) {
|
||||
Text(startCity)
|
||||
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
.lineLimit(1)
|
||||
|
||||
Image(systemName: "arrow.right")
|
||||
.font(.system(size: 10, weight: .semibold))
|
||||
.font(.caption2)
|
||||
.foregroundStyle(Theme.warmOrange)
|
||||
|
||||
Text(endCity)
|
||||
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(Theme.textPrimary(colorScheme))
|
||||
.lineLimit(1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user