import SwiftUI enum TvColors { static let bg = Color(red: 0.02, green: 0.02, blue: 0.025) static let bgElevated = Color(red: 0.06, green: 0.06, blue: 0.07) static let surface = Color(red: 0.11, green: 0.11, blue: 0.125) static let surfaceSoft = Color(red: 0.16, green: 0.16, blue: 0.18) /// Netflix-red #E50914 static let accent = Color(red: 0.898, green: 0.035, blue: 0.078) static let accentSoft = Color(red: 0.898, green: 0.035, blue: 0.078).opacity(0.28) static let text = Color(red: 0.97, green: 0.97, blue: 0.98) static let textDim = Color(red: 0.62, green: 0.62, blue: 0.68) static let danger = Color(red: 1.0, green: 0.42, blue: 0.42) static let success = Color(red: 0.275, green: 0.827, blue: 0.412) } enum TvFonts { static let brand = Font.system(size: 72, weight: .black, design: .rounded) static let hero = Font.system(size: 56, weight: .black, design: .rounded) static let title = Font.system(size: 42, weight: .bold, design: .rounded) static let headline = Font.system(size: 28, weight: .semibold, design: .rounded) static let body = Font.system(size: 24, weight: .regular, design: .rounded) static let caption = Font.system(size: 20, weight: .medium, design: .rounded) static let nav = Font.system(size: 22, weight: .semibold, design: .rounded) static let code = Font.system(size: 64, weight: .bold, design: .monospaced) } struct FocusableCardStyle: ButtonStyle { var scale: CGFloat = 1.08 func makeBody(configuration: Configuration) -> some View { FocusableCardLabel(configuration: configuration, scale: scale) } } private struct FocusableCardLabel: View { let configuration: ButtonStyleConfiguration let scale: CGFloat @Environment(\.isFocused) private var isFocused var body: some View { configuration.label .scaleEffect(isFocused ? scale : (configuration.isPressed ? 0.98 : 1.0)) .shadow(color: isFocused ? TvColors.accent.opacity(0.55) : .clear, radius: isFocused ? 28 : 0, y: 10) .animation(.easeOut(duration: 0.18), value: isFocused) .animation(.easeOut(duration: 0.12), value: configuration.isPressed) } } /// Top-nav: duidelijke focus-ring, aparte “actieve tab”-styling. struct NavTabButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { NavTabButtonLabel(configuration: configuration) } } private struct NavTabButtonLabel: View { let configuration: ButtonStyleConfiguration @Environment(\.isFocused) private var isFocused var body: some View { configuration.label .scaleEffect(isFocused ? 1.06 : (configuration.isPressed ? 0.97 : 1.0)) .shadow(color: isFocused ? Color.white.opacity(0.35) : .clear, radius: isFocused ? 16 : 0, y: 0) .animation(.easeOut(duration: 0.15), value: isFocused) .animation(.easeOut(duration: 0.1), value: configuration.isPressed) } } struct TvPrimaryButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { TvPrimaryButtonLabel(configuration: configuration) } } private struct TvPrimaryButtonLabel: View { let configuration: ButtonStyleConfiguration @Environment(\.isFocused) private var isFocused var body: some View { configuration.label .font(TvFonts.headline) .foregroundStyle(.white) .padding(.horizontal, 36) .padding(.vertical, 18) .background( LinearGradient( colors: [TvColors.accent, TvColors.accent.opacity(0.82)], startPoint: .topLeading, endPoint: .bottomTrailing ), in: Capsule() ) .scaleEffect(isFocused ? 1.06 : (configuration.isPressed ? 0.97 : 1)) .shadow(color: TvColors.accent.opacity(isFocused ? 0.55 : 0.28), radius: isFocused ? 22 : 10, y: 6) .animation(.easeOut(duration: 0.18), value: isFocused) } } struct TvSecondaryButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { TvSecondaryButtonLabel(configuration: configuration) } } private struct TvSecondaryButtonLabel: View { let configuration: ButtonStyleConfiguration @Environment(\.isFocused) private var isFocused var body: some View { configuration.label .font(TvFonts.headline) .foregroundStyle(TvColors.text) .padding(.horizontal, 32) .padding(.vertical, 16) .background( .ultraThinMaterial, in: Capsule() ) .overlay { Capsule() .strokeBorder(isFocused ? TvColors.accent.opacity(0.7) : Color.white.opacity(0.12), lineWidth: isFocused ? 2 : 1) } .scaleEffect(isFocused ? 1.05 : 1) .animation(.easeOut(duration: 0.18), value: isFocused) } }