# Media Components

> Generated reference (regen-owned; see [index.md](index.md)). Binds only under `primitives: nurix`.

Avatars, icons, images, the brand mark and a few media-adjacent controls; import each from its layer-prefixed subpath, e.g. `import { Avatar } from "@nurix/components/element/avatar"`.

## Avatar

Profile image with an initials fallback and one stable accessible name, over Base UI's avatar. `AvatarGroup` stacks avatars into a facepile.

**Props:** `label` (the one accessible name, `role="img"`; omit beside a visible name and the avatar is `aria-hidden`), `size` [xs|sm|default|lg] (24, 32, 36, 40px; default `default`)

**Sub-components:** `Avatar.Image`, `Avatar.Fallback` (shows while the image loads and after it errors), `Avatar.Badge` (`intent` [neutral|info|success|warning|danger], status dot painted from `--intent-*`), `AvatarGroup` (`variant` [default|bare] — `bare` drops the hover lift; `role="group"`, name it with `aria-label`), `AvatarGroup.Count`

```tsx
<Avatar label="Jane Doe, online" size="lg">
  <Avatar.Image src={user.avatarUrl} alt="" />
  <Avatar.Fallback>JD</Avatar.Fallback>
  <Avatar.Badge intent="success" />
</Avatar>
```

## CopyButton

Click-to-copy `Button` (`compound/copy-button`) that swaps its copy glyph for a check, publishes the outcome as `data-status` [success|error] and announces it.

**Props:** `value` (string, or a function resolved at click time), `onCopy(value)`, `onCopyError(error)`, `timeout` (ms the outcome holds; defaults to the `--hold-dwell` token, `Infinity` holds success), `labels` (`{ copy, copied, failed }`), plus Button's `variant` (default `ghost`), `intent`, `size` [xs|sm|default|lg] and `isIconOnly` (on when there are no `children`)

```tsx
<CopyButton value={message.text} size="sm" />
```

## FilePreview

One file tile — a staged attachment or a persisted file (`compound/file-preview`). It renders state only; choosing and uploading the file is `compound/file-picker`.

**Sub-components:** `FilePreview.Group` (snap-scrolling strip), `FilePreview.Media`, `FilePreview.Content`, `FilePreview.Title`, `FilePreview.Description`, `FilePreview.Actions`, `FilePreview.Action`, `FilePreview.Trigger` (overlay button or `render` anchor), `FilePreview.Progress`; also `getFileTypeIcon({ type, name })`

**Props:** root — `status` [idle|uploading|processing|error|success] (omit for a resting file; `idle` dashes the border, `error` tints it), `size` [default|sm|xs], `orientation` [horizontal|vertical], `variant` [default|bare]; Media — `type`, `name` (picks a mime-aware glyph when given no child), `variant="image"`; Progress — `value` (0–100, `null` indeterminate), `variant` [linear|circular|fill], `keepMounted` (otherwise it unmounts at 100)

```tsx
<FilePreview status={file.status} size="sm">
  <FilePreview.Media type={file.type} name={file.name} />
  <FilePreview.Content>
    <FilePreview.Title>{file.name}</FilePreview.Title>
    <FilePreview.Description>{formatBytes(file.size)}</FilePreview.Description>
  </FilePreview.Content>
  <FilePreview.Actions>
    <FilePreview.Action aria-label={`Remove ${file.name}`} onClick={() => remove(file.id)}><XIcon /></FilePreview.Action>
  </FilePreview.Actions>
</FilePreview>
```

## Icon

Draws a Lucide icon from a name held as data (`element/icon`) — a record field, a config value or an `IconPicker` (`compound/icon-picker`) pick. A fixed icon known at build time is a direct `lucide-react` import instead.

**Props:** `value` (Lucide's kebab-case name, e.g. `"arrow-up-right"`), `size` (numeric px, default 24), `label` (standalone meaningful icon → `role="img"`; omit beside visible text), `fallback` (drawn for an unknown name; an empty box of the icon's size otherwise), `strokeWidth` (defaults to the `--edge-stroke` token)

```tsx
<Icon value={record.icon} size={16} />
```

## Image

Content photo or screenshot (`element/image`) with a skeleton while loading and a bordered placeholder (the `alt` text, or `labels.unavailable` — "Image unavailable") on error or an empty `src`, never the native broken-image glyph. A person's photo is `Avatar`.

**Props:** `src`, `alt`, `width`/`height` (both together reserve the box via `aspect-ratio`), `fallback` (replaces the placeholder glyph and caption), `labels` (`{ unavailable }`), `variant` [default|bare] (`bare` drops its own radius, border and fill inside a framing host). Takes no `onClick` — a lightbox wraps it in a trigger.

```tsx
<Image src={attachment.src} alt={attachment.name} width={320} height={180} />
```

## Logo

Official NuPlay brand lockup (`element/logo`) — mark + wordmark, inline SVG.

**Props:** `variant` [blue-black|white-white|lime-white|blue-white] (fixed literal fills, named mark-then-wordmark; default `blue-black`, for light surfaces only), `height` (number = px; default 28px via `h-7`; width follows the aspect), `label` (accessible name, default `"NuPlay"`; pass `aria-hidden` beside the brand name in text). `fill` is not a prop — recolor through `variant`.

```tsx
<Logo variant="white-white" height={20} />
```

## Other Components

- `lib/icons-data` — `iconsData`: `{ name, categories, tags }[]` over the Lucide set (~1,700 entries), for a custom icon search or browser; load it with a dynamic `import()`, keep only names present in the installed `lucide-react`, and render each match with `Icon`. A one-icon pick is `IconPicker` (`compound/icon-picker`), which takes these records as `data`.
- View switching (table/card and similar view-mode strips) has no dedicated component: compose a single-select `ToggleGroup` + `ToggleGroup.Item` (`compound/toggle-group`), guarding the `""` it publishes on deselect; reach for `Tabs` (`compound/tabs`) only when the pick also swaps a visible panel.
