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.
Live preview
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-photoUsage
The demo above, as code.
'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).
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | undefined | Photo URL. While it loads, or when it fails, the fallback shows. |
| alt | string | undefined | Accessible name. Defaults to name. |
| name | string | undefined | Person 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. |
| verified | boolean | false | Shows the verified tick (20, 24 or 32px by size) and adds "verified" to the accessible name. |
| Slot | Description |
|---|---|
| badge | Replaces 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.
| ID | Trigger | Element | Property | From → to | Timing | Reduced motion |
|---|---|---|---|---|---|---|
| APH1 | photo loaded | photo | opacity | 0 → 1 (avatar AVT1) | duration-md (260)ease-out | Shows at once |
| APH2 | photo still loading after the delay, or failed | fallback (initials or icon) | opacity | 0 → 1 (avatar AVT2) | duration-sm (200)ease-outdelay duration-2xs (120) | Shows at once |
| APH3 | verified -> true after first render | verified tick | opacity, scale | 0, scale-swap → 1, 1 (avatar AVT4) | duration-md (260)ease-out | Shows 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.