Avatar

A user, company or AI representation — photo, initials or icon, with optional status, grouping and label.

Examples

Image

Add an img.avatar__image to show a photo. alt supplies the accessible name.

<div class="kompan-avatar kompan-avatar_lg">
  <img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1656019674844-3040aba0350b?q=80&w=150&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="Mette Hansen" />
</div>

Initials

With no image, show initials in avatar__initials — keep them to two characters.

<div style="display:flex;align-items:center;gap:0.75rem">
  <div class="kompan-avatar"><span class="kompan-avatar__initials">MH</span></div>
  <div class="kompan-avatar"><span class="kompan-avatar__initials">LB</span></div>
</div>

Fallbacks

With neither image nor initials, show a user icon. The avatar_ai modifier renders the KOMPAN smiley on brand red.

<div style="display:flex;align-items:center;gap:0.75rem">
  <div class="kompan-avatar"><span class="kompan-avatar__icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg></span></div>
  <div class="kompan-avatar kompan-avatar_ai"><span class="kompan-avatar__icon"><svg viewBox="0 0 26 26" fill="none" aria-hidden="true"><path d="M8 13.33S9.75 15.33 12.67 15.33 17.33 13.33 17.33 13.33M16.33 6.67H16.35M9 6.67H9.01M6 20.67V23.78c0 .71 0 1.07.15 1.25.12.16.32.25.52.25.23 0 .51-.22 1.06-.66l3.18-2.55c.65-.52.97-.78 1.34-.96.32-.17.66-.29 1.01-.36.4-.08.81-.08 1.65-.08h3.36c2.24 0 3.36 0 4.22-.44.75-.38 1.36-.99 1.75-1.75.44-.86.44-1.98.44-4.22V7.07c0-2.24 0-3.36-.44-4.22-.38-.75-.99-1.36-1.75-1.75-.86-.44-1.98-.44-4.22-.44H7.07c-2.24 0-3.36 0-4.22.44-.75.38-1.36.99-1.75 1.75-.44.86-.44 1.98-.44 4.22v8.27c0 1.24 0 1.86.14 2.37.37 1.38 1.45 2.46 2.83 2.83.51.14 1.13.14 2.37.14Z" stroke="currentColor" stroke-width="1.33" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
</div>

Sizes

Six sizes via modifiers: avatar_xs, avatar_sm, avatar_md (default), avatar_lg, avatar_xl and avatar_2xl.

<div style="display:flex;align-items:center;gap:0.75rem">
  <div class="kompan-avatar kompan-avatar_xs"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=160&h=160&fit=crop&crop=faces&q=80" alt="Mette Hansen" /></div>
  <div class="kompan-avatar kompan-avatar_sm"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=160&h=160&fit=crop&crop=faces&q=80" alt="Mette Hansen" /></div>
  <div class="kompan-avatar kompan-avatar_md"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=160&h=160&fit=crop&crop=faces&q=80" alt="Mette Hansen" /></div>
  <div class="kompan-avatar kompan-avatar_lg"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=160&h=160&fit=crop&crop=faces&q=80" alt="Mette Hansen" /></div>
  <div class="kompan-avatar kompan-avatar_xl"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=160&h=160&fit=crop&crop=faces&q=80" alt="Mette Hansen" /></div>
  <div class="kompan-avatar kompan-avatar_2xl"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=160&h=160&fit=crop&crop=faces&q=80" alt="Mette Hansen" /></div>
</div>

Status icon

Add an avatar__status element with a state modifier: online, verified or company.

<div style="display:flex;align-items:center;gap:1.25rem">
  <div class="kompan-avatar kompan-avatar_lg"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=160&h=160&fit=crop&crop=faces&q=80" alt="Mette Hansen" /><span class="kompan-avatar__status kompan-avatar__status_online"><span class="sr-only">Online</span></span></div>
  <div class="kompan-avatar kompan-avatar_lg"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=160&h=160&fit=crop&crop=faces&q=80" alt="Lars Berg" /><span class="kompan-avatar__status kompan-avatar__status_verified"><span class="sr-only">Verified</span></span></div>
  <div class="kompan-avatar kompan-avatar_lg"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=160&h=160&fit=crop&crop=faces&q=80" alt="Anja So" /><span class="kompan-avatar__status kompan-avatar__status_company"><span class="sr-only">Company</span></span></div>
