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
| Component | Placement | Role |
|---|---|---|
| Lightbox | Top-level gallery wrapper. | Owns gallery state, runtime configuration, and PhotoSwipe initialization. |
| Lightbox Item | Direct child of Lightbox. | Each item becomes one gallery entry. Items are matched by position within the root. |
| trigger slot | Inside Lightbox Item. | The preview content users click to open the overlay. Can be an image, text, or any markup. |
| full slot | Inside Lightbox Item. | The content rendered when the lightbox opens. Can be an image, HTML, or any markup. |
Quick Start
<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.
<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
Lightbox (Root)
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
| Prop | Type | Default | Description |
|---|---|---|---|
loop | boolean | true | Allows gallery navigation to wrap from last to first and first to last. When |
closeOnVerticalDrag | boolean | true | Allows vertical drag gestures (swipe up or down) to close the lightbox overlay where supported. |
wheelToZoom | boolean | false | Enables zooming with the mouse scroll wheel when the lightbox is open. |
Animation Props
| Prop | Type | Default | Description |
|---|---|---|---|
showHideAnimationType | "none" | "fade" | "zoom" | none | Opening and closing animation type. |
animationDuration | string | 250 | Show and hide animation duration in milliseconds. Only applies when |
Presentation Props
| Prop | Type | Default | Description |
|---|---|---|---|
bgOpacity | string | 0.8 | Background overlay opacity (0–1) used by the opened lightbox. Clamped to the 0–1 range at runtime. |
spacing | string | 0.12 | Gallery item spacing as a ratio, forwarded to PhotoSwipe. Controls the gap between slides during swipe navigation. |
htmlViewportPadding | string | 24 | Viewport padding in pixels applied around HTML-mode slides. Also sets the |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-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 explicitwidthandheightattributes. 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/heightattributes). 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, anddata-lazy-srcsetattributes as fallbacks.<picture>element source sets are also resolved automatically.
Keyboard Behavior
When the lightbox overlay is open:
| Key | Action |
|---|---|
ArrowLeft | Navigate to the previous gallery item. |
ArrowRight | Navigate to the next gallery item. |
Escape | Close 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 fromcontent.triggerLabel, the trigger's text content, or an inner image'saltattribute. - 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 witharia-disabled="true".
Lightbox Item
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
| Prop | Type | Default | Description |
|---|---|---|---|
triggerLabel | string | "" | 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 |
Styling Props
| Prop | Type | Default | Description |
|---|---|---|---|
class | class | ome-lightbox-item-default | CSS class applied to the item wrapper element. |
triggerClass | class | ome-lightbox-item-default__trigger | CSS class applied to the clickable trigger button. |
fullClass | class | ome-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-allowedvia the fixed styling. - The item is skipped during gallery initialization.
CSS Custom Properties
| Property | Default | Used By | Description |
|---|---|---|---|
--ome-lightbox-trigger-gap | var(--space-xs, 0.5rem) | Trigger button children | Controls the gap between inline-flex children inside the trigger button. |
--ome-lightbox-html-padding | 24px | HTML-mode slides | Viewport 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:
| Property | Default | Description |
|---|---|---|
--pswp-bg | #000 | Overlay background color. |
--pswp-placeholder-bg | #222 | Placeholder background while images load. |
--pswp-root-z-index | 2147483647 | Z-index for the PhotoSwipe root element. |
--pswp-icon-color | #fff | Icon (close, arrows, zoom) fill color. |
--pswp-icon-color-secondary | #4f4f4f | Secondary icon color. |
--pswp-icon-stroke-color | #4f4f4f | Icon stroke color. |
--pswp-icon-stroke-width | 2px | Icon stroke width. |
--pswp-error-text-color | var(--pswp-icon-color) | Error message text color. |
--pswp-transition-duration | (PhotoSwipe default) | Transition duration for UI element fade. |
Common Mistakes
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.
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.
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.
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.
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.