From 32b10bfebc348e21cd2feb018f73d5d56975174d Mon Sep 17 00:00:00 2001 From: Max Elkins Date: Thu, 2 Apr 2026 19:08:25 +0100 Subject: [PATCH 1/2] fix: getting logo interaction working with touch --- website/src/components/HdiLogo.astro | 62 ++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/website/src/components/HdiLogo.astro b/website/src/components/HdiLogo.astro index 75adc14..2de54cd 100644 --- a/website/src/components/HdiLogo.astro +++ b/website/src/components/HdiLogo.astro @@ -90,7 +90,9 @@ // so CSS transitions on color have existing elements to animate between. function applyClasses() { logoPre.querySelectorAll(".hero-logo-letter").forEach((el) => { - el.classList.toggle("hovered", el.dataset.key === hoveredKey); + const key = el.dataset.key!; + el.classList.toggle("hovered", key === hoveredKey); + el.classList.toggle("pressed", pressedKeys.has(key)); }); } @@ -102,12 +104,24 @@ return col < 6 ? "h" : col < 12 ? "d" : col < 18 ? "i" : null; } + function keyFromTouch(touch: Touch): string | null { + const rect = logoPre.getBoundingClientRect(); + const col = Math.floor((touch.clientX - rect.left) / (rect.width / 19)); + return col < 6 ? "h" : col < 12 ? "d" : col < 18 ? "i" : null; + } + // Mouse logoPre.addEventListener("mousemove", (e) => { const key = keyFromMouse(e); - if (key === hoveredKey) return; - hoveredKey = key; - applyClasses(); // class-only update — preserves spans so transition fires + if (key !== hoveredKey) { + hoveredKey = key; + applyClasses(); + } + if (e.buttons === 1) { + pressedKeys.clear(); + if (key) pressedKeys.add(key); + render(); + } }); logoPre.addEventListener("mouseleave", () => { if (hoveredKey === null) return; @@ -127,6 +141,43 @@ render(); }); + // Touch + logoPre.addEventListener( + "touchstart", + (e) => { + e.preventDefault(); + pressedKeys.clear(); + for (const touch of e.touches) { + const key = keyFromTouch(touch); + if (key) pressedKeys.add(key); + } + render(); + }, + { passive: false }, + ); + logoPre.addEventListener( + "touchmove", + (e) => { + e.preventDefault(); + pressedKeys.clear(); + for (const touch of e.touches) { + const key = keyFromTouch(touch); + if (key) pressedKeys.add(key); + } + render(); + }, + { passive: false }, + ); + document.addEventListener("touchend", () => { + if (pressedKeys.size === 0) return; + pressedKeys.clear(); + render(); + }); + document.addEventListener("touchcancel", () => { + pressedKeys.clear(); + render(); + }); + // Keyboard — skip when focus is in a text input to avoid interfering with typing const KEYS = new Set(["h", "d", "i"]); const isTextField = (t: EventTarget | null) => @@ -163,6 +214,9 @@ &.hovered { color: var(--mauve); } + &.pressed { + color: var(--mauve); + } } } From 4f2a169546748164a1038453bf4b97c8575a774f Mon Sep 17 00:00:00 2001 From: Max Elkins Date: Thu, 2 Apr 2026 19:13:02 +0100 Subject: [PATCH 2/2] refactor: simplify logo interactions --- website/src/components/HdiLogo.astro | 77 +++++++++------------------- 1 file changed, 25 insertions(+), 52 deletions(-) diff --git a/website/src/components/HdiLogo.astro b/website/src/components/HdiLogo.astro index 2de54cd..14f4ee2 100644 --- a/website/src/components/HdiLogo.astro +++ b/website/src/components/HdiLogo.astro @@ -96,23 +96,33 @@ }); } - // Maps a mouse event to a key by dividing the pre into 19 character columns. + // Maps a clientX coordinate to a key by dividing the pre into 19 character columns. // h: cols 0–5, d: cols 6–11, i: cols 12–17, col 18 is the trailing '|' (ignored). - function keyFromMouse(e: MouseEvent): string | null { + function keyFromPoint(clientX: number): string | null { const rect = logoPre.getBoundingClientRect(); - const col = Math.floor((e.clientX - rect.left) / (rect.width / 19)); + const col = Math.floor((clientX - rect.left) / (rect.width / 19)); return col < 6 ? "h" : col < 12 ? "d" : col < 18 ? "i" : null; } - function keyFromTouch(touch: Touch): string | null { - const rect = logoPre.getBoundingClientRect(); - const col = Math.floor((touch.clientX - rect.left) / (rect.width / 19)); - return col < 6 ? "h" : col < 12 ? "d" : col < 18 ? "i" : null; + function releaseAll() { + if (pressedKeys.size === 0) return; + pressedKeys.clear(); + render(); + } + + function pressTouches(e: TouchEvent) { + e.preventDefault(); + pressedKeys.clear(); + for (const touch of e.touches) { + const key = keyFromPoint(touch.clientX); + if (key) pressedKeys.add(key); + } + render(); } // Mouse logoPre.addEventListener("mousemove", (e) => { - const key = keyFromMouse(e); + const key = keyFromPoint(e.clientX); if (key !== hoveredKey) { hoveredKey = key; applyClasses(); @@ -129,54 +139,19 @@ applyClasses(); }); logoPre.addEventListener("mousedown", (e) => { - const key = keyFromMouse(e); + const key = keyFromPoint(e.clientX); if (key) { pressedKeys.add(key); render(); } }); - document.addEventListener("mouseup", () => { - if (pressedKeys.size === 0) return; - pressedKeys.clear(); - render(); - }); + document.addEventListener("mouseup", releaseAll); // Touch - logoPre.addEventListener( - "touchstart", - (e) => { - e.preventDefault(); - pressedKeys.clear(); - for (const touch of e.touches) { - const key = keyFromTouch(touch); - if (key) pressedKeys.add(key); - } - render(); - }, - { passive: false }, - ); - logoPre.addEventListener( - "touchmove", - (e) => { - e.preventDefault(); - pressedKeys.clear(); - for (const touch of e.touches) { - const key = keyFromTouch(touch); - if (key) pressedKeys.add(key); - } - render(); - }, - { passive: false }, - ); - document.addEventListener("touchend", () => { - if (pressedKeys.size === 0) return; - pressedKeys.clear(); - render(); - }); - document.addEventListener("touchcancel", () => { - pressedKeys.clear(); - render(); - }); + logoPre.addEventListener("touchstart", pressTouches, { passive: false }); + logoPre.addEventListener("touchmove", pressTouches, { passive: false }); + document.addEventListener("touchend", releaseAll); + document.addEventListener("touchcancel", releaseAll); // Keyboard — skip when focus is in a text input to avoid interfering with typing const KEYS = new Set(["h", "d", "i"]); @@ -211,9 +186,7 @@ .hero-logo-letter { color: var(--green); transition: all 150ms linear; - &.hovered { - color: var(--mauve); - } + &.hovered, &.pressed { color: var(--mauve); }