Home

Avatar

A person's round picture, or their own colour gradient when there is no photo.

ui.avatarcomponents/ui/avatar.tsx

Use case

For the AI

Use an avatar wherever a teammate appears: the person assigned to an order in a table row, the author of a note on a client's history, the account button in the top bar. With no photo, AvatarFallback takes the person's email as seed and draws their gradient, the same one on every screen. Several people on one job overlap as a group.

Use it when

  • A teammate beside their name in a row or a comment
  • Who an order or a task is assigned to
  • The signed-in person's account button
  • Several assignees at once, overlapped

Use something else when

  • Switching between teams or workspaces: Team selector
  • Showing more about the person when the pointer rests on them: Preview card
  • The person's row is still loading: Skeleton
  • Labelling a row with a status or a category, not a person: Badge

Examples

A face, or the person's own gradient (picked from their email, no letters)
+3
A group: assignees, overlapping, each ringed in the ground

Code

Import

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"

Usage

export function Assignee({ name, email, photo }: { name: string; email: string; photo?: string }) {
  return (
    <div className="flex items-center gap-2">
      <Avatar className="size-8">
        {photo ? <AvatarImage src={photo} alt="" /> : null}
        <AvatarFallback seed={email} />
      </Avatar>
      <span className="text-[13px]">{name}</span>
    </div>
  )
}

export function Assignees({ emails, max = 4 }: { emails: string[]; max?: number }) {
  const more = emails.length - max
  return (
    <div className="flex -space-x-2">
      {emails.slice(0, max).map((email) => (
        <Avatar key={email} className="size-8 ring-2 ring-surface">
          <AvatarFallback seed={email} />
        </Avatar>
      ))}
      {more > 0 && (
        <span className="relative grid size-8 place-items-center rounded-full bg-surface-3 text-[11px] font-medium text-ink-3 tabular-nums ring-2 ring-surface">
          +{more}
        </span>
      )}
    </div>
  )
}

Rules

  • A person with no photo is drawn with AvatarFallback seed= (their email, else their name): their gradient from lib/avatar-gradient.ts, never initials in a grey circle.
  • Keep AvatarFallback beside AvatarImage: it shows while the photo loads and if it fails.
  • Size it with a size class on Avatar: size-6, size-8 (the default) or size-10.
  • In a group each avatar carries ring-2 ring-surface and the row overlaps them with -space-x-2; a +3 count is a plain round span on an opaque ground (bg-surface-3), never a translucent fill the face behind would show through.
  • AvatarImage takes alt="" when the name is printed beside it, so it is not read twice.