40 lines
1.2 KiB
TypeScript
40 lines
1.2 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect } from 'react';
|
|
import { usePathname } from 'next/navigation';
|
|
|
|
/**
|
|
* Adds `.is-in` to every `[data-reveal]` element as it scrolls into view, which
|
|
* drives the fade/rise defined in `globals.css`. Re-runs on client-side
|
|
* navigation so freshly mounted pages animate too. The hidden state is gated on
|
|
* `html.js`, so content stays visible without JavaScript, and
|
|
* `prefers-reduced-motion` shows everything immediately.
|
|
*/
|
|
export function RevealOnScroll() {
|
|
const pathname = usePathname();
|
|
|
|
useEffect(() => {
|
|
const els = Array.from(document.querySelectorAll<HTMLElement>('[data-reveal]:not(.is-in)'));
|
|
if (!els.length) return;
|
|
if (typeof IntersectionObserver === 'undefined') {
|
|
els.forEach((el) => el.classList.add('is-in'));
|
|
return;
|
|
}
|
|
const io = new IntersectionObserver(
|
|
(entries) => {
|
|
for (const entry of entries) {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('is-in');
|
|
io.unobserve(entry.target);
|
|
}
|
|
}
|
|
},
|
|
{ rootMargin: '0px 0px -6% 0px', threshold: 0.06 },
|
|
);
|
|
els.forEach((el) => io.observe(el));
|
|
return () => io.disconnect();
|
|
}, [pathname]);
|
|
|
|
return null;
|
|
}
|