Skip to main content
Search
Search the documentation

Avatar stack

Overlapping avatars with a count for the rest - and a list a screen reader can read.

pnpm dlx shadcn-svelte@latest add https://coral.imlargo.dev/r/kit-avatar-stack.json

shadcn ships the overlap, AvatarGroup and AvatarGroupCount. What it leaves to each call site is the arithmetic: slice the list, count the rest, render +N. That arithmetic is usually written so that max={4} draws four avatars and a count, a fifth circle, and six people in five slots renders +1, a circle that hides exactly one avatar the same size as itself.

Amara Diallo
Wei Zhang
Sofia Rossi
Amara Diallo
Wei Zhang
Sofia Rossi
Liam O'Connor
Priya Sharma
Amara Diallo
Wei Zhang
Sofia Rossi
Liam O'Connor

What Coral adds

  • max is the width. It counts circles, the count included, so the space a layout reserves is the space the stack takes.
  • Never +1. When one person would be hidden, they are drawn instead.
  • A list: role="list", so a screen reader announces how many people there are before reading them, and each avatar names its person through kit/avatar.
  • Hidden people are not anonymous. The count is labelled ("3 more") and lists the hidden names on hover.
  • Any item shape: getPerson maps your own type onto name and photo, so a list of users does not have to be reshaped first.

Listing the rest

Amara Diallo
Wei Zhang

The overflow snippet receives the hidden items, the count and its label, enough to put the count in a popover or a tooltip. Render an AvatarGroupCount so it keeps its place in the overlap.

Import

Composes kit/avatar, which the install brings with it.

<script lang="ts">
	import AvatarStack from '#lib/components/coral/kit/avatar-stack/avatar-stack.svelte';
</script>

Props

Everything the shadcn avatar group accepts stays available: class, aria-*, ref. On top of that:

PropTypeDefaultDescription
itemsT[]-Who to show, in order. Required.
maxnumber-Circles drawn, count included. Omit to draw everyone.
getPerson(item: T) => { name, src, alt }identityMaps an item to what the avatar reads.
getKey(item: T, index: number) => unknownindexKeys each avatar.
size'sm' | 'default' | 'lg'defaultForwarded to every avatar; the count follows.
labelstring-Names the whole stack.
overflowLabel(count: number) => string{count} moreAccessible label for the count.
avatarSnippet<[{ item, index }]>-Replaces each avatar.
overflowSnippet<[{ hidden, count, label }]>-Replaces the count.

overflow.ts

import { split } from '#lib/components/coral/kit/avatar-stack/overflow.js';

split(['Amara', 'Wei', 'Sofia', 'Liam', 'Priya'], 4);
// { visible: ['Amara', 'Wei', 'Sofia'], hidden: ['Liam', 'Priya'] }