AntaAvatar
Switch to dark theme
Search documentation
On this page

Avatar

A userpic container. Avatar shows one of three things, in priority order: a supplied image, a seed-generated placeholder figure, or initials. The generated figure imitates a framed portrait — a head and shoulders over a colored background — with no face or gender indicators, so an app can fill an empty slot with a distinct, on-brand picture per user without an uploaded photo.

An alphanumeric seed drives the result deterministically: the same seed always renders the same avatar, and different seeds are distinguishable. A generator config caps each dimension to your brand.

Playground
Preview
PropsCodeCSS

Generated userpic

Pass a seed (a stable user id) and a name. With no generator, each seed draws a varied figure: background, head, and body colors, the figure’s scale, the head and shoulder angles, and the corner radius of each shape all move with the seed, while the head and body hues stay coordinated with the background.

<Avatar seed="user-42" name="Vlad Korobov" />
<Avatar seed="lena-r" name="Lena Rossi" />
<Avatar seed="8f21ac" name="Sam Okafor" />

Initials

Turn the shape dimensions off in the generator and the avatar falls back to initials — the first letter of each of the first three words of name — over the generated background. The background color still varies with the seed.

const initials = {
headRadiusTop: { mode: 'off' },
headRadiusBottom: { mode: 'off' },
bodyBorderRadius: { mode: 'off' },
}
<Avatar name="Vlad Korobov" generator={initials} />
<Avatar name="Marie Skłodowska Curie" generator={initials} />
<Avatar name="Madonna" generator={initials} />

Image and badge

Set src to show an uploaded photo instead of a generated figure, cropped to fill the container.

badge adds a corner badge, colored by tone, on any avatar — image or generated. It takes any named tone, so the application decides what each one means: success for online, critical for busy, warning for idle, neutral for offline. Named tones track light and dark mode automatically.

The badge sits in a hole masked out of the picture rather than behind a painted ring, so whatever is behind the avatar shows through the gap and no ring color has to track the surface.

<Avatar round src="/anta-logo.svg" name="Anta" badge="success" />
<Avatar seed="lena-r" name="Lena Rossi" badge="critical" />
<Avatar seed="8f21ac" name="Sam Okafor" badge="warning" />
<Avatar seed="k-tanaka" name="Kenji Tanaka" badge="info" />

badge also accepts any literal CSS color for a one-off badge, derived in oklch the same way a named tone is.

<Avatar seed="a-lee" name="Ann Lee" badge="#ff1493" />
<Avatar seed="b-qi" name="Bo Qi" badge="rebeccapurple" />

Size

size is small (32px), medium (44px, the default), or large (64px). Pass a number for a pixel size.

<Avatar seed="user-42" name="Vlad Korobov" size="small" />
<Avatar seed="user-42" name="Vlad Korobov" size="large" />
<Avatar seed="user-42" name="Vlad Korobov" size={96} />

Round

round makes the frame a circle. Pass a number (px) or a CSS length for a custom radius instead. The badge hugs the circle’s edge on a round frame.

<Avatar round seed="user-42" name="Vlad Korobov" />
<Avatar round seed="lena-r" name="Lena Rossi" badge="success" />
<Avatar seed="k-tanaka" name="Kenji Tanaka" round={10} />

Brand constraints

The generator config caps the space so every avatar sits on your brand. Each dimension takes one of four modes: off (excluded, a fixed default), any (its full natural range), range (an explicit range), or list (a set of values the seed picks from). Edit the config in the playground’s Code tab to try them.

Color ranges cap OKLCH per channel — lightness, chroma, and hue. Narrow the hue to your brand’s, keep chroma modest, and every generated color stays in band (chroma is clamped into sRGB, so it never clips). harmony ties the head and body hue to the background.

const brand = {
bgColor: { mode: 'range', l: [0.5, 0.62], c: [0.09, 0.15], h: [255, 285] },
harmony: true,
}
<Avatar seed={user.id} name={user.name} generator={brand} />

Color lists pass an explicit palette — a scheme the seed picks from at random. Combine with off shapes for a classic initials avatar restricted to your palette.

