Monet
@etched/monet 0.0.1

Avatar

Ported

A person's photo in a circle, or their initials. Native <span> attributes and ref pass through.

import { Avatar } from "@etched/monet/avatar";

// A stand-in photo, so the page needs no network.
const photo = `data:image/svg+xml,${encodeURIComponent(
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" fill="#c3eac9"/><circle cx="32" cy="26" r="12" fill="#194f29"/><rect x="12" y="42" width="40" height="30" rx="20" fill="#194f29"/></svg>',
)}`;

export function Preview() {
  return (
    <div className="row">
      <Avatar name="Ana Ruiz" size="lg" />
      <Avatar name="Lee Park" src={photo} size="lg" />
      <Avatar name="José Núñez" size="lg" />
    </div>
  );
}

Installation

import { Avatar, AvatarGroup } from "@etched/monet/avatar";

Examples

Initials

import { Avatar, type AvatarSize } from "@etched/monet/avatar";

const sizes: AvatarSize[] = ["xs", "sm", "base", "lg"];

export function Initials() {
  return (
    <div className="row">
      {sizes.map((size) => (
        <Avatar key={size} name="Ana Ruiz" size={size} />
      ))}
    </div>
  );
}

Photo

import { Avatar, type AvatarSize } from "@etched/monet/avatar";

const sizes: AvatarSize[] = ["xs", "sm", "base", "lg"];

// A stand-in photo, so the page needs no network.
const photo = `data:image/svg+xml,${encodeURIComponent(
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" fill="#c3eac9"/><circle cx="32" cy="26" r="12" fill="#194f29"/><rect x="12" y="42" width="40" height="30" rx="20" fill="#194f29"/></svg>',
)}`;

export function Photo() {
  return (
    <div className="row">
      {sizes.map((size) => (
        <Avatar key={size} name="Lee Park" src={photo} size={size} />
      ))}
    </div>
  );
}

A photo that fails shows the initials

import { Avatar } from "@etched/monet/avatar";

export function APhotoThatFailsShowsTheInitials() {
  return (
    <div className="row">
      <Avatar name="José Núñez" src="data:image/png;base64,broken" />
    </div>
  );
}

Beside the name: alt="" so the name is read once

Mira Osei
import { Avatar } from "@etched/monet/avatar";

export function BesideTheNameAltSoTheNameIsReadOnce() {
  return (
    <div className="row">
      <Avatar name="Mira Osei" alt="" size="sm" />
      <span>Mira Osei</span>
    </div>
  );
}

Group

AvatarGroup overlaps the avatars. With more people than max (4 by default, the count included), a "+N" circle stands for the rest, and screen readers read their names. moreLabel writes that name, for example in another language. Name the group with aria-label.

+3
+4
import { AvatarGroup } from "@etched/monet/avatar";

// A stand-in photo, so the page needs no network.
const photo = `data:image/svg+xml,${encodeURIComponent(
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" fill="#c3eac9"/><circle cx="32" cy="26" r="12" fill="#194f29"/><rect x="12" y="42" width="40" height="30" rx="20" fill="#194f29"/></svg>',
)}`;

const team = [{ name: "Priya Shah" }, { name: "Tom Becker", src: photo }, { name: "Noor Haddad" }, { name: "Wei Chen" }, { name: "Dana Cole" }, { name: "Omar Farah" }];

export function Group() {
  return (
    <div className="rows">
      <AvatarGroup aria-label="Interviewers" people={team} size="sm" />
      <AvatarGroup aria-label="Hiring team" people={team.slice(0, 3)} />
      <AvatarGroup aria-label="Referrers" people={team} max={3} size="lg" moreLabel={(names) => `${names.length} more referrers: ${names.join(", ")}`} />
    </div>
  );
}

Accessibility

  • An img, named by name, or by alt when set; alt="" hides it. The photo and the initials are hidden; the name stands for both.
  • AvatarGroup is a group. Past max, the "+N" circle is an img named by the hidden names: "3 more: Wei Chen, Dana Cole, and Omar Farah".
  • Set alt="" when the name shows next to the avatar, so it is not read twice.
  • Name an AvatarGroup with aria-label, such as "Interviewers". Pass translated text to moreLabel.

API reference

AvatarGroup

PropTypeDefaultDescription
people *Omit<AvatarProps, "size">[]–The people, in order. Each takes Avatar's props, such as name and src.
maxnumber4The most circles to show, the count included. With more people, the last circle is "+N".
size"xs" | "sm" | "base" | "lg""base"For every avatar and the count.
moreLabel(names: string[]) => string"2 more: Lee Park and Mira Osei"Names the "+N" circle from the names of the people it stands for. Pass translated text.

Avatar

PropTypeDefaultDescription
name *string–The person's name. It names the avatar, and its first two words give the initials.
srcstring–Photo URL. Until it loads, or when it fails, the initials show.
size"xs" | "sm" | "base" | "lg""base"
altstringname"" when the name is already beside the avatar, so screen readers do not read it twice.