@archpublicwebsite/gallery

Gallery lightbox component for Archipelago hotel websites
732
Install
npm install @archpublicwebsite/gallery
Latest Version:1.0.1
Vue:3
License:MIT
Last Updated:May 5, 2026
Links: npm
Maintainer: archpublicwebsite

@archpublicwebsite/gallery

Full-screen image lightbox gallery component for Vue 3 — works in plain Vue, Nuxt, Vuetify, and any other Vue-based framework.

Features

  • Fully typed — strict TypeScript interfaces for all props, emits, and composable return values
  • FLIP animations — smooth open/close transitions matched to the source image position
  • Swiper-powered — swipe, keyboard navigation, fade/slide effect, accessibility module
  • Zoom — click or pinch to zoom, configurable max ratio
  • Thumbnail strip — responsive breakpoints, active indicator
  • Framework-agnostic styling — Tailwind compiled to plain CSS at build time; no Tailwind required by consumers
  • SSR compatible — Swiper mounted only after onMounted; safe for Nuxt
  • Accessible — ARIA labels, keyboard navigation (← →), prefers-reduced-motion respected
  • Zero peer deps besides Vue — Swiper is bundled; nothing else to install

Installation

npm install @archpublicwebsite/gallery
# or
pnpm add @archpublicwebsite/gallery
# or
yarn add @archpublicwebsite/gallery

Quick Start

<script setup lang="ts">
import type { GalleryImageInput } from "@archpublicwebsite/gallery";

import { ModalGallery, useGallery } from "@archpublicwebsite/gallery";
import '@archpublicwebsite/gallery/style.css';

const images: GalleryImageInput[] = [
  { id: '1', url: 'https://...', alt: 'Pool', caption: 'Infinity Pool' },
  { id: '2', url: 'https://...', alt: 'Lobby', caption: 'Grand Lobby' },
]

const { isOpen, currentIndex, galleryContent, openGallery, closeGallery }
  = useGallery(images)
</script>

<template>
  <!-- trigger — can be a grid, a single button, anything -->
  <img
    v-for="(item, i) in galleryContent.items"
    :key="item.id"
    :src="https://raw.githubusercontent.com/ArchipelagoInternational/archi-ui/HEAD/item.thumbnail || item.url"
    :alt="item.alt"
    @click="openGallery(i)"
  />

  <!-- lightbox -->
  <ModalGallery
    v-if="isOpen"
    :content="galleryContent"
    :current-index="currentIndex"
    style="position: fixed; inset: 0; z-index: 9999"
    @close="closeGallery"
  />
</template>

Framework Examples

Nuxt 3

No plugin needed — import directly in the component.

<script setup lang="ts">
import { ModalGallery, useGallery } from '@archpublicwebsite/gallery';
import '@archpublicwebsite/gallery/style.css';
// ...same as quick start
</script>

Vuetify (inside v-dialog)

<template>
  <v-dialog v-model="isOpen" fullscreen>
    <ModalGallery
      :content="galleryContent"
      :current-index="currentIndex"
      @close="closeGallery"
    />
  </v-dialog>
</template>

React (via any Vue-in-React bridge)

The component is a standard Vue 3 SFC and follows the same composition pattern as @archpublicwebsite/modal and @archpublicwebsite/rangepicker.


useGallery(images) Composable

The composable is the recommended way to manage gallery state. It accepts a static or reactive array of GalleryImageInput objects and returns everything needed to wire up <ModalGallery>.

Input type — GalleryImageInput

Field Type Required Description
id string ✅ Unique identifier
url string ✅ Full-resolution image URL
thumbnail string — Thumbnail URL; derived from url if omitted
alt string — Accessible alt text; falls back to title
caption string — Short label shown in the overlay
title string — Secondary label; used as alt fallback
description string — Longer text shown below caption
[key: string] unknown — Extra API fields are silently ignored
// TypeScript enforces id + url at compile time
const images: GalleryImageInput[] = [
  { id: '1', url: 'https://...', alt: 'Pool' }, // ✅
  { url: 'https://...' }, // ✗ missing id
  { id: 1, url: "https://..." }, // ✗ id must be string
]

