Avatar
An identity chip: a photo when one loads, a tinted initials-or-icon fallback when it doesn't.
Live demo
Avatar presents a person or entity as a compact square or circle. Given a src, it shows the image, cover-cropped to fill; if the image is absent or fails to load, it falls back to initials derived from userName ("Ada Lovelace" → AL) on a soft, hue-tinted background.
Slot your own content to override those initials, or use the icon slot for a glyph instead. The prop is userName, not name, because name carries form-participation meaning on an element and an avatar is not a form control. The fallback tint follows tone, which accepts any of the six semantic roles or the twelve named hues, so a deterministic per-user color is a one-attribute choice. Four sizes (sm, md, lg, xl), three shapes (circle, square, and a diamond that clips the content, the frame, and any effect painted on the host), and an optional corner status dot in any semantic tone round out the surface. frame rings the content in a solid tone or a gradient between two, bevel shades that ring as a raised edge, and the --xtyle-avatar-frame (any color or gradient) and --xtyle-avatar-frame-width custom properties override both. Anything more particular, such as rank pips, a count, or a corner mark, belongs to a mod: the fill renders an empty marks part over the chip and receives the host's own data-* attributes as bindings.hostData, so data-rank="3" is all a mod needs. alt names the image; userName names the avatar when there is no image, so it is announced either way.
When to use
How this component composes with the rest of the set.
Props
13 props, straight from the manifest.
| Prop | Type | Default | Bindings | Description |
|---|---|---|---|---|
src is set; also labels the fallback. | ||||
"Ada Lovelace" → AL, "Prince" → P), and names the avatar when no alt is given. Named userName rather than name because name is a form-participation attribute on an element. Attribute: user-name. | ||||
glare or holo stays inside the shape, while the marks layer and the status dot are free to sit over its corners. | ||||
solid paints it in frameTone (the line color by default), gradient runs from frameTone to frameTone2 (the accent pair by default). The --xtyle-avatar-frame custom property takes any color or gradient instead, and --xtyle-avatar-frame-width sets the thickness. | ||||
frame-tone. | ||||
frame-tone-2. | ||||
--white and --black. Implies a solid frame when frame is not set. | ||||
true pulses slow, "fast" quick. A no-op without status, and held still under prefers-reduced-motion. |
Appearance
Variants
circle
Fully rounded, the default identity shape.
square
A rounded square, for entities and projects rather than people.
diamond
A square turned 45° with the content kept upright, for items, charms, and gems rather than people.
Sizes
sm
Compact, for dense lists.
md
Default.
lg
Large, for headers.
xl
Extra large, for profile hero.
States
fallback
No image: the tinted initials-or-icon stand-in fills the chip.
status
A corner dot conveys presence; its color is the chosen status tone, ringed in the surface.
pulse
With pulse, the status dot breathes on a soft opacity loop so the avatar reads as live, at a slow or fast cadence; held still under reduced-motion.
framed
With frame, the content sits inside a ring painted in a tone, a gradient, or whatever --xtyle-avatar-frame holds.
bevel
With bevel, the frame catches a highlight on its top-left edge and a shade on its bottom-right.
Anatomy
The named parts that make up the component. Created from script it renders a shadow root, where a ::part() handle is reachable from your own stylesheet and the class beside it is internal. Upgraded over server-rendered markup it renders light DOM, where the class is the handle.
avatar
The root chip carrying the size, shape, and tone classes; sizes the image and fallback and anchors the status dot.
image
The portrait image, cover-cropped to fill the chip and clipped to the chip's shape.
fallback
The initials-or-icon stand-in shown when no image is present, painted with the tone's soft tint inside a hairline edge that clears 3:1 against the page.
status-dot
The optional corner indicator, ringed in the surface color so it reads against any background.
marks
An empty layer over the whole chip, rendered for mods: a fill that paints pips, a count, or a corner badge targets it, and bindings.hostData carries the host's data-* attributes to draw from. Hidden while empty.
Tokens & coverage
What the component consumes, checked live against what the algorithm produces.
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.
--accent--accent-2--accent-2-bg--accent-2-text--accent-3--accent-3-bg--accent-3-text--accent-4--accent-4-bg--accent-4-text--accent-bg--accent-text--bg-1--black--black--black-bg--black-text--blue--blue-bg--blue-text--border-thick--border-thin--brown--brown-bg--brown-text--cyan--cyan-bg--cyan-text--danger--danger-bg--danger-text--font-sans--gray--gray-bg--gray-text--green--green-bg--green-text--info--info-bg--info-text--leading-tight--line-2--line-strong--neutral--neutral-bg--neutral-text--orange--orange-bg--orange-text--pink--pink-bg--pink-text--purple--purple-bg--purple-text--radius-full--radius-lg--radius-md--radius-sm--red--red-bg--red-text--space-2--space-4--space-6--space-7--space-8--success--success-bg--success-text--text-body--text-lg--text-sm--text-xs--warn--warn-bg--warn-text--weight-semibold--white--white--white-bg--white-text--yellow--yellow-bg--yellow-textSlots
Custom fallback content shown when no image loads. Overrides the initials userName derives — leave it empty and those initials are what you get.
An icon fallback, used in place of initials when no image loads.
Accessibility
Code
Image, fallback, and status
A photo when one loads, tinted initials or an icon when it doesn't, with an optional presence dot.
<!-- a photo when one loads -->
<xtyle-avatar user-name="Ada Lovelace" alt="Ada Lovelace" src="/avatars/ada.jpg"></xtyle-avatar>
<!-- no image: the initials fall out of the name ("Grace Hopper" → GH) -->
<xtyle-avatar user-name="Grace Hopper" tone="info" size="lg"></xtyle-avatar>
<xtyle-avatar user-name="Katherine Johnson" tone="purple" shape="square" status="success" status-label="Online"></xtyle-avatar>
<!-- slot your own content to override the derived initials -->
<xtyle-avatar user-name="Project Apollo" tone="warn">🚀</xtyle-avatar>
<xtyle-avatar alt="Unknown user" size="xl">
<svg slot="icon" viewBox="0 0 24 24" aria-hidden="true">
<path fill="currentColor" d="M12 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0 2c-5 0-9 2.5-9 6v2h18v-2c0-3.5-4-6-9-6Z" />
</svg>
</xtyle-avatar><script lang="ts">
import { Avatar } from "@xtyle/svelte";
</script>
<!-- a photo when one loads -->
<Avatar userName="Ada Lovelace" alt="Ada Lovelace" src="/avatars/ada.jpg" />
<!-- no image: the initials fall out of the name ("Grace Hopper" → GH) -->
<Avatar userName="Grace Hopper" tone="info" size="lg" />
<Avatar userName="Katherine Johnson" tone="purple" shape="square" status="success" statusLabel="Online" />
<!-- slot your own content to override the derived initials -->
<Avatar userName="Project Apollo" tone="warn">🚀</Avatar>
<Avatar alt="Unknown user" size="xl">
{#snippet icon()}
<svg viewBox="0 0 24 24" aria-hidden="true">
<path fill="currentColor" d="M12 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0 2c-5 0-9 2.5-9 6v2h18v-2c0-3.5-4-6-9-6Z" />
</svg>
{/snippet}
</Avatar>---
import { Avatar } from "@xtyle/astro";
---
<!-- a photo when one loads -->
<Avatar userName="Ada Lovelace" alt="Ada Lovelace" src="/avatars/ada.jpg" />
<!-- no image: the initials fall out of the name ("Grace Hopper" → GH) -->
<Avatar userName="Grace Hopper" tone="info" size="lg" />
<Avatar userName="Katherine Johnson" tone="purple" shape="square" status="success" statusLabel="Online" />
<!-- slot your own content to override the derived initials -->
<Avatar userName="Project Apollo" tone="warn">🚀</Avatar>
<Avatar alt="Unknown user" size="xl">
<svg slot="icon" viewBox="0 0 24 24" aria-hidden="true">
<path fill="currentColor" d="M12 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0 2c-5 0-9 2.5-9 6v2h18v-2c0-3.5-4-6-9-6Z" />
</svg>
</Avatar>Live presence
A pulsing status dot reads as online in real time; statusLabel carries the meaning for assistive tech.
<xtyle-avatar user-name="Grace Hopper" status="success" status-label="Online" pulse></xtyle-avatar><Avatar userName="Grace Hopper" status="success" statusLabel="Online" pulse /><Avatar userName="Grace Hopper" status="success" statusLabel="Online" pulse />Diamonds, frames, and effects
A diamond with a gradient, bevelled frame and a repeating thin glint; a holo foil on a solid frame; a framed square.
<xtyle-avatar shape="diamond" frame="gradient" frame-tone="yellow" frame-tone-2="orange" bevel tone="purple" alt="Ring of embers" data-fx="glare?repeat:infinite,rest:3,width:6%" style="--fx-glare-repeat:infinite;--fx-glare-rest:3;--fx-glare-width:6%">
<xtyle-icon slot="icon" name="ember--flame" aria-hidden="true"></xtyle-icon>
</xtyle-avatar>
<xtyle-avatar shape="diamond" frame="solid" frame-tone="gray" bevel tone="cyan" alt="Tide charm" data-fx="holo">
<xtyle-icon slot="icon" name="tide--drop" aria-hidden="true"></xtyle-icon>
</xtyle-avatar>
<xtyle-avatar shape="square" frame="solid" frame-tone="accent" user-name="Ada Lovelace"></xtyle-avatar><script lang="ts">
import { Avatar, Icon } from "@xtyle/svelte";
</script>
<Avatar shape="diamond" frame="gradient" frameTone="yellow" frameTone2="orange" bevel tone="purple" alt="Ring of embers" data-fx="glare?repeat:infinite,rest:3,width:6%" style="--fx-glare-repeat:infinite;--fx-glare-rest:3;--fx-glare-width:6%">
{#snippet icon()}<Icon name="ember--flame" aria-hidden="true" />{/snippet}
</Avatar>
<Avatar shape="diamond" frame="solid" frameTone="gray" bevel tone="cyan" alt="Tide charm" data-fx="holo">
{#snippet icon()}<Icon name="tide--drop" aria-hidden="true" />{/snippet}
</Avatar>
<Avatar shape="square" frame="solid" frameTone="accent" userName="Ada Lovelace" />---
import { Avatar, Icon } from "@xtyle/astro";
---
<Avatar shape="diamond" frame="gradient" frameTone="yellow" frameTone2="orange" bevel tone="purple" alt="Ring of embers" data-fx="glare?repeat:infinite,rest:3,width:6%" style="--fx-glare-repeat:infinite;--fx-glare-rest:3;--fx-glare-width:6%">
<Icon slot="icon" name="ember--flame" aria-hidden="true" />
</Avatar>
<Avatar shape="diamond" frame="solid" frameTone="gray" bevel tone="cyan" alt="Tide charm" data-fx="holo">
<Icon slot="icon" name="tide--drop" aria-hidden="true" />
</Avatar>
<Avatar shape="square" frame="solid" frameTone="accent" userName="Ada Lovelace" />Data for a mod's marks
The host's data-* attributes reach every fill as bindings.hostData, and the empty marks part is where a mod paints from them.
<!-- a mod's avatar fill reads bindings.hostData.rank and paints pips into the empty marks part -->
<xtyle-avatar shape="diamond" frame="solid" tone="green" alt="Oak ring, rank 3 of 5" data-rank="3" data-rank-max="5">
<xtyle-icon slot="icon" name="oak--leaf" aria-hidden="true"></xtyle-icon>
</xtyle-avatar>