Avatar

Image and fallback initials primitive for identity surfaces such as profiles and assignees.

Name, fallback, and sizes

The name supplies the accessible identity and fallback supplies visible initials when no image is available. md stands alone, sm sits in a row of people, and xs names someone inline in small text.

? ? ?

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-avatar/ui-avatar.html"
>

<ui-avatar
  name="Maya Chen"
> </ui-avatar>
<ui-avatar
  name="Maya Chen"
  size="sm"
> </ui-avatar>
<ui-avatar
  name="Maya Chen"
  size="xs"
> </ui-avatar>
<script setup>
  import { UiAvatar } from "@threadlabs/looma";
</script>

<template>
  <UiAvatar
    name="Maya Chen"
  > </UiAvatar>
  <UiAvatar
    name="Maya Chen"
    size="sm"
  > </UiAvatar>
  <UiAvatar
    name="Maya Chen"
    size="xs"
  > </UiAvatar>
</template>
import { UiAvatar } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiAvatar
      name="Maya Chen"
    > </UiAvatar>
    <UiAvatar
      name="Maya Chen"
      size="sm"
    > </UiAvatar>
    <UiAvatar
      name="Maya Chen"
      size="xs"
    > </UiAvatar>
  </>;
}
/** @jsxImportSource preact */
import { UiAvatar } from "@threadlabs/looma";

export default function Example() {
  return <>
    <UiAvatar
      name="Maya Chen"
    > </UiAvatar>
    <UiAvatar
      name="Maya Chen"
      size="sm"
    > </UiAvatar>
    <UiAvatar
      name="Maya Chen"
      size="xs"
    > </UiAvatar>
  </>;
}
<script>
  import { UiAvatar } from "@threadlabs/looma";
</script>

<UiAvatar
  name="Maya Chen"
> </UiAvatar>
<UiAvatar
  name="Maya Chen"
  size="sm"
> </UiAvatar>
<UiAvatar
  name="Maya Chen"
  size="xs"
> </UiAvatar>
import { Component } from "@angular/core";
import { UiAvatar } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiAvatar],
  template: `
    <span
      ui-avatar
      [name]="'Maya Chen'"
    > </span>
    <span
      ui-avatar
      [name]="'Maya Chen'"
      [size]="'sm'"
    > </span>
    <span
      ui-avatar
      [name]="'Maya Chen'"
      [size]="'xs'"
    > </span>
  `,
})
export default class Example {}

Image

Place an ordinary image inside Avatar; its own alt text supplies the accessible name and the fallback remains available if loading fails.

Maya Chen ?

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-avatar/ui-avatar.html"
>

<ui-avatar
  name="Maya Chen"
  fallback="MC"
> <img
    src="/_looma/portrait.svg"
    alt="Maya Chen"
  > </ui-avatar>
<script setup>
  import { UiAvatar } from "@threadlabs/looma";
</script>

<template>
  <UiAvatar
    name="Maya Chen"
    fallback="MC"
  > <img
      src="/_looma/portrait.svg"
      alt="Maya Chen"
    > </UiAvatar>
</template>
import { UiAvatar } from "@threadlabs/looma";

export default function Example() {
  return <UiAvatar
    name="Maya Chen"
    fallback="MC"
  >{" "}<img
      src="/_looma/portrait.svg"
      alt="Maya Chen"
    />{" "}</UiAvatar>;
}
/** @jsxImportSource preact */
import { UiAvatar } from "@threadlabs/looma";

export default function Example() {
  return <UiAvatar
    name="Maya Chen"
    fallback="MC"
  >{" "}<img
      src="/_looma/portrait.svg"
      alt="Maya Chen"
    />{" "}</UiAvatar>;
}
<script>
  import { UiAvatar } from "@threadlabs/looma";
</script>

<UiAvatar
  name="Maya Chen"
  fallback="MC"
> <img
    src="/_looma/portrait.svg"
    alt="Maya Chen"
  > </UiAvatar>
import { Component } from "@angular/core";
import { UiAvatar } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiAvatar],
  template: `
    <span
      ui-avatar
      [name]="'Maya Chen'"
      [fallback]="'MC'"
    > <img
        src="/_looma/portrait.svg"
        alt="Maya Chen"
      > </span>
  `,
})
export default class Example {}

fallback="UX"

Fallback overrides the initials derived from name without requiring internal hooks or extra markup.

?

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-avatar/ui-avatar.html"
>

