ComponentsAccept urls come stringhe o {src, alt}. Ring-2 ring-bg per gap automatico chiaro/scuro.
SocialList
Esempio01
avatar-circles.tsx 001Default · 4 visible002Team list · with overflowJoined this week · 24 people 003Compact · max 3 tsxsrc/components/avatar-circles.tsx
import { cn } from "@/lib/utils";
type AvatarShape = string | { src: string; alt?: string };
export type AvatarCirclesProps = {
/** Total people the row represents (used for the +N pill). */
numPeople?: number;
/** Avatars to render. Accepts either string URLs or `{ src, alt }`. */
avatarUrls: AvatarShape[];
/** How many avatars to show before collapsing to a +N pill. Default 4. */
max?: number;
className?: string;
};
const normalize = (value: AvatarShape): { src: string; alt: string } =>
typeof value === "string"
? { src: value, alt: "" }
: { src: value.src, alt: value.alt ?? "" };
/**
* <AvatarCircles/> — row of overlapping circular avatars with an optional
* `+N` pill for the overflow. Rings are `ring-bg` so the gaps adapt to
* dark/light theme automatically.
*/
export function AvatarCircles({
numPeople,
avatarUrls,
max = 4,
className,
}: AvatarCirclesProps) {
const visible = avatarUrls.slice(0, max).map(normalize);
const total = numPeople ?? avatarUrls.length;
const overflow = Math.max(0, total - visible.length);
return (
<div className={cn("flex items-center", className)}>
{visible.map((a, i) => (
// eslint-disable-next-line @next/next/no-img-element
<img
key={`${a.src}-${i}`}
src={a.src}
alt={a.alt}
width={40}
height={40}
className={cn(
"h-10 w-10 rounded-full border-2 border-bg bg-bg-alt object-cover",
i > 0 && "-ml-2.5",
)}
/>
))}
{overflow > 0 && (
<span className="-ml-2.5 inline-flex h-10 w-10 items-center justify-center rounded-full border-2 border-bg bg-bg-alt font-mono text-xs text-fg-muted">
+{overflow}
</span>
)}
</div>
);
}
Note — max controlla quanti avatar prima del fallback +N. Niente next/image per restare framework-agnostic dentro il libreria.
Prompt LLM02
Incolla in Claude o ChatGPT per generare la tua variante. Include il contesto del brand, i token e i vincoli del progetto.
Sei un senior frontend engineer. Stai lavorando su un sito Next.js 16 + React 19 + Tailwind v4 in italiano, look chanhdai-inspired: colonna stretta 672px, Geist Sans + Geist Mono, hairline 1px, divisori a stripe diagonale, palette zinc.
Token CSS disponibili: --bg, --bg-alt, --fg, --fg-muted, --fg-soft, --border, --border-strong, --accent. Usa SEMPRE queste variabili tramite le utility tailwind generate (bg-bg, text-fg-muted, border-border, ecc.). Helper "cn" da "@/lib/utils". Niente librerie UI extra: solo lucide-react e tailwind-merge.
Genera <AvatarCircles> riga di avatar overlap + pill +N.
Props:
- numPeople?: number (totale rappresentato).
- avatarUrls: (string | { src; alt? })[].
- max?: number (default 4).
- className?.
Implementazione:
- Server component. Helper normalize per accettare entrambe le shape.
- Visible = avatarUrls.slice(0, max).map(normalize).
- Avatar: <img> 40x40 rounded-full border-2 border-bg bg-bg-alt object-cover. Da i>0 → -ml-2.5.
- Pill +N (se overflow > 0): -ml-2.5 inline-flex h-10 w-10 rounded-full border-2 border-bg bg-bg-alt font-mono 12px text-fg-muted.
Output: file completo src/components/avatar-circles.tsx.Uso tipico03
<AvatarCircles numPeople={42} avatarUrls={["/a.jpg", "/b.jpg"]} max={4} />
Dipendenze04
Ti è servito? Dimmelo, oppure proponi il prossimo componente.