Skip to content

[PC TV] Apple-TV-style depth on focused cards#4579

Closed
david-gonzalez-a8c wants to merge 10 commits into
trunkfrom
adding-depth
Closed

[PC TV] Apple-TV-style depth on focused cards#4579
david-gonzalez-a8c wants to merge 10 commits into
trunkfrom
adding-depth

Conversation

@david-gonzalez-a8c

@david-gonzalez-a8c david-gonzalez-a8c commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds depth to focused cards across the tvOS app: each focused surface gets a soft drop shadow and a focus highlight tuned to its content (full diagonal sheen for artwork cards, edge-stroke highlight for text-heavy ones). A subtle top-to-bottom page gradient replaces the flat background so the shadows have something to read against, and pcBackgroundActive is stepped back from pure white/black so the focused surface reads as a silvery/charcoal material instead of raw brightness.

What changed

  • New focusedCardDepth(...) modifier (FocusedCardDepth.swift) with two styles — .surface (full diagonal specular sheen for artwork) and .content (1.5pt inner-stroke highlight for cover-plus-text cards). Both share the same drop shadow. Accessibility-aware: respects accessibilityReduceTransparency (drops the sheen, keeps the shadow) and accessibilityReduceMotion (drops the easing).
  • Applied to discover category / featured / video / podcast-row cells, the Your Podcasts grid (covers + folders), PlaylistCell, EpisodeRow, NowPlayingRow.
  • RootView background: flat pcBackgroundSurface → top→bottom LinearGradient via new pcBackgroundTop / pcBackgroundBottom tokens.
  • pcBackgroundActive retuned: dark #FBFBFC#D4D6DB, light #161718#2A2D31. Global focus-highlight token — touches every focused surface in the TV app.
  • Rounded corners added to DiscoverCategoryCell and PlaylistCell (16pt) where they were previously .clipped() rectangles, so the depth overlay matches a real card shape.

To test

  1. Home → focus through Now Playing, Up Next, Made for TV, You might like, playlists, category cards. Each should pick up shadow + highlight on focus.
  2. Podcasts tab → covers and folders both lift on focus.
  3. Discover → featured carousel and category grid both lift.
  4. Toggle light/dark appearance; toggle Reduce Motion / Reduce Transparency in Settings.

