/** * useHeadingFlash — watches location.hash and applies a fading highlight * animation to the target heading element on every hash change. * * Handles page refresh / initial load by retrying up to ~500ms until the * target element exists in the DOM. */ import { createEffect, onCleanup } from "solid-js"; import { useLocation } from "@solidjs/router"; const FLASH_CLASS = "heading-flash"; const MAX_RETRIES = 10; const RETRY_INTERVAL = 50; function flashElement(el: HTMLElement) { // Re-trigger the animation: remove and re-add the class el.classList.remove(FLASH_CLASS); void el.offsetWidth; // force reflow el.classList.add(FLASH_CLASS); } export function useHeadingFlash() { const location = useLocation(); createEffect(() => { const hash = location.hash; if (!hash) return; const id = decodeURIComponent(hash.startsWith("#") ? hash.slice(1) : hash); if (!id) return; let retries = 0; let timer: ReturnType; const tryFlash = () => { const el = document.getElementById(id); if (el) { flashElement(el); el.addEventListener("animationend", function onEnd() { el.classList.remove(FLASH_CLASS); el.removeEventListener("animationend", onEnd); }, { once: true }); } else if (retries < MAX_RETRIES) { retries++; timer = setTimeout(tryFlash, RETRY_INTERVAL); } }; // Delay slightly so Solid has a chance to flush DOM updates from // a concurrent SPA navigation before we look for the element. timer = setTimeout(tryFlash, 0); onCleanup(() => clearTimeout(timer)); }); }