# Avatar (Lumen Halo): prompt.md (v1.0.0)

- id: `lumen-avatar` · version 1.0.0 · component · free
- category: Data
- build: Base UI (this item also ships a Radix build)
- install (this build): `npx shadcn@latest add https://beautiful-ui.dev/r/lumen-avatar.json`
- npm dependencies: @base-ui/react@^1
- registry dependencies: utils, https://beautiful-ui.dev/r/lumen-foundation.json
- docs: https://beautiful-ui.dev/components/lumen-avatar
- The install command carries everything this item needs (files, CSS, tokens, npm and registry dependencies). Prefer it to copying source by hand.

People as Lumen draws them: a metallic disc with initials, the photo when it loads, and a status light that glows in the dark. Groups overlap and count the rest. shadcn's Avatar, cut from Lumen glass.

## Build it
- Stack: React 19 (`ref` is a plain prop), TypeScript, Tailwind CSS v4 utilities, a shadcn-initialised project with the `@/*` alias.
- Packages: `@base-ui/react@^1` (Base UI build); `radix-ui@^1` (Radix build).
- Files: `components/ui/lumen/avatar.tsx`.
- Registry dependencies, installed with it automatically: shadcn `utils` (cn), `lumen-foundation`.
- Builds: separate Base UI and Radix files. Install the one that matches the project (see Install): a free item's bare URL installs the Base UI build of it and its dependencies.
- Exports to keep: `Avatar`, `AvatarImage / AvatarFallback`, `AvatarBadge`, `AvatarGroup / AvatarGroupCount`, and every exported type.
- CSS: the install merges this item's rules (the registry `css` field) into your global stylesheet, in `@layer components`, and adds the lumen foundation (tokens, keyframes, motion levels) once. Nothing to import by hand.
- Re-running `add` (or `--overwrite`) re-applies those rules: put overrides in your own CSS, never in the installed rules.
- Tokens: retheme with the `--lumen-*` custom properties (`--lumen-accent`, `--lumen-accent-text`, `--lumen-bad`, `--lumen-bad-text`, `--lumen-focus`, `--lumen-good`, `--lumen-good-text`, `--lumen-hairline`, `--lumen-ink`, `--lumen-muted-ink`, `--lumen-series-1`, `--lumen-series-2`, `--lumen-series-3`, `--lumen-series-4`, `--lumen-series-5`, `--lumen-series-6`, `--lumen-warn`, `--lumen-warn-text`); this item's CSS also reads `--lumen-font-mono`, `--lumen-font-sans`, `--lumen-radius-k`. Never add Tailwind colour classes inside the component.

```tsx
import { Avatar, AvatarBadge, AvatarFallback, AvatarImage } from "@/components/ui/lumen/avatar";
```

## Parts

| Part | data-slot | What it is for |
|---|---|---|
| `Avatar` | `avatar` | The disc. |
| `AvatarImage / AvatarFallback` | `avatar-image` | Photo or initials. |
| `AvatarBadge` | `avatar-badge` | The status light. |
| `AvatarGroup / AvatarGroupCount` | `avatar-group` | Stacks and counts. |

Style a part with `[data-slot="<slot>"]` selectors or its `className`; keep the attributes when editing.

## Sound
- Keep every `data-slot` and `data-sound` attribute: the sound layer reads them.
- Installing this item adds no audio. Nothing plays until the app mounts `GlassSoundProvider` once (install: `npx shadcn@latest add https://beautiful-ui.dev/r/glass-sound.json`, import from `@/components/beautiful-ui/glass-sound`); `GlassSoundToggle` is its mute control. Without a provider the audio engine never loads.

