Animations/Spotlight Navigation Tabs

Spotlight Navigation Tabs

Un fond lumineux (pill) glisse vers le tab actif avec un effet spotlight/glow.

clickhoverCSStabsglow
Trigger: Click + Hover
CSS requis: Non
Difficulte: medium

Preview live

Code

"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>
  );
}

Instructions

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.