/* Reveal-on-scroll for browsers without scroll-driven timelines. ---------------------------------------------------------------------------- motion.css handles `.sh-reveal` with `animation-timeline: view()`, which is Chromium-only; elsewhere every band sits at rest, fully visible. This helper gives those browsers the same `.sh-rise` reveal: it tags each `.sh-reveal` element `.sh-reveal-js` (hidden), then flips it to `.sh-reveal-js--in` (the animation runs) once it scrolls into view. It is a progressive enhancement and is strictly opt-in per page: - Chromium already animates via CSS, so this returns early and never fights the scroll-driven version. - Reduced-motion visitors are left alone (the CSS never hides them). - With JS off, no classes are ever added, so content stays visible. Renders nothing on the server; call from a `useEffect` in the page. */ const SELECTOR = ".sh-reveal"; export function enableReveals(): void { if (typeof window === "undefined") return; if (!("IntersectionObserver" in window)) return; if ("CSS" in window && CSS.supports("animation-timeline: view()")) { /* Chromium already plays the reveal; nothing to add. */ return; } if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; const observer = new IntersectionObserver( (entries) => { for (const entry of entries) { if (!entry.isIntersecting) continue; const element = entry.target; element.classList.add("sh-reveal-js--in"); element.classList.remove("sh-reveal-js"); observer.unobserve(element); } }, { rootMargin: "0px 0px -8% 0px", threshold: 0.05 }, ); for (const element of document.querySelectorAll(SELECTOR)) { element.classList.add("sh-reveal-js"); observer.observe(element); } }