</div>

Label group

Pair an avatar with a name and supporting email using avatar-label.

<div class="kompan-avatar-label">
  <div class="kompan-avatar"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=160&h=160&fit=crop&crop=faces&q=80" alt="Luis Enrique" /><span class="avatar__status avatar__status_verified"><span class="sr-only">Verified</span></span></div>
  <div class="kompan-avatar-label__body">
    <div class="kompan-avatar-label__name">Luis Enrique</div>
    <div class="kompan-avatar-label__email">luienr@kompan.com</div>
  </div>
</div>

Group

Overlap avatars in avatar-group, collapse the overflow into a +N tile, and offer a trailing add button.

<div class="kompan-avatar-group">
  <div class="kompan-avatar"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=160&h=160&fit=crop&crop=faces&q=80" alt="Mette Hansen" /></div>
  <div class="kompan-avatar"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=160&h=160&fit=crop&crop=faces&q=80" alt="Lars Berg" /></div>
  <div class="kompan-avatar"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=160&h=160&fit=crop&crop=faces&q=80" alt="Anja So" /></div>
  <div class="kompan-avatar"><img class="kompan-avatar__image" src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=160&h=160&fit=crop&crop=faces&q=80" alt="Tom Vej" /></div>
  <span class="kompan-avatar-group__overflow">+2</span>
  <button class="kompan-avatar-group__add" aria-label="Add member"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="M12 5v14"/></svg></button>
</div>

Accessibility

Alternative text

Give kompan-avatar__image an alt that names the person. When the avatar sits next to a visible name (inside kompan-avatar-label), set alt="" so the name is not announced twice.

Initials and icon fallbacks are decorative

The kompan-avatar__initials text and the kompan-avatar__icon carry no information on their own. Make sure the person is still named somewhere — a nearby label, or an aria-label on the avatar container.

Status icons need a text equivalent

kompan-avatar__status conveys meaning through colour and shape only, which screen readers cannot see. Add visually-hidden text (class sr-only) such as “Online” or “Verified” inside the status element, or describe it in the surrounding label.

Interactive group controls

In an kompan-avatar-group, the add button must have an aria-label since it shows only a “+” icon, and the “+N” overflow tile should read as a count, e.g. aria-label="2 more".

Contrast and focus

Avatars sit on white surfaces, so the faint contrast ring keeps light photos legible. Any avatar rendered as a button (the add control) must keep a visible focus ring for keyboard users.

Stylesheet

Shipped as @wah-kompan/kompan-ui/components/avatar.css. Every value is a token reference, so it themes with the rest of the system rather than being restyled.

css
.kompan-avatar {
  --kompan-avatar-size: 2.5rem;
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
  width: var(--kompan-avatar-size);
  height: var(--kompan-avatar-size);
  font-size: var(--6-typography-font-size-text-sm);
  font-weight: var(--6-typography-font-weight-semibold);
  vertical-align: middle;
}
.kompan-avatar_xs  { --kompan-avatar-size: 1.5rem; font-size: var(--6-typography-font-size-text-xxs); }
.kompan-avatar_sm  { --kompan-avatar-size: 2rem;   font-size: var(--6-typography-font-size-text-xs); }
.kompan-avatar_md  { --kompan-avatar-size: 2.5rem; font-size: var(--6-typography-font-size-text-sm); }
.kompan-avatar_lg  { --kompan-avatar-size: 3rem;   font-size: var(--6-typography-font-size-text-md); }
.kompan-avatar_xl  { --kompan-avatar-size: 3.5rem; font-size: var(--6-typography-font-size-text-lg); }
.kompan-avatar_2xl { --kompan-avatar-size: 4rem;   font-size: var(--6-typography-font-size-text-xl); }