Return value — UseGalleryReturn

Property Type Description
isOpen Ref<boolean> Whether the lightbox is visible
currentIndex Ref<number> Active slide index
galleryContent ComputedRef<GalleryContent> Structured data to pass to :content
openGallery(index?) (index?: number) => void Open at a given index (default: 0)
closeGallery() () => void Close the lightbox

Reactive images example (fetched from an API):

const { data: rooms } = await useFetch('/api/rooms');

const { isOpen, galleryContent, openGallery, closeGallery } = useGallery(
  () =>
    rooms.value?.map(r => ({ id: r.id, url: r.image, alt: r.name })) ?? [],
)

<ModalGallery> Props

Prop Type Default Description
content GalleryContent required Gallery data — use useGallery() to produce this
currentIndex number 0 Active slide on mount
lightboxOptions LightboxOptions see below Behaviour overrides

LightboxOptions

Option Type Default Description
showThumbnails boolean true Show thumbnail strip at the bottom
showNavigation boolean true Show prev/next arrows
showPagination boolean true Show fraction counter (e.g. "3 / 12")
showCaption boolean true Show caption and description overlay
enableZoom boolean true Enable click/pinch to zoom
thumbnailSize 'small' | 'medium' | 'large' 'medium' Thumbnail strip size
effect 'slide' | 'fade' 'slide' Slide transition effect
maxZoom number 3 Maximum zoom ratio

All options have TypeScript autocomplete and hover documentation.

// ✗ TypeScript catches this at compile time:
<ModalGallery :lightbox-options="{ thumbnailSize: 'huge' }" />
//                                                  ^^^^^^ not assignable to ThumbnailSize

Events

Event Payload Description
close — Fired when the user closes the gallery (close button or animation end)
image-change (index: number, item: GalleryItem) Fired when the active slide changes
<ModalGallery
  ...
  @close="closeGallery"
  @image-change="
    (index, item) => analytics.track('gallery_view', { id: item.id })
  "
/>

Animations

The gallery uses FLIP animations to match the position and size of the source image on open, creating a natural expand-from-thumbnail effect. On close, a ghost image animates back to the original position.

  • Detects the source <img> element in the DOM by matching the URL
  • Falls back to a simple fade if the source cannot be found
  • Respects prefers-reduced-motion: uses a short opacity fade instead

Auto-hiding Chrome

When content.items has only one image, navigation arrows, pagination, and the thumbnail strip are automatically hidden regardless of lightboxOptions. No extra config needed.


Styling & Theming

The package ships a single compiled CSS file — import it once at the top of your app or in the component:

import '@archpublicwebsite/gallery/style.css';

The gallery uses scoped class names prefixed with arch-gallery- to avoid collisions with Vuetify, Bootstrap, or any other UI framework.


Publishing a new version

cd packages/gallery

pnpm version:patch   # 1.0.0 → 1.0.1
pnpm version:minor   # 1.0.0 → 1.1.0
pnpm version:major   # 1.0.0 → 2.0.0

npm publish

prepublishOnly runs pnpm build automatically before publishing.

Related Packages

@lightgallery/vue

Native Vue 3 lightGallery over @lightgallery/headless

809 7,056
vue-tinybox

A slick, yet tiny lightbox gallery for Vue.js

38,510 92
@lupinum/vue-photo

Vue components, composables, and photo utilities for Nuxt Photo.

972 1
@lupinum/nuxt-photo

Photo galleries, lightboxes, and provider-aware image rendering for Nuxt.

938 1
vue-cool-lightbox

A pretty Vue.js component to display an image gallery lightbox inspired by fancy...

229,123 339

Version History

Version Released Vue License
1.0.1 ^3.5.22 MIT
1.0.0 ^3.5.22 MIT