mirror of
https://github.com/R0m1k3/PleinR.git
synced 2026-10-23 07:08:05 +02:00
- Composant Reveal (IntersectionObserver) + keyframes CSS : sections et grilles apparaissent en fondu/glissement, cartes en cascade (stagger). Respecte prefers-reduced-motion + repli no-JS. - Étoiles (sparkles) : l'animation twinkle inclut désormais une dérive (translation) pour un mouvement nettement visible. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XwfhYHzC9hQWPDnQ2p53GL
52 lines
1.2 KiB
TypeScript
52 lines
1.2 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useState, type CSSProperties, type ReactNode } from "react";
|
|
|
|
/**
|
|
* Wraps content and reveals it (via the `is-visible` class) when it scrolls into
|
|
* view. Pair with the `.reveal` or `.reveal-stagger` CSS classes. Respects
|
|
* prefers-reduced-motion (CSS forces content visible there).
|
|
*/
|
|
export function Reveal({
|
|
children,
|
|
className = "",
|
|
style,
|
|
id,
|
|
}: {
|
|
children: ReactNode;
|
|
className?: string;
|
|
style?: CSSProperties;
|
|
id?: string;
|
|
}) {
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
const [visible, setVisible] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const el = ref.current;
|
|
if (!el) return;
|
|
if (typeof IntersectionObserver === "undefined") {
|
|
setVisible(true);
|
|
return;
|
|
}
|
|
const obs = new IntersectionObserver(
|
|
(entries) => {
|
|
entries.forEach((e) => {
|
|
if (e.isIntersecting) {
|
|
setVisible(true);
|
|
obs.disconnect();
|
|
}
|
|
});
|
|
},
|
|
{ threshold: 0.12, rootMargin: "0px 0px -8% 0px" }
|
|
);
|
|
obs.observe(el);
|
|
return () => obs.disconnect();
|
|
}, []);
|
|
|
|
return (
|
|
<div ref={ref} id={id} className={`${className}${visible ? " is-visible" : ""}`} style={style}>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|