Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.
<!-- Free Video Popup widget by haynes lab — https://hayneslab.dev/tools/widgets/video-popup -->
<script data-hl-src="https://www.youtube.com/watch?v=aqz-KE-bpKQ" data-hl-title="Big Buck Bunny — Blender Open Movie">
/*!
* haynes lab — Video Popup widget (free)
* Docs & embed code: https://hayneslab.dev/tools/widgets/video-popup
*/
(function () {
'use strict';
var PREFIX = 'hlw-video-popup';
var STYLE_ID = 'hlw-video-popup-styles';
function findScript() {
return document.currentScript ||
document.querySelector('script[src*="widgets/video-popup.js"]');
}
function injectStyles() {
if (document.getElementById(STYLE_ID)) return;
var css =
'.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
'.' + PREFIX + ' { position: relative; max-width: 640px; margin: 0 auto;' +
' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }' +
'.' + PREFIX + '__trigger { position: relative; display: block; width: 100%; padding: 0;' +
' border: 1px solid #e5e7eb; border-radius: 12px; overflow: hidden; background: #111827; cursor: pointer; }' +
'.' + PREFIX + '__thumb { display: block; width: 100%; aspect-ratio: 16 / 9; object-fit: cover; }' +
'.' + PREFIX + '__placeholder { display: block; width: 100%; aspect-ratio: 16 / 9;' +
' background: linear-gradient(135deg, #1f2937 0%, #111827 100%); }' +
'.' + PREFIX + '__play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);' +
' width: 64px; height: 64px; border-radius: 50%; background: var(--hlw-accent); color: #ffffff;' +
' display: flex; align-items: center; justify-content: center; box-shadow: 0 6px 20px rgba(0,0,0,0.35);' +
' transition: transform 0.15s ease; }' +
'.' + PREFIX + '__trigger:hover .' + PREFIX + '__play { transform: translate(-50%, -50%) scale(1.08); }' +
'.' + PREFIX + '__trigger:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
'.' + PREFIX + '__play svg { display: block; margin-left: 4px; }' +
'.' + PREFIX + '__label { position: absolute; left: 0; right: 0; bottom: 0; padding: 24px 14px 10px;' +
' font-size: 14px; font-weight: 600; color: #ffffff; text-align: left;' +
' background: linear-gradient(transparent, rgba(0,0,0,0.65)); }' +
'.' + PREFIX + '__credit { display: inline-flex; align-items: center; gap: 4px; margin-top: 8px;' +
' font-size: 11px; color: #9ca3af; text-decoration: none; }' +
'.' + PREFIX + '__credit svg { display: block; }' +
'.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
'.' + PREFIX + '__overlay { position: fixed; inset: 0; z-index: 99999; background: rgba(17,24,39,0.85);' +
' display: flex; align-items: center; justify-content: center; padding: 20px; }' +
'.' + PREFIX + '__dialog { position: relative; width: 100%; max-width: 900px; }' +
'.' + PREFIX + '__frame-wrap { position: relative; width: 100%; aspect-ratio: 16 / 9;' +
' background: #000000; border-radius: 12px; overflow: hidden; box-shadow: 0 20px 60px rgba(0,0,0,0.5); }' +
'.' + PREFIX + '__frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }' +
'.' + PREFIX + '__close { position: absolute; top: -12px; right: -12px; width: 36px; height: 36px;' +
' border: 0; border-radius: 50%; background: #ffffff; color: #374151; font-size: 18px; line-height: 1;' +
' cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0;' +
' box-shadow: 0 4px 12px rgba(0,0,0,0.35); }' +
'.' + PREFIX + '__close:hover { color: #111827; }' +
'.' + PREFIX + '__close:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
'.' + PREFIX + '__caption { margin-top: 10px; font-size: 14px; color: #e5e7eb; text-align: center; }' +
'@media (max-width: 560px) { .' + PREFIX + '__overlay { padding: 12px; }' +
' .' + PREFIX + '__close { top: -40px; right: 0; } }';
var style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = css;
document.head.appendChild(style);
}
function el(tag, className, text) {
var node = document.createElement(tag);
if (className) node.className = className;
if (text != null) node.textContent = text;
return node;
}
function parseVideo(src) {
var m;
// YouTube: watch?v=, youtu.be/, /embed/, /shorts/, /live/
m = src.match(/(?:youtube(?:-nocookie)?\.com\/(?:watch\?[^#]*v=|embed\/|shorts\/|live\/)|youtu\.be\/)([\w-]{6,20})/);
if (m) {
return {
type: 'youtube',
id: m[1],
embed: 'https://www.youtube-nocookie.com/embed/' + m[1] + '?autoplay=1&rel=0',
thumb: 'https://i.ytimg.com/vi/' + m[1] + '/hqdefault.jpg'
};
}
// Vimeo: vimeo.com/12345 or player.vimeo.com/video/12345
m = src.match(/(?:player\.)?vimeo\.com\/(?:video\/)?(\d+)/);
if (m) {
return {
type: 'vimeo',
id: m[1],
embed: 'https://player.vimeo.com/video/' + m[1] + '?autoplay=1',
thumb: null
};
}
return null;
}
function playSvg() {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('width', '26');
svg.setAttribute('height', '26');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');
var path = document.createElementNS(svgNS, 'path');
path.setAttribute('d', 'M8 5.14v13.72c0 .8.87 1.3 1.56.88l10.5-6.86a1.04 1.04 0 0 0 0-1.76L9.56 4.26A1.03 1.03 0 0 0 8 5.14z');
path.setAttribute('fill', 'currentColor');
svg.appendChild(path);
return svg;
}
function logoSvg() {
var svgNS = 'http://www.w3.org/2000/svg';
var svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('viewBox', '0 0 32 32');
svg.setAttribute('width', '13');
svg.setAttribute('height', '13');
svg.setAttribute('aria-hidden', 'true');
svg.setAttribute('focusable', 'false');
function circle(cx, cy, r, attrs) {
var c = document.createElementNS(svgNS, 'circle');
c.setAttribute('cx', cx);
c.setAttribute('cy', cy);
c.setAttribute('r', r);
for (var key in attrs) c.setAttribute(key, attrs[key]);
svg.appendChild(c);
}
circle(16, 16, 16, { fill: '#0f0a0a' });
circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
circle(16, 16, 3, { fill: '#06b6d4' });
circle(24.5, 7.5, 2, { fill: '#f97316' });
circle(7, 16, 1.8, { fill: '#f97316' });
circle(20, 20, 1.5, { fill: '#f97316' });
return svg;
}
function build(script) {
var src = script.getAttribute('data-hl-src');
if (!src) {
console.warn('[haynes lab video-popup] Missing required data-hl-src attribute (YouTube or Vimeo URL). Nothing rendered.');
return;
}
var video = parseVideo(src);
if (!video) {
console.warn('[haynes lab video-popup] data-hl-src is not a recognized YouTube or Vimeo URL: ' + src);
return;
}
var thumbnail = script.getAttribute('data-hl-thumbnail') || video.thumb;
var title = script.getAttribute('data-hl-title') || 'Play video';
var accent = script.getAttribute('data-hl-accent') || '#f97316';
var inStage = !!script.closest('.widget-demo__stage');
injectStyles();
var root = el('div', PREFIX);
root.style.setProperty('--hlw-accent', accent);
var trigger = el('button', PREFIX + '__trigger');
trigger.type = 'button';
trigger.setAttribute('aria-haspopup', 'dialog');
trigger.setAttribute('aria-label', title);
if (thumbnail) {
var img = el('img', PREFIX + '__thumb');
img.src = thumbnail;
img.alt = '';
img.loading = 'lazy';
trigger.appendChild(img);
} else {
trigger.appendChild(el('span', PREFIX + '__placeholder'));
}
var play = el('span', PREFIX + '__play');
play.appendChild(playSvg());
trigger.appendChild(play);
if (script.getAttribute('data-hl-title')) {
trigger.appendChild(el('span', PREFIX + '__label', title));
}
root.appendChild(trigger);
var overlay = null;
function close() {
if (!overlay) return;
overlay.remove();
overlay = null;
document.removeEventListener('keydown', onKeydown);
trigger.focus();
}
function onKeydown(e) {
if (e.key === 'Escape') close();
}
function open() {
if (overlay) return;
overlay = el('div', PREFIX + '__overlay');
if (inStage) overlay.style.position = 'absolute';
var dialog = el('div', PREFIX + '__dialog');
dialog.setAttribute('role', 'dialog');
dialog.setAttribute('aria-modal', 'true');
dialog.setAttribute('aria-label', title);
var wrap = el('div', PREFIX + '__frame-wrap');
var frame = el('iframe', PREFIX + '__frame');
frame.src = video.embed;
frame.title = title;
frame.setAttribute('allow', 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share');
frame.setAttribute('allowfullscreen', '');
wrap.appendChild(frame);
dialog.appendChild(wrap);
var closeBtn = el('button', PREFIX + '__close', '×');
closeBtn.type = 'button';
closeBtn.setAttribute('aria-label', 'Close video');
closeBtn.addEventListener('click', close);
dialog.appendChild(closeBtn);
if (script.getAttribute('data-hl-title')) {
dialog.appendChild(el('div', PREFIX + '__caption', title));
}
overlay.appendChild(dialog);
overlay.addEventListener('click', function (e) {
if (e.target === overlay) close();
});
document.addEventListener('keydown', onKeydown);
if (inStage && script.closest('.widget-demo__stage')) {
script.closest('.widget-demo__stage').appendChild(overlay);
} else {
document.body.appendChild(overlay);
}
closeBtn.focus();
}
trigger.addEventListener('click', open);
var credit = el('a', PREFIX + '__credit');
credit.appendChild(logoSvg());
credit.appendChild(document.createTextNode('Powered by haynes lab'));
credit.href = 'https://hayneslab.dev/tools/widgets/video-popup';
credit.target = '_blank';
credit.rel = 'noopener';
root.appendChild(credit);
if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
script.insertAdjacentElement('afterend', root);
} else {
document.body.appendChild(root);
}
}
function init() {
var script = findScript();
if (!script) {
console.warn('[haynes lab video-popup] Could not locate its own script tag. Nothing rendered.');
return;
}
build(script);
}
if (document.body) {
init();
} else {
document.addEventListener('DOMContentLoaded', init);
}
})();
</script>