Skip to content

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.

ts
import { Avatar, AvatarGroup, AvatarGroupOverflow } from "ropav";
JDKMEW+5
size
overlap
front
orientation
template
<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.

+3
+3
avatar-group-front.vue
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.

JDKMEW
JDKMEW
avatar-group-surface.vue
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: alt is empty by default, and a loaded image covers the initials the fallback would have carried. Name the link, not the image.
  • AvatarGroupOverflow reads 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

PropTypeDefaultDescription
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

PropTypeDefaultDescription
countnumber—How many avatars are not shown, rendered as +count. Write the slot instead where the count needs words around it.

Slots: default.