From e28bebea7910e40ab9a2b87fc54fe0ed1ffea871 Mon Sep 17 00:00:00 2001 From: Millian Lamiaux Date: Tue, 21 Apr 2026 23:18:15 +0200 Subject: [PATCH] Redesign FeaturedProgramCard and reorder Home sections - Replace external pill badges (level + FREE) with immersive overlay card: level tag pinned top-right with ultraThinMaterial blur, FREE shown inline with checkmark.seal.fill icon, dark scrim for text legibility - Remove card drop shadow - Move Browse by Zone section above Featured in Home tab scroll order --- .../TabataGo/Views/Tabs/HomeTab.swift | 103 ++++++++++-------- 1 file changed, 58 insertions(+), 45 deletions(-) diff --git a/tabatago-swift/TabataGo/Views/Tabs/HomeTab.swift b/tabatago-swift/TabataGo/Views/Tabs/HomeTab.swift index 7a79046..db2935c 100644 --- a/tabatago-swift/TabataGo/Views/Tabs/HomeTab.swift +++ b/tabatago-swift/TabataGo/Views/Tabs/HomeTab.swift @@ -53,6 +53,22 @@ struct HomeTab: View { } .padding(.horizontal) + // ── Body Zone Grid ── + VStack(alignment: .leading, spacing: 12) { + SectionHeader(title: "Browse by Zone", subtitle: "Target specific muscle groups") + .padding(.horizontal) + + VStack(spacing: 12) { + ForEach(vm.availableZones, id: \.self) { zone in + NavigationLink(destination: BodyZoneView(zone: zone)) { + ZoneCard(zone: zone) + } + .buttonStyle(.plain) + } + } + .padding(.horizontal) + } + // ── Featured Workouts ── if !vm.featuredPrograms.isEmpty { VStack(alignment: .leading, spacing: 12) { @@ -71,22 +87,6 @@ struct HomeTab: View { } } - // ── Body Zone Grid ── - VStack(alignment: .leading, spacing: 12) { - SectionHeader(title: "Browse by Zone", subtitle: "Target specific muscle groups") - .padding(.horizontal) - - VStack(spacing: 12) { - ForEach(vm.availableZones, id: \.self) { zone in - NavigationLink(destination: BodyZoneView(zone: zone)) { - ZoneCard(zone: zone) - } - .buttonStyle(.plain) - } - } - .padding(.horizontal) - } - // ── Loading / Error State ── if vm.isLoading { ProgressView() @@ -148,42 +148,55 @@ struct FeaturedProgramCard: View { let program: WorkoutProgram var body: some View { - VStack(alignment: .leading, spacing: 8) { - // Header gradient area - RoundedRectangle(cornerRadius: 16, style: .continuous) - .fill(Theme.zoneGradient(program.bodyZone)) - .frame(width: 220, height: 110) - .overlay(alignment: .bottomLeading) { - VStack(alignment: .leading, spacing: 4) { + RoundedRectangle(cornerRadius: 20, style: .continuous) + .fill(Theme.zoneGradient(program.bodyZone)) + .frame(width: 220, height: 170) + .overlay { + ZStack(alignment: .topTrailing) { + // ── Bottom scrim for text legibility ── + LinearGradient( + colors: [.clear, .black.opacity(0.55)], + startPoint: .center, + endPoint: .bottom + ) + .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous)) + + // ── Level tag — top right ── + Text(program.level.capitalized) + .font(.caption2.weight(.bold)) + .foregroundStyle(.white) + .padding(.horizontal, 9) + .padding(.vertical, 5) + .background(.ultraThinMaterial.opacity(0.85)) + .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous)) + .padding(12) + + // ── Bottom content ── + VStack(alignment: .leading, spacing: 6) { + Spacer() + Text(program.titleEn) .font(.headline.weight(.bold)) .foregroundStyle(.white) .lineLimit(2) - HStack(spacing: 6) { - Label("\(program.estimatedDuration)m", systemImage: "clock") - Label("\(program.estimatedCalories) kcal", systemImage: "flame") - } - .font(.caption.weight(.medium)) - .foregroundStyle(.white.opacity(0.85)) - } - .padding(12) - } + .shadow(color: .black.opacity(0.3), radius: 4, x: 0, y: 2) - HStack { - LevelBadge(level: program.level) - Spacer() - if program.isFree { - Text("FREE") - .font(.caption2.weight(.bold)) - .foregroundStyle(Theme.success) - .padding(.horizontal, 8) - .padding(.vertical, 3) - .background(Theme.success.opacity(0.15)) - .clipShape(Capsule()) + HStack(spacing: 10) { + Label("\(program.estimatedDuration)m", systemImage: "clock.fill") + Label("\(program.estimatedCalories) kcal", systemImage: "flame.fill") + if program.isFree { + Label("Free", systemImage: "checkmark.seal.fill") + .foregroundStyle(Theme.success) + } + } + .font(.caption.weight(.semibold)) + .foregroundStyle(.white.opacity(0.9)) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(14) } } - } - .frame(width: 220) + } }