From 6e12d0161af5e376587185cf3ee5026d98e91269 Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Sun, 21 Jun 2026 09:12:24 -0400 Subject: [PATCH] fix(theater): replace max-height collapse with grid-rows + visibility; fix keyboard-focus leak when collapsed --- .../AudioPlayerBar/AudioPlayerBar.razor | 2 + DeepDrftPublic.Client/Pages/CutDetail.razor | 4 ++ DeepDrftPublic.Client/Pages/MixDetail.razor | 4 ++ .../Pages/SessionDetail.razor | 2 + .../wwwroot/styles/deepdrft-styles.css | 41 +++++++++++++++---- 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/DeepDrftPublic.Client/Controls/AudioPlayerBar/AudioPlayerBar.razor b/DeepDrftPublic.Client/Controls/AudioPlayerBar/AudioPlayerBar.razor index eb568cf..50b380a 100644 --- a/DeepDrftPublic.Client/Controls/AudioPlayerBar/AudioPlayerBar.razor +++ b/DeepDrftPublic.Client/Controls/AudioPlayerBar/AudioPlayerBar.razor @@ -20,7 +20,9 @@ else { var nowShowing = VisualizerControlState.TheaterMode;
+
+
} diff --git a/DeepDrftPublic.Client/Pages/CutDetail.razor b/DeepDrftPublic.Client/Pages/CutDetail.razor index f913eb9..8d2809b 100644 --- a/DeepDrftPublic.Client/Pages/CutDetail.razor +++ b/DeepDrftPublic.Client/Pages/CutDetail.razor @@ -75,6 +75,7 @@ else a collapsing wrapper so it does not pop — IsContentHidden collapses it to zero height when Theater is on AND this Cut is the playing release. OFF eases it back to its normal layout. *@
+
@* Header split: meta + Play/Share on the LEFT, bordered cover on the RIGHT (spec §3.1). *@
@@ -134,10 +135,12 @@ else
+
@* Theater Mode (Wave 2 §2): eased collapse, mirroring the Header region. *@
+
@* Blurb sits between the header and the track-list divider. *@ @@ -169,6 +172,7 @@ else
}
+
diff --git a/DeepDrftPublic.Client/Pages/MixDetail.razor b/DeepDrftPublic.Client/Pages/MixDetail.razor index 6af366b..1800076 100644 --- a/DeepDrftPublic.Client/Pages/MixDetail.razor +++ b/DeepDrftPublic.Client/Pages/MixDetail.razor @@ -75,6 +75,7 @@ else a collapsing wrapper so it does not pop — collapsed to zero height when Theater is on AND this Mix is the playing release. OFF eases the full-bleed visualizer back behind the hero. *@
+
@* Cover-as-background hero with all metadata overlaid, square `mix-hero` sizing. The cover art IS the background, so no separate cover thumbnail (CoverThumbKey defaults to null). Share and play ride in as slots, matching Sessions. *@ @@ -99,12 +100,15 @@ else
+
@* Theater Mode (Wave 2 §2): eased collapse, mirroring the Hero region. *@
+
@* Blurb sits below the hero, inside the scaffold's foreground stacking context. *@ +
diff --git a/DeepDrftPublic.Client/Pages/SessionDetail.razor b/DeepDrftPublic.Client/Pages/SessionDetail.razor index 02448bc..dd954ae 100644 --- a/DeepDrftPublic.Client/Pages/SessionDetail.razor +++ b/DeepDrftPublic.Client/Pages/SessionDetail.razor @@ -72,6 +72,7 @@ else collapsing wrapper so they do not pop — collapsed to zero height when Theater is on AND this Session is the playing release. The top row above stays. OFF eases this region back in. *@
+
@* The overlay shows the cover thumbnail only when it differs from the resolved hero image — when there is no dedicated hero, heroImage already falls back to release.ImagePath, so the thumb would duplicate the background. That logic lives in ReleaseHeroOverlay. *@ @@ -98,6 +99,7 @@ else
+
} diff --git a/DeepDrftPublic/wwwroot/styles/deepdrft-styles.css b/DeepDrftPublic/wwwroot/styles/deepdrft-styles.css index 9b487be..02894bd 100644 --- a/DeepDrftPublic/wwwroot/styles/deepdrft-styles.css +++ b/DeepDrftPublic/wwwroot/styles/deepdrft-styles.css @@ -370,21 +370,44 @@ h2, h3, h4, h5, h6, } /* Eased content collapse for Theater Mode (Phase 20 Wave 2 §2). The detail content stays mounted and - collapses to zero height (and fades) when .dd-theater-collapsed is applied, so toggling Theater eases - both directions instead of popping — when collapsed the content is fully out of the way and the - visualizer is unobstructed. overflow:hidden clips the content during the transition; the large open - max-height accommodates any realistic content height (it is a ceiling, not a fixed size). The same - pattern drives the player-bar "now showing" band so the bar grows/shrinks smoothly too. */ + collapses smoothly when .dd-theater-collapsed is applied, so toggling Theater eases both directions + instead of popping — when collapsed the content is fully out of the way and the visualizer is + unobstructed. The same pattern drives the player-bar "now showing" band so the bar grows/shrinks + smoothly too. + + Technique: grid-template-rows 1fr → 0fr interpolates the REAL content height (no 400vh ceiling + artifact / delayed-start that the old max-height approach had). The direct child receives + overflow:hidden + min-height:0 so it actually clips during the transition (the grid child is the + collapsing unit). visibility:hidden removes all descendants from the tab order and from pointer/ + keyboard interaction once collapsed — this fixes the Major accessibility defect where Tab could + reach hidden controls. transition-behavior:allow-discrete makes visibility flip discretely: it + flips to hidden AFTER the ease-out finishes (so the animation plays fully), and flips back to + visible BEFORE the ease-in starts (so content is immediately interactive on the way back in). + The visibility transition is listed as 0s so it is instant when it fires; the allow-discrete + behaviour determines which end of the transition that instant flip occurs on. */ .dd-theater-collapsible { - overflow: hidden; - max-height: 400vh; + display: grid; + grid-template-rows: 1fr; opacity: 1; - transition: max-height 0.45s ease, opacity 0.3s ease; + visibility: visible; + transition: grid-template-rows 0.45s ease, opacity 0.3s ease, visibility 0s; + transition-behavior: allow-discrete; +} + +/* The single direct child clips itself during the grid-row collapse. min-height:0 overrides the + implicit min-height:auto that would prevent the row from shrinking past the content's intrinsic + height. overflow:hidden clips painted content when the row is partially collapsed. */ +.dd-theater-collapsible > * { + overflow: hidden; + min-height: 0; } .dd-theater-collapsed { - max-height: 0; + grid-template-rows: 0fr; opacity: 0; + /* visibility flips to hidden instantly, but allow-discrete defers it to AFTER the ease-out + completes (at the end of the 0.45s transition), so the animation still plays in full. */ + visibility: hidden; } /* Honor reduced-motion: collapse still happens (it is layout, not decoration) but instantly, matching