Better Squarespace Gallery Script
This is the result of my discontent with the current lightbox setup within squarespace galleries. I figured I would share the process and the code if you would like to add the improved gallery version on your site as well. The issue I run into when showing people my portfolio online, be it on a mobile or desktop, is that when tapping or clicking on an image the lightbox scales down the image and does not make the most efficient use of the space available on the screen. In the interest of full disclosure, the script was created with the assistance of ChatGPT to ensure security, reliability, in addition to clean code.
As you can see there is a bunch of white space making poor use of space that could otherwise be used as real estate for showcasing your work.
With my script added, this true fullscreen is applied and fills all available space with your image based on its width to height ratio. This applies to all projects within a portfolio in Squarespace. The same applies for mobile viewing.
In order to add this to your site, copy and paste the code below into your portfolio’s “Page Header Injection Code” field. To access this go to your portfolio page in your website menu, click the gear to the right of the page, and then click “Advanced”. A visual guide is also provided below the code.
<style> .image-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: #000; display: none; align-items: center; justify-content: center; overflow: hidden; z-index: 99999; } .modal-content { position: relative; width: 100%; height: 100%; overflow: hidden; } .modal-track { display: flex; height: 100%; transition: transform 0.4s ease; } .modal-track img { flex: 0 0 100%; object-fit: contain; width: 100%; height: 100%; } .close-btn, .arrow { position: absolute; color: #fff; cursor: pointer; z-index: 100000; user-select: none; transition: opacity 0.3s ease; } .arrow-hidden { opacity: 0 !important; } .close-btn { top: 15px; right: 20px; font-size: 35px; } .arrow { top: 50%; transform: translateY(-50%); font-size: 50px; padding: 10px; } .arrow-left { left: 10px; } .arrow-right { right: 10px; } .arrow-hover-zone { position: absolute; top: 0; width: 20%; height: 100%; z-index: 99998; } .arrow-hover-left { left: 0; } .arrow-hover-right { right: 0; } body.modal-open { overflow: hidden; } </style> <script> document.addEventListener('DOMContentLoaded', () => { const isProjectPage = window.location.pathname.split('/').filter(Boolean).length > 1; if (!isProjectPage) return; const imageElements = Array.from(document.querySelectorAll('.sqs-gallery img, img[data-image]')); if (!imageElements.length) return; // Extract original image URLs to bypass lazy loading const images = imageElements.map(img => { const dataSrc = img.getAttribute('data-src') || img.getAttribute('data-image') || img.src; return dataSrc.split('?')[0]; // removes Squarespace parameters for full-quality }); let currentIndex = 0, hideArrowTimer = null; const modal = document.createElement('div'); modal.className = 'image-modal'; modal.innerHTML = ` <div class="modal-content"> <span class="close-btn">×</span> <span class="arrow arrow-left">❮</span> <span class="arrow arrow-right">❯</span> <div class="arrow-hover-zone arrow-hover-left"></div> <div class="arrow-hover-zone arrow-hover-right"></div> <div class="modal-track"></div> </div>`; document.body.appendChild(modal); const track = modal.querySelector('.modal-track'); const closeBtn = modal.querySelector('.close-btn'); const arrowLeft = modal.querySelector('.arrow-left'); const arrowRight = modal.querySelector('.arrow-right'); const hoverLeft = modal.querySelector('.arrow-hover-left'); const hoverRight = modal.querySelector('.arrow-hover-right'); const arrows = [arrowLeft, arrowRight]; function showArrows() { arrows.forEach(arrow => arrow.classList.remove('arrow-hidden')); clearTimeout(hideArrowTimer); hideArrowTimer = setTimeout(() => arrows.forEach(arrow => arrow.classList.add('arrow-hidden')), 1000); } async function preloadImages(urls) { const promises = urls.map(url => new Promise(resolve => { const img = new Image(); img.src = url; img.onload = resolve; img.onerror = resolve; })); await Promise.all(promises); } imageElements.forEach((img, idx) => { img.style.cursor = 'pointer'; img.onclick = async () => { await preloadImages(images); openModal(idx); }; }); closeBtn.onclick = closeModal; arrowLeft.onclick = () => { changeSlide(-1); showArrows(); }; arrowRight.onclick = () => { changeSlide(1); showArrows(); }; modal.onclick = e => { if (e.target === modal) closeModal(); }; hoverLeft.addEventListener('mouseenter', showArrows); hoverRight.addEventListener('mouseenter', showArrows); function openModal(index) { currentIndex = index; track.innerHTML = ''; images.forEach(url => { const modalImg = document.createElement('img'); modalImg.src = url; track.appendChild(modalImg); }); modal.style.display = 'flex'; updateSlide(false); document.body.classList.add('modal-open'); enterFullscreen(modal); showArrows(); } function closeModal() { modal.style.display = 'none'; document.body.classList.remove('modal-open'); exitFullscreen(); } function changeSlide(direction) { currentIndex = (currentIndex + direction + images.length) % images.length; updateSlide(true); } function updateSlide(animate = true) { track.style.transition = animate ? 'transform 0.4s ease' : 'none'; track.style.transform = `translateX(-${currentIndex * 100}%)`; } let startX = 0; track.addEventListener('touchstart', e => { startX = e.changedTouches[0].clientX; showArrows(); }); track.addEventListener('touchend', e => { const diff = startX - e.changedTouches[0].clientX; if (diff > 50) changeSlide(1); else if (diff < -50) changeSlide(-1); showArrows(); }); document.addEventListener('keydown', e => { if (modal.style.display === 'flex') { if (e.key === 'ArrowRight') { changeSlide(1); showArrows(); } else if (e.key === 'ArrowLeft') { changeSlide(-1); showArrows(); } else if (e.key === 'Escape') closeModal(); } }); function enterFullscreen(el) { if (el.requestFullscreen) el.requestFullscreen(); else if (el.webkitRequestFullscreen) el.webkitRequestFullscreen(); else if (el.msRequestFullscreen) el.msRequestFullscreen(); } function exitFullscreen() { if (document.exitFullscreen) document.exitFullscreen(); else if (document.webkitExitFullscreen) document.webkitExitFullscreen(); else if (document.msExitFullscreen) document.msExitFullscreen(); } window.addEventListener('resize', () => updateSlide(false)); }); </script>
From your main Squarespace menu, go to website -> pages.
Under your pages, select the gear icon next to portfolio.
Select “Advanced” from the pop up menu and where it says “Page Header Code Injection” paste the provided code.
The code should apply true fullscreen to all of your projects within your portfolio on Squarespace once added. This code was designed exclusively for portfolios in mind so it will not work outside of projects in your portfolio.
NOTE: Lightboxes should be disabled on your galleries for this to work seamlessly as it effectively replaces that functionality
If you feel this script helped you make a more effective online portfolio in Squarespace you are welcome to donate below, however you are not obliged to do so.
MIT License
Copyright (c) Thomas van der Sloot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.