Home / Snippets / UI Components /
Avatar
Circular user avatars with border-radius: 50%, size variants, initials fallback, status indicators, and overlapping avatar groups — pure CSS.
1.75rem
2.5rem
3.75rem
Quick implementation
.avatar {
--avatar-size: 2.5rem;
width: var(--avatar-size);
height: var(--avatar-size);
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: calc(var(--avatar-size) * 0.38);
color: oklch(0.98 0.005 260);
overflow: hidden;
flex-shrink: 0;
position: relative;
}
.avatar img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
border-radius: 50%;
}
/* Size variants */
.avatar--sm { --avatar-size: 1.75rem; }
.avatar--md { --avatar-size: 2.5rem; }
.avatar--lg { --avatar-size: 3.75rem; }
/* Status indicator */
.avatar--status::after {
content: '';
position: absolute;
bottom: 2px;
right: 2px;
width: calc(var(--avatar-size) * 0.27);
height: calc(var(--avatar-size) * 0.27);
border-radius: 50%;
border: 2px solid var(--bg);
}
.avatar--online::after { background: oklch(0.68 0.19 145); }
.avatar--offline::after { background: oklch(0.55 0.03 260); }
/* Avatar group */
.avatar-group {
display: flex;
}
.avatar-group .avatar {
border: 2px solid var(--bg);
margin-left: -0.6rem;
z-index: 1;
}
.avatar-group .avatar:first-child {
margin-left: 0;
z-index: 5;
}
Prompt this to your LLM
Includes role, constraints, two framework variants, and edge cases to handle.
You are a senior frontend engineer building a reusable avatar component.
Goal: A circular avatar component that handles three cases: an actual
image, an initials fallback, and a status indicator dot — all with
size variants controlled by a single CSS custom property.
Technical constraints:
- Use border-radius: 50% on an equal width and height to create a
perfect circle. Control size with a --avatar-size custom property
so a single property change updates width, height, font-size, and
the status dot proportionally.
- For image avatars, apply object-fit: cover so non-square images
fill the circle without distortion. Set overflow: hidden on the
.avatar element to clip the image to the circle boundary.
- For initials fallback, use a gradient background (oklch colors)
and display flex with align-items: center / justify-content: center.
Font size should scale with the avatar using calc(var(--avatar-size) * 0.38).
- Status indicator: use a ::after pseudo-element, positioned absolute
at bottom-right. Size it proportionally via calc(). Add a border
matching the page background color to create the gap effect between
dot and avatar edge.
- Avatar group (overlapping stack): use negative margin-left on all
but the first item. Apply increasing z-index so the leftmost avatar
appears on top. Add a border matching the page background to create
visible separation between overlapping circles.
- Use oklch() for all color values — no hex or rgba.
Framework variant (pick one):
A) Vanilla CSS classes (.avatar, .avatar--sm, .avatar--online, etc.)
with a matching HTML structure.
B) React component — accept src, initials, size ("sm" | "md" | "lg"),
status ("online" | "offline" | undefined) as props; derive the
correct class names internally.
Edge cases to handle:
- When both src and initials are provided, show the image and hide
initials (image takes priority). Handle broken images by listening
for the error event and falling back to initials display.
- The status dot border color must match the actual background behind
the avatar. If avatars appear on a card (not the page background),
pass the card color as a CSS custom property override.
- In an avatar group, the last item is often a count badge (e.g. "+4").
Style it with a muted background and smaller font — keep it in the
same .avatar wrapper so sizing and overlap are consistent.
Return CSS only (or a React component if variant B is chosen).
Why this matters in 2026
Avatar components are among the most reused UI elements across dashboards, social feeds, comment threads, and team directories. Getting them right in CSS — without reaching for an image-handling library — is a foundational skill. The pattern of using a CSS custom property for size means a design system can offer three or four size tokens (--avatar-sm, --avatar-md, --avatar-lg) and have every other property — font size, status dot size, border width — scale automatically. This is the composable approach that component libraries like Radix UI and shadcn/ui have standardised, but it requires zero JavaScript.
The logic
Why border-radius: 50% creates a circle. CSS border-radius accepts percentage values relative to the element's own dimensions. When applied to an element with equal width and height, 50% rounds every corner by exactly half the side length, meeting in the centre and producing a perfect circle. If the element is not square, 50% produces an ellipse — so equal dimensions are the prerequisite. Setting both via a single custom property (--avatar-size) guarantees the element stays square regardless of which size variant is used.
Object-fit for non-square images. Profile photos are rarely square. object-fit: cover tells the browser to scale the image so it covers the entire box, cropping the excess rather than distorting the image. Combined with overflow: hidden on the avatar container, the image is clipped to the circular boundary. The result is a centred crop — identical to how Twitter, GitHub, and Slack handle avatars. For more control over which part of the image is centred, object-position can shift the focal point (e.g. object-position: top for headshots).
The initials fallback pattern. When no image is available — whether because the user has not uploaded one or an image URL breaks — an initials fallback prevents an empty or broken box. The pattern is a flex container with centred content, a gradient background keyed to the user (often derived from their name or ID), and text showing their initials. Scaling font-size with calc(var(--avatar-size) * 0.38) keeps the letters proportional across all size variants with no extra CSS rules.
Avatar group overlap with negative margin and z-index. Overlapping avatars in a horizontal stack use negative margin-left on every item after the first to pull each circle partially under the previous one. The amount of overlap is a design decision — -0.6rem shows roughly three-quarters of each avatar. Z-index stacking ensures the leftmost avatar appears on top (visually first in reading order), achieved by assigning higher z-index values to earlier siblings via explicit declarations. A border matching the page background on each avatar creates a visible gap between circles, preventing them from merging into a single blob.
Accessibility & performance
Avatar images should have descriptive alt text — typically the person's name (alt="Jane Doe"). For purely decorative avatars where the name is already visible in adjacent text, alt="" is appropriate so screen readers skip the image. Initials-only avatars that convey identity should be wrapped in an element with aria-label equal to the full name, since two letters alone are not meaningful to assistive technology.
The status indicator ::after pseudo-element is invisible to screen readers. If the online/offline state is meaningful — for example in a messaging interface — add a visually hidden <span> inside the avatar element with text like "online" or "offline". The clip-path technique or the classic visually-hidden utility class both work.
Performance-wise, the component is negligible: a handful of CSS rules and no JavaScript. If you are rendering many avatars in a list, consider loading="lazy" on the <img> elements so off-screen avatars do not block initial load. The gradient backgrounds used for initials fallbacks are painted by the browser at render time — they produce no network requests and add zero bytes to page weight.