Optional Custom Lightbox Feature in WordPress

Hey there! 👋 Ever wondered how those fancy image overlays on websites work? You know, the ones that make an image pop out, dim the background, and focus all attention on the image.

Something like this 👇🏻

It’s the kind of feature that screams, “Look at this beautiful picture, distraction-free!”

Without a lightbox? Clicking an image might open it in a new tab. (Yikes, nobody loves that.) With a lightbox? Everything stays on the same page, polished, smooth, and user-friendly. A win for your site’s look and feel! 🎉

But Wait, Why Make It Optional? 🤔

Ah, great question! Not every image needs to be a diva. Some images are better left simple, part of a layout, or don’t need the whole dim-the-lights treatment. That’s why we’re giving editors a choice, a toggle in the WordPress block editor to decide which images deserve the lightbox feature.

Image Setting

It’s all about flexibility. Editors know their content best, and our job is to make their lives easier by giving them the tools they need. A feature that’s there when you need it and stays out of the way when you don’t? Now that’s cool. 😎

What Kind of Choices Do We Have to Implement It? 🤔

When it comes to adding a feature like a lightbox, there are a few paths we can take. Let’s break them down and see what makes sense.

Option 1: Create a New Custom Image Block

One option is to create an entirely new image block with built-in lightbox functionality. Sounds cool, right? But here’s the thing: WordPress already has an Image block that editors are familiar with. Why reinvent the wheel? Plus, managing multiple similar blocks can confuse editors, and let’s not do that to them. 🚫

Option 2: Add a Block Style Variation

Another way is to create a style variation. This adds a lightbox option as a selectable style (e.g., “Default” and “Lightbox-enabled”). It’s straightforward and keeps things clean. But there’s a catch: style variations are primarily for visual changes (like borders or background colors), not functional enhancements. Lightboxes are more than a visual tweak, so this might not be the right fit.

Option 3: Extend the Existing Image Block

This approach takes the existing Image block and spices it up with new functionality. It’s editor-friendly (no new blocks to learn) and fits neatly into the WordPress ecosystem.

Now, you might think, “Hmm, is this really the best approach, or are we skipping alternatives?” Fair question! Let’s clear the air and break it down. 🔮

Here’s why extending the Image block makes perfect sense:

  • The core Image block is already there, and editors know how to use it. By extending it, we’re building on something familiar, not replacing it. This keeps the workflow seamless.
  • Editors can choose to enable the lightbox on a per-image basis. No one-size-fits-all solution here—just good ol’ flexibility.
  • WordPress encourages reusing and extending existing components instead of creating duplicates. This approach aligns with that philosophy and feels… well, WordPress-y.
  • Maintaining one enhanced block is much easier than juggling multiple similar blocks or hacky style variations. Simplicity for the win! 💪

Building the Lightbox Step-by-Step 🛠 ️

Now that we’ve decided on the approach, let’s dive into the nitty-gritty of building this lightbox feature. We’ll break it down into four key steps:

1. Extending the Core Image Block

The first thing we need to do is extend WordPress’s existing Image block. But how do we add new functionality to a block?

Enter the WordPress filter: blocks.registerBlockType. 🎉

This filter allows us to modify the settings of any block, including adding new attributes. In our case, we’ll add a new attribute called openInLightbox. This attribute will store whether the lightbox toggle is enabled for a particular image.

Here’s how it looks:

addFilter(
	'blocks.registerBlockType',
	'my-blocks/add-lightbox-attribute',
	(settings, name) => {
		if (name === 'core/image') {
			return {
				...settings,
				attributes: {
					...settings.attributes,
					openInLightbox: {
						type: 'boolean',
						default: false,
					},
				},
			};
		}
		return settings;
	},
);
  • It hooks into the blocks.registerBlockType filter to modify the settings of the Image block (core/image).
  • Adds a new attribute, openInLightbox, which is a simple boolean (true or false).
  • Sets the default value to false, so the lightbox is disabled unless explicitly toggled on.

Why Use a Boolean State? 🤔

A boolean state is simple, intuitive, and fits naturally into the block editor’s interface. It’s a clear on/off switch—perfect for binary decisions like whether an image should open in a lightbox. No dropdowns, no extra inputs ✅

2. Adding a Toggle for the Lightbox Feature

Now that we’ve added the openInLightbox attribute, the next step is to give editors control over it. How? By adding a toggle button to the block settings panel.

For this, we’ll use the InspectorControls component, which lets us add custom settings to the sidebar. The toggle will allow editors to enable or disable the lightbox for each image.

Here’s the code:

<InspectorControls>
	<PanelBody title="Image Display Settings" initialOpen={false}>
		<ToggleControl
			label="Open in Lightbox"
			checked={attributes.openInLightbox}
			onChange={() =>
				setAttributes({ openInLightbox: !attributes.openInLightbox })
			}
		/>
	</PanelBody>
</InspectorControls>

When editors enable the toggle, the lightbox functionality will be activated for that image. Otherwise, the image will behave as usual.

3. Applying Lightbox Settings to the Frontend

Okay, we’ve got the attribute and toggle set up. Now it’s time to make sure this works where it matters—the frontend.

The goal is simple: add a CSS class (is-lightbox-enabled) to the block only when the toggle is enabled. This class will trigger the lightbox functionality for images.

For this, we use the blocks.getSaveContent.extraProps filter. This handy filter allows us to modify the block’s saved output dynamically.

Here’s the code:

function applyLightboxSettings(props, blockType, attributes) {
	if (blockType.name === 'core/image' && attributes.openInLightbox) {
		props.className = `${props.className || ''} is-lightbox-enabled`;
	}
	return props;
}