<ui-avatar
  name="Maya Chen"
  fallback="UX"
> </ui-avatar>
<script setup>
  import { UiAvatar } from "@threadlabs/looma";
</script>

<template>
  <UiAvatar
    name="Maya Chen"
    fallback="UX"
  > </UiAvatar>
</template>
import { UiAvatar } from "@threadlabs/looma";

export default function Example() {
  return <UiAvatar
    name="Maya Chen"
    fallback="UX"
  > </UiAvatar>;
}
/** @jsxImportSource preact */
import { UiAvatar } from "@threadlabs/looma";

export default function Example() {
  return <UiAvatar
    name="Maya Chen"
    fallback="UX"
  > </UiAvatar>;
}
<script>
  import { UiAvatar } from "@threadlabs/looma";
</script>

<UiAvatar
  name="Maya Chen"
  fallback="UX"
> </UiAvatar>
import { Component } from "@angular/core";
import { UiAvatar } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiAvatar],
  template: `
    <span
      ui-avatar
      [name]="'Maya Chen'"
      [fallback]="'UX'"
    > </span>
  `,
})
export default class Example {}

Active

A small group, as in a page header. A ring marks someone active now, such as editing the same page. The ring is not announced, so alt says what it means.

? ?

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-avatar-group/ui-avatar-group.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-avatar/ui-avatar.html"
>

<ui-avatar-group
  label="On this page"
  size="sm"
>
  <ui-avatar
    size="sm"
    name="Ada Lovelace"
    alt="Ada Lovelace, editing"
    active
  ></ui-avatar>
  <ui-avatar
    size="sm"
    name="Grace Hopper"
  ></ui-avatar>
</ui-avatar-group>
<script setup>
  import { UiAvatar, UiAvatarGroup } from "@threadlabs/looma";
</script>

<template>
  <UiAvatarGroup
    label="On this page"
    size="sm"
  >
    <UiAvatar
      size="sm"
      name="Ada Lovelace"
      alt="Ada Lovelace, editing"
      :active="true"
    />
    <UiAvatar
      size="sm"
      name="Grace Hopper"
    />
  </UiAvatarGroup>
</template>
import { UiAvatar, UiAvatarGroup } from "@threadlabs/looma";

export default function Example() {
  return <UiAvatarGroup
    label="On this page"
    size="sm"
  >
    <UiAvatar
      size="sm"
      name="Ada Lovelace"
      alt="Ada Lovelace, editing"
      active
    />
    <UiAvatar
      size="sm"
      name="Grace Hopper"
    />
  </UiAvatarGroup>;
}
/** @jsxImportSource preact */
import { UiAvatar, UiAvatarGroup } from "@threadlabs/looma";

export default function Example() {
  return <UiAvatarGroup
    label="On this page"
    size="sm"
  >
    <UiAvatar
      size="sm"
      name="Ada Lovelace"
      alt="Ada Lovelace, editing"
      active
    />
    <UiAvatar
      size="sm"
      name="Grace Hopper"
    />
  </UiAvatarGroup>;
}
<script>
  import { UiAvatar, UiAvatarGroup } from "@threadlabs/looma";
</script>

<UiAvatarGroup
  label="On this page"
  size="sm"
>
  <UiAvatar
    size="sm"
    name="Ada Lovelace"
    alt="Ada Lovelace, editing"
    active
  />
  <UiAvatar
    size="sm"
    name="Grace Hopper"
  />
</UiAvatarGroup>
import { Component } from "@angular/core";
import { UiAvatar, UiAvatarGroup } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiAvatarGroup, UiAvatar],
  template: `
    <div
      ui-avatar-group
      [label]="'On this page'"
      [size]="'sm'"
    >
      <span
        ui-avatar
        [size]="'sm'"
        [name]="'Ada Lovelace'"
        [alt]="'Ada Lovelace, editing'"
        [active]="true"
      ></span>
      <span
        ui-avatar
        [size]="'sm'"
        [name]="'Grace Hopper'"
      ></span>
    </div>
  `,
})
export default class Example {}

Shape and decoration

A square avatar follows the theme radius. An unnamed avatar shows a person glyph; decorative avatars are hidden from assistive technology.

? ? ?

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-avatar/ui-avatar.html"
>

<ui-cluster
  gap="s"
>
  <ui-avatar></ui-avatar>
  <ui-avatar
    name="Ada Lovelace"
    shape="square"
  ></ui-avatar>
  <ui-avatar
    name="Ada Lovelace"
    decorative
  ></ui-avatar>