## Match the original
- Read `components/ui/lumen/avatar.tsx` as the reference implementation before changing or recreating anything, and match it: sizes, colours per theme, motion timings, copy and behaviour.
- If you deviate (a prop you can't honour, a style you changed, a dependency you swapped), say so in your reply, part by part.
- Keep the accessibility contract, the keyboard map and the motion levels listed below.

## Use it when
- avatar, profile picture, user, initials, team, presence, online status, avatar group, shadcn avatar, Lumen
- People and teams: rosters, comments, owners
- Presence: AvatarBadge as the status light
- Stacks: AvatarGroup with AvatarGroupCount

### Not when
- Company logos: use an image

## Mistakes
- Say the status in text too: the light alone is colour

## Usage

```tsx
import { Avatar, AvatarBadge, AvatarFallback, AvatarImage } from "@/components/ui/lumen/avatar";

<Avatar>
  <AvatarImage src={user.photo} alt="" />
  <AvatarFallback>MA</AvatarFallback>
  <AvatarBadge aria-label="Online" />
</Avatar>
```

## Props

| Prop | Type | Default | What it does |
|---|---|---|---|
| `Avatar size` | `"default" \| "sm" \| "lg"` | `"default"` | 32, 24 or 40px. |
| `AvatarImage / AvatarFallback` | `as shadcn` |  | The photo, and the initials while it loads or if it fails. |
| `AvatarBadge` | `<span>` |  | The status light; give it an aria-label ("Online"). |
| `AvatarGroup / AvatarGroupCount` | `<div>` |  | Overlapping avatars and a mono count. |

Full docs: https://beautiful-ui.dev/components/lumen-avatar

## Customising
- Colours: set the `--lumen-*` tokens on `:root`, or on a container with the `lumen-scope` class to retheme one area. Add the `lumen-inherit` class to follow your shadcn palette instead (`--chart-N`, `--destructive`).
- Dark mode follows the `.dark` class on an ancestor (the shadcn and next-themes convention).
- Update later by re-running the install with `--overwrite` (review the diff if you edited it). Changelog: https://beautiful-ui.dev/r/changelog.json

## Keyboard

| Keys | Action |
|---|---|
| — | Not focusable itself |

## Motion inventory

| Interaction | What moves |
|---|---|
| None | The photo replaces the initials when it loads |

## Accessibility contract (preserve when editing)
- The image's alt is empty when the name is shown next to it
- The status light needs its own label ("Online")
- Initials reach 4.5:1 on the disc in dark and light

## Install

Base UI project (a base-* style in components.json):

```bash
npx shadcn@latest add https://beautiful-ui.dev/r/lumen-avatar.json
```

Radix project (a radix-*, new-york or default style):

```bash
npx shadcn@latest add https://beautiful-ui.dev/r/radix-nova/lumen-avatar.json
```

Or add the `@beautiful-ui` registry to components.json and run `npx shadcn@latest add @beautiful-ui/lumen-avatar`: the CLI picks the build from your style.

## Credits
- Built on shadcn/ui (https://ui.shadcn.com)

## Source (Base UI build)

### components/ui/lumen/avatar.tsx

```tsx
"use client";

/**
 * Avatar (Lumen Halo) v1.0.0 · Beautiful UI
 * Docs: https://beautiful-ui.dev/components/lumen-avatar · Agent prompt: https://beautiful-ui.dev/md/lumen-avatar.md
 * Licensed to the purchaser under the Beautiful UI license: https://beautiful-ui.dev/license
 */

import * as React from "react";
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar";
import { cn } from "@/lib/utils";

/*
 * Lumen Avatar (Base UI build). shadcn's Avatar in Lumen Halo: a metallic disc with initials (silver in
 * dark, ink in light), the photo when it loads, and a status light that glows in dark. Groups overlap
 * with a ring of the ground and end in a mono count. Same exports, parts, data-slots and sizes.
 */

function Avatar({ className, size = "default", ...props }: AvatarPrimitive.Root.Props & { size?: "default" | "sm" | "lg" }) {
  return <AvatarPrimitive.Root data-slot="avatar" data-size={size} className={cn(`lumen-avatar`, className)} {...props} />;
}

function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
  return <AvatarPrimitive.Image data-slot="avatar-image" className={cn(`lumen-avatar-image`, className)} {...props} />;
}

function AvatarFallback({ className, ...props }: AvatarPrimitive.Fallback.Props) {
  return <AvatarPrimitive.Fallback data-slot="avatar-fallback" className={cn(`lumen-avatar-fallback`, className)} {...props} />;
}

function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
  // A labelled dot ("Online") is an image to assistive tech: aria-label on a bare span is ignored.
  const role = props["aria-label"] || props["aria-labelledby"] ? "img" : undefined;
  return <span data-slot="avatar-badge" role={role} className={cn(`lumen-avatar-badge`, className)} {...props} />;
}

function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
  return <div data-slot="avatar-group" className={cn(`lumen-avatar-group`, className)} {...props} />;
}

function AvatarGroupCount({ className, ...props }: React.ComponentProps<"div">) {
  return <div data-slot="avatar-group-count" className={cn(`lumen-avatar-group-count`, className)} {...props} />;
}

export { Avatar, AvatarImage, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarBadge };
```

### CSS (the registry `css` / `cssVars`, merged into the global stylesheet by the shadcn CLI)

```css
@layer components {
  .lumen-avatar {
    --av: 32px;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: var(--av);
    height: var(--av);
    border-radius: 50%;
    background: var(--lm-avatar-bg);
    box-shadow: var(--lm-avatar-sh);
    color: var(--lm-avatar-fg);
    font: 600 calc(var(--av) * .38)/1 var(--lumen-font-sans, var(--font-sans, var(--font-geist, var(--font-geist-sans, 'Geist')))), system-ui, sans-serif;
    letter-spacing: -.02em;
    user-select: none;
    vertical-align: middle;
  }
  .lumen-avatar[data-size="sm"] {
    --av: 24px;
  }
  .lumen-avatar[data-size="lg"] {
    --av: 40px;
  }
  .lumen-avatar-image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border-radius: 50%;
    object-fit: cover;
  }
  .lumen-avatar-fallback {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    border-radius: 50%;
  }
  .lumen-avatar-badge {
    position: absolute;
    right: -1px;
    bottom: -1px;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: calc(var(--av) * .28);
    height: calc(var(--av) * .28);
    min-width: 7px;
    min-height: 7px;
    border-radius: 50%;
    background: var(--lm-status);
    box-shadow: var(--lm-status-sh);
    color: var(--lm-avatar-fg);
  }
  .lumen-avatar-badge:has(>svg) {
    width: calc(var(--av) * .42);
    height: calc(var(--av) * .42);
    min-width: 12px;
    min-height: 12px;
  }
  .lumen-avatar-badge>svg {
    width: 70%;
    height: 70%;
  }
  .lumen-avatar-group {
    display: inline-flex;
    align-items: center;
  }
  .lumen-avatar-group>.lumen-avatar,.lumen-avatar-group>.lumen-avatar-group-count {
    margin-inline-start: calc(var(--av,32px) * -.12);
    box-shadow: 0 0 0 2px var(--background,#0b0c10),var(--lm-avatar-sh);
  }
  .lumen-avatar-group>:first-child {
    margin-inline-start: 0;
  }
  .lumen-avatar-group-count {
    --av: 32px;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    min-width: var(--av);
    height: var(--av);
    box-sizing: border-box;
    padding: 0 6px;
    border-radius: calc(calc(var(--av) / 2) * var(--lumen-radius-k, 1));
    background: var(--gc-glassB);
    color: var(--gc-m85);
    font: 500 11px/1 var(--lumen-font-mono, var(--font-mono, var(--font-geist-mono, 'Geist Mono'))), ui-monospace, monospace;
    font-variant-numeric: tabular-nums;
  }
  .lumen-avatar-group:has(>[data-size="sm"])>.lumen-avatar-group-count {
    --av: 24px;
  }
  .lumen-avatar-group:has(>[data-size="lg"])>.lumen-avatar-group-count {
    --av: 40px;
  }
}
```

The lumen foundation (the tokens listed above, keyframes and motion levels) installs once with the first component; its CSS is public at https://beautiful-ui.dev/r/lumen-foundation.json.