addFilter(
	'blocks.getSaveContent.extraProps',
	'my-blocks/apply-lightbox-settings',
	applyLightboxSettings,
);

4. Lightbox UI and Frontend Implementation

Now that we’ve set up the Image block with our lightbox toggle, it’s time to bring the feature to life. The goal is simple: when an editor enables the lightbox toggle for an image, clicking on the image should open it in an elegant overlay. Let’s break this into steps.

Step 1: Creating the Lightbox Overlay

We need a fullscreen overlay to display the image when it’s clicked. This overlay should:

  • Cover the entire viewport.
  • Display the clicked image prominently.
  • Have a close button to exit the overlay.

Why ❔

This ensures a seamless, distraction-free viewing experience for users. Without the overlay, clicking the image might either reload the page or just expand it awkwardly within the content.

Solution 💡

We’ll dynamically create a lightbox wrapper in the DOM to serve as our overlay. Here’s the basic structure we’re aiming for:

<div class="my-lightbox-wrapper">
	<div class="my-lightbox">
		<div class="my-lightbox-content">
			<img src="image-url.jpg" alt="Lightbox Image" />
		</div>
		<a href="#" class="my-lightbox-close">X</a>
	</div>
</div>

Step 2: Adding Accessibility Features

Accessibility isn’t just a nice-to-have; it’s essential. A lightbox should be usable by everyone, including people who rely on screen readers or keyboard navigation.

Why ❔

  • Users who cannot use a mouse should be able to open and close the lightbox with their keyboard (e.g., pressing Enter to open and Escape to close).
  • Focus management ensures users don’t lose context when the lightbox opens or closes.

Solution 💡

  1. Keyboard Navigation:
    • Pressing Enter opens the lightbox.
    • Pressing Escape closes it.
  2. Focus Management:
    • When the lightbox opens, focus moves to the close button.
    • When it closes, focus returns to the image.

Code: Here’s how to handle the keyboard interactions:

attachEventListeners() {
	// Open lightbox with keyboard
	this.figureElement.onkeydown = (event) => {
		if (event.key === 'Enter') {
			this.openLightbox(event);
		}
	};

	// Close lightbox with Escape key
	document.addEventListener('keydown', (event) => {
		if (event.key === 'Escape' && this.lightboxWrapper.style.display === 'flex') {
			this.closeLightbox(event);
		}
	});
}

Advantages:

  • Makes the lightbox accessible to everyone.
  • Improves usability, even for those who prefer keyboard shortcuts.

Step 3: Opening and Closing the Lightbox

When the image is clicked, the lightbox should appear. When clicking outside the image or on the close button, it should disappear.

Why ❔

This creates a smooth and intuitive experience for users, preventing confusion or unintended interactions.

Solution 💡

  • Clicking on the image triggers the openLightbox method.
  • Clicking outside the lightbox content or on the close button triggers closeLightbox.
openLightbox = (event) => {
	event.preventDefault();
	this.lightboxWrapper.style.display = 'flex';
};

closeLightbox = (event) => {
	if (event) {
		event.preventDefault();
	}
	this.lightboxWrapper.style.display = 'none';
};

This ensures:

  • The overlay is displayed when needed and hidden when not.
  • There’s no accidental navigation to the image’s URL.

Step 4: Wrapping It All Together

Once we’ve addressed the needs for overlay creation, accessibility, and interactivity, we can initialize our lightbox on images with the is-lightbox-enabled class.

Initialization Code:

document.addEventListener('DOMContentLoaded', () => {
	const lightboxFigures = document.querySelectorAll(
		'figure.is-lightbox-enabled',
	);
	lightboxFigures.forEach((figure) => new Lightbox(figure));
});

Step 5: Styling the Lightbox

Finally, let’s ensure the lightbox looks as polished as it feels. This includes:

  1. A dimmed background for the overlay.
  2. A centered image container.
  3. A prominent close button.

SCSS Example:

.my-lightbox-wrapper {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background: rgba(0, 0, 0, 0.8);
	display: flex;
	justify-content: center;
	align-items: center;
	z-index: 9999;

	.my-lightbox {
		width: 80%;
		height: 80%;

		.my-lightbox-content {
			display: flex;
			justify-content: center;
			align-items: center;

			img {
				max-width: 100%;
				max-height: 100%;
			}
		}

		.my-lightbox-close {
			position: absolute;
			top: 10px;
			right: 10px;
			color: white;
			font-size: 24px;
			cursor: pointer;
		}
	}
}

And Here’s How Your Super Cool Lightbox Feature is Ready!

Oh, really? Yes! 🎉 You’ve just built a sleek, functional, and editor-friendly lightbox feature for WordPress. Pat yourself on the back because this is no small feat! But hey, there’s always room for a little extra. 😉

Let’s explore some enhancements you could add to make this lightbox even more impressive:

Smooth Animations ✨

  • Add a touch of elegance with animations for opening and closing the lightbox.
  • Example: Fade-in and fade-out effects to make transitions seamless.

Lazy Loading 💤

  • Optimize performance by loading images only when they’re about to be displayed in the lightbox.

Multi-Image Navigation ⬅️➡️

  • Add next/previous buttons for navigating through multiple images in a single lightbox session.

Customizable Options 🎛️

  • Let editors choose lightbox settings, like overlay color, transition speed, or lightbox size, directly in the block settings.

And there you have it, a fully functional lightbox feature with all the flexibility editors could ask for! By making it optional, you’ve empowered editors to decide when and where to use it, ensuring a perfect balance between functionality and user control.

The beauty of development is that it’s never truly “done.” There’s always an opportunity to tweak, improve, and innovate. So, whether you stop here or decide to add some fancy enhancements, you’ve created something both useful and elegant.