AvatarGroup
A group lays out a set of avatars so they overlap, and hands each of them its size. Two things about it are not the layout they look like: which avatar wins an overlap is paint order rather than position, so reversing it takes a stacking rule and not a margin; and the gap between two of them is not a gap at all but a ring in the colour of whatever is behind the group, which is the one thing the group cannot know for itself.
import { Avatar, AvatarGroup, AvatarGroupOverflow } from "ropav";<AvatarGroup>
<Avatar>
<AvatarFallback>JD</AvatarFallback>
</Avatar>
<Avatar>
<AvatarFallback>KM</AvatarFallback>
</Avatar>
<Avatar>
<AvatarFallback>EW</AvatarFallback>
</Avatar>
<AvatarGroupOverflow :count="5" />
</AvatarGroup>Which avatar is in front
front chooses that, and it never reorders anyone: both directions read left to right in the order they were written, and only the overlap changes hands.
avatar-group-front.vue
<script setup lang="ts">
import { Avatar, AvatarGroup, AvatarGroupOverflow, AvatarImage } from "ropav";
// Data URIs so the demo needs nothing from the network.
const portrait = (fill: string) =>
"data:image/svg+xml;utf8," +
encodeURIComponent(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect width="64" height="64" fill="${fill}" />
<circle cx="32" cy="25" r="12" fill="#ffffff" />
<circle cx="32" cy="58" r="19" fill="#ffffff" />
</svg>`,
);
const members = [
{ id: 1, src: portrait("#0485f7") },
{ id: 2, src: portrait("#7c5cff") },
{ id: 3, src: portrait("#12a594") },
{ id: 4, src: portrait("#e5484d") },
];
</script>
<template>
<AvatarGroup v-for="front in ['last', 'first'] as const" :key="front" :front="front">
<Avatar v-for="member in members" :key="member.id">
<AvatarImage :src="member.src" />
</Avatar>
<AvatarGroupOverflow :count="3" />
</AvatarGroup>
</template>"last" is free, being the order the document paints in already. "first" reverses it as far as ten avatars, past which the tail falls back — a group that long wants AvatarGroupOverflow rather than an eleventh face.
The ring
--avatar-group-ring-color is the surface showing through between two avatars. It defaults to the page, so a group on anything else has to be told what it is sitting on.
avatar-group-surface.vue
<script setup lang="ts">
import { Avatar, AvatarFallback, AvatarGroup, Card, CardContent } from "ropav";
const members = ["JD", "KM", "EW"];
</script>
<template>
<AvatarGroup>
<Avatar v-for="member in members" :key="member">
<AvatarFallback>{{ member }}</AvatarFallback>
</Avatar>
</AvatarGroup>
<Card>
<CardContent>
<AvatarGroup style="--avatar-group-ring-color: var(--rp-surface)">
<Avatar v-for="member in members" :key="member">
<AvatarFallback>{{ member }}</AvatarFallback>
</Avatar>
</AvatarGroup>
</CardContent>
</Card>
</template>Overlap and spacing
overlap is a share of one avatar rather than a distance, so the tuck reads the same at every size. overlap="none" spaces the avatars out instead and drops the ring with it, there being nothing left for it to cut through.
Accessibility
- The group takes no role of its own. It is a layout container, and an avatar beside the name it belongs to is decorative — so a boundary announced around one says nothing. Pass
role="group"and a name where the set really is the only thing on the page saying who is in it. - A link or button wrapping an avatar has nothing to be called:
altis empty by default, and a loaded image covers the initials the fallback would have carried. Name the link, not the image. AvatarGroupOverflowreads as+5. Write its slot where the number needs words around it.- With forced colours on, the ring goes — box shadows do. Each avatar's own edge separates it from the next one instead, so the stack stays legible.
API
AvatarGroup
| Prop | Type | Default | Description |
|---|---|---|---|
front | "last" | "first" | "last" | Which avatar is in front. "last" is the order the document paints in already, so it costs no stacking rule; "first" reverses it. |
orientation | "horizontal" | "vertical" | "horizontal" | Axis the avatars are laid out along. |
overlap | "none" | "sm" | "md" | "lg" | "md" | How far each avatar tucks under its neighbour, as a share of one avatar. "none" spaces them out instead, and drops the ring with it. |
size | "xs" | "sm" | "md" | "lg" | "md" | Size for every avatar in the group. Each avatar can still set its own. |
Slots: default.
AvatarGroupOverflow
| Prop | Type | Default | Description |
|---|---|---|---|
count | number | — | How many avatars are not shown, rendered as +count. Write the slot instead where the count needs words around it. |
Slots: default.