/** * ScrollToTop — fixed back-to-top button that fades in after scrolling down. * Toggled site-wide via `scrollToTop` in settings.yaml. */ 'use client'; import { useState, useEffect, useRef } from 'react'; const SHOW_THRESHOLD = 300; export function ScrollToTop() { const [visible, setVisible] = useState(false); const frame = useRef(null); useEffect(() => { const measure = () => { frame.current = null; setVisible(window.scrollY > SHOW_THRESHOLD); }; // rAF-throttled: one state update per frame, not one per scroll event. const onScroll = () => { if (frame.current === null) frame.current = requestAnimationFrame(measure); }; measure(); window.addEventListener('scroll', onScroll, { passive: true }); return () => { window.removeEventListener('scroll', onScroll); if (frame.current !== null) cancelAnimationFrame(frame.current); }; }, []); const scrollToTop = () => { const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; window.scrollTo({ top: 0, behavior: reduced ? 'auto' : 'smooth' }); }; return ( ); }