# Feed

Social posts with one to four images and a +N overflow tile. Items without a visible trigger fade in and out instead of morphing.

```tsx
import { cn } from 'cn'
import { landscapes, type Photo, portraits } from '../../data/photos'
import { Lightbox, Viewer } from '../lightbox'

const posts = [
  {
    name: 'Ayla Demir',
    handle: '@ayla',
    time: '2d',
    text: 'Seven days on the coast road. Seven completely different landscapes.',
    photos: landscapes.slice(0, 7),
  },
  {
    name: 'Jonas Berg',
    handle: '@jberg',
    time: '5h',
    text: 'First light over the ridge. Worth every minute of the 4am alarm.',
    photos: landscapes.slice(8, 9),
  },
  {
    name: 'Mira Sato',
    handle: '@mirasato',
    time: '1d',
    text: 'Two frames from the same walk.',
    photos: portraits.slice(0, 2),
  },
  {
    name: 'Leo Hart',
    handle: '@leohart',
    time: '3d',
    text: 'Weekend, in three photos.',
    photos: landscapes.slice(10, 13),
  },
]

function Media({ photos }: { photos: Photo[] }) {
  const n = photos.length
  return (
    <div
      className={cn(
        'mt-3 grid gap-0.5 overflow-hidden rounded-2xl border border-neutral-200 dark:border-neutral-800',
        n > 1 && 'aspect-16/10 grid-cols-2',
        n > 2 && 'grid-rows-2',
      )}
    >
      {photos.slice(0, 4).map((photo, i) => (
        <Lightbox.Trigger
          key={photo.src}
          index={i}
          className={cn(
            'group relative block overflow-hidden bg-neutral-100 outline-none focus-visible:opacity-80 dark:bg-neutral-900',
            n === 1 && 'rounded-2xl',
            n === 3 && i === 0 && 'row-span-2',
          )}
        >
          <img src={photo.thumb} alt={photo.alt} className={cn('size-full object-cover', n === 1 && 'max-h-120')} />
          {i === 3 && n > 4 && (
            <span className="absolute inset-0 grid place-items-center bg-black/45 text-lg font-medium text-white group-data-active:opacity-0">
              +{n - 4}
            </span>
          )}
        </Lightbox.Trigger>
      ))}
    </div>
  )
}

export default function Feed() {
  return (
    <div className="divide-y divide-neutral-200 dark:divide-neutral-800">
      {posts.map((post) => (
        <article key={post.handle} className="flex gap-3 py-4 first:pt-0 last:pb-0">
          <img src={post.photos[0]!.thumb} alt="" className="size-9 shrink-0 rounded-full object-cover" />
          <div className="min-w-0 flex-1">
            <p>
              <span className="font-medium">{post.name}</span>{' '}
              <span className="text-neutral-500">
                {post.handle} · {post.time}
              </span>
            </p>
            <p className="mt-0.5">{post.text}</p>
            <Lightbox.Root items={post.photos.map((p) => ({ ...p, caption: post.text }))}>
              <Media photos={post.photos} />
              <Viewer />
            </Lightbox.Root>
          </div>
        </article>
      ))}
    </div>
  )
}
```
