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 custom cursor, a dot tracking the pointer and a ring that grows over
2// anything interactive. Positions are written in a requestAnimationFrame loop
3// rather than in the mousemove handler, so a burst costs one layout per frame.
4
5export const initCursor = () => {
6 const dot = document.querySelector(".cursor");
7 const circle = document.querySelector(".cursor-circle");
8 if (!dot || !circle) return;
9
10 const pos = { x: 0, y: 0 };
11
12 document.addEventListener(
13 "mousemove",
14 (e) => {
15 pos.x = e.clientX;
16 pos.y = e.clientY;
17 },
18 { passive: true }
19 );
20
21 document.addEventListener(
22 "mouseover",
23 (e) => {
24 const target = e.target;
25 const interactive =
26 target.tagName === "BUTTON" ||
27 target.tagName === "A" ||
28 !!target.closest("a, button") ||
29 (target.classList && target.classList.contains("mouse-activate"));
30 circle.classList.toggle("is-active", interactive);
31 },
32 { passive: true }
33 );
34
35 const animate = () => {
36 const transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`;
37 dot.style.transform = transform;
38 circle.style.transform = transform;
39 window.requestAnimationFrame(animate);
40 };
41
42 window.requestAnimationFrame(animate);
43};