Contenu sticky a gauche pendant que les cards a droite deroulent. Product tour style.
"use client";
import { useRef, useState, useEffect } from "react";
const features = [
{ title: "Feature 1", description: "Description..." },
{ title: "Feature 2", description: "Description..." },
];
export default function StickyScroll() {
const [activeIdx, setActiveIdx] = useState(0);
const cardRefs = useRef<(HTMLDivElement | null)[]>([]);
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const idx = cardRefs.current.indexOf(entry.target as HTMLDivElement);
if (idx >= 0) setActiveIdx(idx);
}
});
}, { threshold: 0.5 });
cardRefs.current.forEach((el) => el && observer.observe(el));
return () => observer.disconnect();
}, []);
return (
<div style={{ display: "flex" }}>
<div style={{ position: "sticky", top: 0, flex: 1 }}>
<h2>{features[activeIdx].title}</h2>
<p>{features[activeIdx].description}</p>
</div>
<div style={{ flex: 1 }}>
{features.map((f, i) => (
<div key={i} ref={el => { cardRefs.current[i] = el; }}>{f.title}</div>
))}
</div>
</div>
);
}Layout flex: contenu sticky a gauche (position: sticky, top: 0), cards scrollables a droite. L'IntersectionObserver detecte quelle card est visible et met a jour le contenu sticky. Ideal pour product tours.