Lunia DS

Base components

Avatar profile photo

A large avatar for profile pages and headers, 72, 96 or 160px with a white ring, which fades the photo in once it loads, like Avatar, and can show a verified tick.

Loading preview

The three sizes side by side with a photo and the verified tick, then initials and the user icon fallbacks at size md, and a profile header with the lg photo over a cover band. Replay loads the photos again.

Installation

Run this in your app after the one-time setup on the Getting started page.

$ npx @ganisigit/lunia-ds add avatar-profile-photo

Usage

The demo above, as code.

demo.tsx
'use client';

import { AvatarProfilePhoto } from './avatar-profile-photo';

// A self-drawn placeholder portrait (a soft head and shoulders on a tinted card), so the demo needs no photos.
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="#d9ccf5"/><circle cx="32" cy="26" r="11" fill="#8b6fd6"/><path d="M10 64c2-13 11-21 22-21s20 8 22 21z" fill="#8b6fd6"/></svg>',
)}`;

export default function AvatarProfilePhotoDemo({ replay = 0 }: { replay?: number }) {
  return (
    // The key remounts the photos on Replay, so they load and fade in again.
    <div key={replay} className="flex w-full max-w-xl flex-col items-center gap-10">
      <div className="flex flex-wrap items-end justify-center gap-6">
        <AvatarProfilePhoto size="sm" src={PHOTO} name="Olivia Rhye" verified />
        <AvatarProfilePhoto size="md" src={PHOTO} name="Olivia Rhye" verified />
        <AvatarProfilePhoto size="lg" src={PHOTO} name="Olivia Rhye" verified />
      </div>
      <div className="flex items-center justify-center gap-6">
        <AvatarProfilePhoto name="Olivia Rhye" />
        <AvatarProfilePhoto name="Olivia Rhye" placeholder="icon" />
      </div>
      <div className="w-full overflow-hidden rounded-xl border border-secondary bg-primary">
        <div className="h-24 bg-linear-to-r from-utility-brand-200 to-utility-pink-200" />
        <div className="flex items-end gap-4 px-6 pb-6">
          <AvatarProfilePhoto size="lg" src={PHOTO} name="Olivia Rhye" verified className="-mt-20 shrink-0" />
          <div className="flex flex-col">
            <span className="text-lg font-semibold text-primary">Olivia Rhye</span>
            <span className="text-sm text-tertiary">Product designer</span>
          </div>
        </div>
      </div>
    </div>
  );
}

API

Parts: root (ring: bg-primary fill, 1px border-secondary_alt, 3, 4 or 6px padding by size), content (photo, initials or user icon on bg-tertiary, with a thin contrast border), verified tick (bottom right).

PropTypeDefaultDescription
srcstringundefinedPhoto URL. While it loads, or when it fails, the fallback shows.
altstringundefinedAccessible name. Defaults to name.
namestringundefinedPerson name. Gives the initials and the default alt.
size"sm" | "md" | "lg""md"72, 96 or 160px.
placeholder"text" | "icon""text"What the fallback shows: the initials, or a user icon. With no name, the icon always shows.
verifiedbooleanfalseShows the verified tick (20, 24 or 32px by size) and adds "verified" to the accessible name.
SlotDescription
badgeReplaces the verified tick, for example with an edit button placed by the app.

Motion

Every row names a token from the Motion page. The last column says what happens when the user asks for reduced motion.

IDTriggerElementPropertyFrom → toTimingReduced motion
APH1photo loadedphotoopacity0 → 1 (avatar AVT1)duration-md (260)ease-outShows at once
APH2photo still loading after the delay, or failedfallback (initials or icon)opacity0 → 1 (avatar AVT2)duration-sm (200)ease-outdelay duration-2xs (120)Shows at once
APH3verified -> true after first renderverified tickopacity, scale0, scale-swap → 1, 1 (avatar AVT4)duration-md (260)ease-outShows at once

Accessibility

  • With a photo, the image has alt set to alt or name; with no photo, the root has role img and aria-label, and the initials or icon are aria-hidden.
  • Verified is part of the name, not the icon only: for example "Olivia Rhye, verified". The tick itself is aria-hidden.
  • The ring and shadow are decoration; the component is not focusable unless the app wraps it in a link or button, which then shows the focus ring.
  • Reduced motion: the photo, fallback and tick show at once.