Tip section: card design per user mockup

Three stacked rounded cards, one per tier, each with an accent-tinted
fuel-pump icon and price (Splash & Dash #64D2FF blue, Half a Tank
#30D158 green, Fill 'Er Up #FFD60A amber — the app's fuel palette),
name + blurb on the left, bold accent price on the right. Cards sit on
clear list rows with their own rounded fill + subtle accent tint.
Purchase flow, per-tier live prices, disabled state and footer copy
unchanged.
This commit is contained in:
FuelBoard Contributor
2026-08-16 08:57:40 +01:00
parent 5060aa9df5
commit 9fdcc7370e
+32 -6
View File
@@ -298,25 +298,47 @@ struct SettingsView: View {
} }
Section { Section {
VStack(spacing: 10) {
ForEach(TipStore.tiers) { tier in ForEach(TipStore.tiers) { tier in
Button { Button {
Task { await tipStore.purchase(tier) } Task { await tipStore.purchase(tier) }
} label: { } label: {
HStack { HStack(spacing: 12) {
Image(systemName: "fuelpump.fill")
.font(.title3)
.foregroundStyle(tier.accent)
.frame(width: 34)
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
Text(tier.name) Text(tier.name)
.font(.headline)
Text(tier.blurb) Text(tier.blurb)
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
Spacer() Spacer(minLength: 8)
Text(tipStore.displayPrice(for: tier)) Text(tipStore.displayPrice(for: tier))
.foregroundStyle(.secondary) .font(.headline.weight(.bold))
.foregroundStyle(tier.accent)
.monospacedDigit() .monospacedDigit()
} }
.padding(.horizontal, 14)
.padding(.vertical, 12)
.frame(maxWidth: .infinity, alignment: .leading)
.background(
RoundedRectangle(cornerRadius: 12)
.fill(Color(.secondarySystemGroupedBackground))
.overlay(
RoundedRectangle(cornerRadius: 12)
.fill(tier.accent.opacity(0.05))
)
)
} }
.buttonStyle(.plain)
.disabled(tipStore.purchaseInProgress) .disabled(tipStore.purchaseInProgress)
} }
}
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets(top: 4, leading: 16, bottom: 4, trailing: 16))
} header: { } header: {
Text("Support FuelBoard") Text("Support FuelBoard")
} footer: { } footer: {
@@ -569,15 +591,19 @@ final class TipStore: ObservableObject {
let name: String let name: String
let blurb: String let blurb: String
let fallbackPrice: String let fallbackPrice: String
let accent: Color // card/price accent (matches the fuel palette)
} }
static let tiers: [TipTier] = [ static let tiers: [TipTier] = [
TipTier(id: "com.apt.fuelboard.tip099", name: "Splash & Dash", TipTier(id: "com.apt.fuelboard.tip099", name: "Splash & Dash",
blurb: "Just enough to keep things moving.", fallbackPrice: "£0.99"), blurb: "Just enough to keep things moving.", fallbackPrice: "£0.99",
accent: Color(red: 0.39, green: 0.82, blue: 1.0)), // #64D2FF
TipTier(id: "com.apt.fuelboard.tip299", name: "Half a Tank", TipTier(id: "com.apt.fuelboard.tip299", name: "Half a Tank",
blurb: "A generous top-up for development.", fallbackPrice: "£2.99"), blurb: "A generous top-up for development.", fallbackPrice: "£2.99",
accent: Color(red: 0.19, green: 0.82, blue: 0.35)), // #30D158
TipTier(id: "com.apt.fuelboard.tip499", name: "Fill 'Er Up", TipTier(id: "com.apt.fuelboard.tip499", name: "Fill 'Er Up",
blurb: "Keeping the app on the road.", fallbackPrice: "£4.99"), blurb: "Keeping the app on the road.", fallbackPrice: "£4.99",
accent: Color(red: 1.0, green: 0.84, blue: 0.04)), // #FFD60A
] ]
@Published private(set) var products: [String: Product] = [:] @Published private(set) var products: [String: Product] = [:]