import SwiftUI import UIKit // MARK: — Appearance preference enum AppearanceMode: String, CaseIterable { case system, light, dark var resolvedScheme: ColorScheme? { switch self { case .system: return nil case .light: return .light case .dark: return .dark } } var label: String { switch self { case .system: return "System" case .light: return "Light" case .dark: return "Dark" } } var icon: String { switch self { case .system: return "circle.lefthalf.filled" case .light: return "sun.max" case .dark: return "moon" } } } enum AppTheme { // MARK: — Surfaces static let background = Color(UIColor.systemBackground) static let surfaceRaised = Color(UIColor.systemBackground) static let surfaceSunken = Color(UIColor { t in t.userInterfaceStyle == .dark ? UIColor(white: 0.11, alpha: 1) : UIColor(white: 0.961, alpha: 1) }) // MARK: — Ink static let ink = Color(UIColor { t in t.userInterfaceStyle == .dark ? UIColor(white: 0.93, alpha: 1) : UIColor(red: 0.059, green: 0.059, blue: 0.059, alpha: 1) }) static let inkSecondary = Color(UIColor { t in t.userInterfaceStyle == .dark ? UIColor(white: 0.58, alpha: 1) : UIColor(white: 0.42, alpha: 1) }) static let inkTertiary = Color(UIColor { t in t.userInterfaceStyle == .dark ? UIColor(white: 0.40, alpha: 1) : UIColor(white: 0.67, alpha: 1) }) static let inkQuaternary = Color(UIColor { t in t.userInterfaceStyle == .dark ? UIColor(white: 0.22, alpha: 1) : UIColor(white: 0.83, alpha: 1) }) // Readable on AppTheme.ink background in both color schemes static let inkInverse = Color(UIColor { t in t.userInterfaceStyle == .dark ? UIColor(red: 0.059, green: 0.059, blue: 0.059, alpha: 1) : UIColor(white: 1.0, alpha: 1) }) // MARK: — Semantic static let positive = Color(hex: "#16A34A") static let destructive = Color(hex: "#DC2626") static let interactive = Color(hex: "#2563EB") // MARK: — Chrome static let separator = Color(UIColor.separator) static let cardShadow = Color(UIColor { t in t.userInterfaceStyle == .dark ? UIColor.clear : UIColor.black.withAlphaComponent(0.048) }) static let cardBorderColor = Color(UIColor.separator) // MARK: — Metrics static let radius: CGFloat = 14 static let radiusLarge: CGFloat = 18 static let hPad: CGFloat = 20 static let cardPad: CGFloat = 16 static let sectionGap: CGFloat = 28 // MARK: — Typography static func display(_ size: CGFloat = 32) -> Font { .system(size: size, weight: .semibold, design: .default) } static func title(_ size: CGFloat = 20) -> Font { .system(size: size, weight: .semibold) } static func headline(_ size: CGFloat = 15) -> Font { .system(size: size, weight: .medium) } static func body(_ size: CGFloat = 15) -> Font { .system(size: size, weight: .regular) } static func caption(_ size: CGFloat = 13) -> Font { .system(size: size, weight: .regular) } static func micro(_ size: CGFloat = 11) -> Font { .system(size: size, weight: .medium) } // MARK: — Back-compat aliases static let blue = interactive static let green = positive static let red = destructive static let textPrimary = ink static let textSecondary = inkSecondary static let textTertiary = inkTertiary static let cardBorder = cardBorderColor static let connectedBorder = positive static let lanBorder = interactive static let cardCornerRadius = radius static let cardBackground = surfaceRaised static let titleFont = Font.system(size: 28, weight: .bold) static let headlineFont = Font.system(size: 17, weight: .semibold) static let bodyFont = Font.system(size: 15, weight: .regular) static let captionFont = Font.system(size: 13, weight: .regular) static let smallFont = Font.system(size: 11, weight: .medium) static let sectionSpacing: CGFloat = 28 } // MARK: — Card modifier struct CardStyle: ViewModifier { var padded: Bool = true func body(content: Content) -> some View { content .if(padded) { $0.padding(AppTheme.cardPad) } .background(AppTheme.surfaceRaised) .clipShape(RoundedRectangle(cornerRadius: AppTheme.radius, style: .continuous)) .shadow(color: AppTheme.cardShadow, radius: 12, x: 0, y: 2) } } extension View { func card(padded: Bool = true) -> some View { modifier(CardStyle(padded: padded)) } @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> T) -> some View { if condition { transform(self) } else { self } } } extension Color { init(hex: String) { var h = hex.trimmingCharacters(in: .init(charactersIn: "#")) if h.count == 3 { h = h.map { "\($0)\($0)" }.joined() } let n = UInt64(h, radix: 16) ?? 0 self.init( red: Double((n >> 16) & 0xFF) / 255, green: Double((n >> 8) & 0xFF) / 255, blue: Double( n & 0xFF) / 255 ) } }