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
+12
Mritunjay
Lead Architect
142
Commits
Sarah
UI Engineer
89
Commits
Alex
Core Contributor
54
Commits
Installation
Install the required dependencies:
npm install framer-motion lucide-reactlib/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>
);
}