Social / React + CSS
Avatar stack
Overlapping profile chips fan out and lift on hover or focus.
Good work is a team sport.
4 people, one shared direction.
"use client";
import styles from "./AvatarStack.module.css";
import { useState } from "react";
const people = [
{
name: "Dianne",
initials: "DI",
color: "#d9cbe6",
role: "Design direction",
},
{ name: "Alex", initials: "AL", color: "#cbd8c0", role: "Product design" },
{ name: "Sam", initials: "SA", color: "#edcdb7", role: "Engineering" },
{ name: "Jo", initials: "JO", color: "#c9dfe8", role: "Research" },
];
export default function AvatarStack({ members = people, onSelect }) {
const [selected, setSelected] = useState(null);
return (
<div className={styles.root}>
<p className={styles.label}>Good work is a team sport.</p>
<div className={styles.stack}>
{members.map((person, index) => (
<button
key={person.name}
type="button"
aria-label={`View ${person.name}`}
aria-pressed={selected === person.name}
style={{ "--avatar": person.color, "--order": index }}
onClick={() => {
setSelected(person.name);
onSelect?.(person);
}}
>
<span className={styles.name}>{person.name}</span>
<span aria-hidden="true">{person.initials}</span>
</button>
))}
</div>
<p className={styles.selected} role="status">
{selected
? `${selected} · ${members.find((person) => person.name === selected)?.role}`
: `${members.length} people, one shared direction.`}
</p>
</div>
);
}
Make it yours
Use for a small team or group of collaborators. Replace the illustrative people with real users and connect onSelect to a profile view when appropriate. For large groups, show a count instead of crowding the row.
- Use a React project with CSS Modules support, such as Next.js or Vite.
- Save the JSX as
AvatarStack.jsxand the CSS asAvatarStack.module.cssin the same folder. - Import
AvatarStackand connect its props to your application. Controlled components need parent state and change handlers.
No third-party component or animation package is required. Use the theme controls to adjust accent, corners, spacing, and type. Copy the Theme CSS into your stylesheet and wrap your components in <div className="tdp-components">. The theme applies to every component inside that wrapper. TDP defaults work without a theme file.
Props
| Prop | Type | Use |
|---|---|---|
members | array | Unique name, initials, color, and role for each member. |
onSelect | function | Receives the selected member. |
Accessibility & behavior
Every profile is a named button. Hover names also appear on focus, and selection is announced. Initials are decorative because the button already has the full name.
Visible focus styles and reduced-motion support are included. Check labels, contrast, keyboard behavior, and screen-reader output again after adapting the component to your product.