</ui-cluster>
<script setup>
  import { UiAvatar, UiCluster } from "@threadlabs/looma";
</script>

<template>
  <UiCluster
    gap="s"
  >
    <UiAvatar />
    <UiAvatar
      name="Ada Lovelace"
      shape="square"
    />
    <UiAvatar
      name="Ada Lovelace"
      :decorative="true"
    />
  </UiCluster>
</template>
import { UiAvatar, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
  >
    <UiAvatar />
    <UiAvatar
      name="Ada Lovelace"
      shape="square"
    />
    <UiAvatar
      name="Ada Lovelace"
      decorative
    />
  </UiCluster>;
}
/** @jsxImportSource preact */
import { UiAvatar, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
  >
    <UiAvatar />
    <UiAvatar
      name="Ada Lovelace"
      shape="square"
    />
    <UiAvatar
      name="Ada Lovelace"
      decorative
    />
  </UiCluster>;
}
<script>
  import { UiAvatar, UiCluster } from "@threadlabs/looma";
</script>

<UiCluster
  gap="s"
>
  <UiAvatar />
  <UiAvatar
    name="Ada Lovelace"
    shape="square"
  />
  <UiAvatar
    name="Ada Lovelace"
    decorative
  />
</UiCluster>
import { Component } from "@angular/core";
import { UiAvatar, UiCluster } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiCluster, UiAvatar],
  template: `
    <div
      ui-cluster
      [gap]="'s'"
    >
      <span
        ui-avatar
      ></span>
      <span
        ui-avatar
        [name]="'Ada Lovelace'"
        [shape]="'square'"
      ></span>
      <span
        ui-avatar
        [name]="'Ada Lovelace'"
        [decorative]="true"
      ></span>
    </div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: span

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
activeMarks the person as active now, such as editing the same page: a fine accent ring around the avatar. The ring is not announced, so say what it means in alt ("Ada Lovelace, editing").activebooleanfalse—
altThe avatar's accessible name, announced for it as an image. When empty, name is used, then "Avatar". Initials come from name first, so alt supplies them only when name is empty. An authored img carries its own alt instead.altstring—
decorativeHides the avatar from assistive technology when adjacent text already identifies the person.decorativebooleanfalse—
fallbackText shown instead of the initials derived from name or alt, whenever there is no image. It is uppercased; keep it to a couple of characters, because the circle clips anything wider.fallbackstring—
nameThe person's name. The initials are drawn from it: the first letters of the first two words, or the first two letters of a single word. It is also the accessible name when alt is empty.namestring—
shapeCircle by default, with a shallow shadow; square uses the theme's medium radius.shapekeywordcirclecircle, square
sizemd is 2.5rem, for a person on their own (a profile, a comment); sm is 1.75rem with smaller initials, for a row of people in a toolbar or header; xs is 1.25rem with small initials, for a person named inline in a line of small text.sizekeywordmdxs, sm, md
srcThe image to show, cropped to fill the circle. Until it loads, or if it fails, the avatar shows initials instead. An img element authored inside the avatar takes precedence over src.srcstring——

Framework props

NameTypeDefaultAllowed values
activebooleanfalse—
altstring—
decorativebooleanfalse—
fallbackstring—
namestring—
shape"circle" | "square"circlecircle, square
size"xs" | "sm" | "md"mdxs, sm, md
srcstring——

Events

No events.

Slots

NamePurpose
defaultDefault child content.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

TokenDeclared valuesFallback
--ui-avatar-active-ring—var(--ui-accent-solid)
--ui-avatar-border—color-mix(in srgb, var(--ui-accent-solid) 35%, var(--ui-surface-default)) | var(--ui-border-strong)
--ui-avatar-radius—var(--ui-radius-round) | var(--ui-radius-md)
--ui-avatar-surface—var(--ui-accent-soft)
--ui-avatar-text—var(--ui-accent-subtle-text, var(--ui-accent-solid))

Shared tokens

TokenFallback
--ui-accent-soft—
--ui-accent-solid—
--ui-accent-subtle-textvar(--ui-accent-solid)
--ui-border-strong—
--ui-border-width—
--ui-font-size-md—
--ui-font-size-sm—
--ui-font-size-xs—
--ui-radius-md—
--ui-radius-round—
--ui-shadow-sm—
--ui-surface-default—