Navigation

v0.0.4
Content

Avatar

A person or a team as a photo or initials, alone or stacked.

On this page

An avatar stands for a person or a team: a photo when there is one, initials when there is not, in a circle that keeps the same size either way. A faint inward stroke finishes the edge, so a pale photo does not dissolve into a pale surface.

Avatar

<span class="avatar">  <img class="avatar-image" src="/demo/portrait-1.svg" alt="Ada Lovelace"></span><span class="avatar" role="img" aria-label="Grace Hopper">GH</span>

#Sizes

The default is 32px. Initials scale with the circle. Use one or two letters.

Sizes

<span class="avatar avatar-xs" role="img" aria-label="Ada Lovelace">A</span><span class="avatar avatar-sm" role="img" aria-label="Ada Lovelace">AL</span><span class="avatar" role="img" aria-label="Ada Lovelace">AL</span><span class="avatar avatar-lg">  <img class="avatar-image" src="/demo/portrait-2.svg" alt="Alan Turing"></span><span class="avatar avatar-xl">  <img class="avatar-image" src="/demo/portrait-3.svg" alt="Katherine Johnson"></span>
ClassSize
avatar-xs20px
avatar-sm24px
avatar32px
avatar-lg40px
avatar-xl56px

#Square

avatar-square is a rounded square with a corner a quarter of its size. Use it for a team, a project or an organization, so people and groups can be told apart by shape.

Square

<span class="avatar avatar-square" role="img" aria-label="Master">M</span><span class="avatar avatar-square avatar-lg" role="img" aria-label="Atlas">AT</span>

#Status

avatar-status puts a presence dot on the lower end of the circle, cut out by a ring of the surface behind it. It is green by default; set --avatar-status, or add a background utility such as bg-warning, for another status. A dot alone is not a message: say the status in the accessible name.

Status

<span class="avatar avatar-lg">  <img class="avatar-image" src="/demo/portrait-1.svg" alt="Ada Lovelace, online">  <span class="avatar-status"></span></span><span class="avatar avatar-lg" role="img" aria-label="Grace Hopper, away">GH  <span class="avatar-status bg-warning"></span></span>

#Stack

avatar-group overlaps avatars by a quarter of their size. Each paints in front of the one before it and is separated from it by a 2px ring in the color of the surface behind the stack. The order follows the markup in both writing directions. Count the people who are not shown in a last avatar, and name the whole group once.

Stack

<div class="avatar-group" role="group" aria-label="Ada Lovelace, Alan Turing, Katherine Johnson and 4 others">  <span class="avatar">    <img class="avatar-image" src="/demo/portrait-1.svg" alt>  </span>  <span class="avatar">    <img class="avatar-image" src="/demo/portrait-2.svg" alt>  </span>  <span class="avatar">    <img class="avatar-image" src="/demo/portrait-3.svg" alt>  </span>  <span class="avatar" aria-hidden="true">+4</span></div>

The ring must match the surface the stack sits on. Inside a sheet, a tray, a card or a dialog it does so by itself, because each surface publishes its color as --surface-color. On a custom background, set --avatar-ring to that background's color.

On a surface

<div class="tray">  <div class="sheet flex items-center justify-between gap-md p-lg">    <span class="text-sm fg-text-strong">Reviewers</span>    <div class="avatar-group" role="group" aria-label="Ada Lovelace and Alan Turing">      <span class="avatar avatar-sm">        <img class="avatar-image" src="/demo/portrait-1.svg" alt>      </span>      <span class="avatar avatar-sm">        <img class="avatar-image" src="/demo/portrait-2.svg" alt>      </span>    </div>  </div></div>

#Accessibility

  • A photo needs alt with the person's name. Initials are not a name: give the avatar role="img" and aria-label.
  • When the name is written next to the avatar, the avatar is decoration: use alt="" or aria-hidden="true".
  • In a stack, name the group once on the container and leave the images empty, so a name is not read twice.

#Classes

