remove Expo project and all related files
Remove the entire Expo/React Native application: routes (app/), source code (src/), assets, iOS native build, config plugins, StoreKit config, npm dependencies, TypeScript/ESLint/Vitest configs, and Expo-specific documentation. The repository now contains only: admin-web, supabase, youtube-worker, tabatago-swift, docs, scripts, and CI/tooling configs.
This commit is contained in:
163
tabatago-swift/TabataGo/Theme/Theme.swift
Normal file
163
tabatago-swift/TabataGo/Theme/Theme.swift
Normal file
@@ -0,0 +1,163 @@
|
||||
import SwiftUI
|
||||
|
||||
/// TabataGo design tokens — adaptive light/dark theme.
|
||||
enum Theme {
|
||||
|
||||
// ─── Brand Colors (invariant — work on both light & dark) ─────
|
||||
static let brand = Color(red: 1.0, green: 0.42, blue: 0.21) // #FF6B35 Flame orange
|
||||
static let brandLight = Color(red: 1.0, green: 0.73, blue: 0.58)
|
||||
static let rest = Color(red: 0.35, green: 0.78, blue: 0.98) // #5AC8FA Ice blue
|
||||
static let success = Color(red: 0.19, green: 0.82, blue: 0.35) // #30D158 Energy green
|
||||
static let prep = Color(red: 1.0, green: 0.58, blue: 0.0) // #FF9500 Orange-yellow
|
||||
|
||||
// ─── Adaptive Surface Colors ──────────────────────────────────
|
||||
// Dark: custom navy surfaces. Light: system defaults.
|
||||
|
||||
/// Main screen background
|
||||
static let surfaceBackground = Color(uiColor: UIColor { traits in
|
||||
traits.userInterfaceStyle == .dark
|
||||
? UIColor(red: 0.04, green: 0.09, blue: 0.16, alpha: 1) // #0A1628
|
||||
: .systemBackground
|
||||
})
|
||||
|
||||
/// Card / grouped inset background
|
||||
static let surfaceCard = Color(uiColor: UIColor { traits in
|
||||
traits.userInterfaceStyle == .dark
|
||||
? UIColor(red: 0.07, green: 0.11, blue: 0.18, alpha: 1) // #111D2E
|
||||
: .secondarySystemBackground
|
||||
})
|
||||
|
||||
/// Elevated card / hover state
|
||||
static let surfaceElevated = Color(uiColor: UIColor { traits in
|
||||
traits.userInterfaceStyle == .dark
|
||||
? UIColor(red: 0.10, green: 0.16, blue: 0.24, alpha: 1) // #192A3E
|
||||
: .tertiarySystemBackground
|
||||
})
|
||||
|
||||
/// Subtle overlay / separator tint
|
||||
static let surfaceOverlay = Color(uiColor: UIColor { traits in
|
||||
traits.userInterfaceStyle == .dark
|
||||
? UIColor(white: 1.0, alpha: 0.05)
|
||||
: UIColor(white: 0.0, alpha: 0.03)
|
||||
})
|
||||
|
||||
/// Adaptive border color
|
||||
static let border = Color(uiColor: UIColor { traits in
|
||||
traits.userInterfaceStyle == .dark
|
||||
? UIColor(white: 1.0, alpha: 0.10)
|
||||
: UIColor(white: 0.0, alpha: 0.08)
|
||||
})
|
||||
|
||||
// ─── Phase Colors ─────────────────────────────────────────────
|
||||
static func phaseColor(_ phase: TimerPhase) -> Color {
|
||||
switch phase {
|
||||
case .prep: return prep
|
||||
case .warmup: return prep
|
||||
case .work: return brand
|
||||
case .rest: return rest
|
||||
case .interBlockRest: return rest.opacity(0.7)
|
||||
case .cooldown: return Color(red: 0.40, green: 0.75, blue: 0.90)
|
||||
case .complete: return success
|
||||
}
|
||||
}
|
||||
|
||||
static func phaseLabel(_ phase: TimerPhase) -> String {
|
||||
switch phase {
|
||||
case .prep: return "GET READY"
|
||||
case .warmup: return "WARM UP"
|
||||
case .work: return "WORK"
|
||||
case .rest: return "REST"
|
||||
case .interBlockRest: return "BREAK"
|
||||
case .cooldown: return "COOL DOWN"
|
||||
case .complete: return "DONE"
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Body Zone Gradients ──────────────────────────────────────
|
||||
static func zoneGradient(_ zone: String) -> LinearGradient {
|
||||
switch zone.lowercased() {
|
||||
case "upper-body", "upper":
|
||||
return LinearGradient(colors: [.orange, .red.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)
|
||||
case "lower-body", "lower":
|
||||
return LinearGradient(colors: [.blue, .purple.opacity(0.8)], startPoint: .topLeading, endPoint: .bottomTrailing)
|
||||
case "full-body", "full":
|
||||
return LinearGradient(colors: [brand, .purple], startPoint: .topLeading, endPoint: .bottomTrailing)
|
||||
default:
|
||||
return LinearGradient(colors: [.gray, .secondary], startPoint: .topLeading, endPoint: .bottomTrailing)
|
||||
}
|
||||
}
|
||||
|
||||
static func zoneColor(_ zone: String) -> Color {
|
||||
switch zone.lowercased() {
|
||||
case "upper-body", "upper": return .orange
|
||||
case "lower-body", "lower": return .blue
|
||||
case "full-body", "full": return brand
|
||||
default: return .gray
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Level Colors ─────────────────────────────────────────────
|
||||
static func levelColor(_ level: String) -> Color {
|
||||
switch level.lowercased() {
|
||||
case "beginner": return success
|
||||
case "intermediate": return prep
|
||||
case "advanced": return brand
|
||||
default: return .gray
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Typography ───────────────────────────────────────────────
|
||||
static let timerFont = Font.system(size: 96, weight: .black, design: .rounded)
|
||||
.monospacedDigit()
|
||||
static let timerSmallFont = Font.system(size: 60, weight: .bold, design: .rounded)
|
||||
.monospacedDigit()
|
||||
static let roundFont = Font.system(size: 22, weight: .semibold, design: .rounded)
|
||||
static let phaseFont = Font.system(size: 18, weight: .bold, design: .rounded)
|
||||
}
|
||||
|
||||
// ─── Glass Effect Modifier ────────────────────────────────────────
|
||||
|
||||
struct GlassCard: ViewModifier {
|
||||
var cornerRadius: CGFloat = 20
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.background(.ultraThinMaterial)
|
||||
.clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func glassCard(cornerRadius: CGFloat = 20) -> some View {
|
||||
modifier(GlassCard(cornerRadius: cornerRadius))
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Stat Badge ───────────────────────────────────────────────────
|
||||
|
||||
struct StatBadge: View {
|
||||
let label: String
|
||||
let value: String
|
||||
var color: Color = .primary
|
||||
var icon: String? = nil
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 4) {
|
||||
if let icon {
|
||||
Image(systemName: icon)
|
||||
.font(.system(size: 16, weight: .semibold))
|
||||
.foregroundStyle(color)
|
||||
}
|
||||
Text(value)
|
||||
.font(.system(size: 22, weight: .bold, design: .rounded))
|
||||
.foregroundStyle(color)
|
||||
.monospacedDigit()
|
||||
Text(label)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 12)
|
||||
.glassCard()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user