Accessible Mobile-friendly Image Viewer – SmartPhoto

Category: Gallery , Javascript , Modal & Popup , Recommended | September 11, 2026
Authorappleple
Last UpdateSeptember 11, 2026
LicenseMIT
Views1,731 views
Accessible Mobile-friendly Image Viewer – SmartPhoto

SmartPhoto is a vanilla JavaScript image lightbox that opens full-size images in a responsive, touch-friendly viewer.

It supports pinch-to-zoom, drag, swipe, thumbnail navigation, captions, and grouped galleries.

Features:

  • Pinch zoom, drag, and swipe gestures for touch screens.
  • Full-size image viewing from existing thumbnail links.
  • JavaScript array data source for dynamic galleries.
  • Independent image groups within the same page.
  • URL hash links for individual photos.
  • Optional navigation arrows and thumbnail strip.
  • Keyboard and screen-reader support.
  • Programmatic navigation, zoom, opening, and closing controls.
  • CSS custom properties for animation timing and viewer colors.

How To Use It

Installation

Load the SmartPhoto stylesheet and JavaScript build before initializing the gallery:

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/smartphoto/css/smartphoto.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/smartphoto/lib/smartphoto.min.js"></script>

Or install the package with NPM:

npm install smartphoto

Then import it into your project:

import SmartPhoto from "smartphoto";
import "smartphoto/css/smartphoto.min.css";

Basic Usage

Each gallery item uses an anchor for the full-size image and an <img> for the thumbnail. SmartPhoto scans elements that match the selector passed to its constructor.

  • href supplies the full-size image URL.
  • data-caption adds text beneath the active image.
  • data-id identifies the photo for programmatic opening and URL hash links.
  • data-group places related photos in the same gallery.
  • alt on the thumbnail supplies image alternative text.
  • data-src supplies a lazy-loaded thumbnail URL when you keep the default lazyAttribute setting.
<a
  href="/images/coast-large.jpg"
  class="js-smartphoto"
  data-caption="Rocky coastline"
  data-id="coast"
  data-group="travel"
>
  <img
    src="/images/coast-thumb.jpg"
    alt="Rocky coastline"
    width="320"
  />
</a>
<a
  href="/images/harbor-large.jpg"
  class="js-smartphoto"
  data-caption="Harbor at sunset"
  data-id="harbor"
  data-group="travel"
>
  <img
    src="/images/harbor-thumb.jpg"
    alt="Harbor at sunset"
    width="320"
  />
</a>
<script>
document.addEventListener("DOMContentLoaded", function () {
  const gallery = new SmartPhoto(".js-smartphoto");
});
</script>

Create a Gallery from JavaScript Data

The constructor also accepts an array of slide objects. This mode is ideal for photo galleries populated from an API, JSON response, CMS payload, or application state.

Provide width and height when those values already exist in your data. SmartPhoto skips its image-size measurement preload for slides that include both dimensions.

Slide objects support these fields:

  • src (required): Full-size image URL.
  • thumb: Thumbnail URL. SmartPhoto falls back to src.
  • caption: Caption text.
  • alt: Alternative text. SmartPhoto falls back to the caption, then src.
  • id: String or number used by show() and URL hashes. SmartPhoto falls back to the slide index.
  • group: Gallery group name. The default group is nogroup.
  • width and height: Natural image dimensions in pixels.
const gallery = new SmartPhoto([
  {
    src: "/images/mountain-large.jpg",
    thumb: "/images/mountain-thumb.jpg",
    caption: "Mountain ridge",
    alt: "Snow-covered mountain ridge",
    id: "mountain",
    group: "landscapes",
    width: 1600,
    height: 1067
  },
  {
    src: "/images/lake-large.jpg",
    thumb: "/images/lake-thumb.jpg",
    caption: "Alpine lake",
    alt: "Blue alpine lake",
    id: "lake",
    group: "landscapes",
    width: 1600,
    height: 1067
  }
]);
gallery.show(0, { group: "landscapes" });
// An ID works as well.
gallery.show("lake", { group: "landscapes" });

Open Photos Programmatically

show() accepts either an image index or ID. The optional group selects a specific gallery, while trigger identifies the element used for the opening transition and focus return.

const gallery = new SmartPhoto(".js-smartphoto");
gallery.show(2);
gallery.show("harbor");
gallery.show("harbor", {
  group: "travel",
  trigger: document.querySelector("#open-travel-gallery")
});

Configuration Options

You can pass an options object as the second constructor argument. Full options:

  • arrows (boolean, default true): Shows previous and next arrow controls.
  • nav (boolean, default true): Shows the thumbnail navigation strip.
  • showAnimation (boolean, default true): Controls the opening and closing transition.
  • verticalGravity (boolean, default false): Adds vertical device-tilt movement when orientation control is active.
  • useOrientationApi (boolean, default false): Uses device orientation data to move a zoomed image.
  • useHistoryApi (boolean, default true): Updates the URL hash with the active group and photo.
  • swipeTopToClose (boolean, default false): Closes the viewer after an upward swipe.
  • swipeBottomToClose (boolean, default true): Closes the viewer after a downward swipe.
  • swipeOffset (number, default 100): Sets the minimum swipe distance in pixels.
  • swipeVelocity (number, default 0.5): Sets the minimum swipe speed in pixels per millisecond for a fast flick.
  • headerHeight (number, default 60): Reserves vertical space for the header during image fitting.
  • footerHeight (number, default 60): Reserves vertical space for the footer during image fitting.
  • resizeStyle ("fit" | "fill", default "fit"): Chooses how images scale inside the available viewer area.
  • animationSpeed (number, default 300): Sets transition duration in milliseconds.
  • forceInterval (number, default 10): Sets the interval used for force calculations during image movement.
  • registance (number, default 0.5): Controls friction for inertial movement on a zoomed image. Keep the registance spelling when setting this option.
  • loadOffset (number, default 2): Sets how many nearby slides SmartPhoto preloads around the active photo.
  • lazyAttribute (string, default "data-src"): Selects the thumbnail attribute used for lazy-loaded images in HTML mode.
  • classNames (object): Overrides individual generated SmartPhoto class names.
  • message (object): Overrides screen-reader text for next, previous, close, and carousel labels.
