Skip to main content

Avatar Group

Info:Media
18

A row of overlapping avatars with a trailing `+N` overflow chip, for a contributor, attendee, or reviewer stack.

avatar stack face pile overlapping avatars contributors overflow

Live demo

live · @xtyle/astro

Avatar Group

A contributor stack

Slot the avatars to show and set overflow to the number of people beyond them; the group overlaps the faces and caps the row with a +N chip. Each avatar keeps its own tone. Hover a covered avatar to raise it to the front.

AL AT GH KJ ED +12

Sizes

AL AT GH +8
size="sm"
AL AT GH +8
size="md"
AL AT GH +8
size="lg"
AL AT GH +8
size="xl"

Spacing

AL AT GH KJ ED
spacing="snug"
AL AT GH KJ ED
spacing="normal"
AL AT GH KJ ED
spacing="loose"

No overflow

Omit overflow (or set it to zero) and the group is just the overlapping faces, no chip.

AL AT GH

Avatar Group overlaps a set of Avatar children into a compact stack, the shape a contributor list, an attendee row, or a "who's viewing" strip takes. Each avatar carries a ring in the page background so the overlap reads as distinct faces rather than a blur, and later avatars sit over earlier ones.

Slot the avatars you want shown and set overflow to the number of people beyond them, and the group renders a trailing +N chip in the neutral tone (its accessible name reads "N more"). size matches the chip to the avatars you used, and spacing tightens or loosens the overlap. It's a role="group"; give it a label to name the set. The overflow count is explicit rather than auto-counted so the stack renders identically with no JavaScript, on the server, and in the browser.

When to use

How this component composes with the rest of the set.

Slot the avatars to show and pass overflow as the remainder: a consumer holding 20 people shows the first 4 (avatars.slice(0, 4)) and sets overflow={16}, so the row reads ● ● ● ● +16.
Each avatar keeps its own tone, src, shape, and size; the group only lays them out, so a group of image avatars, initial-fallback avatars, or a mix all stack the same way.
The ring around each avatar is the page background (--bg-0), so a group over a Panel or Card needs no change: it's the surface token the algorithm already produced. Pair it with a Cluster to sit a group beside a count or a label.
Because the faces overlap, a leading avatar is partly covered by the next; hovering or focusing one raises it above its neighbours so its full face (and initials) reads, no attribute needed.

Props

4 props, straight from the manifest.

PropTypeDefaultBindingsDescription
overflow number
html svelte astro
How many people beyond the slotted avatars to summarize. A positive value renders a trailing `+N` chip (named "N more" for assistive tech); zero or omitted renders no chip. Explicit rather than auto-counted, so the stack is identical with no JavaScript.
size "sm" | "md" | "lg" | "xl"
sm md lg xl
md
html svelte astro
Sizes the `+N` chip to match the avatars you slotted (each `Avatar` still sets its own size).
spacing "snug" | "normal" | "loose"
snug normal loose
normal
html svelte astro
How far the avatars overlap: `snug` stacks them tighter, `loose` spreads them out.
label string
html svelte astro
An accessible name for the group, since it is a set of people (e.g. "Contributors").

Appearance

Sizes

sm

.xtyle-avatar-group--sm

Compact, for dense lists.

md

default
.xtyle-avatar-group

Default.

lg

.xtyle-avatar-group--lg

Large, for headers.

xl

.xtyle-avatar-group--xl

Extra large.

States

avatar-hover

.xtyle-avatar-group ::slotted(*:hover)

Hovering or focusing an avatar raises it above its neighbours so a covered face reads in full.

Anatomy

The named parts that make up the component, with their selectors.

group

.xtyle-avatar-group

The role="group" row that overlaps its slotted avatars.

--space-3

overflow

.xtyle-avatar-group__overflow

The trailing +N chip summarizing the avatars beyond the shown set.

--neutral-bg --neutral-text --bg-0 --border-thick --radius-full

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 18/18 consumed tokens produced default register: 299 tokens

