Responsive Video Light-box with Lity

This tutorial shows you how to make an elegant & responsive video pop-up light-box. Instead of just using the standard WordPress block to embed a video, we’ll use the Lity JavaScript library to pop the video to a fully responsive video player, and it’ll work with YouTube and self-hosted videos.

You don’t need to add any new plugins to your WordPress site. All you need is a custom child theme so we can add a bit of PHP and JavaScript to make it work.

Video: Hosted on YouTubeA video
Hosted on YouTube

Video: A self-hosted videoA video
A self-hosted video

Basically, we need a PHP file on the server to implement a custom shortcode. We need the open source Lity library (a free download) and we need to create a bit of CSS to polish it off. If you’re not experienced at coding, it might sound like hard work, but…

…all we’re going to do is create a single PHP function, add a couple of CSS definitions, and use the Lity JavaScript library to do all the hard work.

Getting Started

We’ll start by sorting-out our files, and we’ll put a placeholder function in there so we can test things. In your custom child theme’s main folder, create a new file called simple-video-lightbox.php and paste the following into it.

<?php

// Block direct access.
if (!defined('WPINC')) {
	exit('Do NOT access this file directly.');
}

function svl_do_shortcode_video($params) {
	// We'll replace this with the proper code later.
	return '<strong>Light-box in here</strong>';
}
add_shortcode('svl_video', 'svl_do_shortcode_video');

infoOur function begins with “svl_“, which stands for “Simple Video Light-Box”.

We also need a folder where we can add our supporting files, so make a sub-folder in your custom child theme called simple-video-lightbox. Next, go to the Lity website and download the current version. Extract the zip file and go into the “dist” (distributable) folder and you’ll see several files. We only need the minified JS & CSS files, so copy lity.min.css and lity.min.js into the new simple-video-lightbox sub-folder.

Grab a copy of the YouTube play button SVG file, save it into the sub-folder, and rename it to youtube-play-button.svg.

Yoiu’ll also need a standard (non-YouTube) play button. FInd or create an SVG and save it in the subfolder, with the name “standard-play-butotn.svg

Create an empty CSS file in the sub-folder called svl-frontend.css.

Finally, we need to tell our custom child theme to use the new code, so open your custom child theme’s functions.php file and add the following into it.

simple video light-box asset files
Asset files for our Simple Video Light-box project
/**
 * Simple Video Light-box.
 */
require_once 'simple-video-lightbox.php';

OK, that should be enough to tell WordPress that we’ve created a new shortcode called svl_video, so let’s see if it works. Edit a page or a post, create a shortcode block and add the new shortcode with a “url” parameter pointing to a YouTube video. Save your content and view it. If everything’s working properly, you should see the text “Light-box in here” rendered when you want your video to be.

video lightbox shortcode
Simple Video Light-box shortcode block

Render the Shortcode’s HTML

Now the “scaffolding” is in place, we can add the code to render the proper HTML. We’re going to create an outer figure container, which will wrap a thumbnail/poster image that we can grab from YouTube (based on the video URL), or we can specify it manually (with the thumb=”…” parameter). Update your simple-video-lightbox.php file with the following.

<?php

/**
 * WP Tutorials : Simple Video Lightbox (svl)
 *
 * https://wp-tutorials.tech/add-functionality/responsive-video-light-box/
 */

// Block direct access.
if (!defined('WPINC')) {
	exit('Do NOT access this file directly.');
}

const SVL_LITY_VERSION = '2.4.1';

function svl_enqueue_assets() {
	global $svl_have_assets_been_queued;

	if (is_null($svl_have_assets_been_queued)) {
		$base_url = get_stylesheet_directory_uri() . '/' . pathinfo(__FILE__, PATHINFO_FILENAME) . '/';
		$theme_version = wp_get_theme()->get('Version');

		// Lity
		wp_enqueue_style('lity', $base_url . 'lity.min.css', null, SVL_LITY_VERSION);
		wp_enqueue_script('lity', $base_url . 'lity.min.js', null, SVL_LITY_VERSION, true);

		// Our frontend CSS.
		wp_enqueue_style('svl-frontend', $base_url . 'svl-frontend.css', null, $theme_version);

		$svl_have_assets_been_queued = true;
	}
}

