12 KiB
Prompt: Redesign All Shareable Cards — Premium Sports-Media Aesthetic
Project Context
This is an iOS SwiftUI app (SportsTime.xcodeproj) for planning multi-stop sports road trips. The app generates shareable image cards (1080x1920 Instagram-story size) for achievements, stadium progress, and trip summaries. These cards are rendered via SwiftUI ImageRenderer and exported as UIImages.
Build command:
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' build
Files to Modify (all under SportsTime/Export/Sharing/)
ShareableContent.swift— Theme definition and shared typesShareCardSportBackground.swift— Card backgroundsShareCardComponents.swift— Reusable header, footer, stats, progress ring, map snapshot generatorAchievementCardGenerator.swift— 4 achievement card types + badge + confettiProgressCardGenerator.swift— Stadium progress cardTripCardGenerator.swift— Trip summary card
Current State (What Exists)
The cards currently work but look generic — flat linear gradients, basic system fonts, simple circles, scattered sport icons. The goal is to make them look like ESPN broadcast graphics meets a premium travel app.
Key Types You'll Reference (defined elsewhere, don't modify):
// Sport enum — .mlb, .nba, .nhl, .nfl, .mls, .wnba, .nwsl
// Each has: .iconName (SF Symbol), .rawValue ("MLB"), .displayName, .color
// ShareTheme — has: gradientColors, accentColor, textColor, secondaryTextColor, useDarkMap
// 8 preset themes: .dark, .light, .midnight, .forest, .sunset, .berry, .ocean, .slate
// ShareCardDimensions — cardSize (1080x1920), mapSnapshotSize, routeMapSize, padding (60)
// AchievementDefinition — has: name, description, iconName, iconColor, sport, category
// AchievementProgress — has: definition, earnedAt (Date?)
// LeagueProgress — has: sport, visitedStadiums, totalStadiums, completionPercentage,
// stadiumsVisited: [Stadium], stadiumsRemaining: [Stadium]
// Trip — has: stops, uniqueSports, cities, totalDistanceMiles, totalGames, formattedDateRange
// TripStop — has: city, coordinate, arrivalDate, departureDate
// Color(hex:) initializer exists in Theme.swift
// Stadium — has: latitude, longitude
Design Direction
Aesthetic: "Stadium Broadcast" — bold condensed type, layered depth, geometric sport motifs, premium stat presentation. Each card should feel like a TV broadcast graphic.
Key Principles:
- Strong typographic hierarchy:
.heavyfor headers,.black+.roundedfor numbers,.lightfor descriptions - Three-layer backgrounds: base gradient → geometric pattern → vignette overlay
- Sport-specific geometric patterns (diamond lattice for MLB, hexagonal for NBA, ice-crystal for NHL)
- "Glass panel" containers:
surfaceColorfill (textColor at 0.08 opacity) +borderColorstroke (textColor at 0.15) - Gold/metallic accents (
#FFD700,#B8860B) for milestones and 100% completion - Tracked ALL CAPS labels for category text
- Maps framed with themed borders and subtle shadows
Detailed Spec Per File
1. ShareableContent.swift — Add Theme Helper Extensions
Add these computed properties to ShareTheme (after the existing theme(byId:) method):
var surfaceColor: Color // textColor.opacity(0.08) — glass panel fill
var borderColor: Color // textColor.opacity(0.15) — panel borders
var glowColor: Color // accentColor.opacity(0.4) — for glow effects
var highlightGradient: [Color] // [accentColor, accentColor.opacity(0.6)]
var midGradientColor: Color // blend of the two gradient colors — for richer angular gradients
You'll also need a Color.blendedWith(_:fraction:) helper that interpolates between two colors using UIColor.getRed.
2. ShareCardSportBackground.swift — Three-Layer Background System
Replace the current flat gradient + scattered icons with a ZStack of three layers:
Layer 1 — Angular gradient: Use AngularGradient (not linear) with the theme's two gradient colors plus the derived midGradientColor. Overlay a semi-transparent LinearGradient to soften it.
Layer 2 — Sport geometric pattern: Use Shape protocol to draw repeating geometric paths:
- MLB: Diamond/rhombus lattice (rotated squares in a grid, ~60pt spacing, staggered rows)
- NBA/WNBA: Hexagonal honeycomb (6-sided shapes, ~40pt size)
- NHL: Angled slash lines (~36pt spacing, rotated ~35 degrees via
CGAffineTransform) - NFL/MLS/NWSL/default: Diagonal stripes (~48pt spacing, 45 degrees)
- All rendered at 0.06-0.08 opacity using
theme.textColor
Layer 3 — Edge vignette: RadialGradient from .clear center to .black.opacity(0.4) at edges. Start radius ~200, end radius ~900.
Important: The ShareCardBackground wrapper view should now always use ShareCardSportBackground (even for no sports — just use the default stripe pattern). This means empty sports set should still get the angular gradient + stripes + vignette.
3. ShareCardComponents.swift — Premium Shared Components
ShareCardHeader:
- Sport icon inside a filled
RoundedRectangle(cornerRadius: 18)(not a Circle), with accent color at 0.25 opacity, 80x80pt, subtle shadow - Title:
.system(size: 44, weight: .heavy, design: .default)— NOT rounded - Thin accent-colored
Rectanglebelow (200pt wide, 2pt tall)
ShareCardFooter:
- Thin horizontal rule above (textColor at 0.15 opacity)
- "SPORTSTIME" in
.system(size: 18, weight: .bold)with.tracking(6)in accent color - "Plan your stadium adventure" in
.system(size: 16, weight: .light) - Compact — no sport icon in the footer
ShareStatsRow — "Broadcast Stats Panel":
HStack(spacing: 0)with each stat takingmaxWidth: .infinity- Thin vertical
Rectangledividers between stats (accent color at 0.4 opacity, 1pt wide, 60pt tall) - Stat value:
.system(size: 48, weight: .black, design: .rounded)in accent color - Stat label:
.system(size: 16, weight: .medium)ALL CAPS with.tracking(2)in secondaryTextColor - Whole row in a glass panel:
surfaceColorfill,borderColorstroke, cornerRadius 20
ShareProgressRing:
lineWidth: 32(thicker than before)- Track ring:
surfaceColor(not just accent at low opacity) - Glow behind progress arc: same arc shape but with
glowColor, lineWidth + 12,.blur(radius: 10) - Tick marks at 25/50/75%: short radial lines on the track ring (textColor at 0.2 opacity)
- Center number:
.system(size: 108, weight: .black, design: .rounded) - "of XX":
.system(size: 28, weight: .light)
ShareMapSnapshotGenerator (UIKit CoreGraphics drawing):
- Stadium dots: 20pt diameter with 2pt white ring border. Visited ones get a small white checkmark drawn on top.
- Route line: 6pt width (was 4pt)
- City labels: Draw text size dynamically using
NSString.size(withAttributes:), then draw a rounded-rect background sized to fit. Use "START" and "END" labels for first/last stops. Use.heavyweight font.
4. AchievementCardGenerator.swift — Badge, Confetti, All 4 Card Types
AchievementBadge:
- Outer gradient ring:
LinearGradientof iconColor → iconColor.opacity(0.5), stroke width = size * 0.025 - Inner fill:
RadialGradientof iconColor.opacity(0.3) → iconColor.opacity(0.1) - Icon: size * 0.45 (slightly larger than current 0.4)
- For earned achievements: thin gold (#FFD700) outer ring at full size
- Subtle drop shadow on the whole badge
- New parameter:
isEarned: Bool(the old badge only tookdefinitionandsize)
ConfettiBurst — "Pyrotechnics":
- 36 particles (was 24)
- 4 shape types: Circle, 6-pointed star (custom
StarShape), Diamond (rotated rect), thin streamer Rectangle - Two burst rings: inner (150-350pt from center) and outer (380-550pt, at 0.6 opacity)
goldHeavy: Boolparameter — when true, uses gold palette (#FFD700, #FFC107, #FFE082, #B8860B, #FF6B35)- Use deterministic seeded random (hash-based, not
CGFloat.random) so the layout is consistent across renders. Formula:abs(seed &* 2654435761) % range
Spotlight Card:
- Blurred glow circle (glowColor, 420pt, blur 40) behind the badge
- Decorative flanking lines: two 100pt accent-colored rectangles with a small circle between them
- Achievement name:
.system(size: 52, weight: .heavy)(NOT rounded) - Description:
.system(size: 26, weight: .light) - Earned date in a pill: Capsule with surfaceColor fill + borderColor stroke
Milestone Card:
- "MILESTONE" label:
.system(size: 26, weight: .black), tracking 6, in gold (#FFD700), with text shadow - Double ring around badge: gold outer (5pt, LinearGradient gold→darkGold), accent inner (3pt)
- Badge at 460pt
- Name text has a
RoundedRectanglebackground with gold gradient (0.15→0.05 opacity) - Confetti with
goldHeavy: true - Earned date in gold pill (gold text, gold-tinted capsule)
Collection Card:
- Year in huge type:
.system(size: 72, weight: .black, design: .rounded)in accent color - Sport label below in ALL CAPS tracked secondary text
- 3-column
LazyVGridwith each cell being a glass panel (surfaceColor + borderColor) containing badge (180pt) + name (2 lines) - Bottom: count in accent-tinted pill ("12 UNLOCKED" in tracked bold text)
Context Card:
- Glass header bar (full-width RoundedRectangle, surfaceColor fill): badge (130pt) + name + "UNLOCKED" tracked text
- Dotted connection line: 5 small circles vertically (accent at 0.3 opacity)
- Map snapshot with shadow
- Trip name in a glass banner at bottom ("Unlocked during" light + trip name bold)
5. ProgressCardGenerator.swift — Stadium Progress Card
- Custom header (not ShareCardHeader): sport icon in rounded rect + "STADIUM QUEST" in
.system(size: 36, weight: .heavy)with.tracking(3) - If 100% complete: "COMPLETED" badge in gold capsule at top
- Progress ring (redesigned version)
- Below ring: percentage number in
.system(size: 56, weight: .black, design: .rounded)+ "% COMPLETE" tracked - Broadcast stats panel: visited / remain / trips
- Map with themed border; gold border if complete
- If 100% complete: swap accent color to gold for the progress ring
6. TripCardGenerator.swift — Trip Summary Card
- Title: "MY [SPORT] ROAD TRIP" (ALL CAPS) via ShareCardHeader
- Map: larger frame (maxHeight: 680), with border and shadow
- Date range: in a glass pill (Capsule with surfaceColor + borderColor)
- Broadcast stats panel: miles / games / cities
- City journey trail: horizontal
ScrollViewwith numbered city badges- Each city:
HStackof numbered circle (accent fill, white number) + 3-letter city abbreviation, inside a glass rounded rect - Arrow connectors between cities (SF Symbol
arrow.right)
- Each city:
Important Patterns
- All views use
ShareCardBackground(theme:sports:)as background - All views are framed to
ShareCardDimensions.cardSize(1080x1920) - All card views are
private struct— only the*Contentstructs are public - Rendering happens via
ImageRenderer(content:)withscale = 3.0 - Keep all existing public API signatures unchanged (the
*Contentstructs and theirrender(theme:)methods) - Glass panels = surfaceColor fill + borderColor stroke + corner radius 16-20
ShareCardSportBackground,ShareCardBackground,ShareCardHeader,ShareCardFooter,ShareStatsRow,ShareProgressRing,ShareMapSnapshotGeneratorare NOT private — they're used across files- Achievement badge, confetti, star shape are private to AchievementCardGenerator
Verification
After implementation, build with:
xcodebuild -project SportsTime.xcodeproj -scheme SportsTime -destination 'platform=iOS Simulator,name=iPhone 17,OS=26.2' build
The build must succeed with zero errors.