# Avatar

A round picture of a person with a fallback while it loads or when it fails, in three sizes, plus an overlapping group.

Docs: https://corsairui.vercel.app/docs/avatar

## Install

```bash
pnpm dlx shadcn@latest add @corsair-ui/avatar
```

Also adds: `utils`.
npm: `@radix-ui/react-avatar`, `class-variance-authority`, `clsx`, `tailwind-merge`.

## API

Built on Radix Avatar (https://www.radix-ui.com/primitives/docs/components/avatar); its props pass through.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `size` | `"sm" \| "default" \| "lg"` | `"default"` | 24, 32 or 48 px. |

## Styling

Restyle it in the project that installs it: `className` on each part, the theme variables, and these attributes with arbitrary variants (`[&_[data-slot=…]]:…`, `data-[state=…]:…`).

data-slot: `avatar`, `avatar-fallback`, `avatar-group`, `avatar-image`

State attributes:

- `data-size`: `default`

## Accessibility

- AvatarImage needs an alt. The fallback shows until the image loads, and if it fails.

## Examples

On the docs page: Sizes and fallback, Group. The usage examples in the source comments below are the reference.

## Source

What `shadcn add` writes into the project:

### avatar.tsx

```tsx
"use client";

import * as AvatarPrimitive from "@radix-ui/react-avatar";
import { cva, type VariantProps } from "class-variance-authority";
import type { ComponentProps } from "react";
import { cn } from "@/registry/default/lib/utils";

const avatarVariants = cva("relative flex shrink-0 overflow-hidden rounded-full", {
  variants: {
    size: {
      sm: "size-6 text-[0.625rem]",
      default: "size-8 text-xs",
      lg: "size-12 text-base",
    },
  },
  defaultVariants: {
    size: "default",
  },
});

interface AvatarProps
  extends ComponentProps<typeof AvatarPrimitive.Root>, VariantProps<typeof avatarVariants> {}

/**
 * A round picture of a person or an account, with a fallback (initials or an
 * icon) shown while the image loads or when it fails. Give the image an
 * `alt` with the person's name; when the fallback shows instead, its text is
 * what screen readers read, so keep it meaningful or add an `aria-label`.
 *
 * @example
 * <Avatar>
 *   <AvatarImage src="/people/ada.jpg" alt="Ada Lovelace" />
 *   <AvatarFallback>AL</AvatarFallback>
 * </Avatar>
 */
function Avatar({ className, size, ...props }: AvatarProps) {
  return (
    <AvatarPrimitive.Root
      data-slot="avatar"
      data-size={size ?? "default"}
      className={cn(avatarVariants({ size }), className)}
      {...props}
    />
  );
}

function AvatarImage({ className, ...props }: ComponentProps<typeof AvatarPrimitive.Image>) {
  return (
    <AvatarPrimitive.Image
      data-slot="avatar-image"
      className={cn("aspect-square size-full object-cover", className)}
      {...props}
    />
  );
}

/** Shown until the image has loaded, and in its place when it cannot load. */
function AvatarFallback({ className, ...props }: ComponentProps<typeof AvatarPrimitive.Fallback>) {
  return (
    <AvatarPrimitive.Fallback
      data-slot="avatar-fallback"
      className={cn(
        "bg-muted text-muted-foreground flex size-full items-center justify-center rounded-full font-medium uppercase select-none",
        className
      )}
      {...props}
    />
  );
}

/**
 * A row of overlapping avatars. Each one gets a ring in the background
 * colour so the edges stay readable where they overlap.
 */
function AvatarGroup({ className, ...props }: ComponentProps<"div">) {
  return (
    <div
      data-slot="avatar-group"
      className={cn(
        "[&>[data-slot=avatar]]:ring-background flex -space-x-2 [&>[data-slot=avatar]]:ring-2",
        className
      )}
      {...props}
    />
  );
}

export { Avatar, AvatarFallback, AvatarGroup, AvatarImage, avatarVariants, type AvatarProps };
```