.kompan-avatar__image,
.kompan-avatar__initials,
.kompan-avatar__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  overflow: hidden;
  border-radius: var(--2-radius-general-radius-full);
  box-shadow: inset 0 0 0 var(--7-stroke-weights-stroke-weight-stroke-weight-sm)
    var(--1-color-mode-sections-default-border-border-secondary);
  background: var(--1-color-mode-sections-default-background-bg-secondary);
  color: var(--1-color-mode-sections-default-foreground-fg-primary);
  user-select: none;
}
.kompan-avatar__image { object-fit: cover; }
.kompan-avatar__icon { color: var(--1-color-mode-sections-default-foreground-fg-tertiary); }
.kompan-avatar__icon svg { width: 55%; height: 55%; }
.kompan-avatar_ai .kompan-avatar__icon {
  background: var(--1-color-mode-sections-default-background-bg-brand-solid);
  color: var(--1-color-mode-sections-default-button-primary-fg);
}

.kompan-avatar__status {
  position: absolute;
  right: 0;
  bottom: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 35%;
  height: 35%;
  border-radius: var(--2-radius-general-radius-full);
  box-shadow: 0 0 0 var(--7-stroke-weights-stroke-weight-stroke-weight-md)
    var(--1-color-mode-sections-default-background-bg-primary);
  color: var(--1-color-mode-sections-default-text-text-solid);
  font-size: 0.62em;
  line-height: 1;
}
.kompan-avatar__status svg { width: 64%; height: 64%; }
.kompan-avatar__status_online   { background: var(--1-color-mode-semantic-background-bg-success-solid); }
.kompan-avatar__status_verified { background: var(--1-color-mode-utility-blue-utility-blue-600); }
.kompan-avatar__status_verified::after {
  content: '✓';
  font-weight: var(--6-typography-font-weight-bold);
}
.kompan-avatar__status_company {
  background: var(--1-color-mode-sections-default-background-bg-solid);
  border-radius: 30%;
}

.kompan-avatar-group { display: inline-flex; align-items: center; }
.kompan-avatar-group > * {
  border-radius: var(--2-radius-general-radius-full);
  box-shadow: 0 0 0 var(--7-stroke-weights-stroke-weight-stroke-weight-md)
    var(--1-color-mode-sections-default-background-bg-primary);
}
.kompan-avatar-group > * + * { margin-left: calc(var(--3-spacing-spacing-md) * -1); }
.kompan-avatar-group__overflow,
.kompan-avatar-group__add {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: var(--2-radius-general-radius-full);
  font-size: var(--6-typography-font-size-text-sm);
  font-weight: var(--6-typography-font-weight-semibold);
}
.kompan-avatar-group__overflow {
  background: var(--1-color-mode-sections-default-background-bg-secondary);
  color: var(--1-color-mode-sections-default-foreground-fg-primary);
}
.kompan-avatar-group__add {
  background: transparent;
  color: var(--1-color-mode-sections-default-foreground-fg-tertiary);
  border: var(--7-stroke-weights-stroke-weight-stroke-weight-sm) dashed
    var(--1-color-mode-sections-default-border-border-secondary);
  cursor: pointer;
}
.kompan-avatar-group__add svg { width: 45%; height: 45%; }

.kompan-avatar-label {
  display: inline-flex;
  align-items: center;
  gap: var(--3-spacing-spacing-lg);
}
.kompan-avatar-label__body { min-width: 0; line-height: 1.25; }
.kompan-avatar-label__name {
  font-size: var(--6-typography-font-size-text-sm);
  font-weight: var(--6-typography-font-weight-semibold);
  color: var(--1-color-mode-sections-default-foreground-fg-primary);
}
.kompan-avatar-label__email {
  font-size: var(--6-typography-font-size-text-sm);
  color: var(--1-color-mode-sections-default-foreground-fg-tertiary);
}