const palette = {
bgColor: { mode: 'list', values: ['#6c5ce7', '#00b894', '#e17055', '#0984e3', '#fdcb6e'] },
headRadiusTop: { mode: 'off' },
headRadiusBottom: { mode: 'off' },
bodyBorderRadius: { mode: 'off' },
}
<Avatar name={user.name} generator={palette} />

The dimensions

DimensionRange
bgColor, headColor, bodyColorOKLCH l / c / h caps, or a palette via list
figureScaleRelative size of the whole figure
figureTranslateX and Y shift from the centered position
figureAngleRotation of the whole figure
headAngle, bodyAngleRotation of each part within the figure
figureGapSpace between head and body, 0 to 1 of head height
headRadiusTop, headRadiusBottom0 square, 1 fully round, above 1 elongated
bodyBorderRadius0 square to 1 fully round shoulders

Corner radius is normalized: 0 is a square corner and 1 is fully round, so a head with both radii at 1 is a circle. Above 1 the head elongates into an oval, spending the excess on that half’s height — 1.3 on both radii is a tall oval, and more at the bottom than the top is an egg.

Two rules keep the head reading as a head. The bottom is never rounded less than the top, so a value below the top radius is raised to match it: a rounder crown over a squarer jaw reads mechanical, and the reverse reads natural. And under any both radii average near or above 1, so heads read round to oval unless you narrow them yourself.

Generator config

generator takes an AvatarGenConfig. Every field is optional — anything you leave out keeps its default from the table above.

interface AvatarGenConfig {
bgColor?: ColorDim
headColor?: ColorDim
bodyColor?: ColorDim
figureScale?: ScalarDim
figureTranslate?: Vec2Dim
figureAngle?: ScalarDim
headAngle?: ScalarDim
bodyAngle?: ScalarDim
figureGap?: ScalarDim
headRadiusTop?: ScalarDim
headRadiusBottom?: ScalarDim
bodyBorderRadius?: ScalarDim
harmony?: boolean
}
type DimMode = 'off' | 'any' | 'range' | 'list'

Modes

Every dimension carries a mode. It decides which of the dimension’s other fields are read; the rest are ignored.

modeBehaviorFields read
offExcluded. The dimension holds a fixed neutral default.none
anyVaries across the dimension’s full natural range (the default).none
rangeVaries within an explicit range.min / max, or l / c / h, or x / y
listPicks one value at random from an explicit set.values

Dimension types

ScalarDim — a single number: scale, an angle, the gap, a corner radius.

interface ScalarDim {
mode?: DimMode
min?: number // range: lower bound
max?: number // range: upper bound
values?: number[] // list: the allowed values
}

ColorDim — a color. range caps OKLCH per channel as [min, max] pairs, with chroma clamped into sRGB so a value can never clip. list passes a palette of any CSS colors.

interface ColorDim {
mode?: DimMode
l?: [number, number] // range: lightness, 0 to 1
c?: [number, number] // range: chroma, 0 to ~0.4
h?: [number, number] // range: hue, 0 to 360
values?: string[] // list: the allowed CSS colors
}

Vec2Dim — the figure’s offset from center, used only by figureTranslate. Each axis runs from −50 to 50 in the frame’s coordinates, where 0 centers the head.

interface Vec2Dim {
mode?: DimMode // 'list' is not used here
x?: [number, number] // range: horizontal bounds
y?: [number, number] // range: vertical bounds
values?: [number, number][] // list: allowed [x, y] points
}

harmony is a plain boolean rather than a dimension: when true, headColor and bodyColor take their hue from the background so the parts stay coordinated.

Defaults per dimension

What off pins each dimension to, and what any samples. Corner radii are sampled toward the top of their range, so heads average round to oval.