Live coverage check against the xtyle-default register (derive(xtyleDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--bg-0 --border-thick --font-sans --leading-tight --neutral-bg --neutral-text --radius-full --space-2 --space-3 --space-4 --space-6 --space-7 --space-8 --text-body --text-lg --text-sm --text-xs --weight-semibold

Slots

default
html svelte astro

The Avatar children to overlap, in stacking order (the first sits at the bottom of the stack).

Accessibility

The row is a role="group"; a label names it so assistive tech announces the set ("Contributors").
The +N chip is a role="img" with an accessible name of "N more", so the hidden count is spoken rather than read as a bare +16 glyph.
Each slotted Avatar keeps its own accessible name (its alt or status), so the people in the stack are individually announced.

Code

A contributor stack

Overlapping avatars with a +N chip for the rest; each keeps its own tone.

<xtyle-avatar-group label="Contributors" overflow="3">
	<xtyle-avatar alt="Ada Lovelace" tone="accent">AL</xtyle-avatar>
	<xtyle-avatar alt="Alan Turing" tone="success">AT</xtyle-avatar>
	<xtyle-avatar alt="Grace Hopper" tone="warn">GH</xtyle-avatar>
	<xtyle-avatar alt="Katherine Johnson" tone="info">KJ</xtyle-avatar>
</xtyle-avatar-group>
<script lang="ts">
	import { AvatarGroup, Avatar } from "@xtyle/svelte";

	const people = [
		{ name: "Ada Lovelace", initials: "AL", tone: "accent" },
		{ name: "Alan Turing", initials: "AT", tone: "success" },
		{ name: "Grace Hopper", initials: "GH", tone: "warn" },
		{ name: "Katherine Johnson", initials: "KJ", tone: "info" },
	];
	const shown = people.slice(0, 4);
</script>

<AvatarGroup label="Contributors" overflow={people.length - shown.length + 3}>
	{#each shown as p}
		<Avatar alt={p.name} tone={p.tone}>{p.initials}</Avatar>
	{/each}
</AvatarGroup>
---
import { AvatarGroup, Avatar } from "@xtyle/astro";
const people = [
	{ name: "Ada Lovelace", initials: "AL", tone: "accent" },
	{ name: "Alan Turing", initials: "AT", tone: "success" },
	{ name: "Grace Hopper", initials: "GH", tone: "warn" },
	{ name: "Katherine Johnson", initials: "KJ", tone: "info" },
];
---

<AvatarGroup label="Contributors" overflow={3}>
	{people.map((p) => <Avatar alt={p.name} tone={p.tone}>{p.initials}</Avatar>)}
</AvatarGroup>

Sizes and spacing

size matches the chip to the avatars and spacing="snug" tightens the overlap for a dense viewer strip.

<xtyle-avatar-group size="sm" spacing="snug" overflow="12" label="Viewers">
	<xtyle-avatar alt="Ada" tone="accent" size="sm">AD</xtyle-avatar>
	<xtyle-avatar alt="Alan" tone="success" size="sm">AL</xtyle-avatar>
	<xtyle-avatar alt="Grace" tone="warn" size="sm">GR</xtyle-avatar>
</xtyle-avatar-group>
<script lang="ts">
	import { AvatarGroup, Avatar } from "@xtyle/svelte";
</script>

<AvatarGroup size="sm" spacing="snug" overflow={12} label="Viewers">
	<Avatar alt="Ada" tone="accent" size="sm">AD</Avatar>
	<Avatar alt="Alan" tone="success" size="sm">AL</Avatar>
	<Avatar alt="Grace" tone="warn" size="sm">GR</Avatar>
</AvatarGroup>
---
import { AvatarGroup, Avatar } from "@xtyle/astro";
---

<AvatarGroup size="sm" spacing="snug" overflow={12} label="Viewers">
	<Avatar alt="Ada" tone="accent" size="sm">AD</Avatar>
	<Avatar alt="Alan" tone="success" size="sm">AL</Avatar>
	<Avatar alt="Grace" tone="warn" size="sm">GR</Avatar>
</AvatarGroup>