(Squarespace) How to make Video Popup on Portfolio item click

To make video popup on Portfolio item click, like this.

You can also use this tool to add Portfolio, so you can add Video Popup easier without using complex code.

#1. First, find Portfolio item url

In my example, we will have:

/projects/apple

/projects/google

/projects/microsoft

#2. Use this code to Code Injection > Footer (or Page Header Injection)

<div id="videoLightbox" class="lightbox">
    <div class="lightbox-content">
        <button class="close-btn" id="closeBtn">&times;</button>
        <div class="loading" id="loading">Loading....</div>
        <div class="video-container" id="videoContainer">
            <iframe id="videoPlayer" class="video-player" frameborder="0" allow="autoplay; fullscreen; picture-in-picture"></iframe>
        </div>
        <div class="video-controls" id="videoControls">
            <div class="progress-container" id="progressContainer">
                <div class="progress-bar" id="progressBar"></div>
            </div>
            <div class="controls-bottom">
                <span class="play-pause-text" id="playPauseBtn">Play</span>
                <span class="fullscreen-text" id="fullscreenBtn">Fullscreen</span>
            </div>
        </div>
    </div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
    const lightbox = document.getElementById('videoLightbox');
    const videoContainer = document.getElementById('videoContainer');
    const closeBtn = document.getElementById('closeBtn');
    const loading = document.getElementById('loading');
    const playPauseBtn = document.getElementById('playPauseBtn');
    const progressContainer = document.getElementById('progressContainer');
    const progressBar = document.getElementById('progressBar');
    const fullscreenBtn = document.getElementById('fullscreenBtn');
    const videoControls = document.getElementById('videoControls');
    let player = null;
    let isPlaying = false;
    let duration = 0;
    let currentTime = 0;
    function getVimeoId(url) {
        const match = url.match(/vimeo\.com\/(\d+)/);
        return match ? match[1] : null;
    }
    function cleanupPlayer() {
        if (player) {
            try {
                player.destroy();
            } catch(e) {
                console.log('Error destroying player:', e);
            }
            player = null;
        }
        
        const existingIframe = document.getElementById('videoPlayer');
        if (existingIframe) {
            existingIframe.remove();
        }
    }
    function createNewIframe() {
        const newIframe = document.createElement('iframe');
        newIframe.id = 'videoPlayer';
        newIframe.className = 'video-player';
        newIframe.setAttribute('frameborder', '0');
        newIframe.setAttribute('allow', 'autoplay; fullscreen; picture-in-picture');
        videoContainer.appendChild(newIframe);
        return newIframe;
    }
    function openLightbox(videoUrl) {
        const vimeoId = getVimeoId(videoUrl);
        if (!vimeoId) return;
        cleanupPlayer();
        lightbox.classList.add('active');
        loading.style.display = 'block';
        videoControls.style.display = 'none';
        
        setTimeout(() => {
            const iframe = createNewIframe();
           iframe.src = `https://player.vimeo.com/video/${vimeoId}?api=1&autoplay=1&controls=0`;
            
            if (window.Vimeo) {
                setTimeout(() => {
                    initializeVimeoPlayer(iframe);
                }, 500);
            } else {
                const script = document.createElement('script');
                script.src = 'https://player.vimeo.com/api/player.js';
                script.onload = function() {
                    setTimeout(() => {
                        initializeVimeoPlayer(iframe);
                    }, 500);
                };
                document.head.appendChild(script);
            }
        }, 100);
    }
    function initializeVimeoPlayer(iframe) {
        try {
            player = new Vimeo.Player(iframe);
            
            player.ready().then(function() {
                loading.style.display = 'none';
                videoControls.style.display = 'block';
                
                player.play().catch(function(error) {
                    console.log('Autoplay failed:', error);
                });
                
                return player.getDuration();
            }).then(function(dur) {
                duration = dur;
            }).catch(function(error) {
                console.log('Player error:', error);
                loading.style.display = 'none';
                videoControls.style.display = 'block';
            });
            player.on('play', function() {
                isPlaying = true;
                playPauseBtn.textContent = 'Pause';
            });
            player.on('pause', function() {
                isPlaying = false;
                playPauseBtn.textContent = 'Play';
            });
            player.on('timeupdate', function(data) {
                currentTime = data.seconds;
                const progress = (currentTime / duration) * 100;
                progressBar.style.width = `${progress}%`;
            });
        } catch(error) {
            console.log('Error initializing player:', error);
            loading.style.display = 'none';
        }
    }
    function closeLightbox() {
        lightbox.classList.remove('active');
        
        setTimeout(() => {
            cleanupPlayer();
            isPlaying = false;
            duration = 0;
            currentTime = 0;
            progressBar.style.width = '0%';
            playPauseBtn.textContent = 'Play';
        }, 300);
    }
    const linkMapping = {
        'netflix': {
            from: '/projects/apple',
            to: 'https://vimeo.com/1006416923'
        },
        'nokia': {
            from: '/projects/google',
            to: 'https://vimeo.com/1077793525'
        },
        'openai': {
            from: '/projects/microsoft',
            to: 'https://vimeo.com/810778889'
        }
    };
    function setupVideoLinks() {
        Object.values(linkMapping).forEach(mapping => {
            const links = document.querySelectorAll(`a[href="${mapping.from}"]`);
            links.forEach(link => {
                link.addEventListener('click', function(e) {
                    e.preventDefault();
                    openLightbox(mapping.to);
                });
            });
        });
        document.querySelectorAll('a[href*="vimeo.com"]').forEach(item => {
            item.addEventListener('click', function(e) {
                e.preventDefault();
                const videoUrl = this.getAttribute('href');
                openLightbox(videoUrl);
            });
        });
    }
    setupVideoLinks();
    closeBtn.addEventListener('click', closeLightbox);
    
    lightbox.addEventListener('click', function(e) {
        if (e.target === lightbox) {
            closeLightbox();
        }
    });
    document.addEventListener('keydown', function(e) {
        if (lightbox.classList.contains('active')) {
            if (e.key === 'Escape') {
                closeLightbox();
            } else if (e.key === ' ' && player) {
                e.preventDefault();
                if (isPlaying) {
                    player.pause();
                } else {
                    player.play();
                }
            }
        }
    });
    playPauseBtn.addEventListener('click', function() {
        if (!player) return;
        
        if (isPlaying) {
            player.pause();
        } else {
            player.play();
        }
    });
    progressContainer.addEventListener('click', function(e) {
        if (!player) return;
        
        const rect = progressContainer.getBoundingClientRect();
        const clickX = e.clientX - rect.left;
        const width = rect.width;
        const percentage = clickX / width;
        const seekTime = percentage * duration;
        
        player.setCurrentTime(seekTime);
    });
    fullscreenBtn.addEventListener('click', function() {
        if (!player) return;
        player.requestFullscreen();
    });
});
</script>

