The WeightedBubble component displays a group of user avatars arranged in a bubble layout where each avatar's size and position is determined by its weight. Higher-weighted users appear larger and closer to the center, creating a visual hierarchy that represents relative importance or activity.
Pass an array of users with weights to create a weighted bubble visualization.
import { WeightedBubble } from "@228-co/reef/components";
import type { WeightedUserDetails } from "@228-co/reef/user-details";
const users: WeightedUserDetails[] = [
{ avatarId: "user1", displayName: "Alice", weight: 50 },
{ avatarId: "user2", displayName: "Bob", weight: 30 },
{ avatarId: "user3", displayName: "Charlie", weight: 20 },
{ avatarId: "user4", displayName: "Diana", weight: 15 },
{ avatarId: "user5", displayName: "Eve", weight: 10 },
];
<WeightedBubble users={users} />With a single user, the avatar takes center position at maximum size.
const singleUser: WeightedUserDetails[] = [
{ avatarId: "solo", displayName: "Solo User", weight: 100 },
];
<WeightedBubble users={singleUser} />With two users, the higher-weighted user is centered and larger.
const twoUsers: WeightedUserDetails[] = [
{ avatarId: "first", displayName: "First User", weight: 70 },
{ avatarId: "second", displayName: "Second User", weight: 30 },
];
<WeightedBubble users={twoUsers} />When more than 8 users are provided, the component displays the top 7 by weight and groups the remaining users into a "+N" indicator. The overflow indicator's size is proportional to the combined weight of the hidden users.
const manyUsers: WeightedUserDetails[] = [
{ avatarId: "user1", displayName: "Alice", weight: 100 },
{ avatarId: "user2", displayName: "Bob", weight: 80 },
{ avatarId: "user3", displayName: "Charlie", weight: 60 },
// ... 9 more users
];
// Displays top 7 users + "+5" indicator
<WeightedBubble users={manyUsers} />| Name | Type | Default | Description |
|---|---|---|---|
users | WeightedUserDetails[] | required | Array of users with weights. Each user has avatarId, displayName, optional profilePicture, optional presence, and a weight number. |
Each user in the array must conform to the WeightedUserDetails type, which extends the base UserDetails with a weight property.
| Name | Type | Default | Description |
|---|---|---|---|
avatarId | string | required | Unique identifier for the user's avatar. |
displayName | string | required | The user's display name, shown in tooltips and aria labels. |
profilePicture | string | undefined | URL to the user's profile picture. |
presence | boolean | undefined | Whether the user is currently online/present. |
weight | number | required | Numeric weight determining the user's visual prominence. Higher weights result in larger avatars positioned closer to the center. |
The WeightedBubble uses a circle-packing algorithm to position avatars:
Center placement - The highest-weighted user is placed at the center of the container.
Size scaling - Avatar sizes are proportional to the square root of their weight ratio (area-based scaling).
Collision detection - Each avatar is positioned to avoid overlapping with existing avatars while staying as close to the center as possible.
Size constraints - Avatars have minimum (15%) and maximum (50%) size constraints relative to the container.
The component includes an aria-label that lists all user display names in the group, ensuring screen readers can announce the full group membership.
// Generated aria-label example: // "group: Alice, Bob, Charlie, Diana, Eve"
Avatar - The underlying avatar component used for each user.
AvatarList - For displaying avatars in a linear horizontal list.
MultipleAvatars - For displaying stacked/overlapping avatars.