b7c91081b2
build-and-deploy / build (push) Failing after 11m10s
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.
92 lines
3.4 KiB
JavaScript
92 lines
3.4 KiB
JavaScript
/* Single external script (no inline JS anywhere → strict script-src 'self').
|
|
Loaded blocking in <head> so the theme is set before first paint (no flash). */
|
|
(function () {
|
|
// --- Pre-paint theme -----------------------------------------------------
|
|
try {
|
|
var stored = localStorage.getItem("theme");
|
|
var theme =
|
|
stored ||
|
|
(window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark");
|
|
document.documentElement.dataset.theme = theme;
|
|
} catch (e) {
|
|
document.documentElement.dataset.theme = "dark";
|
|
}
|
|
|
|
function onReady(fn) {
|
|
if (document.readyState !== "loading") fn();
|
|
else document.addEventListener("DOMContentLoaded", fn);
|
|
}
|
|
|
|
onReady(function () {
|
|
// --- Theme toggle ------------------------------------------------------
|
|
var btn = document.getElementById("theme-toggle");
|
|
if (btn) {
|
|
btn.addEventListener("click", function () {
|
|
var root = document.documentElement;
|
|
var next = root.dataset.theme === "light" ? "dark" : "light";
|
|
root.dataset.theme = next;
|
|
try {
|
|
localStorage.setItem("theme", next);
|
|
} catch (e) {
|
|
/* ignore */
|
|
}
|
|
});
|
|
}
|
|
|
|
// --- Reveal on scroll (progressive enhancement, motion-aware) ----------
|
|
var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
var targets = document.querySelectorAll("[data-reveal]");
|
|
if (!reduce && "IntersectionObserver" in window) {
|
|
targets.forEach(function (el) {
|
|
el.classList.add("reveal");
|
|
});
|
|
var io = new IntersectionObserver(
|
|
function (entries) {
|
|
entries.forEach(function (entry) {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add("is-visible");
|
|
io.unobserve(entry.target);
|
|
}
|
|
});
|
|
},
|
|
{ rootMargin: "0px 0px -10% 0px", threshold: 0.1 },
|
|
);
|
|
targets.forEach(function (el) {
|
|
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();
|
|
}
|
|
});
|
|
})();
|