Image Lightbox

 
 
 
 

The lightbox opens an image at full viewport size in a native <dialog>. It is one implementation shared by the Fitted Image block (a single image) and the Swiper Gallery block (every slide): the same trigger markup from method_get_lightbox_attributes() (lib/blocks.php), the same script in lib/blocks/utils/lightbox.js, the same styles in global.scss.


How it works

  • The trigger is a real link. method_get_lightbox_attributes( $attachment_id ) returns the attributes for an <a>: an href to the full-size attachment, data-method-lightbox, data-method-lightbox-src / -width / -height / -alt, data-method-lightbox-srcset when a full srcset exists, and aria-haspopup="dialog". It returns an empty array when the attachment has no usable full-size image, and the block then renders as if the lightbox were off. Without the script, or in a browser without <dialog>, the link simply opens the uncropped image.
  • One dialog per page. Each block’s view script calls initMethodLightbox(); the first call installs the behaviour and later calls return at once (window.methodLightboxLoaded), so any mix of lightbox blocks shares a single lazily built <dialog class="method-lightbox"> with an aria-label, a 44×44px close button and a <figure> for the image.
  • One delegated click listener handles [data-method-lightbox]. Modifier and middle clicks keep their native behaviour (the image opens in a new tab); clicks Swiper has already cancelled after a drag are ignored, so swiping a gallery never opens it.
  • Native dialog semantics. showModal() gives focus trapping, Escape to close, inert page content and focus restoration for free. The page is scroll-locked with html.method-lightbox-open; clicking anything but the image closes; the image gets sizes="100vw" plus the full srcset so the browser picks a viewport-sized candidate; an is-loading spinner shows until the image settles (skipped when it is already cached).

Using it in a block

A block wraps its image in the trigger and bundles the shared script into its own view script:

render callback
// render callback, as the Fitted Image block does it
$lightbox = $image_id ? method_get_lightbox_attributes( $image_id ) : array();

$open  = '<div ' . get_block_wrapper_attributes( array( 'class' => 'my-block', 'id' => $methodId ) ) . '>';
$close = '</div>';

if ( $lightbox ) {
    // The whole block becomes the trigger: an <a> to the full-size image.
    $open  = '<a ' . get_block_wrapper_attributes( array_merge(
        array( 'class' => 'my-block method-lightbox-trigger', 'id' => $methodId ),
        $lightbox
    ) ) . '>';
    $close = '</a>';
}
frontend.js
// view script
import { initMethodLightbox } from '../../utils/lightbox';
initMethodLightbox();

The Fitted Image block does this when its lightbox attribute is on, wrapping the whole block in the trigger instead of a <div> and using the full-size version of whichever image rendered (the chosen image or the post thumbnail). The Swiper Gallery renders each .method-swiper-gallery-item as the trigger.


Styles

  • .method-lightbox-trigger — zoom-in cursor.
  • dialog.method-lightbox — resets the UA dialog box to cover the viewport (100dvh where supported) with a 92% black surround; the image is letterboxed with object-fit: contain.
  • .method-lightbox close button and spinner; the image fade and spinner speed respect prefers-reduced-motion.

Changelog

VersionDescription
2.0.0-beta23Introduced with the Fitted Image lightbox option and the rebuilt Swiper Gallery: method_get_lightbox_attributes(), lib/blocks/utils/lightbox.js, the shared dialog and styles (eacd345).