Checklist

  • I have considered if this change warrants user-facing release notes and have added them to CHANGELOG.md if necessary. (Visual refinement only; no behavioural change.)
  • I have considered adding unit tests for my changes. (UI-only modifier; verified by #Preview in FocusedCardDepth.swift.)
  • I have updated (or requested that someone edit) the Event Horizon schema to reflect any new or changed analytics. (No analytics changes.)

david-gonzalez-a8c and others added 7 commits June 19, 2026 18:02
Three pieces that work together to give focused cards more depth, in the
spirit of the Apple TV home screen carousels:

- New `focusedCardDepth(isFocused:cornerRadius:)` view modifier that
  layers a soft drop shadow and a diagonal "lit from above" sheen on a
  focused card surface.
- RootView background swapped from a flat fill to a subtle top→bottom
  gradient (new `pcBackgroundTop` / `pcBackgroundBottom` tokens). The
  gradient is a touch lighter than `pcBackgroundSurface` at the top so
  the new shadows have something to read against.
- Applied the modifier to `DiscoverCategoryCell`, `PlaylistCell`, and
  `DiscoverFeaturedPodcastCell`, with explicit `RoundedRectangle` clips
  on the first two (the existing `.clipped()` was a bare rectangle).

Co-Authored-By: Claude <noreply@anthropic.com>
- pcBackgroundActive steps back from the white/black extremes
  (#FBFBFC → #D4D6DB in dark, #161718 → #2A2D31 in light) so focused
  cards read as silvery/charcoal and let the depth treatment do the
  lifting instead of raw brightness.
- Sheen gradient stops bumped (top white .22 → .45, bottom black .10 →
  .28) and blend mode switched from .plusLighter to .overlay; the old
  mode was silently swallowing the dark side of the gradient.
- Shadow opacity .45 → .6, radius 32 → 44, y-offset 20 → 26.

Co-Authored-By: Claude <noreply@anthropic.com>
…ills

Extend the depth treatment to the three pill types that were missing it
from the Home screen:

- `DiscoverVideoEpisodeCell` (Made for TV row)
- `NowPlayingRowLabel` (currently-playing pill)
- `EpisodeRow` (Up Next pills, also picked up wherever EpisodeRow is
  used elsewhere)

Also push the inner sheen harder: highlight stop bumped to .85 and the
gradient now ramps through .45 → .15 → clear → .45 black so the lit
edge has a more pronounced glint rather than a uniform glow.

Co-Authored-By: Claude <noreply@anthropic.com>
The full diagonal sheen looks great on artwork cards but muddies the
cover and text on content-heavy cards. Split the modifier into two
styles sharing the same drop shadow:

- `.surface` (default): the existing full diagonal sheen, for cards
  that are mostly artwork or a flat colour.
- `.content`: a thin inner stroke that lights the top edge and lays a
  subtle rim line at the bottom, leaving the body of the card alone.

`DiscoverVideoEpisodeCell`, `NowPlayingRow`, and `EpisodeRow` switch to
`.content`. The other three (DiscoverCategoryCell, PlaylistCell,
DiscoverFeaturedPodcastCell) keep `.surface` via the default.

Co-Authored-By: Claude <noreply@anthropic.com>
…on bare covers

Three tweaks following on-device review:

- PlaylistCell switched to .content — the diagonal sheen was washing the
  "New playlist" / "Smart playlist" / episode-count text on the left
  side of the pill.
- .surface highlight rebuilt as two stacked passes: a `.plusLighter`
  top-leading glint that's purely additive (always brightens, never
  darkens, so it pops on busy artwork) plus an `.overlay`-blended
  bottom-trailing falloff. The single `.overlay` pass was capping the
  highlight intensity against artwork; splitting makes it specular.
- DiscoverPodcastRow's bare cover thumbnails now get the .surface
  treatment via a small `FocusedPodcastCover` wrapper that reads the
  `.card` button's `\.isFocused`.

Co-Authored-By: Claude <noreply@anthropic.com>
The Podcasts tab grid was using `NavigationLink + PodcastImage +
.buttonStyle(.card)` with no depth treatment, so its covers felt flat
next to Discover rows like "You might like" (which I'd already wired
up with the depth modifier).

Added a parameterless `focusedCardDepth(cornerRadius:style:)` overload
that reads `\.isFocused` from the environment, so any view sitting
inside a Button/NavigationLink label can opt in without a wrapper
struct. Used it for the Your Podcasts grid, and refactored the
DiscoverPodcastRow wrapper away to share the same call shape.

Co-Authored-By: Claude <noreply@anthropic.com>
The .surface specular was reading as too aggressive in the on-device
review. Halved the plus-lighter highlight stops (.55/.25/.08 → .28/.12/
.04) and roughly halved the overlay falloff (.30/.55 → .16/.30) so it
reads as a subtle gleam rather than a metallic sheen.

In return, push the shared drop shadow harder across both styles —
opacity .6 → .85, radius 44 → 60, y 26 → 36 — so cards feel more
clearly lifted off the page now that the highlight is doing less of
the lifting itself.

Co-Authored-By: Claude <noreply@anthropic.com>
@dangermattic

dangermattic commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator
1 Warning
⚠️ View files have been modified, but no screenshot or video is included in the pull request. Consider adding some for clarity.
1 Message
📖 This PR is still a Draft: some checks will be skipped.

Generated by 🚫 Danger

david-gonzalez-a8c and others added 2 commits June 19, 2026 18:40
- Respect `accessibilityReduceTransparency`: skip the blend-mode sheen
  (the shadow still lifts the card, just without the translucent
  overlay).
- Respect `accessibilityReduceMotion`: drop the easeInOut timing curve
  on focus state changes.
- `FolderCardView` now also picks up `.surface` depth so folder cells
  match the podcasts they sit next to in the Podcasts grid.
- Added a `#Preview` showing both styles side by side for future
  tuning.

Co-Authored-By: Claude <noreply@anthropic.com>
`EpisodeRow` paints its own background, clip, and focused-card depth
when used standalone (Up Next, podcast detail list, player button).
Inside `EpisodeRowWithActions`, the outer container needs to be the
single card surface so the depth treatment renders across both the row
and the more-button gutter — previously the inner row's shadow got
clipped by the wider outer clipShape, leaving the depth half-applied.

Adds a `providesCardSurface` flag (default true) on `EpisodeRow` with
a `fileprivate` init for same-file callers, and moves the card surface
onto the outer container in `EpisodeRowWithActions`.

Co-Authored-By: Claude <noreply@anthropic.com>
@david-gonzalez-a8c david-gonzalez-a8c changed the title [PC TV] Experiment: Apple-TV-style depth on focused cards [PC TV] Apple-TV-style depth on focused cards Jun 19, 2026
@david-gonzalez-a8c david-gonzalez-a8c marked this pull request as ready for review June 19, 2026 16:46
@david-gonzalez-a8c david-gonzalez-a8c requested a review from a team as a code owner June 19, 2026 16:46
@david-gonzalez-a8c david-gonzalez-a8c requested review from Copilot and kean and removed request for a team June 19, 2026 16:46
@david-gonzalez-a8c david-gonzalez-a8c marked this pull request as draft June 19, 2026 16:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the tvOS app’s focus visuals to better match Apple TV carousel cards by introducing a reusable focused-card “depth” treatment (shadow + highlight) and applying it across key TV UI surfaces, along with a subtle page background gradient and retuned focus highlight color.

Changes:

  • Added a new focusedCardDepth(...) SwiftUI modifier with two highlight styles and accessibility-aware behavior.
  • Applied the modifier across multiple tvOS card/row views, including a small EpisodeRow/EpisodeRowWithActions refactor to avoid double-painted card surfaces.
  • Updated the tvOS root background from a flat fill to a top→bottom gradient using new background tokens, and retuned the global focused-surface color token.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
podcasts.xcodeproj/project.pbxproj Project file adjustment (FirebaseManager build file entry ordering).
Pocket Casts TV App/UI/Style/FocusedCardDepth.swift Introduces the new focused-card depth modifier (shadow + highlight styles).
Pocket Casts TV App/UI/Style/Color+Style.swift Adds top/bottom background tokens and retunes pcBackgroundActive.
Pocket Casts TV App/UI/RootView.swift Switches the root background to a subtle vertical gradient.
Pocket Casts TV App/UI/Podcasts/PodcastsView.swift Applies depth treatment to podcast cover cards in the grid.
Pocket Casts TV App/UI/Podcasts/FolderCardView.swift Applies depth treatment to folder cards.
Pocket Casts TV App/UI/Podcasts/EpisodeRow.swift Adds optional surface-providing behavior and applies depth treatment.
Pocket Casts TV App/UI/Playlists/PlaylistCell.swift Updates clipping to rounded corners and applies depth treatment.
Pocket Casts TV App/UI/Player/NowPlayingRow.swift Applies depth treatment to the Now Playing card.
Pocket Casts TV App/UI/Discover/DiscoverVideoEpisodeCell.swift Applies depth treatment to the “Made for TV” episode card.
Pocket Casts TV App/UI/Discover/DiscoverPodcastRow.swift Applies depth treatment to bare podcast cover cards.
Pocket Casts TV App/UI/Discover/DiscoverFeaturedPodcastCell.swift Applies depth treatment to the featured podcast card.
Pocket Casts TV App/UI/Discover/DiscoverCategoryCell.swift Updates clipping to rounded corners and applies depth treatment.
Comments suppressed due to low confidence (2)

Pocket Casts TV App/UI/Podcasts/EpisodeRow.swift:83

  • focusedCardDepth draws its highlight using a .continuous rounded rect, but this card is clipped with the default rounded-rect style. That can cause subtle corner mismatches (highlight/shadow not aligning with the clipped surface).
struct MoreButtonStyle: ButtonStyle {
    @Environment(\.isFocused) var isFocused: Bool

Pocket Casts TV App/UI/Podcasts/EpisodeRow.swift:160

  • Same as above: the outer container applies focusedCardDepth, but the card is clipped with the default rounded-rect style. Using .continuous here keeps the clip shape aligned with the depth modifier's highlight/shadow.
        }
        .contextMenu {
            EpisodeActionButtons(model: model, context: context, isShowingShowNotes: $isShowingShowNotes)

Comment on lines +50 to 54
// Focus highlight (`bg-active`). The focused surface inverts against the page but stays a
// step back from pure black/white — a silvery card in dark mode, a charcoal card in light —
// so the sheen and shadow on focused cards do the lifting instead of raw brightness.
static let pcBackgroundActive = Color(uiColor: .appearance(light: "#2A2D31", dark: "#D4D6DB"))

Comment on lines 79 to 82
.clipShape(RoundedRectangle(cornerRadius: 12))
.clipped()
.focusedCardDepth(isFocused: isFocused, cornerRadius: 12, style: .content)
.focusSection()
@@ -67,6 +67,7 @@ private struct NowPlayingRowLabel: View {
.padding(32)
.background(isFocused ? Color.pcBackgroundActive : Color.pcBackgroundSunken)
.clipShape(RoundedRectangle(cornerRadius: 12))
@@ -113,7 +113,7 @@ struct DiscoverFeaturedPodcastCell: View {
}
.background(Color.pcBackgroundSunken)
.clipShape(RoundedRectangle(cornerRadius: 12))
@@ -31,6 +31,7 @@ struct FolderCardView: View {
.frame(width: cardSize, height: cardSize)
.background(Color(uiColor: AppTheme.folderColor(colorInt: model.folder.color)))
.clipShape(RoundedRectangle(cornerRadius: 12))
Comment on lines +75 to +80
.shadow(
color: .black.opacity(isFocused ? 0.85 : 0),
radius: isFocused ? 60 : 0,
x: 0,
y: isFocused ? 36 : 0
)
@david-gonzalez-a8c

Copy link
Copy Markdown
Contributor Author

Closing in favour of #4580 to get a clean review history (Copilot's earlier comments were against superseded code).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants