Create a custom thumbnail layout
Compose explicit primitives for any thumbnail layout.
<PhotoAlbum> owns rows, columns, and masonry. A bespoke layout owns its
explicit photo collection and composes three primitives:
<LightboxProvider>owns state;<PhotoTrigger>owns accessible activation and the FLIP source ref;<Lightbox>renders the included viewer.
<script setup lang="ts">
import { Lightbox, LightboxProvider, PhotoImage, PhotoTrigger } from '@lupinum/nuxt-photo/app'
import type { PhotoItem } from '@lupinum/nuxt-photo/app'
const photos: PhotoItem[] = [
/* … */
]
</script>
<template>
<LightboxProvider :photos="photos">
<div class="hex-grid">
<PhotoTrigger
v-for="(photo, index) in photos"
:key="photo.id"
:photo="photo"
:index="index"
v-slot="{ hidden }"
>
<PhotoImage :photo="photo" context="thumb" :style="{ opacity: hidden ? 0 : 1 }" />
</PhotoTrigger>
</div>
<Lightbox />
</LightboxProvider>
</template>PhotoTrigger supplies keyboard handling, ARIA labeling, activation, and the
transition ref. Its hidden slot prop prevents the source thumbnail and ghost
image from appearing at the same time.
Programmatic buttons inside the provider
Call useLightbox() from a descendant component:
<script setup lang="ts">
import { useLightbox } from '@lupinum/nuxt-photo/app'
const lightbox = useLightbox()
</script>
<template>
<button @click="lightbox.openById('desert-01')">Open desert</button>
</template>Why PhotoGroup is not used here
PhotoGroup gives descendant <Photo> and <PhotoAlbum> components one explicit
canonical collection, but it does not return generic trigger attribute bags.
Use LightboxProvider when a bespoke layout needs direct control over every
trigger and lower-level component rather than ready-made components.
Use the PhotoGroup reference when ready-made descendants should share one collection. Continue with Build a lightbox from primitives when the viewer structure also needs to change.