Styleframe Logo
Layout

Avatar Group

A stacked, overlapping set of avatars with a separating ring and an optional "+N" overflow count.

Overview

The Avatar Group lays out multiple Avatar items in an overlapping stack — useful for showing the members of a team, project, or conversation in a compact space. The useAvatarGroupRecipe() composable creates a recipe that overlaps its avatar children, draws a ring (in the page background color) around each so they read as separate, and exposes a size axis that controls the overlap amount.

The recipe coordinates its children with CSS only — it targets > .avatar items directly, so any standard Avatar becomes a stack member with no extra wiring. A trailing "+N" overflow indicator is simply another avatar.

Why use the Avatar Group recipe?

The Avatar Group recipe helps you:

  • Show many people in little space: Overlap avatars into a compact, scannable stack.
  • Keep avatars legible: Each item gets a background-colored ring so overlapping edges stay distinct.
  • Scale the overlap: A size axis matches the avatars' size and tightens or loosens the overlap accordingly.
  • Handle overflow: Add a "+N" count as a trailing neutral avatar to summarize hidden members.

Usage

Register the recipes

Register the Avatar Group recipe alongside the Avatar recipe — the group styles the .avatar children it contains:

src/components/avatar-group.styleframe.ts
import { styleframe } from 'virtual:styleframe';
import { useAvatarRecipe, useAvatarGroupRecipe } from '@styleframe/theme';

const s = styleframe();

const avatar = useAvatarRecipe(s);
const avatarGroup = useAvatarGroupRecipe(s);

export default s;

Build the component

Wrap a set of avatars with the avatarGroup() class. Give the group and its avatars a matching size so the overlap is proportional:

src/components/AvatarGroup.tsx
import { avatarGroup, type AvatarGroupProps } from "virtual:styleframe";

export function AvatarGroup({
    children,
    ...variants
}: AvatarGroupProps & { children?: React.ReactNode }) {
    return <div className={avatarGroup(variants)}>{children}</div>;
}

See it in action

Sizes

The group's size controls the overlap amount and should match the size of the avatars inside it. Larger avatars overlap by a larger margin.

SizeOverlap
xs-0.375rem
sm-0.5rem
md-0.625rem
lg-0.75rem
xl-1rem

Overflow count

When there are more members than you want to show, render a trailing "+N" avatar to summarize the rest. It is just another .avatar (neutral, soft) inside the group, so it inherits the ring and overlap automatically.

src/components/MemberStack.vue
<div :class="avatarGroup({ size: 'md' })">
    <span :class="avatar({ size: 'md' })"><img src="/users/ada.jpg" alt="Ada" /></span>
    <span :class="avatar({ size: 'md' })"><img src="/users/alan.jpg" alt="Alan" /></span>
    <span :class="avatar({ size: 'md', color: 'neutral', variant: 'soft' })">+3</span>
</div>

Accessibility

  • Label the group with aria-label (e.g. aria-label="Project members") so its purpose is announced.
  • Keep each avatar's alt meaningful — the names are the content; the stacking is purely visual.
  • Describe the overflow in text (e.g. aria-label="3 more members") rather than relying on the "+N" visual alone.

Customization

Overriding Defaults

src/components/avatar-group.styleframe.ts
const avatarGroup = useAvatarGroupRecipe(s, {
    defaultVariants: { size: 'lg' },
});

Filtering Variants

src/components/avatar-group.styleframe.ts
const avatarGroup = useAvatarGroupRecipe(s, {
    filter: {
        size: ['sm', 'md', 'lg'],
    },
});
Good to know: The ring color is @color.background, so overlapping avatars are separated by a cut-out that adapts to light and dark mode automatically.

API Reference

useAvatarGroupRecipe(s, options?)

Creates the avatar group recipe. Registers selectors that add a ring to each > .avatar, lift the hovered avatar, and apply a per-size negative margin for the overlap.

Parameters:

ParameterTypeDescription
sStyleframeThe Styleframe instance
optionsDeepPartial<RecipeConfig>Optional overrides for the recipe configuration
options.baseVariantDeclarationsBlockCustom base styles
options.variantsVariantsCustom variant definitions
options.defaultVariantsRecord<keyof Variants, string>Default variant values
options.filterRecord<string, string[]>Limit which variant values are generated

Variants:

VariantOptionsDefault
sizexs, sm, md, lg, xlmd

Learn more about recipes ->

Best Practices

  • Match the group size to the avatars inside it so the overlap is proportional.
  • Cap the stack at a handful of avatars and use the "+N" overflow for the rest.
  • Order by relevance: the first avatar sits on top of the stack visually — lead with the most important member.
  • Use images for distinct people; the ring keeps overlapping faces legible.

FAQ