const gallery = new SmartPhoto(".js-smartphoto", {
  arrows: true,
  nav: false,
  resizeStyle: "fit",
  animationSpeed: 250,
  loadOffset: 1
});

The message object accepts four text values:

  • gotoNextImage: Label for next-image navigation.
  • gotoPrevImage: Label for previous-image navigation.
  • closeDialog: Label for the close control.
  • carouselLabel: Accessible name for the image collection.

Disable Arrows or Thumbnail Navigation

A minimal viewer can remove either navigation interface during initialization.

const gallery = new SmartPhoto(".js-smartphoto", {
  arrows: false,
  nav: false
});

Fit or Fill Images

resizeStyle accepts fit or fill. Use fit when the entire image should stay inside the available viewer area.

const gallery = new SmartPhoto(".js-smartphoto", {
  resizeStyle: "fit"
});

Styling and CSS Custom Properties

SmartPhoto exposes four CSS custom properties for its main visual settings. The JavaScript animationSpeed option controls per-instance animation timing.

  • --smartphoto-animation-speed: Transition duration. Default 300ms.
  • --smartphoto-animation-function: Transition easing. Default ease-out.
  • --smartphoto-backdrop-color: Viewer backdrop color. Default rgba(0, 0, 0, 1).
  • --smartphoto-header-color: Header background color. Default rgba(0, 0, 0, 0.2).

Set the variables on .smartphoto for viewer-specific styling or :root for site-wide defaults.

.smartphoto {
  --smartphoto-animation-speed: 450ms;
  --smartphoto-animation-function: ease-in-out;
  --smartphoto-backdrop-color: rgba(10, 12, 18, 0.96);
  --smartphoto-header-color: rgba(10, 12, 18, 0.55);
}

API Methods

const gallery = new SmartPhoto(".js-smartphoto");
// Subscribe to a SmartPhoto event.
gallery.on("change", function (event) {
  console.log(event);
});
// Remove the viewer and its registered event listeners.
gallery.destroy();
// Invoke the disposable cleanup hook directly.
gallery[Symbol.dispose]();
// Move to a slide index in the active group.
gallery.gotoSlide(2);
// Close the viewer with an optional animation direction.
gallery.hidePhoto("top");
gallery.hidePhoto("bottom");
// Zoom the active image.
gallery.zoomPhoto();
// Return the active image to its normal scale.
gallery.zoomOutPhoto();
// Register a new thumbnail element in HTML mode.
gallery.addNewItem(document.querySelector("#new-photo"));
// Open by index or ID.
gallery.show(1);
gallery.show("harbor");
// Open a photo from a specific group.
gallery.show("harbor", {
  group: "travel"
});
// Close the viewer.
gallery.hide();
// Navigate through the active group.
gallery.next();
gallery.prev();
// Add either a slide object or HTML element.
gallery.addItem({
  src: "/images/new-large.jpg",
  thumb: "/images/new-thumb.jpg",
  caption: "New photo",
  id: "new-photo",
  group: "travel"
});
// Read the active slide index.
console.log(gallery.currentIndex);

Events

// Fires when the viewer opens.
gallery.on("open", function (event) {
  console.log("open");
});
// Fires after the viewer closes.
gallery.on("close", function (event) {
  console.log("close");
});
// Fires after the active photo changes.
gallery.on("change", function (event) {
  console.log("change");
});
// Fires when all images in a group have loaded.
gallery.on("loadall", function (event) {
  console.log("loadall");
});
// Fires when a swipe begins.
gallery.on("swipestart", function (event) {
  console.log("swipestart");
});
// Fires when a swipe ends.
gallery.on("swipeend", function (event) {
  console.log("swipeend");
});
// Fires when a pinch gesture begins.
gallery.on("gesturestart", function (event) {
  console.log("gesturestart");
});
// Fires when a pinch gesture ends.
gallery.on("gestureend", function (event) {
  console.log("gestureend");
});
// Fires after SmartPhoto zooms into the active image.
gallery.on("zoomin", function (event) {
  console.log("zoomin");
});
// Fires after SmartPhoto returns the image to normal scale.
gallery.on("zoomout", function (event) {
  console.log("zoomout");
});

Alternatives:

FAQs:

Q: How do I create multiple SmartPhoto galleries on one page?
A: Give related HTML thumbnails the same data-group value. Slide-array mode uses the group field, and show() accepts a group option when you need to open a specific collection.

Q: Why doesn’t tilting the device move a zoomed image?
A: SmartPhoto sets useOrientationApi to false by default. Set it to true during initialization when your interface needs device-orientation movement.

Q: How should I remove SmartPhoto from a dynamic application?
A: Call destroy() before permanently discarding the instance. The method removes the viewer and its registered event listeners.

Changelog:

v2.1.16 (09/11/2026)

  • update

You Might Be Interested In:


3 thoughts on “Accessible Mobile-friendly Image Viewer – SmartPhoto

  1. Mosleh Uddin

    Is there any option to loop. when image end starts from first. arrow will always show.

    Reply
    1. Sandesh Sapkota

      How to disable the (bottom par)t images thumbnails ?

      Reply
  2. Qronicle

    The data-group attribute needs to be on the anchor element instead of the image within in order to work.

    Reply

Leave a Reply