Avatar
.avatar is a round box that holds either an image or a person's
initials. One custom property, --size, drives its width, its height
and the font size of the initials — calc(var(--size) * .38) —
so a size variant is a single declaration and the initials never overflow.
When to use it
Use an avatar wherever a row, a comment or a message needs to be attributed to a
person. It is a grid with place-items: center, so
whatever goes inside is centred without any help, and
overflow: hidden means an image is clipped to the circle rather than
needing its own radius.
flex: 0 0 auto is set so an avatar in a flex row never shrinks when the
text beside it is long. That is the one declaration that stops a list of comments
from squashing every face into an oval.
<span class="avatar">SR</span>
<span class="avatar"><img alt="" src="data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22160%22 height=%22160%22%3E%3Crect width=%22160%22 height=%22160%22 fill=%22%238fa8a4%22/%3E%3Ccircle cx=%2280%22 cy=%2262%22 r=%2226%22 fill=%22%23dfe9e7%22/%3E%3Ccircle cx=%2280%22 cy=%22150%22 r=%2246%22 fill=%22%23dfe9e7%22/%3E%3C/svg%3E"></span>
<span class="avatar">SR</span>
<span class="avatar"><img alt="" src="data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22160%22 height=%22160%22%3E%3Crect width=%22160%22 height=%22160%22 fill=%22%238fa8a4%22/%3E%3Ccircle cx=%2280%22 cy=%2262%22 r=%2226%22 fill=%22%23dfe9e7%22/%3E%3Ccircle cx=%2280%22 cy=%22150%22 r=%2246%22 fill=%22%23dfe9e7%22/%3E%3C/svg%3E"></span>
Sizes
Five sizes, each setting --size and nothing else. Because the font size
is computed from it, the initials scale with the circle rather than being set
separately for each variant.
<span class="avatar avatar-xs">SR</span>
<span class="avatar avatar-sm">SR</span>
<span class="avatar">SR</span>
<span class="avatar avatar-lg">SR</span>
<span class="avatar avatar-xl">SR</span>
<span class="avatar avatar-xs">SR</span>
<span class="avatar avatar-sm">SR</span>
<span class="avatar">SR</span>
<span class="avatar avatar-lg">SR</span>
<span class="avatar avatar-xl">SR</span>
| Class | --size | Initials |
|---|---|---|
.avatar-xs |
24px |
9.1px |
.avatar-sm |
32px |
12.2px |
.avatar |
40px |
15.2px |
.avatar-lg |
56px |
21.3px |
.avatar-xl |
80px |
30.4px |
--size is a plain custom property, so a size Deck does not ship is
style="--size:120px" or a class of your own. There is no need to add a
variant to the framework for a one-off.
Square
.avatar-square swaps --r-full for
--r-sm. Use it for anything that is not a person — an organisation, a
project, a repository. Keeping people round and things square is a cheap signal that
costs one class.
<span class="avatar avatar-square">AC</span>
<span class="avatar avatar-square avatar-lg"><img alt="" src="data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22160%22 height=%22160%22%3E%3Crect width=%22160%22 height=%22160%22 fill=%22%238fa8a4%22/%3E%3Ccircle cx=%2280%22 cy=%2262%22 r=%2226%22 fill=%22%23dfe9e7%22/%3E%3Ccircle cx=%2280%22 cy=%22150%22 r=%2246%22 fill=%22%23dfe9e7%22/%3E%3C/svg%3E"></span>
<span class="avatar avatar-square">AC</span>
<span class="avatar avatar-square avatar-lg"><img alt="" src="data:image/svg+xml;utf8,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22160%22 height=%22160%22%3E%3Crect width=%22160%22 height=%22160%22 fill=%22%238fa8a4%22/%3E%3Ccircle cx=%2280%22 cy=%2262%22 r=%2226%22 fill=%22%23dfe9e7%22/%3E%3Ccircle cx=%2280%22 cy=%22150%22 r=%2246%22 fill=%22%23dfe9e7%22/%3E%3C/svg%3E"></span>
Overlapping stack
.avatar-stack is a flex row that pulls each avatar after the first back
by calc(var(--size, 40px) * -.32) and gives every one a two-pixel ring
in the page's surface colour, so the overlap reads as depth rather than as a
collision. The offset is computed from --size, so it stays
proportional at every size.
<div class="avatar-stack">
<span class="avatar avatar-sm">SR</span>
<span class="avatar avatar-sm">AC</span>
<span class="avatar avatar-sm">MJ</span>
<span class="avatar avatar-sm">+4</span>
</div>
<div class="avatar-stack">
<span class="avatar avatar-lg">SR</span>
<span class="avatar avatar-lg">AC</span>
<span class="avatar avatar-lg">MJ</span>
</div>
<div class="avatar-stack">
<span class="avatar avatar-sm">SR</span>
<span class="avatar avatar-sm">AC</span>
<span class="avatar avatar-sm">MJ</span>
<span class="avatar avatar-sm">+4</span>
</div>
<div class="avatar-stack">
<span class="avatar avatar-lg">SR</span>
<span class="avatar avatar-lg">AC</span>
<span class="avatar avatar-lg">MJ</span>
</div>
The ring is box-shadow: 0 0 0 2px var(--surface). On a background that
is not --surface — inside a .card-brand, say — the ring is
the wrong colour. Set --surface on the stack, or accept it.
In a chat thread
src/24-media.css overrides the avatar inside .msg: 28px,
align-self: flex-end so it lines up with the last line of the bubble
rather than the first, and visibility: hidden on every message after
the first from the same sender. The space is kept so the bubbles stay aligned; only
the face is dropped.
<div class="chat">
<div class="msg">
<span class="avatar">AC</span>
<div class="bubble">Did the export finish?</div>
</div>
<div class="msg">
<span class="avatar">AC</span>
<div class="bubble">I can rerun it if not.</div>
</div>
</div>
<div class="chat">
<div class="msg">
<span class="avatar">AC</span>
<div class="bubble">Did the export finish?</div>
</div>
<div class="msg">
<span class="avatar">AC</span>
<div class="bubble">I can rerun it if not.</div>
</div>
</div>
Classes
Generated from src/07-components.css by tools/docs/extract.mjs.
| Class | Layer | What it does | Source |
|---|---|---|---|
.avatar |
deck.components |
A round box holding an image or initials. --size drives the width, the height and the font size together, so a size variant is one declaration and the initials can never overflow the circle. | 07-components.css:176 |
.avatar-lg |
deck.components |
56px. | 07-components.css:197 |
.avatar-sm |
deck.components |
32px. | 07-components.css:195 |
.avatar-square |
deck.components |
A square avatar, for anything that is not a person — an organisation, a project, a repository. | 07-components.css:202 |
.avatar-stack |
deck.components |
Overlapping avatars. The offset is a fraction of --size, so it stays proportional at every size, and the ring is the surface colour, which is wrong on any other background. | 07-components.css:207 |
.avatar-xl |
deck.components |
80px. | 07-components.css:199 |
.avatar-xs |
deck.components |
24px. | 07-components.css:193 |
Tokens
The avatar's fill is the brand tint, which is why a wall of initials reads as one
palette rather than as noise. --size is local to the component and is
the only thing a size variant touches.
| Token | Live | Declared value |
|---|---|---|
--brand-soft |
light-dark(var(--brand-50), oklch(26% .05 var(--hue-brand))) |
|
--brand-soft-text |
light-dark(var(--brand-800), var(--brand-200)) |
|
--r-full |
999px |
|
--r-sm |
7px |
|
--surface |
light-dark(var(--ink-0), var(--ink-950)) |
Accessibility
-
An avatar beside a name needs empty alt text. If the person's
name is already in the row,
alt=""is correct — otherwise a screen reader reads the name twice. Every example on this page usesalt=""for that reason. -
An avatar on its own needs a real name. In an
.avatar-stackwith no names beside it, each image needsalt="Ada Chen", or the stack is a row of unlabelled pictures. -
Initials are read as letters.
<span class="avatar">SR</span>is announced "S R", which is rarely useful. Addaria-hidden="true"when the name is beside it, or<span class="sr-only">Santos Rivera</span>when it is not. -
user-select: noneis set. Initials cannot be selected or copied, which stops a double-click on a row from highlighting "SR". It also means the initials are not available to a reader who copies the row, so the name has to exist somewhere else in the markup. -
No focus style, because it is not interactive. An avatar that
opens a profile needs to be a link or a button around the avatar, and the target
needs to clear 44px —
.avatar-smat 32px does not on its own. -
The overlap hides part of each face. In an
.avatar-stack, 32% of every avatar but the last is covered. That is fine for a decorative "who is on this" summary and wrong for a list a reader has to identify people from. -
Contrast. Initials are
--brand-soft-texton--brand-soft, which clears 4.5:1 in both themes. An image avatar has no contrast requirement because it is a picture, not text.
Right to left
The stack's offset is margin-inline-start, so under
dir="rtl" the avatars overlap from the other side and the first one in
the markup is still the one on top. Nothing else about the component is
directional — a circle has no start edge.
<div dir="rtl" class="avatar-stack">
<span class="avatar">ا</span>
<span class="avatar">ب</span>
<span class="avatar">ج</span>
</div>
<div dir="rtl" class="avatar-stack">
<span class="avatar">ا</span>
<span class="avatar">ب</span>
<span class="avatar">ج</span>
</div>
Reduced motion
The avatar has no transition and no animation, so
prefers-reduced-motion changes nothing about it. Nothing to report for
this component.
Printing
src/99-print.css lists .avatar among the elements that
keep their fill on paper, with print-color-adjust: exact. Without that,
an initials avatar would print as dark letters on white and lose the circle
entirely.
Overriding it
--size is the intended override and it needs no layer at all — set it
inline or in your own class. Reach for app.components only to change
the look.
/* A size Deck does not ship — no layer needed */
<span class="avatar" style="--size:120px">SR</span>
@layer app.components {
.avatar {
background: var(--ink-100);
color: var(--text);
font-weight: 700;
}
}
Prefer setting --size over setting inline-size and
block-size: the font size and the stack offset are both computed from
it, so overriding the dimensions directly leaves the initials and the overlap at the
old scale.
When not to use it
-
Not for a logo. An avatar clips to a circle and centres its
contents; a wordmark comes out cropped. Use an
<img>with.ratio-1x1, or.icon-tilefor a square badge with a tinted background. -
Not for an icon. Use
.icon-tile. It is built for a glyph on a tint and does not compute a font size from its own width. -
Not as a button on its own.
.avatar-smis 32px, below the 44px target. Wrap it in a.btn.btn-icon, which brings the target and the focus ring, rather than attaching a handler to the avatar. -
Not for a list of people you have to tell apart.
.avatar-stackcovers a third of each face. For a real roster use a.listwith an avatar and a name in each row. -
Not for status. An avatar with a coloured ring to mean "online"
is invisible to a screen reader and to anyone who cannot distinguish the colour.
Use
.indicator, which positions a dot and can carry.sr-onlytext.