Un fond lumineux (pill) glisse vers le tab actif avec un effet spotlight/glow.
"use client";
import { useState, useRef, useEffect } from "react";
export default function SpotlightTabs({ tabs }: { tabs: string[] }) {
const [active, setActive] = useState(0);
const [hovered, setHovered] = useState<number | null>(null);
const [pos, setPos] = useState({ left: 0, width: 0 });
const tabRefs = useRef<(HTMLButtonElement | null)[]>([]);
useEffect(() => {
const idx = hovered ?? active;
const el = tabRefs.current[idx];
if (el) {
const parent = el.parentElement!.getBoundingClientRect();
const rect = el.getBoundingClientRect();
setPos({ left: rect.left - parent.left, width: rect.width });
}
}, [active, hovered]);
return (
<div className="relative inline-flex bg-[#111] rounded-full p-1">
<div style={{
position: "absolute", top: 4, height: "calc(100% - 8px)",
left: pos.left, width: pos.width,
background: "rgba(225,255,108,0.1)",
boxShadow: "0 0 20px rgba(225,255,108,0.15)",
borderRadius: 9999,
transition: "left 0.3s, width 0.3s",
}} />
{tabs.map((tab, i) => (
<button key={i} ref={el => { tabRefs.current[i] = el; }}
onClick={() => setActive(i)}
onMouseEnter={() => setHovered(i)}
onMouseLeave={() => setHovered(null)}
style={{ color: active === i ? "#E1FF6C" : "#737373" }}>
{tab}
</button>
))}
</div>
);
}Variante de fluid-tabs avec un spotlight/glow. La pill suit le hover en priorite, puis revient au tab actif. Le boxShadow cree l'effet glow. Style pill rounded-full.