npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@archpublicwebsite/gallery

v1.0.1

Published

Gallery lightbox component for Archipelago hotel websites

Readme

@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="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.