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

@@ -223,7 +223,7 @@ struct RoutePreviewStrip: View {
.frame(width: 8, height: 8)
Text(abbreviateCity(city))
.font(.system(size: 10, weight: .medium))
.font(.caption2)
.foregroundStyle(Theme.textSecondary(colorScheme))
.lineLimit(1)
}
@@ -256,7 +256,7 @@ struct PlanningProgressView: View {
// Current step text
Text(steps[currentStep])
.font(.system(size: 16, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
.animation(.easeInOut, value: currentStep)
}
@@ -288,9 +288,9 @@ struct StatPill: View {
var body: some View {
HStack(spacing: 6) {
Image(systemName: icon)
.font(.system(size: 12))
.font(.caption)
Text(value)
.font(.system(size: 13, weight: .medium))
.font(.footnote)
}
.foregroundStyle(Theme.textSecondary(colorScheme))
.padding(.horizontal, 12)
@@ -313,16 +313,16 @@ struct EmptyStateView: View {
var body: some View {
VStack(spacing: 20) {
Image(systemName: icon)
.font(.system(size: 48))
.font(.largeTitle)
.foregroundStyle(Theme.warmOrange.opacity(0.7))
VStack(spacing: 8) {
Text(title)
.font(.system(size: 20, weight: .semibold))
.font(.title3)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text(message)
.font(.system(size: 15))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
.multilineTextAlignment(.center)
}
@@ -330,7 +330,7 @@ struct EmptyStateView: View {
if let actionTitle = actionTitle, let action = action {
Button(action: action) {
Text(actionTitle)
.font(.system(size: 16, weight: .semibold))
.font(.body)
.foregroundStyle(.white)
.padding(.horizontal, 24)
.padding(.vertical, 12)
@@ -383,26 +383,26 @@ struct LoadingOverlay: View {
}
Image(systemName: icon)
.font(.system(size: 24))
.font(.title2)
.foregroundStyle(Theme.warmOrange)
.opacity(progress != nil ? 1 : 0)
}
VStack(spacing: Theme.Spacing.xs) {
Text(message)
.font(.system(size: Theme.FontSize.cardTitle, weight: .semibold))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
if let detail = detail {
Text(detail)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
.multilineTextAlignment(.center)
}
if let progress = progress {
Text("\(Int(progress * 100))%")
.font(.system(size: Theme.FontSize.micro, weight: .medium, design: .monospaced))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}

View File

@@ -327,17 +327,6 @@ enum Theme {
colorScheme == .dark ? Color.black.opacity(0.3) : Color.black.opacity(0.08)
}
// MARK: - Typography
enum FontSize {
static let heroTitle: CGFloat = 34
static let sectionTitle: CGFloat = 24
static let cardTitle: CGFloat = 18
static let body: CGFloat = 16
static let caption: CGFloat = 14
static let micro: CGFloat = 12
}
// MARK: - Spacing
enum Spacing {

View File

@@ -143,7 +143,7 @@ struct BadgeStyle: ViewModifier {
func body(content: Content) -> some View {
content
.font(.system(size: Theme.FontSize.micro, weight: .semibold))
.font(.caption)
.padding(.horizontal, 10)
.padding(.vertical, 5)
.background(filled ? color : color.opacity(0.2))
@@ -165,7 +165,7 @@ struct SectionHeaderStyle: ViewModifier {
func body(content: Content) -> some View {
content
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
}

View File

@@ -410,7 +410,7 @@ struct ProgressShareView: View {
private var previewCard: some View {
VStack(spacing: Theme.Spacing.md) {
Text("Preview")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textMuted(colorScheme))
// Mini preview
@@ -486,7 +486,7 @@ struct ProgressShareView: View {
// Style selector
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text("Style")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textMuted(colorScheme))
HStack(spacing: Theme.Spacing.sm) {
@@ -499,7 +499,7 @@ struct ProgressShareView: View {
// Username toggle
Toggle(isOn: $includeUsername) {
Text("Include Username")
.font(.system(size: Theme.FontSize.body))
.font(.body)
}
.padding(.horizontal)
@@ -512,7 +512,7 @@ struct ProgressShareView: View {
// Map toggle
Toggle(isOn: $includeMap) {
Text("Include Map")
.font(.system(size: Theme.FontSize.body))
.font(.body)
}
.padding(.horizontal)
}
@@ -525,7 +525,7 @@ struct ProgressShareView: View {
withAnimation { cardStyle = style }
} label: {
Text(label)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(cardStyle == style ? .white : Theme.textPrimary(colorScheme))
.padding(.horizontal, Theme.Spacing.md)
.padding(.vertical, Theme.Spacing.sm)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -65,11 +65,11 @@ struct AchievementsListView: View {
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text("\(earned) / \(total)")
.font(.system(size: Theme.FontSize.heroTitle, weight: .bold, design: .rounded))
.font(.largeTitle)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("Achievements Earned")
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
if earned == total && total > 0 {
@@ -77,7 +77,7 @@ struct AchievementsListView: View {
Image(systemName: "star.fill")
Text("All achievements unlocked!")
}
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
.font(.subheadline)
.foregroundStyle(Theme.warmOrange)
}
}
@@ -190,9 +190,9 @@ struct CategoryFilterButton: View {
Button(action: action) {
HStack(spacing: Theme.Spacing.xs) {
Image(systemName: icon)
.font(.system(size: 14))
.font(.subheadline)
Text(title)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
}
.padding(.horizontal, Theme.Spacing.md)
.padding(.vertical, Theme.Spacing.sm)
@@ -233,14 +233,14 @@ struct AchievementCard: View {
.frame(width: 60, height: 60)
Image(systemName: "lock.fill")
.font(.system(size: 14))
.font(.subheadline)
.foregroundStyle(.white)
}
}
// Title
Text(achievement.definition.name)
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
.font(.subheadline)
.foregroundStyle(achievement.isEarned ? Theme.textPrimary(colorScheme) : Theme.textMuted(colorScheme))
.multilineTextAlignment(.center)
.lineLimit(2)
@@ -250,7 +250,7 @@ struct AchievementCard: View {
if achievement.isEarned {
if let earnedAt = achievement.earnedAt {
Text(earnedAt.formatted(date: .abbreviated, time: .omitted))
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.warmOrange)
}
} else {
@@ -260,7 +260,7 @@ struct AchievementCard: View {
.progressViewStyle(AchievementProgressStyle())
Text(achievement.progressText)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -371,11 +371,11 @@ struct AchievementDetailSheet: View {
// Title and description
VStack(spacing: Theme.Spacing.sm) {
Text(achievement.definition.name)
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text(achievement.definition.description)
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
.multilineTextAlignment(.center)
@@ -384,7 +384,7 @@ struct AchievementDetailSheet: View {
Image(systemName: achievement.definition.category.iconName)
Text(achievement.definition.category.displayName)
}
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(categoryColor)
.padding(.horizontal, Theme.Spacing.sm)
.padding(.vertical, Theme.Spacing.xs)
@@ -401,7 +401,7 @@ struct AchievementDetailSheet: View {
.foregroundStyle(.green)
Text("Earned on \(earnedAt.formatted(date: .long, time: .omitted))")
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
}
@@ -409,7 +409,7 @@ struct AchievementDetailSheet: View {
// Progress section
VStack(spacing: Theme.Spacing.sm) {
Text("Progress")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textMuted(colorScheme))
ProgressView(value: achievement.progressPercentage)
@@ -417,7 +417,7 @@ struct AchievementDetailSheet: View {
.frame(width: 200)
Text("\(achievement.currentProgress) / \(achievement.totalRequired)")
.font(.system(size: Theme.FontSize.cardTitle, weight: .bold))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
}
@@ -428,7 +428,7 @@ struct AchievementDetailSheet: View {
Image(systemName: sport.iconName)
Text(sport.displayName)
}
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(sport.themeColor)
.padding(.horizontal, Theme.Spacing.md)
.padding(.vertical, Theme.Spacing.sm)

View File

@@ -72,24 +72,24 @@ struct GameMatchConfirmationView: View {
.frame(width: 80, height: 80)
Image(systemName: "photo.fill")
.font(.system(size: 36))
.font(.largeTitle)
.foregroundStyle(Theme.warmOrange)
}
VStack(spacing: Theme.Spacing.xs) {
if let date = candidate.metadata.captureDate {
Label(formatDate(date), systemImage: "calendar")
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
if candidate.metadata.hasValidLocation {
Label("Location data available", systemImage: "location.fill")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
} else {
Label("No location data", systemImage: "location.slash")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(.red)
}
}
@@ -108,7 +108,7 @@ struct GameMatchConfirmationView: View {
Image(systemName: "mappin.circle.fill")
.foregroundStyle(Theme.warmOrange)
Text("Nearest Stadium")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
}
@@ -116,11 +116,11 @@ struct GameMatchConfirmationView: View {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text(match.stadium.name)
.font(.system(size: Theme.FontSize.cardTitle, weight: .bold, design: .rounded))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text(match.stadium.fullAddress)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
@@ -129,11 +129,11 @@ struct GameMatchConfirmationView: View {
// Distance badge
VStack(spacing: 2) {
Text(match.formattedDistance)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(confidenceColor(match.confidence))
Text(match.confidence.description)
.font(.system(size: 10))
.font(.caption2)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -155,7 +155,7 @@ struct GameMatchConfirmationView: View {
Image(systemName: "sportscourt.fill")
.foregroundStyle(Theme.warmOrange)
Text(matchOptionsTitle)
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
}
@@ -204,7 +204,7 @@ struct GameMatchConfirmationView: View {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundStyle(.red)
Text(reason.description)
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
.padding(Theme.Spacing.md)
@@ -216,7 +216,7 @@ struct GameMatchConfirmationView: View {
VStack(alignment: .leading, spacing: 4) {
HStack {
Text(match.matchupDescription)
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Image(systemName: match.game.sport.iconName)
@@ -225,7 +225,7 @@ struct GameMatchConfirmationView: View {
}
Text(match.gameDateTime)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
// Confidence
@@ -234,7 +234,7 @@ struct GameMatchConfirmationView: View {
.fill(combinedConfidenceColor(match.confidence.combined))
.frame(width: 8, height: 8)
Text(match.confidence.combined.description)
.font(.system(size: 10))
.font(.caption2)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -272,7 +272,7 @@ struct GameMatchConfirmationView: View {
Image(systemName: "checkmark.circle.fill")
Text("Confirm & Import")
}
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(.white)
.frame(maxWidth: .infinity)
.padding(Theme.Spacing.md)
@@ -287,7 +287,7 @@ struct GameMatchConfirmationView: View {
dismiss()
} label: {
Text("Skip This Photo")
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
}

View File

@@ -96,11 +96,11 @@ struct PhotoImportView: View {
VStack(spacing: Theme.Spacing.sm) {
Text("Import from Photos")
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("Select photos taken at stadiums to automatically log your visits. We'll use GPS and date data to match them to games.")
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
.multilineTextAlignment(.center)
.padding(.horizontal, Theme.Spacing.xl)
@@ -114,7 +114,7 @@ struct PhotoImportView: View {
Image(systemName: "photo.stack")
Text("Select Photos")
}
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(.white)
.frame(maxWidth: .infinity)
.padding(Theme.Spacing.md)
@@ -137,7 +137,7 @@ struct PhotoImportView: View {
Image(systemName: "info.circle.fill")
.foregroundStyle(Theme.warmOrange)
Text("How it works")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
}
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
@@ -147,7 +147,7 @@ struct PhotoImportView: View {
InfoRow(icon: "hand.tap", text: "You confirm or edit the rest")
}
}
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
.padding(Theme.Spacing.md)
.background(Theme.cardBackground(colorScheme))
@@ -164,11 +164,11 @@ struct PhotoImportView: View {
ThemedSpinner(size: 50, lineWidth: 4)
Text("Processing photos...")
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
Text("\(viewModel.processedCount) of \(viewModel.totalCount) photos")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textMuted(colorScheme))
Spacer()
@@ -222,7 +222,7 @@ struct PhotoImportView: View {
Image(systemName: "plus.circle")
Text("Add More Photos")
}
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.warmOrange)
}
.padding(.top, Theme.Spacing.md)
@@ -259,11 +259,11 @@ struct PhotoImportView: View {
private func summaryBadge(count: Int, label: String, color: Color) -> some View {
VStack(spacing: 4) {
Text("\(count)")
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(color)
Text(label)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
.frame(maxWidth: .infinity)
@@ -283,10 +283,10 @@ struct PhotoImportView: View {
.foregroundStyle(color)
VStack(alignment: .leading, spacing: 2) {
Text(title)
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text(subtitle)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
Spacer()
@@ -372,7 +372,7 @@ struct PhotoImportCandidateCard: View {
.foregroundStyle(isConfirmed ? .green : Theme.textMuted(colorScheme))
}
}
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
// Match result
@@ -407,10 +407,10 @@ struct PhotoImportCandidateCard: View {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text("\(matches.count) possible games")
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("Tap to select the correct game")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.warmOrange)
}
Spacer()
@@ -424,7 +424,7 @@ struct PhotoImportCandidateCard: View {
Image(systemName: "exclamationmark.triangle")
.foregroundStyle(.red)
Text(reason.description)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
}
@@ -434,7 +434,7 @@ struct PhotoImportCandidateCard: View {
VStack(alignment: .leading, spacing: 4) {
HStack {
Text(match.matchupDescription)
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Image(systemName: match.game.sport.iconName)
@@ -442,7 +442,7 @@ struct PhotoImportCandidateCard: View {
}
Text("\(match.stadium.name)\(match.gameDateTime)")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
// Confidence badge
@@ -463,7 +463,7 @@ struct PhotoImportCandidateCard: View {
}()
return Text(text)
.font(.system(size: 10, weight: .medium))
.font(.caption2)
.foregroundStyle(color)
.padding(.horizontal, 6)
.padding(.vertical, 2)
@@ -496,7 +496,7 @@ struct GameMatchPickerSheet: View {
VStack(alignment: .leading, spacing: 4) {
HStack {
Text(match.fullMatchupDescription)
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
@@ -506,7 +506,7 @@ struct GameMatchPickerSheet: View {
}
Text("\(match.stadium.name)\(match.gameDateTime)")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
.padding(.vertical, 4)

View File

@@ -178,21 +178,21 @@ struct ProgressTabView: View {
VStack(spacing: 0) {
Text("\(progress.visitedStadiums)")
.font(.system(size: 24, weight: .bold, design: .rounded))
.font(.title3)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("/\(progress.totalStadiums)")
.font(.system(size: 12, weight: .medium))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text(viewModel.selectedSport.displayName)
.font(.system(size: Theme.FontSize.cardTitle, weight: .bold, design: .rounded))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("Stadium Quest")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
if progress.isComplete {
@@ -200,11 +200,11 @@ struct ProgressTabView: View {
Image(systemName: "checkmark.seal.fill")
Text("Complete!")
}
.font(.system(size: Theme.FontSize.caption, weight: .semibold))
.font(.subheadline)
.foregroundStyle(Theme.warmOrange)
} else {
Text("\(progress.totalStadiums - progress.visitedStadiums) stadiums remaining")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -254,7 +254,7 @@ struct ProgressTabView: View {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green)
Text("Visited (\(viewModel.visitedStadiums.count))")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
@@ -280,7 +280,7 @@ struct ProgressTabView: View {
Image(systemName: "circle.dotted")
.foregroundStyle(Theme.textMuted(colorScheme))
Text("Not Yet Visited (\(viewModel.unvisitedStadiums.count))")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
@@ -307,7 +307,7 @@ struct ProgressTabView: View {
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
HStack {
Text("Achievements")
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
@@ -319,7 +319,7 @@ struct ProgressTabView: View {
Text("View All")
Image(systemName: "chevron.right")
}
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.warmOrange)
}
}
@@ -341,11 +341,11 @@ struct ProgressTabView: View {
VStack(alignment: .leading, spacing: 4) {
Text("Track Your Progress")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("Earn badges for stadium visits")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
@@ -371,7 +371,7 @@ struct ProgressTabView: View {
private var recentVisitsSection: some View {
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
Text("Recent Visits")
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
ForEach(viewModel.recentVisits) { visitSummary in
@@ -422,7 +422,7 @@ struct LeagueSelectorButton: View {
}
Text(sport.rawValue)
.font(.system(size: Theme.FontSize.micro, weight: isSelected ? .bold : .medium))
.font(.caption)
.foregroundStyle(isSelected ? Theme.textPrimary(colorScheme) : Theme.textMuted(colorScheme))
}
.frame(maxWidth: .infinity)
@@ -451,14 +451,14 @@ struct ProgressStatPill: View {
VStack(spacing: 4) {
HStack(spacing: 4) {
Image(systemName: icon)
.font(.system(size: 12))
.font(.caption)
Text(value)
.font(.system(size: Theme.FontSize.body, weight: .bold))
.font(.body)
}
.foregroundStyle(Theme.textPrimary(colorScheme))
Text(label)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
.frame(maxWidth: .infinity)
@@ -483,12 +483,12 @@ struct StadiumChip: View {
VStack(alignment: .leading, spacing: 2) {
Text(stadium.name)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
.lineLimit(1)
Text(stadium.city)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -524,7 +524,7 @@ struct RecentVisitRow: View {
VStack(alignment: .leading, spacing: 4) {
Text(visit.stadium.name)
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
HStack(spacing: Theme.Spacing.sm) {
@@ -534,7 +534,7 @@ struct RecentVisitRow: View {
Text(matchup)
}
}
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
@@ -545,7 +545,7 @@ struct RecentVisitRow: View {
Image(systemName: "photo")
Text("\(visit.photoCount)")
}
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
@@ -585,17 +585,17 @@ struct StadiumDetailSheet: View {
.frame(width: 80, height: 80)
Image(systemName: visitStatus.isVisited ? "checkmark.seal.fill" : sport.iconName)
.font(.system(size: 36))
.font(.largeTitle)
.foregroundStyle(visitStatus.isVisited ? .green : sport.themeColor)
}
Text(stadium.name)
.font(.system(size: Theme.FontSize.cardTitle, weight: .bold, design: .rounded))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
.multilineTextAlignment(.center)
Text(stadium.fullAddress)
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
if visitStatus.isVisited {
@@ -604,7 +604,7 @@ struct StadiumDetailSheet: View {
.foregroundStyle(.green)
Text("Visited \(visitStatus.visitCount) time\(visitStatus.visitCount == 1 ? "" : "s")")
}
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(.green)
}
}
@@ -613,23 +613,23 @@ struct StadiumDetailSheet: View {
if case .visited(let visits) = visitStatus {
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
Text("Visit History")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
ForEach(visits.sorted(by: { $0.visitDate > $1.visitDate })) { visit in
HStack {
VStack(alignment: .leading, spacing: 2) {
Text(visit.shortDateDescription)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
if let matchup = visit.matchup {
Text(matchup)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
}
Spacer()
Text(visit.visitType.displayName)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
.padding(Theme.Spacing.sm)

View File

@@ -156,7 +156,7 @@ struct StadiumVisitSheet: View {
awayTeamFocused = false
} label: {
Text(team.name)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
.padding(.horizontal, 10)
.padding(.vertical, 6)
@@ -192,7 +192,7 @@ struct StadiumVisitSheet: View {
homeTeamFocused = false
} label: {
Text(team.name)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
.padding(.horizontal, 10)
.padding(.vertical, 6)
@@ -409,11 +409,11 @@ struct StadiumPickerSheet: View {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text(stadium.name)
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text(stadium.fullAddress)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}

View File

@@ -147,23 +147,23 @@ struct VisitDetailView: View {
.frame(width: 80, height: 80)
Image(systemName: visit.sportEnum?.iconName ?? "sportscourt")
.font(.system(size: 36))
.font(.largeTitle)
.foregroundStyle(sportColor)
}
VStack(spacing: Theme.Spacing.xs) {
Text(stadium.name)
.font(.system(size: Theme.FontSize.cardTitle, weight: .bold, design: .rounded))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
.multilineTextAlignment(.center)
Text(stadium.fullAddress)
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
// Visit type badge
Text(visit.visitType.displayName)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(.white)
.padding(.horizontal, Theme.Spacing.sm)
.padding(.vertical, 4)
@@ -189,7 +189,7 @@ struct VisitDetailView: View {
Image(systemName: "sportscourt.fill")
.foregroundStyle(sportColor)
Text("Game Info")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
@@ -215,7 +215,7 @@ struct VisitDetailView: View {
}
}
}
.font(.system(size: Theme.FontSize.body))
.font(.body)
.padding(Theme.Spacing.lg)
.background(Theme.cardBackground(colorScheme))
.clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.large))
@@ -233,7 +233,7 @@ struct VisitDetailView: View {
Image(systemName: "info.circle.fill")
.foregroundStyle(Theme.warmOrange)
Text("Details")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
@@ -275,7 +275,7 @@ struct VisitDetailView: View {
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
.font(.system(size: Theme.FontSize.body))
.font(.body)
.padding(Theme.Spacing.lg)
.background(Theme.cardBackground(colorScheme))
.clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.large))
@@ -293,12 +293,12 @@ struct VisitDetailView: View {
Image(systemName: "note.text")
.foregroundStyle(Theme.routeGold)
Text("Notes")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
Text(visit.notes ?? "")
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
.frame(maxWidth: .infinity, alignment: .leading)
@@ -318,7 +318,7 @@ struct VisitDetailView: View {
// Date
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text("Date")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
DatePicker("", selection: $editVisitDate, displayedComponents: .date)
@@ -328,7 +328,7 @@ struct VisitDetailView: View {
// Visit Type
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text("Visit Type")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
Picker("", selection: $editVisitType) {
@@ -343,7 +343,7 @@ struct VisitDetailView: View {
if editVisitType == .game {
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
Text("Game Info")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
HStack {
@@ -374,7 +374,7 @@ struct VisitDetailView: View {
// Seat location
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text("Seat Location")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
TextField("e.g., Section 120, Row 5", text: $editSeatLocation)
@@ -384,7 +384,7 @@ struct VisitDetailView: View {
// Notes
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text("Notes")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
TextEditor(text: $editNotes)

View File

@@ -144,11 +144,11 @@ struct RegionMapSelector: View {
VStack(alignment: .leading, spacing: 1) {
Text(region.shortName)
.font(.system(size: 11, weight: isSelected ? .bold : .medium))
.font(.caption2)
.foregroundStyle(isSelected ? regionColor(region) : Theme.textPrimary(colorScheme))
Text(states)
.font(.system(size: 9))
.font(.caption2)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -159,7 +159,7 @@ struct RegionMapSelector: View {
HStack(spacing: Theme.Spacing.md) {
if selectedRegions.isEmpty {
Text("Tap map to select regions")
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
} else {
Spacer()
@@ -168,7 +168,7 @@ struct RegionMapSelector: View {
selectedRegions.removeAll()
} label: {
Text("Clear")
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.warmOrange)
}
}

View File

@@ -187,15 +187,15 @@ struct TripCreationView: View {
private var heroHeader: some View {
VStack(spacing: Theme.Spacing.sm) {
Image(systemName: "map.fill")
.font(.system(size: 40))
.font(.largeTitle)
.foregroundStyle(Theme.warmOrange)
Text("Plan Your Adventure")
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("Select your games, set your route, and hit the road")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
.multilineTextAlignment(.center)
}
@@ -214,7 +214,7 @@ struct TripCreationView: View {
.pickerStyle(.segmented)
Text(viewModel.planningMode.description)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
.padding(.top, Theme.Spacing.xs)
}
@@ -248,7 +248,7 @@ struct TripCreationView: View {
HStack {
ThemedSpinnerCompact(size: 14)
Text("Searching...")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textMuted(colorScheme))
}
.padding(.top, Theme.Spacing.xs)
@@ -281,7 +281,7 @@ struct TripCreationView: View {
HStack {
ThemedSpinnerCompact(size: 14)
Text("Searching...")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textMuted(colorScheme))
}
.padding(.top, Theme.Spacing.xs)
@@ -363,16 +363,16 @@ struct TripCreationView: View {
HStack(spacing: Theme.Spacing.sm) {
Image(systemName: "mappin.circle.fill")
.foregroundStyle(Theme.warmOrange)
.font(.system(size: 14))
.font(.subheadline)
VStack(alignment: .leading, spacing: 2) {
Text(result.name)
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
if !result.address.isEmpty {
Text(result.address)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -401,7 +401,7 @@ struct TripCreationView: View {
HStack(spacing: Theme.Spacing.sm) {
ThemedSpinnerCompact(size: 20)
Text("Loading games...")
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
.frame(maxWidth: .infinity, alignment: .center)
@@ -425,10 +425,10 @@ struct TripCreationView: View {
VStack(alignment: .leading, spacing: 4) {
Text("Browse Teams & Games")
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("\(viewModel.availableGames.count) games available")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
@@ -451,7 +451,7 @@ struct TripCreationView: View {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green)
Text("\(viewModel.mustSeeGameIds.count) game(s) selected")
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
@@ -460,7 +460,7 @@ struct TripCreationView: View {
viewModel.deselectAllGames()
} label: {
Text("Deselect All")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(.red)
}
}
@@ -470,18 +470,18 @@ struct TripCreationView: View {
HStack(spacing: Theme.Spacing.sm) {
SportColorBar(sport: game.game.sport)
Text("\(game.awayTeam.abbreviation) @ \(game.homeTeam.abbreviation)")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
Text(game.game.formattedDate)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
if viewModel.selectedGames.count > 3 {
Text("+ \(viewModel.selectedGames.count - 3) more")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
}
@@ -550,13 +550,13 @@ struct TripCreationView: View {
}
Text("Select Games")
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
Text("\(viewModel.selectedGamesCount) selected")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
Image(systemName: "chevron.right")
@@ -576,7 +576,7 @@ struct TripCreationView: View {
// Region selector
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text("Regions")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
RegionMapSelector(
@@ -588,12 +588,12 @@ struct TripCreationView: View {
if viewModel.selectedRegions.isEmpty {
Text("Select at least one region")
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.warmOrange)
.padding(.top, Theme.Spacing.xxs)
} else {
Text("Games will be found in selected regions")
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
.padding(.top, Theme.Spacing.xxs)
}
@@ -602,7 +602,7 @@ struct TripCreationView: View {
// Route preference
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text("Route Preference")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
Picker("Route Preference", selection: $viewModel.routePreference) {
@@ -622,7 +622,7 @@ struct TripCreationView: View {
if !viewModel.allowRepeatCities {
Text("Each city will only be visited on one day")
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
.padding(.leading, 32)
}
@@ -640,11 +640,11 @@ struct TripCreationView: View {
Image(systemName: "mappin.circle")
.foregroundStyle(Theme.warmOrange)
Text("Must-Stop Locations")
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
Text("\(viewModel.mustStopLocations.count)")
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
@@ -652,11 +652,11 @@ struct TripCreationView: View {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text(location.name)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
if let address = location.address, !address.isEmpty {
Text(address)
.font(.system(size: Theme.FontSize.micro))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -681,7 +681,7 @@ struct TripCreationView: View {
Image(systemName: "plus.circle.fill")
Text("Add Location")
}
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.warmOrange)
}
}
@@ -748,7 +748,7 @@ struct TripCreationView: View {
Image(systemName: "exclamationmark.triangle.fill")
.foregroundStyle(.orange)
Text(message)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
.padding(Theme.Spacing.md)
@@ -861,7 +861,7 @@ struct GamePickerSheet: View {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green)
Text("\(selectedGamesCount) game(s) selected")
.font(.system(size: 15, weight: .semibold))
.font(.subheadline)
Spacer()
}
.padding(Theme.Spacing.md)
@@ -934,23 +934,23 @@ struct SportSection: View {
} label: {
HStack(spacing: Theme.Spacing.sm) {
Image(systemName: sport.iconName)
.font(.system(size: 20))
.font(.title3)
.foregroundStyle(sport.themeColor)
.frame(width: 32)
Text(sport.rawValue)
.font(.system(size: 17, weight: .bold))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("\(teams.flatMap { $0.games }.count) games")
.font(.system(size: 13))
.font(.subheadline)
.foregroundStyle(Theme.textMuted(colorScheme))
Spacer()
if selectedCount > 0 {
Text("\(selectedCount)")
.font(.system(size: 12, weight: .bold))
.font(.caption)
.foregroundStyle(.white)
.padding(.horizontal, 8)
.padding(.vertical, 4)
@@ -1035,18 +1035,18 @@ struct TeamSection: View {
}
Text("\(teamData.team.city) \(teamData.team.name)")
.font(.system(size: 15, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Text("\(teamData.games.count)")
.font(.system(size: 12))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
Spacer()
if selectedCount > 0 {
Text("\(selectedCount)")
.font(.system(size: 11, weight: .bold))
.font(.caption2)
.foregroundStyle(.white)
.padding(.horizontal, 6)
.padding(.vertical, 3)
@@ -1055,7 +1055,7 @@ struct TeamSection: View {
}
Image(systemName: "chevron.right")
.font(.system(size: 12, weight: .semibold))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
.rotationEffect(.degrees(isExpanded ? 90 : 0))
}
@@ -1072,7 +1072,7 @@ struct TeamSection: View {
VStack(alignment: .leading, spacing: 0) {
// Date header
Text(dateGroup.date)
.font(.system(size: 12, weight: .semibold))
.font(.caption)
.foregroundStyle(Theme.warmOrange)
.padding(.horizontal, Theme.Spacing.md)
.padding(.top, Theme.Spacing.sm)
@@ -1116,24 +1116,24 @@ struct GameCalendarRow: View {
HStack(spacing: Theme.Spacing.sm) {
// Selection indicator
Image(systemName: isSelected ? "checkmark.circle.fill" : "circle")
.font(.system(size: 22))
.font(.title3)
.foregroundStyle(isSelected ? Theme.warmOrange : Theme.textMuted(colorScheme))
VStack(alignment: .leading, spacing: 2) {
Text("vs \(game.awayTeam.name)")
.font(.system(size: 14, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
HStack(spacing: Theme.Spacing.xs) {
Text(game.game.gameTime)
.font(.system(size: 12))
.font(.caption)
.foregroundStyle(Theme.textSecondary(colorScheme))
Text("")
.foregroundStyle(Theme.textMuted(colorScheme))
Text(game.stadium.name)
.font(.system(size: 12))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
.lineLimit(1)
}
@@ -1424,11 +1424,11 @@ struct TripOptionsView: View {
// Hero header
VStack(spacing: 8) {
Image(systemName: "point.topright.arrow.triangle.backward.to.point.bottomleft.scurvepath.fill")
.font(.system(size: 40))
.font(.largeTitle)
.foregroundStyle(Theme.warmOrange)
Text("\(filteredAndSortedOptions.count) of \(options.count) Routes")
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
.padding(.top, Theme.Spacing.lg)
@@ -1484,11 +1484,11 @@ struct TripOptionsView: View {
} label: {
HStack(spacing: 8) {
Image(systemName: sortOption.icon)
.font(.system(size: 14))
.font(.subheadline)
Text(sortOption.rawValue)
.font(.system(size: 14, weight: .medium))
.font(.subheadline)
Image(systemName: "chevron.down")
.font(.system(size: 12))
.font(.caption)
}
.foregroundStyle(Theme.textPrimary(colorScheme))
.padding(.horizontal, 16)
@@ -1535,11 +1535,11 @@ struct TripOptionsView: View {
} label: {
HStack(spacing: 6) {
Image(systemName: paceFilter.icon)
.font(.system(size: 12))
.font(.caption)
Text(paceFilter.rawValue)
.font(.system(size: 13, weight: .medium))
.font(.subheadline)
Image(systemName: "chevron.down")
.font(.system(size: 10))
.font(.caption2)
}
.foregroundStyle(paceFilter == .all ? Theme.textPrimary(colorScheme) : Theme.warmOrange)
.padding(.horizontal, 12)
@@ -1556,7 +1556,7 @@ struct TripOptionsView: View {
private var citiesPicker: some View {
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Label("Max Cities", systemImage: "mappin.circle")
.font(.system(size: 13, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
ScrollView(.horizontal, showsIndicators: false) {
@@ -1593,7 +1593,7 @@ struct TripOptionsView: View {
.foregroundStyle(Theme.textMuted(colorScheme))
Text("No routes match your filters")
.font(.system(size: Theme.FontSize.body, weight: .medium))
.font(.body)
.foregroundStyle(Theme.textSecondary(colorScheme))
Button {
@@ -1603,7 +1603,7 @@ struct TripOptionsView: View {
}
} label: {
Text("Reset Filters")
.font(.system(size: 14, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.warmOrange)
}
}
@@ -1657,19 +1657,19 @@ struct TripOptionCard: View {
// Vertical route display
VStack(alignment: .leading, spacing: 0) {
Text(uniqueCities.first ?? "")
.font(.system(size: 15, weight: .semibold))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
VStack(spacing: 0) {
Text("|")
.font(.system(size: 10))
.font(.caption2)
Image(systemName: "chevron.down")
.font(.system(size: 8, weight: .bold))
.font(.caption2)
}
.foregroundStyle(Theme.warmOrange)
Text(uniqueCities.last ?? "")
.font(.system(size: 15, weight: .semibold))
.font(.subheadline)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
@@ -1680,7 +1680,7 @@ struct TripOptionCard: View {
Label("\(Int(option.totalDistanceMiles)) mi", systemImage: "car")
}
}
.font(.system(size: 12))
.font(.caption)
.foregroundStyle(Theme.textSecondary(colorScheme))
// Bottom row: sports with game counts
@@ -1688,9 +1688,9 @@ struct TripOptionCard: View {
ForEach(gamesPerSport, id: \.sport) { item in
HStack(spacing: 3) {
Image(systemName: item.sport.iconName)
.font(.system(size: 9))
.font(.caption2)
Text("\(item.sport.rawValue.uppercased()) \(item.count)")
.font(.system(size: 9, weight: .semibold))
.font(.caption2)
}
.padding(.horizontal, 6)
.padding(.vertical, 3)
@@ -1711,7 +1711,7 @@ struct TripOptionCard: View {
HStack(spacing: 4) {
ThemedSpinnerCompact(size: 12)
Text("Generating...")
.font(.system(size: 11))
.font(.caption2)
.foregroundStyle(Theme.textMuted(colorScheme))
}
}
@@ -1777,7 +1777,7 @@ struct ThemedSection<Content: View>: View {
var body: some View {
VStack(alignment: .leading, spacing: Theme.Spacing.sm) {
Text(title)
.font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded))
.font(.title2)
.foregroundStyle(Theme.textPrimary(colorScheme))
VStack(alignment: .leading, spacing: Theme.Spacing.md) {
@@ -1804,7 +1804,7 @@ struct ThemedTextField: View {
var body: some View {
VStack(alignment: .leading, spacing: Theme.Spacing.xs) {
Text(label)
.font(.system(size: Theme.FontSize.caption))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
HStack(spacing: Theme.Spacing.sm) {
@@ -1813,7 +1813,7 @@ struct ThemedTextField: View {
.frame(width: 24)
TextField(placeholder, text: $text)
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
.padding(Theme.Spacing.md)
@@ -1836,7 +1836,7 @@ struct ThemedToggle: View {
.frame(width: 24)
Text(label)
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
@@ -1859,7 +1859,7 @@ struct ThemedStepper: View {
var body: some View {
HStack {
Text(label)
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
@@ -1877,7 +1877,7 @@ struct ThemedStepper: View {
.disabled(value <= range.lowerBound)
Text("\(value)")
.font(.system(size: Theme.FontSize.body, weight: .bold))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
.frame(minWidth: 30)
@@ -1907,7 +1907,7 @@ struct ThemedDatePicker: View {
Image(systemName: "calendar")
.foregroundStyle(Theme.warmOrange)
Text(label)
.font(.system(size: Theme.FontSize.body))
.font(.body)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
@@ -2004,26 +2004,26 @@ struct DateRangePicker: View {
// Start date
VStack(alignment: .leading, spacing: 4) {
Text("START")
.font(.system(size: 10, weight: .semibold))
.font(.caption2)
.foregroundStyle(Theme.textMuted(colorScheme))
Text(startDate.formatted(.dateTime.month(.abbreviated).day().year()))
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.warmOrange)
}
.frame(maxWidth: .infinity, alignment: .leading)
// Arrow
Image(systemName: "arrow.right")
.font(.system(size: 14, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textMuted(colorScheme))
// End date
VStack(alignment: .trailing, spacing: 4) {
Text("END")
.font(.system(size: 10, weight: .semibold))
.font(.caption2)
.foregroundStyle(Theme.textMuted(colorScheme))
Text(endDate.formatted(.dateTime.month(.abbreviated).day().year()))
.font(.system(size: Theme.FontSize.body, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.warmOrange)
}
.frame(maxWidth: .infinity, alignment: .trailing)
@@ -2041,7 +2041,7 @@ struct DateRangePicker: View {
}
} label: {
Image(systemName: "chevron.left")
.font(.system(size: 16, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.warmOrange)
.frame(width: 36, height: 36)
.background(Theme.warmOrange.opacity(0.15))
@@ -2051,7 +2051,7 @@ struct DateRangePicker: View {
Spacer()
Text(monthYearString)
.font(.system(size: Theme.FontSize.cardTitle, weight: .bold, design: .rounded))
.font(.headline)
.foregroundStyle(Theme.textPrimary(colorScheme))
Spacer()
@@ -2062,7 +2062,7 @@ struct DateRangePicker: View {
}
} label: {
Image(systemName: "chevron.right")
.font(.system(size: 16, weight: .semibold))
.font(.body)
.foregroundStyle(Theme.warmOrange)
.frame(width: 36, height: 36)
.background(Theme.warmOrange.opacity(0.15))
@@ -2075,7 +2075,7 @@ struct DateRangePicker: View {
HStack(spacing: 0) {
ForEach(Array(daysOfWeek.enumerated()), id: \.offset) { _, day in
Text(day)
.font(.system(size: 12, weight: .semibold))
.font(.caption)
.foregroundStyle(Theme.textMuted(colorScheme))
.frame(maxWidth: .infinity)
}
@@ -2109,7 +2109,7 @@ struct DateRangePicker: View {
Image(systemName: "calendar.badge.clock")
.foregroundStyle(Theme.warmOrange)
Text("\(tripDuration) day\(tripDuration == 1 ? "" : "s")")
.font(.system(size: Theme.FontSize.caption, weight: .medium))
.font(.subheadline)
.foregroundStyle(Theme.textSecondary(colorScheme))
}
.frame(maxWidth: .infinity, alignment: .center)
@@ -2247,7 +2247,7 @@ struct SportSelectionChip: View {
}
Image(systemName: sport.iconName)
.font(.system(size: 20))
.font(.title3)
.foregroundStyle(isSelected ? .white : sport.themeColor)
}

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)