Custom Vimeo Lightbox (Advanced)
A fully custom Vimeo lightbox with play/pause, mute, fullscreen, timeline scrubbing, duration display, and placeholder image. Opens over a dark overlay with smooth transitions. Supports switching between multiple videos.
Setup — External Scripts
<script src="https://player.vimeo.com/api/player.js"></script>Code
<div class="vimeo-lightbox" data-vimeo-lightbox-init="" data-vimeo-activated="false" data-vimeo-playing="false" data-vimeo-fullscreen="false" data-vimeo-hover="false" data-vimeo-loaded="false" data-vimeo-update-size="true" data-vimeo-muted="false">
<div data-vimeo-lightbox-control="close" class="vimeo-lightbox__bg">
<svg class="vimeo-lightbox__loading-svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="L9" x="0px" y="0px" viewbox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve" width="100%"><path fill="currentColor" d="M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50"></path><animatetransform attributename="transform" attributetype="XML" type="rotate" dur="1s" from="0 50 50" to="360 50 50" repeatcount="indefinite"></animatetransform></svg>
</div>
<div class="vimeo-lightbox__calc">
<div class="vimeo-lightbox__calc-wrap">
<div data-vimeo-lightbox-player="" class="vimeo-lightbox__player">
<div class="vimeo-lightbox__before"></div>
<iframe src="" width="640" height="360" frameborder="0" allowfullscreen="true" allow="autoplay; encrypted-media" class="vimeo-lightbox__iframe"></iframe>
<img src="https://d3e54v103j8qbb.cloudfront.net/plugins/Basic/assets/placeholder.60f9b1840c.svg" loading="lazy" alt="" class="vimeo-lightbox__placeholder">
<div class="vimeo-lightbox__dark"></div>
<div data-vimeo-control="play" class="vimeo-lightbox__play">
<div class="vimeo-lightbox__btn">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 24 24" fill="none" class="vimeo-lightbox__btn-play-svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M6.38178 6.13982C6.40762 5.94017 6.48425 5.74878 6.60555 5.58089C6.72686 5.413 6.88949 5.27326 7.08049 5.17279C7.2715 5.07232 7.48559 5.01391 7.70574 5.00219C7.92587 4.99048 8.14597 5.02579 8.3485 5.10532C9.37235 5.50436 11.6669 6.45272 14.5784 7.98469C17.4908 9.51754 19.5395 10.8562 20.4294 11.4635C21.1891 11.9829 21.191 13.013 20.4304 13.5342C19.5491 14.1381 17.5256 15.4591 14.5784 17.0113C11.6283 18.5635 9.36078 19.5005 8.34657 19.8942C7.47311 20.2343 6.49554 19.7183 6.38178 18.8597C6.24873 17.856 6 15.5769 6 12.4989C6 9.42262 6.24777 7.14443 6.38178 6.13982Z" fill="currentColor"></path></svg>
</div>
</div>
<div data-vimeo-control="pause" class="vimeo-lightbox__pause">
<div class="vimeo-lightbox__btn">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 24 24" fill="none" class="vimeo-lightbox__btn-pause-svg"><path d="M8 6.5C8 6.22386 8.22386 6 8.5 6H10.5C10.7761 6 11 6.22386 11 6.5V17.5C11 17.7761 10.7761 18 10.5 18H8.5C8.22386 18 8 17.7761 8 17.5V6.5Z" fill="currentColor"></path><path d="M14 6.5C14 6.22386 14.2239 6 14.5 6H16.5C16.7761 6 17 6.22386 17 6.5V17.5C17 17.7761 16.7761 18 16.5 18H14.5C14.2239 18 14 17.7761 14 17.5V6.5Z" fill="currentColor"></path></svg>
</div>
</div>
<div class="vimeo-lightbox__interface">
<div class="vimeo-lightbox__interface-bottom">
<div class="vimeo-lightbox__duration">
<span data-vimeo-duration="" class="vimeo-lightbox__duration-span">0:00</span>
</div>
<div class="vimeo-lightbox__timeline">
<progress min="0" max="100" value="0" class="vimeo-lightbox__timeline-progress"></progress>
<input type="range" min="0" max="100" step="0.01" data-vimeo-control="timeline" class="vimeo-lightbox__timeline-input" value="0">
</div>
<div data-vimeo-control="mute" class="vimeo-lightbox__mute">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 24 24" fill="none" class="vimeo-lightbox__volume-up-svg"><path d="M3 8.99998V15H7L12 20V3.99998L7 8.99998H3ZM16.5 12C16.5 10.23 15.48 8.70998 14 7.96998V16.02C15.48 15.29 16.5 13.77 16.5 12ZM14 3.22998V5.28998C16.89 6.14998 19 8.82998 19 12C19 15.17 16.89 17.85 14 18.71V20.77C18.01 19.86 21 16.28 21 12C21 7.71998 18.01 4.13998 14 3.22998Z" fill="currentColor"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 24 24" fill="none" class="vimeo-lightbox__volume-mute-svg"><path d="M16.5 12C16.5 10.23 15.48 8.71 14 7.97V10.18L16.45 12.63C16.48 12.43 16.5 12.22 16.5 12ZM19 12C19 12.94 18.8 13.82 18.46 14.64L19.97 16.15C20.63 14.91 21 13.5 21 12C21 7.72 18.01 4.14 14 3.23V5.29C16.89 6.15 19 8.83 19 12ZM4.27 3L3 4.27L7.73 9H3V15H7L12 20V13.27L16.25 17.52C15.58 18.04 14.83 18.45 14 18.7V20.76C15.38 20.45 16.63 19.81 17.69 18.95L19.73 21L21 19.73L12 10.73L4.27 3ZM12 4L9.91 6.09L12 8.18V4Z" fill="currentColor"></path></svg>
</div>
<div class="vimeo-lightbox__fullscreen" data-vimeo-control="fullscreen">
<svg class="vimeo-lightbox__fullscreen-scale-svg" xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 24 24" fill="none"><rect x="3" y="14" width="2" height="7" fill="currentColor"></rect><rect x="3" y="3" width="2" height="7" fill="currentColor"></rect><rect x="19" y="3" width="2" height="7" fill="currentColor"></rect><rect x="19" y="14" width="2" height="7" fill="currentColor"></rect><rect x="3" y="19" width="7" height="2" fill="currentColor"></rect><rect x="14" y="19" width="7" height="2" fill="currentColor"></rect><rect x="3" y="3" width="7" height="2" fill="currentColor"></rect><rect x="14" y="3" width="7" height="2" fill="currentColor"></rect></svg>
<svg class="vimeo-lightbox__fullscreen-shrink-svg" xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 24 24" fill="none"><rect x="7" y="2" width="2" height="7" fill="currentColor"></rect><rect x="15" y="2" width="2" height="7" fill="currentColor"></rect><rect x="15" y="15" width="2" height="7" fill="currentColor"></rect><rect x="8" y="15" width="2" height="7" fill="currentColor"></rect><rect x="2" y="7" width="7" height="2" fill="currentColor"></rect><rect x="3" y="15" width="7" height="2" fill="currentColor"></rect><rect x="15" y="7" width="7" height="2" fill="currentColor"></rect><rect x="15" y="15" width="7" height="2" fill="currentColor"></rect></svg>
</div>
</div>
</div>
<div class="vimeo-lightbox__loading">
<svg class="vimeo-lightbox__loading-svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="L9" x="0px" y="0px" viewbox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve" width="100%"><path fill="currentColor" d="M73,50c0-12.7-10.3-23-23-23S27,37.3,27,50 M30.9,50c0-10.5,8.5-19.1,19.1-19.1S69.1,39.5,69.1,50"></path><animatetransform attributename="transform" attributetype="XML" type="rotate" dur="1s" from="0 50 50" to="360 50 50" repeatcount="indefinite"></animatetransform></svg>
</div>
</div>
</div>
</div>
<button data-vimeo-lightbox-control="close" class="vimeo-lightbox__close"><div class="vimeo-lightbox__close-bar"></div><div class="vimeo-lightbox__close-bar is--duplicate"></div></button>
</div>.vimeo-lightbox {
z-index: 300;
pointer-events: none;
justify-content: center;
align-items: center;
padding: 5vw;
display: flex;
position: fixed;
inset: 0;
overflow: hidden;
}
.vimeo-lightbox__calc {
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
display: flex;
position: relative;
}
.vimeo-lightbox__calc-wrap {
width: 100%;
position: relative;
}
/* Cover */
.vimeo-lightbox[data-vimeo-update-size="cover"] .vimeo-lightbox__calc-wrap {
height: 100%;
}
.vimeo-lightbox[data-vimeo-update-size="cover"] [data-vimeo-lightbox-player] {
width: 100%;
min-width: 100%;
max-width: 100%;
height: 100%;
min-height: 100%;
max-height: 100%;
}
.vimeo-lightbox__player {
pointer-events: auto;
color: #efeeec;
isolation: isolate;
background-color: #131313;
border-radius: 1em;
justify-content: center;
align-items: center;
width: 100%;
display: flex;
position: relative;
overflow: hidden;
transition: all 0.3s cubic-bezier(0.625, 0.05, 0, 1);
opacity: 0;
visibility: hidden;
transform: scale(0.9) rotate(0.001deg) translateX(0);
}
.vimeo-lightbox[data-vimeo-activated="true"] .vimeo-lightbox__player {
opacity: 1;
visibility: visible;
transform: scale(1) rotate(0.001deg) translateX(0);
}
.vimeo-lightbox__before {
padding-top: 62.5%;
}
/* Dark BG */
.vimeo-lightbox__bg {
pointer-events: auto;
background-color: #131313;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
display: flex;
position: absolute;
top: 0;
left: 0;
transition: all 0.3s cubic-bezier(0.625, 0.05, 0, 1);
opacity: 0;
visibility: hidden;
}
.vimeo-lightbox[data-vimeo-activated="loading"] .vimeo-lightbox__bg,
.vimeo-lightbox[data-vimeo-activated="true"] .vimeo-lightbox__bg {
opacity: 1;
visibility: visible;
}
/* Close Button */
.vimeo-lightbox__close {
z-index: 600;
pointer-events: auto;
border-radius: 50%;
justify-content: center;
align-items: center;
width: 3em;
height: 3em;
display: flex;
position: absolute;
top: 2.5vw;
right: 2.5vw;
transition: all 0.5s cubic-bezier(0.625, 0.05, 0, 1);
opacity: 0;
visibility: hidden;
}
.vimeo-lightbox[data-vimeo-activated="true"] .vimeo-lightbox__close {
opacity: 1;
visibility: visible;
}
.vimeo-lightbox__close-bar {
background-color: currentColor;
width: 1em;
height: .125em;
position: absolute;
transform: rotate(-45deg);
}
.vimeo-lightbox__close-bar.is--duplicate {
transform: rotate(45deg);
}
.vimeo-lightbox__iframe {
pointer-events: none;
width: 100%;
height: 100%;
position: absolute;
}
/* Loading */
.vimeo-lightbox__loading {
pointer-events: none;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
transition: opacity .3s linear;
display: flex;
position: absolute;
opacity: 0;
}
.vimeo-lightbox__loading-svg {
color: #ff4c24;
width: 6em;
}
.vimeo-lightbox[data-vimeo-playing="true"] .vimeo-lightbox__loading {
opacity: 1;
}
.vimeo-lightbox[data-vimeo-playing="true"][data-vimeo-loaded="true"] .vimeo-lightbox__loading {
opacity: 0;
}
/* Dark (Overlay) */
.vimeo-lightbox__dark {
opacity: .5;
pointer-events: none;
background-color: #131313;
width: 100%;
height: 100%;
transition: opacity .3s linear;
position: absolute;
}
.vimeo-lightbox[data-vimeo-playing="false"] .vimeo-lightbox__dark {
opacity: 0.33;
}
.vimeo-lightbox[data-vimeo-activated="false"][data-vimeo-playing="false"] .vimeo-lightbox__dark {
opacity: 0;
}
.vimeo-lightbox[data-vimeo-activated="true"][data-vimeo-loaded="true"] .vimeo-lightbox__dark {
opacity: 0;
}
@media (hover: hover) and (pointer: fine) {
.vimeo-lightbox[data-vimeo-hover="true"]:hover .vimeo-lightbox__dark {
opacity: 0.33 !important;
}
}
/* Placeholder */
.vimeo-lightbox__placeholder {
object-fit: cover;
width: 100%;
height: 100%;
transition: opacity .3s linear;
display: block;
position: absolute;
}
.vimeo-lightbox[data-vimeo-loaded="true"] .vimeo-lightbox__placeholder {
opacity: 0;
}
.vimeo-lightbox__play, .vimeo-lightbox__pause {
cursor: pointer;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
display: flex;
position: absolute;
}
.vimeo-lightbox__btn {
-webkit-backdrop-filter: blur(1em);
backdrop-filter: blur(1em);
background-color: #64646433;
border-radius: 50%;
justify-content: center;
align-items: center;
width: 6em;
height: 6em;
transition: opacity .3s linear;
display: flex;
position: relative;
}
.vimeo-lightbox__btn-play-svg {
width: 40%;
}
.vimeo-lightbox__btn-pause-svg {
width: 50%;
}
/* Pause */
.vimeo-lightbox .vimeo-lightbox__pause {
display: none;
}
.vimeo-lightbox[data-vimeo-playing="true"] .vimeo-lightbox__pause {
display: flex;
}
.vimeo-lightbox .vimeo-lightbox__pause .vimeo-lightbox__btn {
opacity: 0;
}
.vimeo-lightbox[data-vimeo-activated="true"][data-vimeo-playing="false"] .vimeo-lightbox__pause .vimeo-lightbox__btn,
.vimeo-lightbox[data-vimeo-activated="true"][data-vimeo-hover="true"]:hover .vimeo-lightbox__pause .vimeo-lightbox__btn {
opacity: 1;
}
@media (hover: none) and (pointer: coarse) {
.vimeo-lightbox[data-vimeo-activated="true"][data-vimeo-playing="true"] .vimeo-lightbox__pause .vimeo-lightbox__btn {
opacity: 0 !important;
}
}
/* Play */
.vimeo-lightbox[data-vimeo-playing="true"] .vimeo-lightbox__play {
opacity: 0;
}
/* Interface - Variables */
.vimeo-lightbox {
--timeline-rounded-corners: 1.5em;
--timeline-dot-height: 0.75em;
--timeline-dot-color: #FF4C24;
--progress-bg: rgba(239, 238, 236, 0.2);
--progress-fill-bg: #FF4C24;
--progress-height: 0.2em;
}
.vimeo-lightbox__interface {
pointer-events: none;
flex-flow: column;
justify-content: flex-end;
align-items: stretch;
width: 100%;
height: 100%;
padding: min(2em, 4vw);
transition-property: opacity;
transition-duration: .3s;
transition-timing-function: linear;
display: flex;
position: absolute;
opacity: 0;
}
.vimeo-lightbox[data-vimeo-activated="false"][data-vimeo-playing="false"] .vimeo-lightbox__interface {
opacity: 1;
}
.vimeo-lightbox .vimeo-lightbox__interface * {
pointer-events: all;
}
.vimeo-lightbox[data-vimeo-activated="true"][data-vimeo-playing="false"] .vimeo-lightbox__interface,
.vimeo-lightbox[data-vimeo-activated="true"][data-vimeo-hover="true"]:hover .vimeo-lightbox__interface {
opacity: 1;
}
@media (hover: none) and (pointer: coarse) {
.vimeo-lightbox[data-vimeo-activated="true"][data-vimeo-playing="true"] .vimeo-lightbox__interface {
opacity: 0 !important;
}
}
.vimeo-lightbox__interface-bottom {
grid-column-gap: 1em;
grid-row-gap: 1em;
justify-content: flex-start;
align-items: center;
display: flex;
}
/* Interface - Timeline */
.vimeo-lightbox__timeline {
flex-grow: 1;
justify-content: center;
align-items: center;
height: 1.5em;
display: flex;
position: relative;
}
.vimeo-lightbox__timeline-input {
pointer-events: auto;
cursor: pointer;
-webkit-appearance: none;
appearance: none;
background-color: #0000;
width: 100%;
height: 100%;
display: block;
position: relative;
}
.vimeo-lightbox__timeline-progress {
vertical-align: top;
-webkit-appearance: none;
appearance: none;
height: var(--progress-height);
border-radius: var(--timeline-rounded-corners);
color: var(--progress-fill-bg);
background-color: #0000;
border: none;
width: 100%;
margin: 0;
padding: 0;
position: absolute;
left: 0;
overflow: hidden;
}
.vimeo-lightbox progress::-webkit-progress-bar {
border-radius: var(--timeline-rounded-corners);
background-color: var(--progress-bg);
box-shadow: 0;
}
.vimeo-lightbox progress::-webkit-progress-value {
background: var(--progress-fill-bg);
}
.vimeo-lightbox progress::-moz-progress-bar {
border-radius: var(--timeline-rounded-corners);
background: var(--progress-fill-bg);
box-shadow: 0;
}
.vimeo-lightbox progress::-ms-fill {
border-radius: var(--timeline-rounded-corners);
}
/* Interface - Range */
.vimeo-lightbox [type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
}
.vimeo-lightbox [type="range"]:focus {
outline: none;
}
.vimeo-lightbox [type="range"]::-ms-track {
width: 100%;
cursor: pointer;
background-color: transparent;
border-color: transparent;
color: transparent;
}
.vimeo-lightbox [type="range"]::-webkit-slider-runnable-track {
width: 100%;
height: var(--progress-height);
cursor: pointer;
background-color: transparent;
border-color: transparent;
color: transparent;
border-radius: var(--timeline-rounded-corners);
}
.vimeo-lightbox [type="range"]::-webkit-slider-thumb {
box-shadow: 0;
height: var(--timeline-dot-height);
width: var(--timeline-dot-height);
border-radius: var(--timeline-rounded-corners);
background-color: var(--timeline-dot-color);
cursor: pointer;
-webkit-appearance: none;
margin-top: calc((var(--progress-height) / 2) - (var(--timeline-dot-height) / 2));
}
.vimeo-lightbox [type="range"]::-webkit-slider-runnable-track,
.vimeo-lightbox [type="range"]:focus::-webkit-slider-runnable-track {
background-color: transparent;
border-color: transparent;
color: transparent;
}
.vimeo-lightbox [type="range"]::-moz-range-track {
width: 100%;
height: var(--progress-height);
cursor: pointer;
background: var(--progress-bg);
border-radius: var(--timeline-rounded-corners);
border: 0;
overflow: hidden;
opacity: 1 !important;
}
.vimeo-lightbox [type="range"]::-moz-range-thumb {
box-shadow: 0;
border: 0;
height: var(--timeline-dot-height);
width: var(--timeline-dot-height);
border-radius: var(--timeline-rounded-corners);
background: var(--timeline-dot-color);
cursor: pointer;
}
/* Interface - Duration */
.vimeo-lightbox__duration {
flex-shrink: 0;
width: 2.25em;
}
.vimeo-lightbox__duration-span {
text-align: center;
white-space: nowrap;
-webkit-user-select: none;
user-select: none;
width: 100%;
display: block;
}
/* Interface - Mute */
.vimeo-lightbox__mute {
cursor: pointer;
flex-shrink: 0;
justify-content: center;
align-items: center;
width: 1.5em;
height: 1.5em;
display: flex;
position: relative;
}
.vimeo-lightbox__volume-up-svg {
width: 100%;
position: absolute;
}
.vimeo-lightbox__volume-mute-svg {
width: 100%;
position: absolute;
}
.vimeo-lightbox .vimeo-lightbox__mute svg:nth-child(2),
.vimeo-lightbox[data-vimeo-muted="true"] .vimeo-lightbox__mute svg:nth-child(1) {
display: none;
}
.vimeo-lightbox .vimeo-lightbox__mute svg:nth-child(1),
.vimeo-lightbox[data-vimeo-muted="true"] .vimeo-lightbox__mute svg:nth-child(2) {
display: block;
}
/* Interface - Fullscreen */
.vimeo-lightbox__fullscreen {
cursor: pointer;
justify-content: center;
align-items: center;
width: 1.5em;
height: 1.5em;
display: flex;
position: relative;
}
.vimeo-lightbox__fullscreen-scale-svg {
width: 100%;
position: absolute;
}
.vimeo-lightbox__fullscreen-shrink-svg {
width: 100%;
position: absolute;
}
.vimeo-lightbox .vimeo-lightbox__fullscreen svg:nth-child(2),
.vimeo-lightbox[data-vimeo-fullscreen="true"] .vimeo-lightbox__fullscreen svg:nth-child(1) {
display: none;
}
.vimeo-lightbox .vimeo-lightbox__fullscreen svg:nth-child(1),
.vimeo-lightbox[data-vimeo-fullscreen="true"] .vimeo-lightbox__fullscreen svg:nth-child(2) {
display: block;
}function initVimeoLightboxAdvanced() {
const lightbox = document.querySelector('[data-vimeo-lightbox-init]');
if (!lightbox) return;
const openButtons = document.querySelectorAll('[data-vimeo-lightbox-control="open"]');
const closeButtons = document.querySelectorAll('[data-vimeo-lightbox-control="close"]');
let iframe = lightbox.querySelector('iframe');
const placeholder = lightbox.querySelector('.vimeo-lightbox__placeholder');
const calcEl = lightbox.querySelector('.vimeo-lightbox__calc');
const wrapEl = lightbox.querySelector('.vimeo-lightbox__calc-wrap');
const playerContainer = lightbox.querySelector('[data-vimeo-lightbox-player]');
let player = null;
let currentVideoID = null;
let videoAspectRatio = null;
let globalMuted = lightbox.getAttribute('data-vimeo-muted') === 'true';
const isTouch = ('ontouchstart' in window) || navigator.maxTouchPoints > 0;
const playedOnce = new Set();
function formatTime(s) {
const m = Math.floor(s / 60);
const sec = Math.floor(s % 60).toString().padStart(2, '0');
return `${m}:${sec}`;
}
function clampWrapSize(ar) {
const w = calcEl.offsetWidth;
const h = calcEl.offsetHeight;
wrapEl.style.maxWidth = Math.min(w, h / ar) + 'px';
}
function adjustCoverSizing() {
if (!videoAspectRatio) return;
const cH = playerContainer.offsetHeight;
const cW = playerContainer.offsetWidth;
const r = cH / cW;
const wEl = lightbox.querySelector('.vimeo-lightbox__iframe');
if (r > videoAspectRatio) {
wEl.style.width = (r / videoAspectRatio * 100) + '%';
wEl.style.height = '100%';
} else {
wEl.style.height = (videoAspectRatio / r * 100) + '%';
wEl.style.width = '100%';
}
}
function closeLightbox() {
lightbox.setAttribute('data-vimeo-activated', 'false');
if (player) {
player.pause();
lightbox.setAttribute('data-vimeo-playing', 'false');
}
}
document.addEventListener('keydown', e => {
if (e.key === 'Escape') closeLightbox();
});
closeButtons.forEach(btn => btn.addEventListener('click', closeLightbox));
function setupPlayerEvents() {
player.on('play', () => {
lightbox.setAttribute('data-vimeo-loaded', 'true');
lightbox.setAttribute('data-vimeo-playing', 'true');
});
player.on('ended', closeLightbox);
player.on('pause', () => {
lightbox.setAttribute('data-vimeo-playing', 'false');
});
const durEl = lightbox.querySelector('[data-vimeo-duration]');
player.getDuration().then(d => {
if (durEl) durEl.textContent = formatTime(d);
lightbox.querySelectorAll('[data-vimeo-control="timeline"],progress')
.forEach(el => el.max = d);
});
const tl = lightbox.querySelector('[data-vimeo-control="timeline"]');
const pr = lightbox.querySelector('progress');
player.on('timeupdate', data => {
if (tl) tl.value = data.seconds;
if (pr) pr.value = data.seconds;
if (durEl) durEl.textContent = formatTime(Math.trunc(data.seconds));
});
if (tl) {
['input','change'].forEach(evt =>
tl.addEventListener(evt, e => {
const v = e.target.value;
player.setCurrentTime(v);
if (pr) pr.value = v;
})
);
}
let hoverTimer;
playerContainer.addEventListener('mousemove', () => {
lightbox.setAttribute('data-vimeo-hover', 'true');
clearTimeout(hoverTimer);
hoverTimer = setTimeout(() => {
lightbox.setAttribute('data-vimeo-hover', 'false');
}, 3000);
});
const fsBtn = lightbox.querySelector('[data-vimeo-control="fullscreen"]');
if (fsBtn) {
const isFS = () => document.fullscreenElement || document.webkitFullscreenElement;
if (!(document.fullscreenEnabled || document.webkitFullscreenEnabled)) {
fsBtn.style.display = 'none';
}
fsBtn.addEventListener('click', () => {
if (isFS()) {
lightbox.setAttribute('data-vimeo-fullscreen', 'false');
(document.exitFullscreen || document.webkitExitFullscreen).call(document);
} else {
lightbox.setAttribute('data-vimeo-fullscreen', 'true');
(playerContainer.requestFullscreen || playerContainer.webkitRequestFullscreen)
.call(playerContainer);
}
});
['fullscreenchange','webkitfullscreenchange'].forEach(evt =>
document.addEventListener(evt, () =>
lightbox.setAttribute('data-vimeo-fullscreen', isFS() ? 'true' : 'false')
)
);
}
}
async function runSizing() {
const mode = lightbox.getAttribute('data-vimeo-update-size');
const w = await player.getVideoWidth();
const h = await player.getVideoHeight();
const ar = h / w;
const bef = lightbox.querySelector('.vimeo-lightbox__before');
if (mode === 'true') {
if (bef) bef.style.paddingTop = (ar * 100) + '%';
clampWrapSize(ar);
} else if (mode === 'cover') {
videoAspectRatio = ar;
if (bef) bef.style.paddingTop = '0%';
adjustCoverSizing();
} else {
clampWrapSize(ar);
}
}
window.addEventListener('resize', () => {
if (player) runSizing();
});
async function openLightbox(id, placeholderBtn) {
lightbox.setAttribute('data-vimeo-activated', 'loading');
lightbox.setAttribute('data-vimeo-loaded', 'false');
if (player && id !== currentVideoID) {
await player.pause();
await player.unload();
const oldIframe = iframe;
const newIframe = document.createElement('iframe');
newIframe.className = oldIframe.className;
newIframe.setAttribute('frameborder', '0');
newIframe.setAttribute('allowfullscreen', 'true');
newIframe.setAttribute('allow', 'autoplay; encrypted-media');
oldIframe.parentNode.replaceChild(newIframe, oldIframe);
iframe = newIframe;
player = null;
currentVideoID = null;
lightbox.setAttribute('data-vimeo-playing', 'false');
}
if (placeholderBtn) {
['src','srcset','sizes','alt','width'].forEach(attr => {
const val = placeholderBtn.getAttribute(attr);
if (val != null) placeholder.setAttribute(attr, val);
});
}
if (!player) {
iframe.src = `https://player.vimeo.com/video/${id}?api=1&background=1&autoplay=0&loop=0&muted=0`;
player = new Vimeo.Player(iframe);
setupPlayerEvents();
currentVideoID = id;
await runSizing();
}
lightbox.setAttribute('data-vimeo-activated', 'true');
if (!isTouch) {
player.setVolume(globalMuted ? 0 : 1).then(() => {
lightbox.setAttribute('data-vimeo-playing', 'true');
setTimeout(() => player.play(), 50);
});
} else if (playedOnce.has(currentVideoID)) {
player.setVolume(globalMuted ? 0 : 1).then(() => {
lightbox.setAttribute('data-vimeo-playing', 'true');
player.play();
});
}
}
lightbox.querySelector('[data-vimeo-control="play"]').addEventListener('click', () => {
if (isTouch) {
if (!playedOnce.has(currentVideoID)) {
player.setVolume(0).then(() => {
lightbox.setAttribute('data-vimeo-playing', 'true');
player.play();
if (!globalMuted) {
setTimeout(() => {
player.setVolume(1);
lightbox.setAttribute('data-vimeo-muted', 'false');
}, 100);
}
playedOnce.add(currentVideoID);
});
} else {
player.setVolume(globalMuted ? 0 : 1).then(() => {
lightbox.setAttribute('data-vimeo-playing', 'true');
player.play();
});
}
} else {
player.setVolume(globalMuted ? 0 : 1).then(() => {
lightbox.setAttribute('data-vimeo-playing', 'true');
setTimeout(() => player.play(), 50);
});
}
});
lightbox.querySelector('[data-vimeo-control="pause"]').addEventListener('click', () => {
player.pause();
});
lightbox.querySelector('[data-vimeo-control="mute"]').addEventListener('click', () => {
globalMuted = !globalMuted;
player.setVolume(globalMuted ? 0 : 1).then(() =>
lightbox.setAttribute('data-vimeo-muted', globalMuted ? 'true' : 'false')
);
});
openButtons.forEach(btn => {
btn.addEventListener('click', () => {
const vid = btn.getAttribute('data-vimeo-lightbox-id');
const img = btn.querySelector('[data-vimeo-lightbox-placeholder]');
openLightbox(vid, img);
});
});
}
// Initialize Vimeo Lightbox (Advanced)
document.addEventListener('DOMContentLoaded', function() {
initVimeoLightboxAdvanced();
});Attributes
| Name | Type | Default | Description |
|---|---|---|---|
| data-vimeo-lightbox-init | boolean | Marks the lightbox root element. The script queries this to initialise the player. | |
| data-vimeo-lightbox-control | string | Add to any clickable element. Use "open" to open the lightbox and "close" to close it. | |
| data-vimeo-lightbox-id | string | The numeric Vimeo video ID (e.g. 1019191082). Add to the open button. The video must allow embedding in Vimeo settings. | |
| data-vimeo-lightbox-placeholder | boolean | Add to an <img> inside the open button to provide a placeholder image. Can be hidden with display: none — used as data only. | |
| data-vimeo-activated | string | false | Reflects the lightbox open state. Values: false (closed), loading (fetching video info), true (open and ready). |
| data-vimeo-playing | boolean | false | Reflects playback state. true while playing, false when paused or stopped. Updated automatically. |
| data-vimeo-loaded | boolean | false | Set to true once the video starts playing. Use to hide the loading spinner and placeholder. |
| data-vimeo-muted | boolean | false | Reflects and controls the mute state. Set to "true" to start muted. Updated automatically when toggled. |
| data-vimeo-fullscreen | boolean | false | Reflects fullscreen state. Updated automatically. If fullscreen is not supported, the fullscreen button is hidden. |
| data-vimeo-hover | boolean | false | Set to true while the mouse is moving over the player. Resets to false after 3 seconds of inactivity. Use to show/hide controls. |
| data-vimeo-update-size | string | true | Controls aspect ratio behaviour. "true" calculates the ratio from the video and applies padding-top. "cover" scales the iframe to fill the container. |
| data-vimeo-control | string | Add to UI elements inside the player. Accepted values: play, pause, mute, fullscreen, timeline. | |
| data-vimeo-duration | boolean | Displays the video duration on load, then updates to show elapsed time during playback. | |
| data-vimeo-lightbox-player | boolean | Marks the inner player container. Used for sizing calculations and fullscreen requests. |
Notes
- •The Vimeo video must have embedding allowed in its Vimeo privacy settings.
- •The Vimeo Player API script must be loaded before the lightbox script runs.
- •On desktop, the video starts playing automatically when the lightbox opens. On touch devices, the user must tap play first.
- •When switching to a different video, the old iframe is replaced with a fresh one to ensure a clean player state.
- •Pressing Escape always closes the lightbox.
- •If fullscreen is not supported by the browser or device, the fullscreen button is automatically hidden.
Guide
Open the lightbox
Add [data-vimeo-lightbox-control="open"] to any element. Set the video ID on [data-vimeo-lightbox-id] — this is the numeric ID from the Vimeo URL (e.g. 1019191082). To show a placeholder image while loading, add an <img> with [data-vimeo-lightbox-placeholder] as a child of the open button — it can be hidden with display: none.
<button data-vimeo-lightbox-control="open" data-vimeo-lightbox-id="1019191082">
<img data-vimeo-lightbox-placeholder="" src="image.jpg" alt="">
<span>Open Video</span>
</button>Close the lightbox
Add [data-vimeo-lightbox-control="close"] to any element. Clicking it pauses the video and closes the lightbox. Pressing the Escape key also closes it.
<button data-vimeo-lightbox-control="close">Close Video</button>Activated state
When the lightbox opens it sets [data-vimeo-activated="loading"], which shows the dark background immediately via CSS. Once the video information is fetched it changes to [data-vimeo-activated="true"] and reveals the player.
Update size (aspect ratio)
Set [data-vimeo-update-size="true"] to have the script retrieve the video's dimensions and update .vimeo-lightbox__before with the correct padding-top percentage (e.g. 56.25% for 16:9). Set it to "cover" to make the iframe fill its parent container — add position: relative to the parent.
Muted
Set [data-vimeo-muted="true"] on the lightbox element to start the video muted. The user can toggle the mute state by clicking an element with [data-vimeo-control="mute"].
Play / Pause
Add [data-vimeo-control="play"] and [data-vimeo-control="pause"] to elements inside the player. These update [data-vimeo-playing] automatically, which you can use in CSS to show or hide each button.
Fullscreen
Add [data-vimeo-control="fullscreen"] to an element to toggle fullscreen. [data-vimeo-fullscreen] is updated automatically. On devices or browsers that do not support fullscreen, the button is hidden automatically.
Loading
While the video is loading, .vimeo-lightbox__loading is visible. Once [data-vimeo-loaded="true"] is set (on first play), you can hide the loader and the placeholder via CSS.
Timeline
The timeline uses an <input type="range"> with [data-vimeo-control="timeline"] for scrubbing, paired with a <progress> element for the filled bar. Both are updated automatically during playback.
Duration
Add [data-vimeo-duration] to a text element. It shows the total duration on load and updates to the elapsed time during playback.
Hover
[data-vimeo-hover="true"] is set when the mouse moves over the player and resets to false after 3 seconds of no movement. Use it in CSS to show or hide the interface while the video plays.