From fd8782a7a28c46a8bfef47a1154417ed8b6c7528 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 21 Jul 2026 06:31:32 +0000 Subject: [PATCH] Soften now-playing gradient-to-black seam at the page boundary The scroll crossfade darkened the entire now-playing backdrop by a single uniform opacity, so mid-page the half-darkened gradient met the neighbor page's solid systemGroupedBackground fill in a crisp horizontal step. Replace the uniform tint in NowPlayingBackgroundFade with a directional vertical LinearGradient (systemGroupedBackground -> clear) that dissolves the backdrop into the neighbor's background only from the abutting edge (top when paging to Library, bottom when paging to Up Next), ramping smoothly into the artwork gradient. The page boundary becomes background-on-background (no seam) and the fall-off is continuous (no crisp line). At rest every stop is clear, so the full gradient shows untouched. Still a leaf reading scrollFraction, so the heavy backdrop / now-playing bodies aren't invalidated per frame. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0152PJuDNPYYxd7SDm4TSvbV --- App/Continuity/Views/MainPagerView.swift | 27 ++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/App/Continuity/Views/MainPagerView.swift b/App/Continuity/Views/MainPagerView.swift index a09192b..98b4448 100644 --- a/App/Continuity/Views/MainPagerView.swift +++ b/App/Continuity/Views/MainPagerView.swift @@ -104,10 +104,29 @@ private struct NowPlayingBackgroundFade: View { @Environment(MainPagerState.self) private var pagerState var body: some View { - // Opacity 0 at center (full gradient) → 1 at either neighbor (exact background match). - Color(uiColor: .systemGroupedBackground) - .opacity(min(1, abs(pagerState.scrollFraction - 1))) - .ignoresSafeArea() + // Distance from the now-playing page center: 0 at rest (full gradient), 1 at a neighbor. + let progress = min(1, abs(pagerState.scrollFraction - 1)) + // The revealed neighbor sits ABOVE when paging to Library (fraction < 1) and BELOW when + // paging to Up Next — so the neighbor's opaque background abuts the now-playing page at + // its top or bottom edge respectively. A *uniform* tint (the old approach) darkened the + // whole backdrop evenly, leaving a crisp step at that page boundary: half-darkened + // gradient meeting the neighbor's solid fill. Instead dissolve the backdrop into the + // neighbor's background from the abutting edge inward, as a soft vertical ramp — the + // boundary is background-on-background (no seam) and the fall-off into the gradient is + // continuous (no crisp internal line). At rest (progress 0) every stop is clear, so the + // full artwork gradient shows untouched. + let towardLibrary = pagerState.scrollFraction < 1 + let neighbor = Color(uiColor: .systemGroupedBackground) + LinearGradient( + stops: [ + .init(color: neighbor.opacity(progress), location: 0), + .init(color: neighbor.opacity(progress * 0.5), location: 0.28), + .init(color: .clear, location: 0.6), + ], + startPoint: towardLibrary ? .top : .bottom, + endPoint: towardLibrary ? .bottom : .top + ) + .ignoresSafeArea() } }