# Media Image

> A photo, or a video's still, with your photo consent rule applied: faces blurred, or the photo hidden, until consent is on file.

- Name: `media-image`
- Status: beta
- Categories: media, display
- File: the install writes `media-image.tsx` to your `ui` alias, `@/components/ui` by default.
- Gallery: https://arbokui.com/components/media-image
- Registry item: https://arbokui.com/r/media-image.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/media-image
   ```

## Use it for

- Any photo of people, so a face with no consent on file never shows.
- Tag each person with their consent and, where you know it, their face. A tagged face is blurred alone. With no face, the whole photo is.
- Size its box. It fills it, and fit says whether the photo covers the box or fits inside it.
- Pass blurredSrc, a copy the server blurred, wherever you can. The blur here is only a look, and the original still reaches the browser.
- Pass your app's rule as photoConsent: who needs consent, and whether a photo without it is blurred or hidden.
- Use MediaGrid for a set of photos and Lightbox to view one large.

## Avoid it for

- A person's profile photo. Use Avatar, which they chose to share.
- Hiding a face from a screen reader only. The rule also hides it from sight, so pass every tagged person.
- An image with no people in it, like a crest or a map. Use a plain img.

## Props

- `item`: `MediaItem`. Required. The photo or video, with everyone tagged in it.
- `MediaItem`: `{ src?: string; picture?: ReactNode; aspect?: number; blurredSrc?: string; alt: string; caption?: string; media?: "photo" | "video"; video?: string; poster?: string; duration?: string; people?: MediaPerson[]; id?: string | number }`. Type. src is the photo or the video's still. picture draws your own element in its place, like a next/image, with the same blur and hiding. aspect is width over height, so face blurs land before the photo loads. media defaults to video when video is set.
- `MediaPerson`: `{ person: string; photo?: string; consent?: "allowed" | "missing" | "withdrawn"; junior?: boolean; face?: { x: number; y: number; w: number; h: number } }`. Type. Someone tagged in it. consent left out means missing. face is where their face is, as fractions of the photo.
- `fit`: `"cover" | "contain"`. Default `"cover"`. cover fills the box, cropped from the middle. contain fits the whole photo inside it.
- `alt`: `string`. Replaces item.alt. Pass "" when something around it already names the photo.
- `lazy`: `boolean`. Default `true`. Loads the photo as it nears the screen. Turn it off for the one photo a viewer shows.
- `photoConsent`: `{ who?: "everyone" | "juniors" | "nobody"; missing?: "blur" | "hide" }`. Default `{ who: "everyone", missing: "blur" }`. Your consent rule. who says whose face needs consent on file. missing says whether such a face is blurred or the photo hidden. Withdrawn consent always hides the photo.
- `badge`: `boolean`. Default `true`. A blurred photo says so in a small badge: Face blurred, Faces blurred, or Blurred. Turn it off when the words around it say so.
- `className`: `string`. Extra classes on the box. Later classes win.
- `consentOf`: `(item: MediaItem, rule?: PhotoConsentRule) => MediaConsent`. Function. What a photo may show: state clear, blurred, or hidden, who is missing, who withdrew, and whether the whole photo blurs.

## Accessibility

- The photo is named by item.alt, or by alt. Pass alt="" when a caption beside it already names it.
- A blurred photo's badge is text, so screen readers hear it after the name.
- A hidden photo loads nothing. Its box says Consent withdrawn, or Waiting for consent, in words.
- The badge and each face's blur keep a border in forced-colors mode.

## Examples

### A photo with a junior who has no consent on file

```tsx
<div className="size-48 overflow-hidden rounded-lg">
  <MediaImage
    item={{
      src: "/photos/team-offsite.jpg",
      alt: "Team photo at the offsite",
      people: [
        { person: "Josh Tait", consent: "allowed" },
        { person: "Ella Clarke", consent: "missing", junior: true, face: { x: 0.52, y: 0.2, w: 0.1, h: 0.14 } },
      ],
    }}
  />
</div>
```

### Hide instead of blur, for juniors only

```tsx
<div className="aspect-video w-full overflow-hidden rounded-lg">
  <MediaImage
    item={{ src: "/photos/summer-picnic.jpg", alt: "Summer picnic", people: [{ person: "Ella Clarke", junior: true }] }}
    fit="contain"
    photoConsent={{ who: "juniors", missing: "hide" }}
  />
</div>
```
