Skip to main content

Lightbox

Overview

Use the Lightbox family when you need an overlay gallery for images or custom HTML content. The root component manages gallery-wide behavior — navigation, animation, and presentation — while each Lightbox Item defines a clickable trigger and the full-size content shown when the overlay opens. Lightbox works well for image galleries, product photo viewers, portfolio showcases, and any content that benefits from a zoomable, swipeable full-screen presentation.

The component uses PhotoSwipe under the hood and automatically detects image mode versus HTML mode based on what you place in each item's slots. Items within the same root form a gallery — arrow keys navigate between them.

Authoring Structure

Lightbox
└── Lightbox Item
├── trigger slot
│ └── preview content (what users click)
└── full slot
└── opened content (what the overlay shows)

Placement Rules

ComponentPlacementRole
LightboxTop-level gallery wrapper.Owns gallery state, runtime configuration, and PhotoSwipe initialization.
Lightbox ItemDirect child of Lightbox.Each item becomes one gallery entry. Items are matched by position within the root.
trigger slotInside Lightbox Item.The preview content users click to open the overlay. Can be an image, text, or any markup.
full slotInside Lightbox Item.The content rendered when the lightbox opens. Can be an image, HTML, or any markup.

Quick Start

Basic image gallery
<OmeLightbox>
{#slot default}
<OmeLightboxItem content='{{"triggerLabel":"Open image one"}}'>
{#slot trigger}
<img src="/wp-content/uploads/thumb-1.jpg" alt="Preview one" />
{/slot}
{#slot full}
<img src="/wp-content/uploads/full-1.jpg" alt="Gallery image one" width="1200" height="800" />
{/slot}
</OmeLightboxItem>
{/slot}
</OmeLightbox>

When both the trigger and full slots contain exactly one <img> each, the runtime automatically enters image mode — PhotoSwipe handles pinch-to-zoom, swipe gestures, and responsive image sizing natively.

Custom gallery behavior with HTML slides
<OmeLightbox
behavior='{{"loop":"{false}","wheelToZoom":"{true}"}}'
animation='{{"showHideAnimationType":"fade","animationDuration":"300"}}'
>
{#slot default}
<OmeLightboxItem content='{{"triggerLabel":"Open HTML slide"}}'>
{#slot trigger}<div class="card-preview">View Details</div>{/slot}
{#slot full}
<div>
<h2>Product Details</h2>
<p>Full product description rendered inside the lightbox overlay.</p>
</div>
{/slot}
</OmeLightboxItem>
{/slot}
</OmeLightbox>

When the full slot contains anything other than a single image, the runtime enters HTML mode — the content is rendered inside a centered, padded container within the overlay.


Family Components

The root component wraps all items and manages shared behavior: gallery navigation, animation settings, overlay appearance, and gesture handling. It does not render visible UI itself — only a container element with data attributes that the runtime reads on initialization.

Behavior Props

PropTypeDefaultDescription
loopbooleantrue

Allows gallery navigation to wrap from last to first and first to last. When false, navigation stops at the first and last items.

closeOnVerticalDragbooleantrue

Allows vertical drag gestures (swipe up or down) to close the lightbox overlay where supported.

wheelToZoombooleanfalse

Enables zooming with the mouse scroll wheel when the lightbox is open.

Animation Props

PropTypeDefaultDescription
showHideAnimationType"none" | "fade" | "zoom"none

Opening and closing animation type. "none" opens instantly, "fade" uses an opacity transition, and "zoom" scales from the trigger element.

animationDurationstring250

Show and hide animation duration in milliseconds. Only applies when showHideAnimationType is not "none".

Presentation Props

PropTypeDefaultDescription
bgOpacitystring0.8

Background overlay opacity (0–1) used by the opened lightbox. Clamped to the 0–1 range at runtime.

spacingstring0.12

Gallery item spacing as a ratio, forwarded to PhotoSwipe. Controls the gap between slides during swipe navigation.

htmlViewportPaddingstring24

Viewport padding in pixels applied around HTML-mode slides. Also sets the --ome-lightbox-html-padding CSS custom property.

Styling Props

PropTypeDefaultDescription
classclassome-lightbox-default

CSS class applied to the root gallery wrapper element.

Item Detection and Mode

The runtime automatically detects what mode to use for each Lightbox Item:

  • Image mode — When both the trigger slot and the full slot each contain exactly one <img> element, and the full-slot image has explicit width and height attributes. PhotoSwipe renders the image natively with pinch-to-zoom and responsive sizing. The trigger image is automatically used as the placeholder thumbnail (msrc) during the open animation.

  • HTML mode — When the full slot contains anything other than a single image (or when the image is missing width/height attributes). The full slot's inner HTML is wrapped in a centered, scrollable container inside the overlay.

  • Lazy-loaded images — The runtime resolves lazy-loaded images by checking data-src, data-lazy-src, data-srcset, and data-lazy-srcset attributes as fallbacks. <picture> element source sets are also resolved automatically.

Keyboard Behavior

When the lightbox overlay is open:

KeyAction
ArrowLeftNavigate to the previous gallery item.
ArrowRightNavigate to the next gallery item.
EscapeClose the lightbox overlay and return focus to the trigger.

When loop is true, navigating past the last item wraps to the first, and vice versa. Arrow keys, Escape, focus trapping, and focus return are always enabled and cannot be disabled.

Accessibility

The runtime enforces accessibility automatically:

  • Each trigger button has aria-haspopup="dialog" and an accessible label derived from content.triggerLabel, the trigger's text content, or an inner image's alt attribute.
  • If no accessible name can be derived, a fallback label of "Open lightbox item" is applied.
  • Focus is trapped inside the lightbox overlay while open (trapFocus: true).
  • Focus returns to the triggering element when the overlay closes (returnFocus: true).
  • Invalid items (missing trigger or full content) are marked with data-ome-invalid="true" and their triggers are disabled with aria-disabled="true".

Each LightboxItem represents a single gallery entry. It provides two slots — trigger (the clickable preview) and full (the content shown in the overlay). Items are ordered by their position within the root; arrow key navigation follows this order.

Content Props

PropTypeDefaultDescription
triggerLabelstring""

Accessible label for the trigger button. Also used as fallback visible text when the trigger slot is empty. If omitted and the trigger slot contains content, the label is inferred from the trigger's text or image alt attribute.

Styling Props

PropTypeDefaultDescription
classclassome-lightbox-item-default

CSS class applied to the item wrapper element.

triggerClassclassome-lightbox-item-default__trigger

CSS class applied to the clickable trigger button.

fullClassclassome-lightbox-item-default__full

CSS class applied to the hidden full-content wrapper.

Slot Rules

Every useful Lightbox Item needs content in the full slot — that is the content the overlay displays. The trigger slot can be custom markup (an image, text, icon, etc.) or fallback text from content.triggerLabel.

Trigger Fallback Rendering

When the trigger slot is empty and content.triggerLabel has a value, the item renders the label text as the visible trigger button content:

<button aria-label="Open image one">
Open image one
</button>

When the trigger slot has content, the content.triggerLabel is used only as the aria-label for accessibility.

Invalid Item Handling

If an item is missing its trigger or full container, the runtime marks it as invalid:

  • data-ome-invalid="true" is set on the item element.
  • The trigger button is disabled (disabled + aria-disabled="true").
  • The trigger cursor changes to not-allowed via the fixed styling.
  • The item is skipped during gallery initialization.

CSS Custom Properties

PropertyDefaultUsed ByDescription
--ome-lightbox-trigger-gapvar(--space-xs, 0.5rem)Trigger button childrenControls the gap between inline-flex children inside the trigger button.
--ome-lightbox-html-padding24pxHTML-mode slidesViewport padding around HTML-mode slide content. Set from presentation.htmlViewportPadding.

PhotoSwipe CSS Custom Properties

The following PhotoSwipe variables are set by the runtime and can be overridden in your own CSS:

PropertyDefaultDescription
--pswp-bg#000Overlay background color.
--pswp-placeholder-bg#222Placeholder background while images load.
--pswp-root-z-index2147483647Z-index for the PhotoSwipe root element.
--pswp-icon-color#fffIcon (close, arrows, zoom) fill color.
--pswp-icon-color-secondary#4f4f4fSecondary icon color.
--pswp-icon-stroke-color#4f4f4fIcon stroke color.
--pswp-icon-stroke-width2pxIcon stroke width.
--pswp-error-text-colorvar(--pswp-icon-color)Error message text color.
--pswp-transition-duration(PhotoSwipe default)Transition duration for UI element fade.

Common Mistakes

Leaving the full slot empty

The full slot is the content the overlay displays. If it is empty, the item will be marked as invalid and skipped during gallery initialization. Always add content to the full slot.

Missing width and height on full-slot images

When using image mode, the full-slot <img> must have explicit width and height attributes. Without deterministic dimensions, the runtime falls back to HTML mode, which loses PhotoSwipe's native pinch-to-zoom and responsive sizing. A console warning is logged when this fallback occurs.

Expecting the trigger slot to also be the overlay content

The trigger and full slots are separate by design. The trigger is what users click to open the lightbox. The full slot is what appears inside the overlay. They are not automatically shared or duplicated.

Wrapping Lightbox Items in an extra container

Do not place another block between Lightbox and Lightbox Item. The runtime scans for direct [data-ome-lightbox-item] descendants of the root to build the gallery. Extra wrappers between the root and items will still work (the query uses querySelectorAll), but the positional index used for gallery navigation is based on DOM order — keep items as close to the root as possible for predictable behavior.

Expecting a separate media-src prop API

The Lightbox family is slot-driven. There is no separate prop for specifying the full-size image URL — place the image in the full slot and the runtime extracts src, srcset, dimensions, and alt text automatically.


FAQs

How does image mode detection work?

The runtime checks if both the trigger slot and the full slot each contain exactly one <img> element. If they do, and the full-slot image has explicit width and height attributes, it enters image mode. The full-slot image's src (or data-src / data-lazy-src for lazy-loaded images) becomes the lightbox source, and the trigger image's src becomes the placeholder thumbnail during the open animation. If any of these conditions are not met, the item falls back to HTML mode.

Can I mix image items and HTML items in the same gallery?

Yes. Each item is independently detected as image mode or HTML mode. You can have image items alongside HTML items in the same Lightbox root. Arrow key navigation works across all items regardless of mode.

How do I customize the overlay appearance?

Use the presentation.bgOpacity prop to change the overlay darkness, and override the PhotoSwipe CSS custom properties (like --pswp-bg, --pswp-icon-color) in your own stylesheet to change colors, z-index, and other visual aspects. The presentation.htmlViewportPadding prop controls the padding around HTML-mode slides.

Does Lightbox support lazy-loaded images?

Yes. The runtime resolves lazy-loaded images by checking data-src, data-lazy-src, data-srcset, and data-lazy-srcset attributes as fallbacks. This is compatible with common lazy-loading plugins like WP Rocket and BJ Lazy Load. <picture> element sources are also resolved automatically.

Can I open the lightbox programmatically?

The lightbox instance is stored on the root element as element._omeLightbox. You can use element._omeLightbox.lightbox.loadAndOpen(index, dataSource) to open the gallery at a specific item index. The runtime also syncs data-ome-state ("open" / "closed") and data-ome-current-index on the root element for CSS targeting.

What happens if I have only one item?

A single-item gallery works normally — the overlay opens with the item, and navigation arrows are automatically hidden by PhotoSwipe when there is only one slide.