function svl_do_shortcode_video($atts) {
	$html = '';

	if (is_admin()) {
		// Don't do anything.
	} elseif (wp_doing_ajax()) {
		// Don't do anything.
	} else {
		// Enqueue the assets.
		svl_enqueue_assets();

		// Parse the arguments passed from the shortcode.
		$args = shortcode_atts(
			array(
				'url' => '',
				'thumb' => '',
				'alt' => 'A video',
				'w' => 400,
				'h' => 300,
				'caption' => '',
			),
			$atts
		);

		if (empty($args['alt']) && !empty($args['caption'])) {
			$args['alt'] = $args['caption'];
		}

		$video_url = null;
		$css_classes = array('svl-container');

		if (empty($video_url = $args['url'])) {
			// No video URL specified.
		} elseif (strpos($video_url, 'youtube.com') > 0) {
			// We have a YouTube video URL, so parse the URL to grab the video
			// reference (v=xxxxxx) and maybe try to get the post JPEG for the
			// video too.
			$youtube_video_ref = null;
			$css_classes[] = 'youtube';

			if (!empty($args['thumb'])) {
				// We've set the thumbnail/poster in the "thumb" arg, so we
				// don't need to fetch the poster from YouTube.
			} else {
				$url_parts = parse_url($video_url);

				$query_args = null;
				if (is_array($url_parts) && array_key_exists('query', $url_parts)) {
					parse_str($url_parts['query'], $query_args);
				}

				if (is_array($query_args) && array_key_exists('v', $query_args) && !empty($query_args['v'])) {
					$youtube_video_ref = $query_args['v'];
				}

				if (!empty($youtube_video_ref)) {
					// Point the thumbnail/poster to YouTube,
					$args['thumb'] = sprintf(
						'https://img.youtube.com/vi/%s/0.jpg',
						$youtube_video_ref
					);
				}
			}
		} else {
			// Non-YouTube video, possibly self-hosted.
		}

		// If we've got a valid video URL, render the HTML.
		if (empty($video_url)) {
			// No video ref found, so do nothing.
			$html .= '<p>ERROR: No video_url</p>';
		} else {
			$html .= sprintf('<figure class="%s">', implode(' ', $css_classes));

			$html .= sprintf('<a href="%s" title="%s" data-lity>', esc_url($video_url), esc_attr($args['alt']));

			// For the SEO and screen-reader.
			if (!empty($args['caption'])) {
				$html .= sprintf('<span class="screen-reader-text">Video: %s</span>', esc_attr($args['caption']));
			}

			// The thumbnail / poster image.
			$html .= sprintf(
				'<img src="%s" width="%d" height="%d" alt="%s" />',
				esc_url($args['thumb']),
				$args['w'],
				$args['h'],
				esc_attr($args['alt'])
			);

			$html .= '</a>';

			if (!empty($args['caption'])) {
				$html .= sprintf('<span class="screen-reader-text">Video: %s</span>', esc_attr($args['caption']));
				$html .= sprintf('<figcaption>%s</figcaption>', esc_html($args['caption']));
			}

			$html .= '</figure>'; // .svl-container
		}
	}

	return $html;
}
add_shortcode('svl_video', 'svl_do_shortcode_video');

This might seem like a bit of a chunk but it breaks-down quite nicely, and there are loads of comments too. The parameters for our shortcode come through in $atts so we need to pull the video URL, and possibly an SEO-friendly image alt tag for our thumbnail image.

  1. Parse $atts to grab the shortcode’s parameters.
  2. Enqueue the CSS/JS for Lity, along with our own CSS.
  3. If we’ve specified a video URL with “youtube.com” in it, and we have not specified a thumbnail URL…
    1. Try to auto-create a thumbnail image URL based on the YouTube video URL.
  4. If the shortcode hasn’t specified a video URL…
    1. Output an error message.
  5. Else…
    1. Open the <figure> element.
    2. Open the <a> for Lity.
    3. If a caption has been specified…
      1. Output it in a special screen reader <span> element – good for SEO too.
    4. Render the <img> for the thumbnail/poster.
    5. If a caption has been specified…
      1. Render a <figcaption>
    6. Close-off the <figure> element.
  6. Return $html so WordPress can render it for us.

That should be enough to “make it work”. Save your changes, reload your content and you should see your video thumbnail. Click on it – it should open the video light-box and play 😎

What if it Didn’t Work…

  • If it’s a YouTube URL but you don’t see a video thumbnail, check your YouTube URL is correct and that the video reference is specified with “v=xxxxxxxxxx”
  • The thumbnail shows OK, but clicking on it takes you to the YouTube page instead of showing the pop-out light-box.
    • Check your browser’s JavaScript console to see if it was unable to load the lity.min.js file for some reason.

Adding Some Style

We’ve now got a working shortcode for playing YouTube videos, but it’s not obvious that the image is a video – it just looks like an image. So let’s put a Play button over the image with some CSS. Paste the following into your svl-frontend.css file..

/**
 * Simple Video Light-box
 *
 * https://wp-tutorials.tech/add-functionality/responsive-video-light-box/
 */

.svl-container {
	position:  relative;
	margin-bottom:  1em;
}

.svl-container > a {
	display:  block;
	border:  1px solid lightgrey;
	box-shadow: 0 0 1.00em rgba( 0, 0, 0, 0.20 );
	border-radius:  0.35em;
	overflow: hidden;
}

.svl-container > a:hover {
	box-shadow: 0 0 1.00em rgba( 0, 0, 0, 0.40 );
}

.svl-container > a img {
	display:  block;
	width:  100%;
	object-fit:  cover;
}

.svl-container > a::before {
	content: url( 'standard-play-butotn.svg' );
	position:  absolute;
	left:  50%;
	top:  50%;
	width:  25%;
	max-width:  5em;
	transform: translate(-50%, -50%);
	transition: 0.3s;
	opacity: 0.85;
	z-index:  10;
}

.svl-container.youtube > a::before {
	content: url( 'youtube-play-button.svg' );
}

.svl-container > a:hover::before {
	opacity: 1.00;
}

.svl-container figcaption {
	text-align: center;
	position:  absolute;
	bottom:  0;
	width:  100%;
	padding: 0.5em 0;
	color:  white;
	background-color:  #444444c0;
	pointer-events: none;
}

The CSS is pretty straightforward – easy to hack it about to suit your needs.

Wrapping it Up

I think it’s nicer to have videos embedded like this, rather than with the oEmbed YouTube stuff, because it avoids using an iFrame. iFrames were a bad idea back in the 2000s, and they’re still a bad idea now. So there.

Now that you’ve got 100% control of the HTML, you can really milk it for SEO. Try extending this code a bit …

  • Set the aria-label property of the <a> element for better accessibility.
  • Sprinkle some custom JavaScript mojo over it to render some cool mouse-over effects.
  • Check out the Lity website for more info too, because it’s not just limited to showing videos.

Have fun adding video thumbnails all over your WordPress site! 👍

Leave a Comment

Your email address will not be published. Required fields are marked *