Dimensionoffany samples
bgColormid greyl 0.5–0.82, c 0.02–0.12, any hue
headColorlight greyl 0.78–0.96, c 0.015–0.075, any hue
bodyColorlight greyl 0.68–0.9, c 0.015–0.09, any hue
figureScale10.76–1.2
figureTranslate[0, 0]X −15 to 15; Y −14 to 4
figureAngle0−15° to 15°
headAngle0−20° to 20°
bodyAngle0−16° to 16°
figureGap0.250.12–0.42
headRadiusTop10.35–1.25
headRadiusBottom10.45–1.5
bodyBorderRadius10.25–1
harmonytrue

With every shape dimension (headRadiusTop, headRadiusBottom, bodyBorderRadius) set to off, no figure shape is configured and the avatar renders initials instead.

The generation helpers are exported too, for a consumer that needs the result outside a component: resolveAvatar(config, seed) returns the resolved colors and geometry, avatarToSvg(resolved) renders it, and getInitials(name) derives the fallback letters.

Component props

Prop Type Default Description
seed? string Alphanumeric seed that deterministically drives the generated userpic. The same seed always renders the same avatar. Falls back to name when omitted, so a stable user id or name is a good value.
name? string Person or entity name. Supplies the initials fallback (up to 3 letters from the first 3 words) and the accessible name, and seeds generation when seed is absent.
src? string Image URL. When set, the image is shown instead of a generated userpic, cropped to fill the container.
size? smallmediumlargenumber medium Size of the square container. A number is a pixel size.
badge? neutralbrandinfosuccesswarningcriticalstring Corner badge, colored by tone. Pass a named tone or any literal CSS color; the application decides what each tone means (success for online, critical for busy, neutral for offline). Omit for no badge.
round? booleannumberstring Fully-round (circular) frame. Pass a number (px) or a CSS length string for a custom radius instead.
generator? AvatarGenConfig Brand generation constraints — each dimension is OFF / ANY / RANGE / LIST. Colors cap OKLCH ranges or pass an explicit palette. Omit for the default varied figure. Define one config for the app and reuse it across avatars.
Inherited props (className, id, slot, style, tabIndex, title)
Prop Type Default Description
className? string CSS class on the component's root element (merged with the component's own classes). Use it directly for layout and positioning — grid/flex placement, margins, alignment — rather than wrapping the component in a <div>/<span>.
id? string HTML id attribute.
slot? string Assigns the element to a named <slot> of a parent web component (e.g. slot="header" inside a <Card>, slot="footer" inside a <Dialog>).
style? CSSProperties Inline styles on the component's root element. Set layout/positioning here (or via className) directly on the component instead of adding a wrapper.
tabIndex? number Tab order. Set to -1 to skip the element when tabbing.
title? string HTML title attribute — native browser tooltip on hover.

Plus every standard DOM event handler (onClick, onFocus, onKeyDown, …) and any data-* or aria-* attribute — forwarded as-is to the underlying <a-*> element.

Vanilla web component

Use the web component directly when you are not using React or Preact. The generation config rides the config attribute as a JSON string, so the element generates from plain markup with no JavaScript of your own.

<a-avatar seed="user-42" name="Vlad Korobov"></a-avatar>
<a-avatar
name="Vlad Korobov"
config='{"headRadiusTop":{"mode":"off"},"headRadiusBottom":{"mode":"off"},"bodyBorderRadius":{"mode":"off"}}'
></a-avatar>

Styling

Reach for the props first: size sets the dimensions and generator the generated look. Everything else is --avatar-* custom properties on the host and ::part() on the shadow content.

The container is a rounded square by default; round makes it a circle and takes a length for a custom radius, so reach for the prop before the custom property. The badge is sized and placed with --avatar-badge-ratio (its size as a fraction of the frame), --avatar-badge-inset, and --avatar-badge-gap (the width of the cutout around it). Its fill comes from badge, so route color through the prop rather than overriding --avatar-badge-color.

a-avatar.badge-dot {
--avatar-badge-ratio: 0.34; /* a larger badge */
--avatar-badge-gap: 4px; /* in a wider cutout */
}

The shadow content exposes ::part(frame) (the picture — the generated figure or the image, whichever is showing), ::part(image) (only the src image), and ::part(badge) (the corner badge) for anything the custom properties do not cover.