From 9b06bb48c6eb059e7b3ebc706f1f7b8287a62e90 Mon Sep 17 00:00:00 2001 From: s8n Date: Sat, 9 May 2026 01:21:01 +0100 Subject: [PATCH] doc 26 INC2+INC3: pin backdrop, transparent sub-sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After INC1 fixed the Abspielen + first-fold backdrop, owner reported black band hiding artwork in More from Season 1 / below-fold sections. Two more patches required: INC2 — pin .backdropContainer + .backgroundContainer position:fixed; height 100vh so backdrop persists during scroll. Added vertical fade ::after. INC3 — extend transparent-scope to ALL detail-page sub-sections (.detailVerticalSection, .scrollSlider, .padded-bottom-page, .itemsContainer etc) so section wrappers don't paint over the pinned backdrop section by section. bin/headless-test.py now takes top + scrolled viewport screenshots. full_page=True hides position:fixed regressions, dual-screenshot exposes them. Use both to bisect. bin/apply-26-incident-fixes.sh updated with INC2+INC3. Open: AV1+Opus playback (Mike Nolan Show) still tracked for 10.11.8 migration. .detailLogo regression possible — test in actual browser. --- bin/apply-26-incident-fixes.sh | 65 ++++++++++++++++--- bin/headless-test.py | 20 ++++-- ...26-05-09-page-unresponsive-and-playback.md | 55 ++++++++++++++++ 3 files changed, 127 insertions(+), 13 deletions(-) diff --git a/bin/apply-26-incident-fixes.sh b/bin/apply-26-incident-fixes.sh index 1c93d24..b05c09e 100755 --- a/bin/apply-26-incident-fixes.sh +++ b/bin/apply-26-incident-fixes.sh @@ -40,15 +40,11 @@ s = open(p).read() patch = """ /* ARRFLIX 2026-05-09 — incident fixes (see docs/26-incident-2026-05-09-...). - 1. Cineplex theme hardcodes German "Abspielen" via ::after on .play_arrow. - Override with English. The German text was in CSS, not Jellyfin locale — - so all Traefik Accept-Language rewrites + force-english-all-users.sh - chases never fixed it. - 2. The BLACK-PASS block paints #000 !important on .layout-desktop, - .pageContainer, .padded-bottom-page etc — these wrap the backdrop layer - (z-index:-1) and occlude it. Use :has() to scope only when an - .itemDetailPage descendant exists, so backdrop renders only on detail - pages. */ + INC1: Cineplex theme hardcodes German "Abspielen" via content: ::after. + INC1: BLACK-PASS occludes backdrop; transparent-scope via :has(). + INC2: pin backdrop position:fixed so it persists across scroll. + INC3: extend transparent-scope through detail-page sub-sections so + section wrappers don't paint over the pinned backdrop. */ .mainDetailButtons .material-icons.play_arrow::after { content: "Play" !important; } @@ -64,6 +60,57 @@ patch = """ background-color: transparent !important; background: transparent !important; } +.layout-desktop .backdropContainer, +.layout-mobile .backdropContainer, +.layout-tv .backdropContainer, +.layout-desktop .backgroundContainer, +.layout-mobile .backgroundContainer, +.layout-tv .backgroundContainer { + position: fixed !important; + top: 0 !important; + left: 0 !important; + width: 100vw !important; + height: 100vh !important; + z-index: 0 !important; +} +.layout-desktop .backgroundContainer.withBackdrop::after, +.layout-mobile .backgroundContainer.withBackdrop::after, +.layout-tv .backgroundContainer.withBackdrop::after { + content: ""; + position: absolute; + inset: 0; + background: linear-gradient( + 180deg, + rgba(0,0,0,0.00) 0%, + rgba(0,0,0,0.00) 35%, + rgba(0,0,0,0.40) 70%, + rgba(0,0,0,0.75) 100% + ); + pointer-events: none; + z-index: 1; +} +.itemDetailPage, +.itemDetailPage > *, +.detailPageContent, +.detailPagePrimaryContainer, +.detailPageWrapperContainer, +.detailPageContent > *, +.detailVerticalSection, +.detailVerticalSection-extrabottompadding, +.detailSection, +.detailSectionContent, +.itemsContainer, +.scrollSlider, +.scrollSliderContainer, +.padded-bottom-page, +.detailPagePrimaryContent, +.sectionTitleContainer, +.detailRibbon, +.subtitleAudioContainer, +.detailPageRoot { + background-color: transparent !important; + background: transparent !important; +} """ s = s.replace("", patch + "") open(p, "w").write(s) diff --git a/bin/headless-test.py b/bin/headless-test.py index 1ea85da..60e61b4 100755 --- a/bin/headless-test.py +++ b/bin/headless-test.py @@ -123,18 +123,24 @@ async def main(): await page.goto(target, wait_until="networkidle", timeout=30000) await asyncio.sleep(4) # let SPA paint backdrop - # Probe key DOM elements + # Probe key DOM elements (extended) probe = await page.evaluate("""() => { const result = {}; const sel = ['.itemBackdrop', '.detailBackdrop', '.backdropContainer', '.backgroundContainer', '.layout-desktop', 'body', '#reactRoot', '.itemDetailPage', - 'video', '.htmlvideoplayer', '.btnPlay', '.detailPagePrimaryContainer']; + 'video', '.htmlvideoplayer', '.btnPlay', '.detailPagePrimaryContainer', + '.detailSection', '.detailVerticalSection', '.itemsContainer', + '.padded-bottom-page', '.mainAnimatedPages', '.pageContainer', + '.cardScalable', '.scrollSlider', '.sectionTitleContainer', + '.detailPageContent', '.detailPageWrapperContainer']; for (const s of sel) { - const el = document.querySelector(s); - if (!el) { result[s] = ''; continue; } + const els = document.querySelectorAll(s); + if (els.length === 0) { result[s] = ''; continue; } + const el = els[0]; const cs = getComputedStyle(el); result[s] = { + count: els.length, display: cs.display, opacity: cs.opacity, visibility: cs.visibility, @@ -152,8 +158,14 @@ async def main(): return result; }""") + # Two screenshots: top viewport + scrolled to mid-page (so fixed backdrop renders correctly) screenshot = os.path.join(OUT, f"{URL.replace('https://','').replace('.','_')}-detail.png") await page.screenshot(path=screenshot, full_page=False) + # Scroll halfway down to verify pinned backdrop persists + await page.evaluate("() => window.scrollTo(0, document.body.scrollHeight * 0.5)") + await asyncio.sleep(1) + scrolled = os.path.join(OUT, f"{URL.replace('https://','').replace('.','_')}-scrolled.png") + await page.screenshot(path=scrolled, full_page=False) print(f"[+] screenshot: {screenshot}") with open(os.path.join(OUT, "probe.json"), "w") as f: diff --git a/docs/26-incident-2026-05-09-page-unresponsive-and-playback.md b/docs/26-incident-2026-05-09-page-unresponsive-and-playback.md index 3abe894..fa14e7f 100644 --- a/docs/26-incident-2026-05-09-page-unresponsive-and-playback.md +++ b/docs/26-incident-2026-05-09-page-unresponsive-and-playback.md @@ -539,6 +539,61 @@ These are the dead-ends. Future operators (and future me) should skip: --- +## Iteration 2 — backdrop visible only on top viewport (2026-05-09 follow-up) + +After INC1 (`:has()` transparent-scope) shipped and prod showed backdrop on +detail-page top, owner reported "in the middle of the More from Season 1 +is black, it's hiding the artwork". Below-the-fold sections (Next Up, Seasons, +More Like This) showed solid black instead of continuing the backdrop. + +### Root cause (INC2) + +`.backdropContainer` defaults to non-fixed positioning — it scrolls out of +view. INC1 made wrappers transparent so backdrop showed through, but only +where the backdrop EXISTED in the DOM viewport. Once user scrolls down, +backdrop is above viewport, sections see body's `#000` bg. + +### Fix INC2 + +Pin `.backdropContainer` + `.backgroundContainer` to `position: fixed; top:0; +height:100vh; z-index:0`. Added `::after` vertical gradient (transparent at +top → 75% black at bottom) so text remains readable as user scrolls into +backdrop area. + +### Root cause (INC3) + +INC2 alone didn't fix it visually — section wrappers (`.detailVerticalSection`, +`.scrollSliderContainer`, `.padded-bottom-page`, `.itemsContainer` etc) still +painted opaque bg from BLACK-PASS + finity. Pinned backdrop sat behind, but +sections occluded it section-by-section. + +### Fix INC3 + +Extended transparent-scope to all detail-page sub-sections: +`.itemDetailPage > *`, `.detailPageContent`, `.detailPagePrimaryContainer`, +`.detailPageWrapperContainer`, `.detailVerticalSection*`, `.detailSection*`, +`.itemsContainer`, `.scrollSlider*`, `.padded-bottom-page`, +`.sectionTitleContainer`, `.detailRibbon`, `.subtitleAudioContainer`, +`.detailPageRoot`. + +### Verification (INC2 + INC3) + +Updated `bin/headless-test.py` to take TWO viewport screenshots: top-of-page ++ scrolled to 50% page height. With INC2/INC3 applied, scrolled screenshot +shows R&M backdrop persisting behind "Seasons" + "More Like This" sections +(previously: solid black). + +### Lesson learned + +When pinning a backdrop with `position:fixed`, transparency must extend +RECURSIVELY through every wrapper ON TOP of the backdrop layer, not just the +top-level page wrappers. Test with scrolled screenshot — full-page screenshot +in playwright stretches viewport and hides `position:fixed` issues. + +`bin/headless-test.py` now takes both top + scrolled. Use both to bisect. + +--- + ## Open follow-ups (for separate sessions) - **AV1+Opus playback** (Bug E): Chrome's AV1 DirectStream codec-tag mislabel