Remember to update Portfolio item url + Video URL

#3. Use this code to Custom CSS

/* Portfolio video popup */
.lightbox {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.9);
    z-index: 9999;
    opacity: 0;
    transition: opacity 0.3s ease;
}
.lightbox.active {
    display: flex;
    opacity: 1;
    align-items: center;
    justify-content: center;
}
.lightbox-content {
    position: relative;
    width: 90%;
    max-width: 900px;
    background: #000;
    border-radius: 10px;
    overflow: hidden;
    transform: scale(0.8);
    transition: transform 0.3s ease;
}
.lightbox.active .lightbox-content {
    transform: scale(1);
}
.video-container {
    position: relative;
    width: 100%;
    height: 0;
    padding-bottom: 56.25%;
}
.video-player {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
.video-controls {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    background: linear-gradient(transparent, rgba(0,0,0,0.8));
    padding: 20px 15px 15px;
    opacity: 0;
    transition: opacity 0.3s ease;
}
.lightbox-content:hover .video-controls {
    opacity: 1;
}
.progress-container {
    width: 100%;
    height: 2px;
    background: hsla(0,0%,100%,.2);
    border-radius: 3px;
    cursor: pointer;
    margin-bottom: 15px;
}
.progress-bar {
    height: 100%;
    background: #fff;
    border-radius: 3px;
    width: 0%;
    transition: width 0.1s ease;
}
.controls-bottom {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: 100%;
}
.play-pause-text {
    color: white;
    font-size: 14px;
    cursor: pointer;
    transition: opacity 0.2s ease;
}
.play-pause-text:hover {
    opacity: 0.8;
}
.fullscreen-text {
    color: white;
    font-size: 14px;
    cursor: pointer;
    transition: opacity 0.2s ease;
}
.fullscreen-text:hover {
    opacity: 0.8;
}
.close-btn {
    position: absolute;
    top: 15px;
    right: 15px;
    background: rgba(0,0,0,0.6);
    color: white;
    border: none;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 20px;
    z-index: 10;
    transition: background 0.2s ease;
}
.close-btn:hover {
    background: rgba(0,0,0,0.8);
}
.loading {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: white;
    font-size: 18px;
}
@media (max-width: 768px) {
    .lightbox-content {
        width: 95%;
        margin: 20px;
    }
    
    .video-controls {
        padding: 15px 10px 10px;
    }
}

1 Like