Files
bztmon-site/public/site.js
T
jwright 65ba1ec7dc facelift: fleet constellation hero + Bat-Computer type/token layer
Chakra Petch site-wide (300-700) + JetBrains Mono variable, all self-hosted (CSP zero-external
holds). Token layer gains the mock-exact text tiers, glass panel, cyan-tinted borders, glows,
tighter radii; light 'blueprint' variants included. New Constellation.astro: the six real nodes
as IC chips on dual-trace PCB paths (cyan out / magenta back, dasharray packets), radar ring on
the cave hub, vias at bends - pure SVG + scoped CSS keyframes, full reduced-motion fallback.
Hero rebuilt: scrim stack + masked grid over the board art, two-line gradient nameplate, typing
bar cycling six real fleet facts via the external site.js (facts from src/data/fleet.ts), scroll
indicator. Facts wrap, never clip.
2026-07-03 18:58:49 +10:00

89 lines
3.3 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";
}
// Mark JS as live BEFORE first paint. CSS only hides the pre-reveal animation
// state under `html.js`, so with JS off every element stays visible (the
// Motion layer in /_astro reveals them once it runs). Progressive enhancement.
document.documentElement.classList.add("js");
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 */
}
});
}
// --- Hero typing loop (fleet facts) -------------------------------------
// Facts arrive via a data attribute (no inline JS → script-src 'self' holds).
// Reduced motion or JS-off: the first fact renders statically from markup.
var factBox = document.getElementById("fleet-fact");
var factText = document.getElementById("fleet-fact-text");
var reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (factBox && factText && !reduced) {
var facts;
try {
facts = JSON.parse(factBox.dataset.facts || "[]");
} catch (e) {
facts = [];
}
if (facts.length > 1) {
var fi = 0; // current fact
var ci = facts[0].length; // start fully typed (matches SSR markup)
var deleting = false;
var tick = function () {
var fact = facts[fi];
if (!deleting && ci === fact.length) {
// hold at end of fact, then start the next
deleting = true;
setTimeout(tick, 3200);
return;
}
if (deleting) {
// swap instantly to the next fact (clean cut, like a terminal)
deleting = false;
fi = (fi + 1) % facts.length;
ci = 0;
factText.textContent = "";
setTimeout(tick, 260);
return;
}
ci++;
factText.textContent = facts[fi].slice(0, ci);
setTimeout(tick, 45);
};
setTimeout(tick, 3200);
}
}
// Scroll reveals, hero entrance, backdrop parallax, scroll-progress bar,
// nav underline and button springs all live in the Motion layer
// (src/scripts/anim.ts → bundled /_astro/*.js). This file owns only the
// pre-paint theme + the typing loop so it stays a tiny blocking <head> script.
});
})();