Components · Display
Avatar
A photo, or initials when there is none.
ALGHAT
- ALAda LovelaceOwner
- GHGrace HopperAdmin
ALGHAT
- ALAda LovelaceOwner
- GHGrace HopperAdmin
Both previews render the same markup; the HTML tab below is that markup without a framework.
Install
- React
- npx shadcn@
latest add https:// www.themes.dev/ caseroom/ r/ avatar.json - Astro
- npx shadcn@
latest add https:// www.themes.dev/ caseroom/ r/ astro/ avatar.json
Or copy the files below into your project. They need clsx, tailwind-merge, and the Caseroom theme. The HTML needs only the compiled CSS.
import type { HTMLAttributes } from 'react';
import { cn } from '@/lib/utils';
import { avatarVariants, initials, type AvatarVariants } from './avatar.variants';
export type AvatarProps = HTMLAttributes<HTMLSpanElement> & AvatarVariants & { name: string; src?: string };
/** A photo, or the initials of `name` when there is none. The name is the accessible label either way. */
export function Avatar({ name, src, size, className, ...props }: AvatarProps) {
return (
<span className={cn(avatarVariants({ size }), className)} {...props}>
{src ? <img src={src} alt={name} className="size-full object-cover" /> : <span role="img" aria-label={name}>{initials(name)}</span>}
</span>
);
}import { cva, type VariantProps } from 'class-variance-authority';
export const avatarVariants = cva(
'relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground ring-1 ring-border',
{
variants: { size: { sm: 'size-6 text-[10px]', default: 'size-8 text-xs', lg: 'size-11 text-sm' } },
defaultVariants: { size: 'default' },
},
);
export type AvatarVariants = VariantProps<typeof avatarVariants>;
/** "Ada Lovelace" → "AL". */
export function initials(name: string) {
return name
.split(/\s+/)
.filter(Boolean)
.slice(0, 2)
.map((part) => part[0]!.toUpperCase())
.join('');
}---
import type { HTMLAttributes } from 'astro/types';
import { cn } from '@/lib/utils';
import { avatarVariants, initials, type AvatarVariants } from './avatar.variants';
type Props = HTMLAttributes<'span'> & AvatarVariants & { name: string; src?: string };
const { name, src, size, class: className, ...rest } = Astro.props;
---
<span class={cn(avatarVariants({ size }), className)} {...rest}>
{src ? <img src={src} alt={name} class="size-full object-cover" /> : <span role="img" aria-label={name}>{initials(name)}</span>}
</span>import { cva, type VariantProps } from 'class-variance-authority';
export const avatarVariants = cva(
'relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground ring-1 ring-border',
{
variants: { size: { sm: 'size-6 text-[10px]', default: 'size-8 text-xs', lg: 'size-11 text-sm' } },
defaultVariants: { size: 'default' },
},
);
export type AvatarVariants = VariantProps<typeof avatarVariants>;
/** "Ada Lovelace" → "AL". */
export function initials(name: string) {
return name
.split(/\s+/)
.filter(Boolean)
.slice(0, 2)
.map((part) => part[0]!.toUpperCase())
.join('');
}<div class="grid w-full max-w-xs gap-5">
<div class="flex items-center gap-3">
<span class="relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground ring-1 ring-border size-11 text-sm">
<span role="img" aria-label="Ada Lovelace">AL</span>
</span>
<span class="relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground ring-1 ring-border size-8 text-xs">
<span role="img" aria-label="Grace Hopper">GH</span>
</span>
<span class="relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground ring-1 ring-border size-6 text-[10px]">
<span role="img" aria-label="Alan Turing">AT</span>
</span>
</div>
<ul class="grid gap-3 text-sm">
<li class="flex items-center gap-3">
<span class="relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground ring-1 ring-border size-8 text-xs">
<span role="img" aria-label="Ada Lovelace">AL</span>
</span>
<span class="min-w-0"><span class="block font-medium">Ada Lovelace</span><span class="block text-muted-foreground">Owner</span></span>
</li>
<li class="flex items-center gap-3">
<span class="relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground ring-1 ring-border size-8 text-xs">
<span role="img" aria-label="Grace Hopper">GH</span>
</span>
<span class="min-w-0"><span class="block font-medium">Grace Hopper</span><span class="block text-muted-foreground">Admin</span></span>
</li>
</ul>
</div>