User Spotlight

The UserSpotlight is a high-end showcase component for ForkUI. It uses Framer Motion to handle staggered entry animations and layout transitions. Designed with an organic-industrial feel, it features a high-contrast monochrome profile layout that prioritizes typography and user-presence, avoiding standard social card cliches for a more architectural look.

framer-motionlucide-react

ForkUI // Team Alpha-Alpha

Contributors

MritunjaySarahAlex
+12
Mritunjay

Mritunjay

Lead Architect

142

Commits

Sarah

Sarah

UI Engineer

89

Commits

Alex

Alex

Core Contributor

54

Commits

Installation

Install the required dependencies:

npm install framer-motion lucide-react
lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}
components/ui/user-spotlight.tsx
"use client";
import React, { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import {
  ShieldCheck,
  Zap,
  Star,
  ChevronRight,
  Mail,
  UserPlus,
  Ban,
} from "lucide-react";

const USERS = [
  {
    name: "Mritunjay",
    role: "Lead Architect",
    img: "https://api.dicebear.com/7.x/avataaars/svg?seed=Mritunjay",
    status: "Active",
    commits: 142,
    color: "text-emerald-500",
    pulse: [20, 40, 30, 70, 50, 80],
  },
  {
    name: "Sarah",
    role: "UI Engineer",
    img: "https://api.dicebear.com/7.x/avataaars/svg?seed=Sarah",
    status: "Active",
    commits: 89,
    color: "text-amber-500",
    pulse: [40, 20, 50, 30, 60, 40],
  },
  {
    name: "Alex",
    role: "Core Contributor",
    img: "https://api.dicebear.com/7.x/avataaars/svg?seed=Alex",
    status: "Idle",
    commits: 54,
    color: "text-zinc-400",
    pulse: [10, 15, 10, 12, 10, 8],
  },
];

export function UserSpotlight() {
  const [selectedUser, setSelectedUser] = useState<string | null>(null);

  const handleInvite = () => {
    console.log("Invite protocol initiated...");
  };

  return (
    <div className="p-8 rounded-[2.5rem] bg-white dark:bg-zinc-950 border border-zinc-200 dark:border-zinc-900 shadow-xl overflow-hidden relative group">
      {/* HEADER */}
      <div className="flex items-end justify-between mb-10 px-2">
        <div>
          <h3 className="text-[10px] font-black uppercase tracking-[0.3em] text-zinc-400 mb-1">
            ForkUI // Team Alpha-Alpha
          </h3>
          <h2 className="text-2xl font-black tracking-tightest uppercase italic opacity-80">
            Contributors
          </h2>
        </div>
        <div className="flex -space-x-3">
          {USERS.map((u) => (
            <motion.img
              key={u.name}
              whileHover={{ y: -5, zIndex: 10 }}
              src={u.img}
              className="w-8 h-8 rounded-full border-2 border-white dark:border-zinc-950 bg-zinc-100 dark:bg-zinc-800 cursor-default"
              alt={u.name}
            />
          ))}
          <div className="w-8 h-8 rounded-full bg-zinc-100 dark:bg-zinc-800 border-2 border-white dark:border-zinc-950 flex items-center justify-center text-[8px] font-black">
            +12
          </div>
        </div>
      </div>

      {/* USER LIST */}
      <div className="space-y-3">
        {USERS.map((user, i) => {
          const isSelected = selectedUser === user.name;
          return (
            <motion.div
              key={user.name}
              initial={{ opacity: 0, x: -10 }}
              animate={{ opacity: 1, x: 0 }}
              transition={{ delay: i * 0.1 }}
              whileHover={{ scale: 1.02, x: 5 }}
              whileTap={{ scale: 0.98 }}
              className={`group relative overflow-hidden rounded-3xl border transition-all  ${
                selectedUser === user.name
                  ? "border-emerald-500 bg-emerald-50/50 dark:bg-emerald-500/5 shadow-lg shadow-emerald-500/5"
                  : "border-zinc-100 dark:border-zinc-900 bg-zinc-50/50 dark:bg-zinc-900/30 hover:bg-white dark:hover:bg-zinc-900 hover:shadow-xl hover:shadow-emerald-500/5"
              }`}
            >
              <div
                onClick={() => setSelectedUser(isSelected ? null : user.name)}
                className="flex items-center justify-between p-4 cursor-pointer"
              >
                <div className="flex items-center  gap-4">
                  {/* AVATAR SYSTEM */}
                  <div className="relative">
                    <div className="w-12 h-12 rounded-2xl overflow-hidden bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-800">
                      <img
                        src={user.img}
                        className="w-full h-full object-cover"
                        alt={user.name}
                      />
                    </div>
                    <div
                      className={`absolute -bottom-1 -right-1 w-4 h-4 rounded-full border-2 border-white dark:border-zinc-950 flex items-center justify-center bg-white dark:bg-zinc-900 shadow-sm`}
                    >
                      <div
                        className={`w-1.5 h-1.5 rounded-full ${user.status === "Active" ? "bg-emerald-500 animate-pulse" : "bg-zinc-400"}`}
                      />
                    </div>
                  </div>

                  {/* IDENTITY */}
                  <div>
                    <div className="flex items-center gap-1.5">
                      <p className="text-sm font-black tracking-tight dark:text-white">
                        {user.name}
                      </p>
                      {user.commits > 100 && (
                        <Star
                          size={10}
                          className="fill-amber-400 text-amber-400"
                        />
                      )}
                    </div>
                    <p className="text-[9px] font-bold text-zinc-400 uppercase tracking-widest">
                      {user.role}
                    </p>
                  </div>
                </div>

                {/* MINI ACTIVITY GRAPH */}
                <div className="hidden md:flex items-end gap-0.5 h-6 px-4">
                  {user.pulse.map((h, idx) => (
                    <div
                      key={idx}
                      className={`w-1 rounded-full transition-all duration-500 ${user.status === "Active" ? "bg-emerald-500/40 group-hover:bg-emerald-500" : "bg-zinc-300 dark:bg-zinc-700"}`}
                      style={{ height: `${h}%` }}
                    />
                  ))}
                </div>

                {/* STATS & ACTION */}
                <div className="flex items-center gap-4">
                  <div className="text-right">
                    <p className="text-[10px] font-black font-mono">
                      {user.commits}
                    </p>
                    <p className="text-[8px] font-bold text-zinc-400 uppercase tracking-tighter">
                      Commits
                    </p>
                  </div>
                  <div
                    className={`p-2 rounded-xl transition-colors ${
                      selectedUser === user.name
                        ? "bg-emerald-500 text-white"
                        : "bg-zinc-100 dark:bg-zinc-800 text-zinc-400 group-hover:text-zinc-900 dark:group-hover:text-white"
                    }`}
                  >
                    <ChevronRight
                      size={14}
                      className={
                        selectedUser === user.name
                          ? "rotate-90 transition-transform"
                          : ""
                      }
                    />
                  </div>
                </div>
              </div>
              <AnimatePresence>
                {isSelected && (
                  <motion.div
                    initial={{ height: 0, opacity: 0 }}
                    animate={{ height: "auto", opacity: 1 }}
                    exit={{ height: 0, opacity: 0 }}
                    className="border-t border-emerald-500/10 dark:border-emerald-500/10 bg-white/50 dark:bg-zinc-950/50"
                  >
                    <div className="p-3 grid grid-cols-3 gap-2">
                      <button className="flex flex-col items-center justify-center py-3 rounded-2xl hover:bg-zinc-100 dark:hover:bg-zinc-900 transition-colors gap-1.5">
                        <Mail size={14} className="text-zinc-400" />
                        <span className="text-[8px] font-black uppercase tracking-tighter">
                          Email
                        </span>
                      </button>
                      <button className="flex flex-col items-center justify-center py-3 rounded-2xl hover:bg-zinc-100 dark:hover:bg-zinc-900 transition-colors gap-1.5">
                        <UserPlus size={14} className="text-zinc-400" />
                        <span className="text-[8px] font-black uppercase tracking-tighter">
                          Assign
                        </span>
                      </button>
                      <button className="flex flex-col items-center justify-center py-3 rounded-2xl hover:bg-rose-500/10 transition-colors gap-1.5 group/btn">
                        <Ban
                          size={14}
                          className="text-zinc-400 group-hover/btn:text-rose-500"
                        />
                        <span className="text-[8px] font-black uppercase tracking-tighter group-hover/btn:text-rose-500">
                          Revoke
                        </span>
                      </button>
                    </div>
                  </motion.div>
                )}
              </AnimatePresence>
            </motion.div>
          );
        })}
      </div>

      {/* FOOTER ACTION */}
      <button
        onClick={handleInvite}
        className="w-full mt-6 py-3 rounded-2xl border border-dashed border-zinc-200 dark:border-zinc-800 text-[10px] font-black uppercase tracking-[0.2em] text-zinc-400 hover:text-emerald-500 hover:border-emerald-500/50 hover:bg-emerald-500/5 transition-all active:scale-[0.98]"
      >
        + Invite Contributor
      </button>
    </div>
  );
}