From b7c91081b28a84cfa852fb3acb61994cadbc2d25 Mon Sep 17 00:00:00 2001 From: Jonathon Wright Date: Sat, 27 Jun 2026 21:02:46 +1000 Subject: [PATCH] fix(ui): scroll-coupled backdrop pan that works in every browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CSS animation-timeline: scroll() is Chromium-only, so the backdrop didn't move for non-Chromium browsers. Replace it with a tiny rAF-throttled scroll handler in site.js (the existing same-origin script — CSP stays strict, no inline JS) that drives a --bg-shift CSS var off scroll position. The board now pans vertically in lock-step with scroll and sits still at rest, in all browsers. --- public/site.js | 32 +++++++++++++++++++++++++++++++ src/styles/global.css | 44 +++++-------------------------------------- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/public/site.js b/public/site.js index 549431e..d6e49a0 100644 --- a/public/site.js +++ b/public/site.js @@ -55,5 +55,37 @@ io.observe(el); }); } + + // --- Scroll-coupled background pan ------------------------------------- + // The Bat-Computer backdrop pans vertically in lock-step with scroll — + // moving ONLY while you scroll, still at rest. Plain scroll listener so it + // works in every browser (unlike CSS animation-timeline: scroll(), which is + // Chromium-only). rAF-throttled; sets a CSS var the transform reads. + var bgImgs = document.querySelectorAll(".site-bg__img"); + if (bgImgs.length) { + var ticking = false; + function updateBg() { + ticking = false; + var doc = document.documentElement; + var max = doc.scrollHeight - window.innerHeight; + var p = max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0; + // +half a range at the top → -half at the bottom: scrolling down pans + // the camera down the board. Range kept within the image's overhang so + // an edge never shows (image is 215% tall → 57.5vh spare each side). + var shift = (0.5 - p) * window.innerHeight * 0.7; + for (var i = 0; i < bgImgs.length; i++) { + bgImgs[i].style.setProperty("--bg-shift", shift.toFixed(1) + "px"); + } + } + function onScroll() { + if (!ticking) { + ticking = true; + window.requestAnimationFrame(updateBg); + } + } + window.addEventListener("scroll", onScroll, { passive: true }); + window.addEventListener("resize", onScroll, { passive: true }); + updateBg(); + } }); })(); diff --git a/src/styles/global.css b/src/styles/global.css index 7d8b506..62803ad 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -372,12 +372,15 @@ svg { top: 50%; left: 50%; /* Zoomed in — larger than viewport so the board reads big/immersive and the - scroll-pan has room to travel. */ + scroll-pan has room to travel without ever revealing an edge. */ width: 215%; height: 215%; object-fit: cover; - transform: translate(-50%, -50%); + /* --bg-shift is driven by the scroll handler in site.js (scroll-coupled pan: + moves only as the page scrolls). Defaults to 0 → centred when JS is off. */ + transform: translate(-50%, calc(-50% + var(--bg-shift, 0px))); will-change: transform; + transition: transform 0.15s linear; } .site-bg__img--light { display: none; @@ -420,43 +423,6 @@ svg { rgba(246, 248, 251, 0.82) 100% ); } -/* BASELINE motion (works in EVERY browser, no JS): a slow, continuous top↔bottom - drift. This is the guaranteed fallback — Firefox/Safari don't support - scroll-driven animations, so without this the board would sit dead-still. */ -@media (prefers-reduced-motion: no-preference) { - .site-bg__img { - animation: bgDrift 48s ease-in-out infinite alternate; - } -} -/* ENHANCEMENT: where scroll-driven animations exist (Chromium), couple the pan - to scroll position instead — overrides the timed drift on the same element. */ -@supports (animation-timeline: scroll()) { - @media (prefers-reduced-motion: no-preference) { - .site-bg__img { - animation: bgPan linear both; - animation-timeline: scroll(root block); - } - } -} -/* Continuous timed drift (fallback) — gentle, alternates so it never jumps. */ -@keyframes bgDrift { - from { - transform: translate(-50%, -38%); - } - to { - transform: translate(-50%, -62%); - } -} -/* Scroll-coupled monotonic top→bottom pan (Chromium): ~44% travel of the - 215%-tall image, tied directly to page scroll progress. */ -@keyframes bgPan { - from { - transform: translate(-50%, -28%); - } - to { - transform: translate(-50%, -72%); - } -} @media (prefers-reduced-motion: reduce) { .scroll-progress {