UI overhaul: new color palette, trip creation improvements, crash fix
Theme: - New teal/cyan/mint/pink/gold color palette replacing orange/cream - Added Theme.swift, ViewModifiers.swift, AnimatedComponents.swift Trip Creation: - Removed Drive/Fly toggle (drive-only for now) - Removed Lodging Type picker - Renamed "Number of Stops" to "Number of Cities" with explanation - Added explanation for "Find Other Sports Along Route" - Removed staggered animation from trip options list Bug Fix: - Disabled AI route description generation (Foundation Models crashes in iOS 26.2 Simulator due to NLLanguageRecognizer assertion failure) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
186
SportsTime/Core/Theme/Theme.swift
Normal file
186
SportsTime/Core/Theme/Theme.swift
Normal file
@@ -0,0 +1,186 @@
|
||||
//
|
||||
// Theme.swift
|
||||
// SportsTime
|
||||
//
|
||||
// Central design system for colors, typography, spacing, and animations.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Theme
|
||||
|
||||
enum Theme {
|
||||
|
||||
// MARK: - Accent Colors (Same in Light/Dark)
|
||||
|
||||
static let warmOrange = Color(hex: "4ECDC4") // Strong Cyan (primary accent)
|
||||
static let warmOrangeGlow = Color(hex: "6ED9D1") // Lighter cyan glow
|
||||
|
||||
static let routeGold = Color(hex: "FFE66D") // Royal Gold
|
||||
static let routeAmber = Color(hex: "FF6B6B") // Grapefruit Pink
|
||||
|
||||
// New palette colors
|
||||
static let primaryCyan = Color(hex: "4ECDC4") // Strong Cyan
|
||||
static let darkTeal = Color(hex: "1A535C") // Dark Teal
|
||||
static let mintCream = Color(hex: "F7FFF7") // Mint Cream
|
||||
static let grapefruit = Color(hex: "FF6B6B") // Grapefruit Pink
|
||||
static let royalGold = Color(hex: "FFE66D") // Royal Gold
|
||||
|
||||
// MARK: - Sport Colors
|
||||
|
||||
static let mlbRed = Color(hex: "E31937")
|
||||
static let nbaOrange = Color(hex: "F58426")
|
||||
static let nhlBlue = Color(hex: "003087")
|
||||
static let nflBrown = Color(hex: "8B5A2B")
|
||||
static let mlsGreen = Color(hex: "00A651")
|
||||
|
||||
// MARK: - Dark Mode Colors
|
||||
|
||||
static let darkBackground1 = Color(hex: "1A535C") // Dark Teal
|
||||
static let darkBackground2 = Color(hex: "143F46") // Darker teal
|
||||
static let darkCardBackground = Color(hex: "1E5A64") // Slightly lighter teal
|
||||
static let darkCardBackgroundLight = Color(hex: "2A6B75") // Card elevated
|
||||
static let darkSurfaceGlow = Color(hex: "4ECDC4").opacity(0.15) // Cyan glow
|
||||
static let darkTextPrimary = Color(hex: "F7FFF7") // Mint Cream
|
||||
static let darkTextSecondary = Color(hex: "B8E8E4") // Light cyan-tinted
|
||||
static let darkTextMuted = Color(hex: "7FADA8") // Muted teal
|
||||
|
||||
// MARK: - Light Mode Colors
|
||||
|
||||
static let lightBackground1 = Color(hex: "F7FFF7") // Mint Cream
|
||||
static let lightBackground2 = Color(hex: "E8F8F5") // Slightly darker mint
|
||||
static let lightCardBackground = Color.white
|
||||
static let lightCardBackgroundElevated = Color(hex: "F7FFF7") // Mint cream
|
||||
static let lightSurfaceBorder = Color(hex: "4ECDC4").opacity(0.3) // Cyan border
|
||||
static let lightTextPrimary = Color(hex: "1A535C") // Dark Teal
|
||||
static let lightTextSecondary = Color(hex: "2A6B75") // Medium teal
|
||||
static let lightTextMuted = Color(hex: "5A9A94") // Light teal
|
||||
|
||||
// MARK: - Adaptive Gradients
|
||||
|
||||
static func backgroundGradient(_ colorScheme: ColorScheme) -> LinearGradient {
|
||||
colorScheme == .dark
|
||||
? LinearGradient(colors: [darkBackground1, darkBackground2], startPoint: .top, endPoint: .bottom)
|
||||
: LinearGradient(colors: [lightBackground1, lightBackground2], startPoint: .top, endPoint: .bottom)
|
||||
}
|
||||
|
||||
// MARK: - Adaptive Colors
|
||||
|
||||
static func cardBackground(_ colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark ? darkCardBackground : lightCardBackground
|
||||
}
|
||||
|
||||
static func cardBackgroundElevated(_ colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark ? darkCardBackgroundLight : lightCardBackgroundElevated
|
||||
}
|
||||
|
||||
static func textPrimary(_ colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark ? darkTextPrimary : lightTextPrimary
|
||||
}
|
||||
|
||||
static func textSecondary(_ colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark ? darkTextSecondary : lightTextSecondary
|
||||
}
|
||||
|
||||
static func textMuted(_ colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark ? darkTextMuted : lightTextMuted
|
||||
}
|
||||
|
||||
static func surfaceGlow(_ colorScheme: ColorScheme) -> Color {
|
||||
colorScheme == .dark ? darkSurfaceGlow : lightSurfaceBorder
|
||||
}
|
||||
|
||||
static func cardShadow(_ colorScheme: ColorScheme) -> Color {
|
||||
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 {
|
||||
static let xxs: CGFloat = 4
|
||||
static let xs: CGFloat = 8
|
||||
static let sm: CGFloat = 12
|
||||
static let md: CGFloat = 16
|
||||
static let lg: CGFloat = 20
|
||||
static let xl: CGFloat = 24
|
||||
static let xxl: CGFloat = 32
|
||||
}
|
||||
|
||||
// MARK: - Corner Radius
|
||||
|
||||
enum CornerRadius {
|
||||
static let small: CGFloat = 8
|
||||
static let medium: CGFloat = 12
|
||||
static let large: CGFloat = 16
|
||||
static let xlarge: CGFloat = 20
|
||||
}
|
||||
|
||||
// MARK: - Animation
|
||||
|
||||
enum Animation {
|
||||
static let springResponse: Double = 0.3
|
||||
static let springDamping: Double = 0.7
|
||||
static let staggerDelay: Double = 0.1
|
||||
static let routeDrawDuration: Double = 2.0
|
||||
|
||||
static var spring: SwiftUI.Animation {
|
||||
.spring(response: springResponse, dampingFraction: springDamping)
|
||||
}
|
||||
|
||||
static var gentleSpring: SwiftUI.Animation {
|
||||
.spring(response: 0.5, dampingFraction: 0.8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Color Hex Extension
|
||||
|
||||
extension Color {
|
||||
init(hex: String) {
|
||||
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
var int: UInt64 = 0
|
||||
Scanner(string: hex).scanHexInt64(&int)
|
||||
let a, r, g, b: UInt64
|
||||
switch hex.count {
|
||||
case 3: // RGB (12-bit)
|
||||
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
|
||||
case 6: // RGB (24-bit)
|
||||
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
|
||||
case 8: // ARGB (32-bit)
|
||||
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
|
||||
default:
|
||||
(a, r, g, b) = (255, 0, 0, 0)
|
||||
}
|
||||
self.init(
|
||||
.sRGB,
|
||||
red: Double(r) / 255,
|
||||
green: Double(g) / 255,
|
||||
blue: Double(b) / 255,
|
||||
opacity: Double(a) / 255
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Environment Key for Theme
|
||||
|
||||
private struct ColorSchemeKey: EnvironmentKey {
|
||||
static let defaultValue: ColorScheme = .light
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var themeColorScheme: ColorScheme {
|
||||
get { self[ColorSchemeKey.self] }
|
||||
set { self[ColorSchemeKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user