"use client";
interface MaskedAvatarsProps {
/** Array of image URLs or initials fallback strings */
avatars: Array<{ src?: string; name: string }>;
/** Max avatars to show before +N. Default: 5 */
max?: number;
/** Size in px. Default: 40 */
size?: number;
}
export function MaskedAvatars({ avatars, max = 5, size = 40 }: MaskedAvatarsProps) {
const visible = avatars.slice(0, max);
const overflow = avatars.length - max;
return (
<div className="flex items-center" role="group" aria-label={`${avatars.length} members`}>
{visible.map((avatar, i) => (
<div
key={i}
title={avatar.name}
className="relative rounded-full border-2 border-zinc-950 bg-gradient-to-br from-indigo-500 to-purple-600 flex items-center justify-center text-white font-bold overflow-hidden transition-transform duration-200 hover:scale-110 hover:z-10 cursor-pointer"
style={{
width: size,
height: size,
fontSize: size * 0.35,
marginLeft: i === 0 ? 0 : -(size * 0.3),
zIndex: visible.length - i,
}}
>
{avatar.src ? (
<img
src={avatar.src}
alt={avatar.name}
className="w-full h-full object-cover"
/>
) : (
<span>{avatar.name.slice(0, 2).toUpperCase()}</span>
)}
</div>
))}
{overflow > 0 && (
<div
className="relative rounded-full border-2 border-zinc-950 bg-zinc-800 flex items-center justify-center text-zinc-400 font-semibold"
style={{
width: size,
height: size,
fontSize: size * 0.32,
marginLeft: -(size * 0.3),
}}
>
+{overflow}
</div>
)}
</div>
);
}