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.


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. defined('WPINC') || die(); 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-button.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. */ require_once dirname(__FILE__) . '/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.

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. defined('WPINC') || die(); 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() || 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') === false) { // Non-YouTube video, possibly self-hosted. } else { // 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 ); } } } // 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.
- Parse
$atts
to grab the shortcode’s parameters. - Enqueue the CSS/JS for Lity, along with our own CSS.
- If we’ve specified a video URL with “youtube.com” in it, and we have not specified a thumbnail URL…
- Try to auto-create a thumbnail image URL based on the YouTube video URL.
- If the shortcode hasn’t specified a video URL…
- Output an error message.
- Else…
- Open the
<figure>
element. - Open the
<a>
for Lity. - If a caption has been specified…
- Output it in a special screen reader <span> element – good for SEO too.
- Render the
<img>
for the thumbnail/poster. - If a caption has been specified…
- Render a
<figcaption>
- Render a
- Close-off the
<figure>
element.
- Open the
- 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-button.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! 👍
Hi there, great tutorial. Worked great except one thing. I don’t use a child theme, and was wondering if I put the files in a different directory, how to call it.
I’m glad the tutorial works well for you. I use this on lots of my projects and it’s great on mobile devices.
If you have a site that doesn’t use a child theme, you’ll need to create a small custom plugin, which isn’t as difficult as it sounds.
This tutorial shows you how to make a single-flie plugin: https://wp-tutorials.tech/add-functionality/create-a-private-wordpress-site-without-a-plugin/
…just replace the PHP with the code from the video tutorial, and replace get_stylesheet_directory_uri() with plugin_dir_url(__FILE__) and you will be about 90% of the way there.
Let us know how you get on.