ClassDescription
avatarThe circle, 32px; it holds initials or an avatar-image. Initials need role="img" and aria-label with the name
avatar-imageThe photo, cropped to the circle; its alt is the person's name
avatar-xs20px; the default is 32px. With avatar
avatar-sm24px; the default is 32px. With avatar
avatar-lg40px; the default is 32px. With avatar
avatar-xl56px; the default is 32px. With avatar
avatar-squareA rounded square with a corner a quarter of its size, for a team, a project or an organization. With avatar
avatar-statusA presence dot on the lower end of the circle, cut out by a ring of the surface behind it; say the status in the accessible name as well
avatar-groupA stack: each avatar overlaps the one before it by a quarter of its size, separated by a 2px ring of the surface color

#Custom properties

PropertyDefaultDescription
--avatar-size2remDiameter
--avatar-ringColor of the ring around a stacked avatar and a status dot. It is not declared: it falls back to --surface-color, then to --color-surface-base
--avatar-statusColor of the status dot. It is not declared: it falls back to --color-success. On avatar-status

#Source

@master/ui/components/avatar.css
/* * Avatar: a person or a team as a photo or as initials, in a circle of one fixed size. * Three layers stay independent: the image, a faint inward stroke that keeps pale photos from losing their edge, * and, in a stack, an opaque ring in the color of the surface behind it that separates overlapping circles. */@layer components {  .avatar {    --avatar-size: 2rem;    --avatar-radius: 999px;    position: relative;    display: inline-grid;    flex: none;    place-items: center;    width: var(--avatar-size);    height: var(--avatar-size);    border-radius: var(--avatar-radius);    background: var(--color-surface-chip);    color: var(--color-text-body);    font: var(--font-weight-control) calc(var(--avatar-size) * .4)/1 var(--font-family-sans);    font-feature-settings: inherit;    letter-spacing: 0;    text-decoration: none;    text-transform: uppercase;    vertical-align: middle;    user-select: none;    -webkit-user-select: none;    /* The inward stroke paints above the image and never changes its crop or the box. */    &::after {      content: '';      position: absolute;      inset: 0;      border-radius: inherit;      corner-shape: inherit;      box-shadow: inset 0 0 0 1px var(--color-line-image-strong);      pointer-events: none;    }    &.avatar-xs { --avatar-size: 1.25rem; }    &.avatar-sm { --avatar-size: 1.5rem; }    &.avatar-lg { --avatar-size: 2.5rem; }    &.avatar-xl { --avatar-size: 3.5rem; }    /* A rounded square, for a team, a project or an organization. */    &.avatar-square {      --avatar-radius: calc(var(--avatar-size) * .25 * var(--corner-scale));      corner-shape: var(--corner-shape);    }    &:focus-visible {      @apply --focus-ring;    }    @media (forced-colors: active) {      outline: 1px solid CanvasText;      outline-offset: -1px;    }  }  /* The photo fills the circle; the box stays the same when it is missing and initials show instead. */  .avatar-image {    grid-area: 1 / 1;    width: 100%;    height: 100%;    border-radius: inherit;    corner-shape: inherit;    object-fit: cover;    /* A faint inward edge, so a pale photo keeps its circle on a pale surface. */    outline: 1px solid var(--color-line-image);    outline-offset: -1px;  }  /*   * A presence dot on the lower end of the circle, cut out by a ring of the surface behind it.   * Its center sits on the circle's edge at 45°. Say the status in words as well.   */  .avatar-status {    --avatar-status-size: max(.5rem, calc(var(--avatar-size) * .25));    position: absolute;    inset-block-end: calc(var(--avatar-size) * .146 - var(--avatar-status-size) / 2);    inset-inline-end: calc(var(--avatar-size) * .146 - var(--avatar-status-size) / 2);    z-index: 1;    width: var(--avatar-status-size);    height: var(--avatar-status-size);    border-radius: 999px;    background: var(--avatar-status, var(--color-success));    box-shadow: 0 0 0 2px var(--avatar-ring, var(--surface-color, var(--color-surface-base)));    @media (forced-colors: active) {      background: Highlight;      forced-color-adjust: none;    }  }  /*   * A stack: each avatar overlaps the one before it by a quarter of its size and paints in front of it,   * separated by a 2px ring of the surface color. Order follows the markup in both writing directions.   */  .avatar-group {    display: inline-flex;    align-items: center;    isolation: isolate;    vertical-align: middle;    .avatar {      box-shadow: 0 0 0 2px var(--avatar-ring, var(--surface-color, var(--color-surface-base)));      &:not(:first-child) {        margin-inline-start: calc(var(--avatar-size) * -.25);      }    }  }}