Fix hero to match reference: white surface, image right, outlined CTA
Completely rebuilt FeaturedGameCard to match the Dribbble vtv reference. White/cream background surface instead of dark card. Stadium image sits on the right side and fades into white via left-to-right gradient. Dark text on light background. "Watch Now" as outlined orange pill (not filled). Plus icon for add-to-multiview. Title uses split weight: thin "Houston Astros vs" + bold orange "Seattle Mariners" — mimicking the "modern family" typography split. Cleaned up DashboardView header: removed bulky MLB/date/stat pills section. Replaced with compact inline date nav: chevron left, date text, chevron right, "Today" link, game count + live count on the right. One line instead of a full section. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -371,77 +371,71 @@ struct DashboardView: View {
|
||||
|
||||
@ViewBuilder
|
||||
private var headerSection: some View {
|
||||
VStack(spacing: 24) {
|
||||
HStack(alignment: .bottom) {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text("MLB")
|
||||
.font(.headline.weight(.black))
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
.kerning(4)
|
||||
Text(viewModel.displayDateString)
|
||||
.font(.system(size: 40, weight: .bold))
|
||||
.foregroundStyle(DS.Colors.textPrimary)
|
||||
.contentTransition(.numericText())
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack(spacing: 16) {
|
||||
statPill("\(viewModel.games.count)", label: "Games")
|
||||
if !viewModel.liveGames.isEmpty {
|
||||
statPill("\(viewModel.liveGames.count)", label: "Live", color: .red)
|
||||
}
|
||||
if !viewModel.activeStreams.isEmpty {
|
||||
statPill("\(viewModel.activeStreams.count)/4", label: "Streams", color: .green)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 16) {
|
||||
HStack(alignment: .center) {
|
||||
// Date navigation — compact inline
|
||||
HStack(spacing: 12) {
|
||||
Button {
|
||||
Task { await viewModel.goToPreviousDay() }
|
||||
} label: {
|
||||
Label("Previous Day", systemImage: "chevron.left")
|
||||
Image(systemName: "chevron.left")
|
||||
.font(.system(size: dateNavIconSize, weight: .semibold))
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
}
|
||||
|
||||
Text(viewModel.displayDateString)
|
||||
.font(dateFont)
|
||||
.foregroundStyle(DS.Colors.textPrimary)
|
||||
.contentTransition(.numericText())
|
||||
|
||||
Button {
|
||||
Task { await viewModel.goToNextDay() }
|
||||
} label: {
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.system(size: dateNavIconSize, weight: .semibold))
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
}
|
||||
|
||||
if !viewModel.isToday {
|
||||
Button {
|
||||
Task { await viewModel.goToToday() }
|
||||
} label: {
|
||||
Label("Today", systemImage: "calendar")
|
||||
}
|
||||
.tint(.blue)
|
||||
}
|
||||
|
||||
Button {
|
||||
Task { await viewModel.goToNextDay() }
|
||||
} label: {
|
||||
HStack(spacing: 6) {
|
||||
Text("Next Day")
|
||||
Image(systemName: "chevron.right")
|
||||
Text("Today")
|
||||
.font(todayBtnFont)
|
||||
.foregroundStyle(DS.Colors.interactive)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
Spacer()
|
||||
|
||||
HStack(spacing: 12) {
|
||||
Text("\(viewModel.games.count) games")
|
||||
.font(metaCountFont)
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
|
||||
if !viewModel.liveGames.isEmpty {
|
||||
HStack(spacing: 5) {
|
||||
Circle().fill(DS.Colors.live).frame(width: 7, height: 7)
|
||||
Text("\(viewModel.liveGames.count) live")
|
||||
.font(metaCountFont)
|
||||
.foregroundStyle(DS.Colors.live)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func statPill(_ value: String, label: String, color: Color = DS.Colors.interactive) -> some View {
|
||||
VStack(spacing: 2) {
|
||||
Text(value)
|
||||
.font(DS.Fonts.dataValue)
|
||||
.foregroundStyle(color)
|
||||
Text(label)
|
||||
.dataLabelStyle()
|
||||
}
|
||||
.padding(.horizontal, 20)
|
||||
.padding(.vertical, 10)
|
||||
.background(DS.Colors.panelFill)
|
||||
.clipShape(RoundedRectangle(cornerRadius: DS.Radii.compact))
|
||||
.shadow(color: DS.Shadows.card, radius: DS.Shadows.cardRadius, y: DS.Shadows.cardY)
|
||||
}
|
||||
#if os(tvOS)
|
||||
private var dateFont: Font { .system(size: 32, weight: .bold) }
|
||||
private var dateNavIconSize: CGFloat { 22 }
|
||||
private var todayBtnFont: Font { .system(size: 22, weight: .bold, design: .rounded) }
|
||||
private var metaCountFont: Font { .system(size: 22, weight: .medium) }
|
||||
#else
|
||||
private var dateFont: Font { .system(size: 22, weight: .bold) }
|
||||
private var dateNavIconSize: CGFloat { 16 }
|
||||
private var todayBtnFont: Font { .system(size: 15, weight: .bold, design: .rounded) }
|
||||
private var metaCountFont: Font { .system(size: 14, weight: .medium) }
|
||||
#endif
|
||||
|
||||
// MARK: - Featured Channels
|
||||
|
||||
|
||||
@@ -21,103 +21,107 @@ struct FeaturedGameCard: View {
|
||||
|
||||
var body: some View {
|
||||
Button(action: onSelect) {
|
||||
ZStack(alignment: .leading) {
|
||||
// Stadium photo background
|
||||
stadiumBackground
|
||||
ZStack(alignment: .topLeading) {
|
||||
// White/cream base
|
||||
DS.Colors.panelFill
|
||||
|
||||
// Left gradient overlay for text readability
|
||||
// Stadium image on the right side, fading into white on the left
|
||||
HStack(spacing: 0) {
|
||||
LinearGradient(
|
||||
colors: [
|
||||
Color(red: 0.08, green: 0.08, blue: 0.10),
|
||||
Color(red: 0.08, green: 0.08, blue: 0.10).opacity(0.95),
|
||||
Color(red: 0.08, green: 0.08, blue: 0.10).opacity(0.6),
|
||||
.clear
|
||||
],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
.frame(maxWidth: .infinity)
|
||||
Spacer()
|
||||
ZStack(alignment: .leading) {
|
||||
stadiumImage
|
||||
.frame(width: imageWidth)
|
||||
|
||||
// White fade from left edge of image
|
||||
LinearGradient(
|
||||
colors: [
|
||||
DS.Colors.panelFill,
|
||||
DS.Colors.panelFill.opacity(0.8),
|
||||
DS.Colors.panelFill.opacity(0.3),
|
||||
.clear
|
||||
],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
.frame(width: fadeWidth)
|
||||
}
|
||||
}
|
||||
|
||||
// Content overlay
|
||||
HStack(alignment: .top, spacing: 0) {
|
||||
heroContent
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.horizontal, heroPadH)
|
||||
.padding(.vertical, heroPadV)
|
||||
// Text content on the left
|
||||
VStack(alignment: .leading, spacing: contentSpacing) {
|
||||
// Status badge
|
||||
statusBadge
|
||||
|
||||
Spacer(minLength: 0)
|
||||
// Giant matchup title — thin "away" bold "home"
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
HStack(spacing: 0) {
|
||||
Text(game.awayTeam.displayName)
|
||||
.font(titleThinFont)
|
||||
.foregroundStyle(DS.Colors.textSecondary)
|
||||
Text(" vs ")
|
||||
.font(titleThinFont)
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
}
|
||||
Text(game.homeTeam.displayName)
|
||||
.font(titleBoldFont)
|
||||
.foregroundStyle(DS.Colors.interactive)
|
||||
}
|
||||
|
||||
// Metadata line
|
||||
metadataLine
|
||||
|
||||
// Live score or description
|
||||
if game.isLive {
|
||||
liveSection
|
||||
} else if game.isFinal {
|
||||
finalSection
|
||||
} else {
|
||||
scheduledSection
|
||||
}
|
||||
|
||||
// CTA buttons
|
||||
HStack(spacing: 14) {
|
||||
if game.hasStreams {
|
||||
Label("Watch Now", systemImage: "play.fill")
|
||||
.font(ctaFont)
|
||||
.foregroundStyle(DS.Colors.interactive)
|
||||
.padding(.horizontal, ctaPadH)
|
||||
.padding(.vertical, ctaPadV)
|
||||
.overlay(
|
||||
Capsule().strokeBorder(DS.Colors.interactive, lineWidth: 2)
|
||||
)
|
||||
}
|
||||
|
||||
Image(systemName: "plus")
|
||||
.font(ctaFont)
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
.padding(ctaPadV)
|
||||
.overlay(
|
||||
Circle().strokeBorder(DS.Colors.textQuaternary, lineWidth: 1.5)
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, heroPadH)
|
||||
.padding(.vertical, heroPadV)
|
||||
.frame(maxWidth: textAreaWidth, alignment: .topLeading)
|
||||
}
|
||||
.frame(height: heroHeight)
|
||||
.clipShape(RoundedRectangle(cornerRadius: DS.Radii.featured, style: .continuous))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: DS.Radii.featured, style: .continuous)
|
||||
.strokeBorder(game.isLive ? DS.Colors.live.opacity(0.3) : .white.opacity(0.1), lineWidth: game.isLive ? 2 : 1)
|
||||
)
|
||||
.shadow(color: .black.opacity(0.2), radius: 30, y: 12)
|
||||
.clipShape(RoundedRectangle(cornerRadius: heroRadius, style: .continuous))
|
||||
.shadow(color: .black.opacity(0.08), radius: 30, y: 12)
|
||||
}
|
||||
.platformCardStyle()
|
||||
}
|
||||
|
||||
// MARK: - Hero Content (left side overlay)
|
||||
// MARK: - Live Section
|
||||
|
||||
@ViewBuilder
|
||||
private var heroContent: some View {
|
||||
VStack(alignment: .leading, spacing: heroContentSpacing) {
|
||||
// Status badge
|
||||
statusBadge
|
||||
|
||||
// Giant matchup title
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("\(game.awayTeam.code) vs \(game.homeTeam.code)")
|
||||
.font(matchupFont)
|
||||
.foregroundStyle(DS.Colors.onDarkPrimary)
|
||||
|
||||
Text("\(game.awayTeam.displayName) at \(game.homeTeam.displayName)")
|
||||
.font(subtitleFont)
|
||||
.foregroundStyle(DS.Colors.onDarkSecondary)
|
||||
}
|
||||
|
||||
// Live data OR scheduled/final data
|
||||
if game.isLive {
|
||||
liveDataSection
|
||||
} else if game.isFinal {
|
||||
finalDataSection
|
||||
} else {
|
||||
scheduledDataSection
|
||||
}
|
||||
|
||||
// Metadata line
|
||||
metadataLine
|
||||
|
||||
// CTA
|
||||
HStack(spacing: 14) {
|
||||
if game.hasStreams {
|
||||
Label("Watch Now", systemImage: "play.fill")
|
||||
.font(ctaFont)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, ctaPadH)
|
||||
.padding(.vertical, ctaPadV)
|
||||
.background(DS.Colors.interactive)
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Live Data
|
||||
|
||||
@ViewBuilder
|
||||
private var liveDataSection: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
// Score
|
||||
HStack(alignment: .firstTextBaseline, spacing: 20) {
|
||||
private var liveSection: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 16) {
|
||||
if let away = game.awayTeam.score, let home = game.homeTeam.score {
|
||||
Text("\(away) - \(home)")
|
||||
.font(liveScoreFont)
|
||||
.foregroundStyle(DS.Colors.onDarkPrimary)
|
||||
.font(scoreFont)
|
||||
.foregroundStyle(DS.Colors.textPrimary)
|
||||
.monospacedDigit()
|
||||
.contentTransition(.numericText())
|
||||
}
|
||||
@@ -129,7 +133,6 @@ struct FeaturedGameCard: View {
|
||||
}
|
||||
}
|
||||
|
||||
// Count + outs diamond
|
||||
if let linescore = game.linescore {
|
||||
DiamondView(
|
||||
balls: linescore.balls ?? 0,
|
||||
@@ -140,55 +143,33 @@ struct FeaturedGameCard: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Final Data
|
||||
// MARK: - Final Section
|
||||
|
||||
@ViewBuilder
|
||||
private var finalDataSection: some View {
|
||||
private var finalSection: some View {
|
||||
if let away = game.awayTeam.score, let home = game.homeTeam.score {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 16) {
|
||||
HStack(spacing: 12) {
|
||||
Text("\(away) - \(home)")
|
||||
.font(liveScoreFont)
|
||||
.foregroundStyle(DS.Colors.onDarkPrimary)
|
||||
.font(scoreFont)
|
||||
.foregroundStyle(DS.Colors.textPrimary)
|
||||
.monospacedDigit()
|
||||
|
||||
Text("FINAL")
|
||||
.font(inningFont)
|
||||
.foregroundStyle(DS.Colors.onDarkTertiary)
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Scheduled Data
|
||||
// MARK: - Scheduled Section
|
||||
|
||||
@ViewBuilder
|
||||
private var scheduledDataSection: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
private var scheduledSection: some View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
if let pitchers = game.pitchers {
|
||||
HStack(spacing: 10) {
|
||||
if let url = game.awayPitcherHeadshotURL {
|
||||
PitcherHeadshotView(url: url, teamCode: game.awayTeam.code, name: nil, size: pitcherSize)
|
||||
}
|
||||
if let url = game.homePitcherHeadshotURL {
|
||||
PitcherHeadshotView(url: url, teamCode: game.homeTeam.code, name: nil, size: pitcherSize)
|
||||
}
|
||||
Text(pitchers)
|
||||
.font(pitcherFont)
|
||||
.foregroundStyle(DS.Colors.onDarkSecondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 16) {
|
||||
if let record = game.awayTeam.record {
|
||||
Text("\(game.awayTeam.code) \(record)")
|
||||
.font(recordFont)
|
||||
.foregroundStyle(DS.Colors.onDarkTertiary)
|
||||
}
|
||||
if let record = game.homeTeam.record {
|
||||
Text("\(game.homeTeam.code) \(record)")
|
||||
.font(recordFont)
|
||||
.foregroundStyle(DS.Colors.onDarkTertiary)
|
||||
}
|
||||
Text(pitchers)
|
||||
.font(descFont)
|
||||
.foregroundStyle(DS.Colors.textSecondary)
|
||||
.lineLimit(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,59 +184,50 @@ struct FeaturedGameCard: View {
|
||||
Circle().fill(DS.Colors.live).frame(width: 8, height: 8)
|
||||
Text(inning ?? "LIVE")
|
||||
.font(badgeFont)
|
||||
.foregroundStyle(.white)
|
||||
.foregroundStyle(DS.Colors.live)
|
||||
}
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 7)
|
||||
.background(DS.Colors.live.opacity(0.3))
|
||||
.clipShape(Capsule())
|
||||
|
||||
case .scheduled(let time):
|
||||
Text(time)
|
||||
.font(badgeFont)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 7)
|
||||
.background(.white.opacity(0.15))
|
||||
.clipShape(Capsule())
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
|
||||
case .final_:
|
||||
Text("FINAL")
|
||||
.font(badgeFont)
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 7)
|
||||
.background(.white.opacity(0.15))
|
||||
.clipShape(Capsule())
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
|
||||
case .unknown:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Metadata
|
||||
// MARK: - Metadata Line
|
||||
|
||||
@ViewBuilder
|
||||
private var metadataLine: some View {
|
||||
HStack(spacing: 14) {
|
||||
HStack(spacing: metaSeparatorWidth) {
|
||||
if let venue = game.venue {
|
||||
Label(venue, systemImage: "mappin.and.ellipse")
|
||||
.font(metaFont)
|
||||
.foregroundStyle(DS.Colors.onDarkTertiary)
|
||||
Text(venue)
|
||||
}
|
||||
if let record = game.awayTeam.record {
|
||||
Text("\(game.awayTeam.code) \(record)")
|
||||
}
|
||||
if let record = game.homeTeam.record {
|
||||
Text("\(game.homeTeam.code) \(record)")
|
||||
}
|
||||
|
||||
if !game.broadcasts.isEmpty {
|
||||
Text("\(game.broadcasts.count) feed\(game.broadcasts.count == 1 ? "" : "s")")
|
||||
.font(metaFont)
|
||||
.foregroundStyle(DS.Colors.onDarkTertiary)
|
||||
}
|
||||
}
|
||||
.font(metaFont)
|
||||
.foregroundStyle(DS.Colors.textTertiary)
|
||||
}
|
||||
|
||||
// MARK: - Stadium Background
|
||||
// MARK: - Stadium Image
|
||||
|
||||
@ViewBuilder
|
||||
private var stadiumBackground: some View {
|
||||
private var stadiumImage: some View {
|
||||
if let url = stadiumImageURL {
|
||||
AsyncImage(url: url) { phase in
|
||||
switch phase {
|
||||
@@ -263,65 +235,82 @@ struct FeaturedGameCard: View {
|
||||
image
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
case .failure:
|
||||
fallbackBackground
|
||||
.frame(height: heroHeight)
|
||||
.clipped()
|
||||
default:
|
||||
fallbackBackground
|
||||
.redacted(reason: .placeholder)
|
||||
fallbackImage
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fallbackBackground
|
||||
fallbackImage
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var fallbackBackground: some View {
|
||||
private var fallbackImage: some View {
|
||||
ZStack {
|
||||
Color(red: 0.08, green: 0.08, blue: 0.10)
|
||||
LinearGradient(
|
||||
colors: [awayColor.opacity(0.3), homeColor.opacity(0.3)],
|
||||
colors: [awayColor.opacity(0.15), homeColor.opacity(0.15)],
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
|
||||
HStack(spacing: fallbackLogoGap) {
|
||||
TeamLogoView(team: game.awayTeam, size: fallbackLogoSize)
|
||||
.opacity(0.4)
|
||||
TeamLogoView(team: game.homeTeam, size: fallbackLogoSize)
|
||||
.opacity(0.4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Platform Sizing
|
||||
|
||||
#if os(tvOS)
|
||||
private var heroHeight: CGFloat { 500 }
|
||||
private var heroHeight: CGFloat { 480 }
|
||||
private var heroRadius: CGFloat { 28 }
|
||||
private var heroPadH: CGFloat { 60 }
|
||||
private var heroPadV: CGFloat { 50 }
|
||||
private var heroContentSpacing: CGFloat { 18 }
|
||||
private var pitcherSize: CGFloat { 40 }
|
||||
private var matchupFont: Font { .system(size: 52, weight: .black, design: .rounded) }
|
||||
private var subtitleFont: Font { .system(size: 26, weight: .semibold) }
|
||||
private var liveScoreFont: Font { .system(size: 72, weight: .black, design: .rounded) }
|
||||
private var contentSpacing: CGFloat { 16 }
|
||||
private var imageWidth: CGFloat { 900 }
|
||||
private var fadeWidth: CGFloat { 400 }
|
||||
private var textAreaWidth: CGFloat { 700 }
|
||||
private var metaSeparatorWidth: CGFloat { 18 }
|
||||
private var fallbackLogoSize: CGFloat { 120 }
|
||||
private var fallbackLogoGap: CGFloat { 40 }
|
||||
|
||||
private var titleThinFont: Font { .system(size: 48, weight: .light) }
|
||||
private var titleBoldFont: Font { .system(size: 52, weight: .black, design: .rounded) }
|
||||
private var scoreFont: Font { .system(size: 64, weight: .black, design: .rounded) }
|
||||
private var inningFont: Font { .system(size: 28, weight: .bold, design: .rounded) }
|
||||
private var badgeFont: Font { .system(size: 22, weight: .black, design: .rounded) }
|
||||
private var badgeFont: Font { .system(size: 22, weight: .bold, design: .rounded) }
|
||||
private var metaFont: Font { .system(size: 22, weight: .medium) }
|
||||
private var descFont: Font { .system(size: 24, weight: .medium) }
|
||||
private var ctaFont: Font { .system(size: 24, weight: .bold) }
|
||||
private var ctaPadH: CGFloat { 32 }
|
||||
private var ctaPadV: CGFloat { 14 }
|
||||
private var pitcherFont: Font { .system(size: 24, weight: .semibold) }
|
||||
private var recordFont: Font { .system(size: 22, weight: .bold, design: .monospaced) }
|
||||
private var metaFont: Font { .system(size: 22, weight: .medium) }
|
||||
#else
|
||||
private var heroHeight: CGFloat { 320 }
|
||||
private var heroHeight: CGFloat { 340 }
|
||||
private var heroRadius: CGFloat { 22 }
|
||||
private var heroPadH: CGFloat { 28 }
|
||||
private var heroPadV: CGFloat { 28 }
|
||||
private var heroContentSpacing: CGFloat { 12 }
|
||||
private var pitcherSize: CGFloat { 30 }
|
||||
private var matchupFont: Font { .system(size: 32, weight: .black, design: .rounded) }
|
||||
private var subtitleFont: Font { .system(size: 16, weight: .semibold) }
|
||||
private var liveScoreFont: Font { .system(size: 48, weight: .black, design: .rounded) }
|
||||
private var contentSpacing: CGFloat { 10 }
|
||||
private var imageWidth: CGFloat { 400 }
|
||||
private var fadeWidth: CGFloat { 200 }
|
||||
private var textAreaWidth: CGFloat { 350 }
|
||||
private var metaSeparatorWidth: CGFloat { 12 }
|
||||
private var fallbackLogoSize: CGFloat { 60 }
|
||||
private var fallbackLogoGap: CGFloat { 20 }
|
||||
|
||||
private var titleThinFont: Font { .system(size: 28, weight: .light) }
|
||||
private var titleBoldFont: Font { .system(size: 32, weight: .black, design: .rounded) }
|
||||
private var scoreFont: Font { .system(size: 40, weight: .black, design: .rounded) }
|
||||
private var inningFont: Font { .system(size: 18, weight: .bold, design: .rounded) }
|
||||
private var badgeFont: Font { .system(size: 14, weight: .black, design: .rounded) }
|
||||
private var badgeFont: Font { .system(size: 14, weight: .bold, design: .rounded) }
|
||||
private var metaFont: Font { .system(size: 14, weight: .medium) }
|
||||
private var descFont: Font { .system(size: 15, weight: .medium) }
|
||||
private var ctaFont: Font { .system(size: 16, weight: .bold) }
|
||||
private var ctaPadH: CGFloat { 22 }
|
||||
private var ctaPadV: CGFloat { 10 }
|
||||
private var pitcherFont: Font { .system(size: 15, weight: .semibold) }
|
||||
private var recordFont: Font { .system(size: 14, weight: .bold, design: .monospaced) }
|
||||
private var metaFont: Font { .system(size: 14, weight: .medium) }
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user