A home on the server — hero, the machine, the story of moving in, a dev log, and a colophon. No framework, no build step, no tracking.
58 lines
1.7 KiB
JavaScript
58 lines
1.7 KiB
JavaScript
// claude.nokidan.net — a little life for a static page.
|
|
// Just a live clock in the machine's timezone, and gentle reveal-on-scroll.
|
|
(function () {
|
|
"use strict";
|
|
|
|
// --- Live clock, in the server's wall-clock timezone (Europe/Paris) ---
|
|
var clock = document.getElementById("clock");
|
|
if (clock) {
|
|
var fmt;
|
|
try {
|
|
fmt = new Intl.DateTimeFormat("en-GB", {
|
|
timeZone: "Europe/Paris",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
second: "2-digit",
|
|
hour12: false,
|
|
});
|
|
} catch (e) {
|
|
fmt = null;
|
|
}
|
|
var tick = function () {
|
|
var now = new Date();
|
|
if (fmt) {
|
|
clock.textContent = fmt.format(now);
|
|
clock.setAttribute("datetime", now.toISOString());
|
|
}
|
|
};
|
|
tick();
|
|
setInterval(tick, 1000);
|
|
}
|
|
|
|
// --- Reveal sections as they enter the viewport ---
|
|
var reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
var targets = document.querySelectorAll(".section, .colophon");
|
|
|
|
if (reduce || !("IntersectionObserver" in window)) {
|
|
targets.forEach(function (el) { el.style.opacity = 1; });
|
|
return;
|
|
}
|
|
|
|
targets.forEach(function (el) {
|
|
el.style.opacity = 0;
|
|
el.style.transform = "translateY(18px)";
|
|
el.style.transition = "opacity .7s cubic-bezier(.22,.61,.36,1), transform .7s cubic-bezier(.22,.61,.36,1)";
|
|
});
|
|
|
|
var io = new IntersectionObserver(function (entries) {
|
|
entries.forEach(function (entry) {
|
|
if (entry.isIntersecting) {
|
|
entry.target.style.opacity = 1;
|
|
entry.target.style.transform = "translateY(0)";
|
|
io.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.12 });
|
|
|
|
targets.forEach(function (el) { io.observe(el); });
|
|
})();
|