orchard
mirrorEvery site I host, in one repo, along with the Cloudflare Tunnel and Caddy that front them. It's all Go, Vite, and SQLite, and it runs on a desktop at home with nothing listening on an inbound port.
blogbuncaddycloudflare-tunneldockergogolanghomelabhtml-templatemonorepoself-hostedseosqlitestatic-sitetypstuptime-monitoringviteweb-analytics
1// The full-screen menu overlay. Nothing mounts or unmounts, so it is one
2// "is-open" class and the CSS holds the rest, including a delayed z-index
3// transition that keeps the overlay above the page for the whole slide-out.
4
5export const initMenu = () => {
6 const button = document.querySelector(".menu-hamburger");
7 const overlay = document.querySelector(".menu-overlay");
8 if (!button || !overlay) return;
9
10 const image = overlay.querySelector(".menu-overlayGridRight img[data-src]");
11
12 const setOpen = (open) => {
13 if (open && image && !image.src) {
14 // srcset first, or the browser starts the fallback download before it
15 // has any candidates to choose from.
16 if (image.dataset.srcset) image.srcset = image.dataset.srcset;
17 image.src = image.dataset.src;
18 }
19 overlay.classList.toggle("is-open", open);
20 // The overlay is hidden by z-index alone, so without inert its links stay
21 // in the tab order while it is closed.
22 overlay.toggleAttribute("inert", !open);
23 button.setAttribute("aria-expanded", open ? "true" : "false");
24 // Cleared rather than set to "scroll", which would override the stylesheet
25 // and force a scrollbar gutter on short pages.
26 document.body.style.overflowY = open ? "hidden" : "";
27 };
28
29 button.addEventListener("click", () => {
30 setOpen(!overlay.classList.contains("is-open"));
31 });
32
33 // Links navigate away, so they need no close handler. Escape is the only
34 // other way out.
35 document.addEventListener("keydown", (e) => {
36 if (e.key === "Escape" && overlay.classList.contains("is-open")) {
37 setOpen(false);
38 }
39 });
40};