Beautiful Responsive Gallery Template
Preview
Edit
Download
Run
Download Options
HTML File
Download as .html file
CSS File
Download as .css file
JavaScript File
Download as .js file
Combined HTML
All code in one HTML file
Project ZIP
All files in a ZIP archive
Cancel
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Beautiful Responsive Gallery</title> <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet"> </head> <body> <!-- Hero Section --> <section class="hero-section"> <div class="hero-content"> <h1 class="hero-title">Beautiful Gallery</h1> <p class="hero-subtitle">Discover stunning visuals in our curated collection</p> <div class="hero-stats"> <div class="stat-item"> <span class="stat-number">150+</span> <span class="stat-label">Images</span> </div> <div class="stat-item"> <span class="stat-number">12</span> <span class="stat-label">Categories</span> </div> <div class="stat-item"> <span class="stat-number">4K</span> <span class="stat-label">Views</span> </div> </div> </div> </section> <!-- Main Container --> <div class="container"> <!-- Filter Navigation --> <nav class="filter-nav" id="filterNav"> <button class="filter-btn active" data-filter="all"> <span>All Images</span> </button> <button class="filter-btn" data-filter="nature"> <span>Nature</span> </button> <button class="filter-btn" data-filter="architecture"> <span>Architecture</span> </button> <button class="filter-btn" data-filter="portrait"> <span>Portrait</span> </button> <button class="filter-btn" data-filter="abstract"> <span>Abstract</span> </button> <button class="filter-btn" data-filter="landscape"> <span>Landscape</span> </button> <button class="filter-btn" data-filter="urban"> <span>Urban</span> </button> </nav> <!-- Gallery Grid --> <div class="gallery-grid" id="galleryGrid"> <!-- Gallery items will be dynamically generated --> </div> <!-- Loading Indicator --> <div class="loading" id="loadingIndicator" style="display: none;"> <div class="spinner"></div> Loading beautiful images... </div> </div> <!-- Floating Action Button --> <button class="fab" id="scrollToTop" title="Scroll to top"> ↑ </button> <!-- Lightbox Modal --> <div class="lightbox" id="lightbox"> <div class="lightbox-content"> <img class="lightbox-image" id="lightboxImage" src="" alt=""> <button class="lightbox-close" id="lightboxClose">×</button> <button class="lightbox-nav lightbox-prev" id="lightboxPrev">‹</button> <button class="lightbox-nav lightbox-next" id="lightboxNext">›</button> <div class="lightbox-info"> <div class="lightbox-title" id="lightboxTitle"></div> <div class="lightbox-description" id="lightboxDescription"></div> </div> </div> </div> <script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'96da9d2645bf3d23',t:'MTc1NDk0NTUxNy4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body> </html>
CSS
:root { --primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); --secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); --accent-gradient: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); --warm-gradient: linear-gradient(135deg, #fa709a 0%, #fee140 100%); --cool-gradient: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); --dark-gradient: linear-gradient(135deg, #2c3e50 0%, #34495e 100%); --bg-primary: #fafbfc; --bg-secondary: #ffffff; --text-primary: #2d3748; --text-secondary: #718096; --border-light: #e2e8f0; --shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.08); --shadow-medium: 0 8px 30px rgba(0, 0, 0, 0.12); --shadow-strong: 0 20px 60px rgba(0, 0, 0, 0.15); --transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: 'Inter', sans-serif; background: var(--bg-primary); color: var(--text-primary); line-height: 1.6; overflow-x: hidden; } /* Header Section */ .hero-section { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 5rem 0; text-align: center; position: relative; overflow: hidden; } .hero-section::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="dots" width="20" height="20" patternUnits="userSpaceOnUse"><circle cx="10" cy="10" r="1.5" fill="rgba(255,255,255,0.1)"/></pattern></defs><rect width="100" height="100" fill="url(%23dots)"/></svg>'); } .hero-content { position: relative; z-index: 2; max-width: 800px; margin: 0 auto; padding: 0 2rem; } .hero-title { font-family: 'Playfair Display', serif; font-size: 4rem; font-weight: 700; color: white; margin-bottom: 1.5rem; opacity: 0; animation: fadeInUp 1s ease-out 0.3s forwards; } .hero-subtitle { font-size: 1.5rem; color: rgba(255, 255, 255, 0.9); font-weight: 300; margin-bottom: 2rem; opacity: 0; animation: fadeInUp 1s ease-out 0.6s forwards; } .hero-stats { display: flex; justify-content: center; gap: 3rem; opacity: 0; animation: fadeInUp 1s ease-out 0.9s forwards; } .stat-item { text-align: center; } .stat-number { font-size: 2.5rem; font-weight: 600; color: white; display: block; } .stat-label { font-size: 1rem; color: rgba(255, 255, 255, 0.8); text-transform: uppercase; letter-spacing: 1px; } /* Main Container */ .container { max-width: 1400px; margin: 0 auto; padding: 4rem 2rem; } /* Filter Navigation */ .filter-nav { display: flex; justify-content: center; flex-wrap: wrap; gap: 1rem; margin-bottom: 4rem; opacity: 0; animation: fadeInUp 1s ease-out 1.2s forwards; } .filter-btn { background: var(--bg-secondary); border: 2px solid var(--border-light); color: var(--text-secondary); padding: 1rem 2rem; border-radius: 50px; cursor: pointer; font-weight: 500; font-size: 1rem; transition: var(--transition); position: relative; overflow: hidden; } .filter-btn::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: var(--primary-gradient); transition: left 0.3s ease; z-index: 1; } .filter-btn span { position: relative; z-index: 2; } .filter-btn:hover::before, .filter-btn.active::before { left: 0; } .filter-btn:hover, .filter-btn.active { color: white; border-color: transparent; transform: translateY(-2px); box-shadow: var(--shadow-medium); } /* Gallery Grid */ .gallery-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem; opacity: 0; animation: fadeInUp 1s ease-out 1.5s forwards; } .gallery-item { background: var(--bg-secondary); border-radius: 20px; overflow: hidden; box-shadow: var(--shadow-soft); transition: var(--transition); cursor: pointer; position: relative; transform: translateY(0); } .gallery-item:hover { transform: translateY(-10px); box-shadow: var(--shadow-strong); } .gallery-item:nth-child(6n+1) .image-container::before { background: var(--primary-gradient); } .gallery-item:nth-child(6n+2) .image-container::before { background: var(--secondary-gradient); } .gallery-item:nth-child(6n+3) .image-container::before { background: var(--accent-gradient); } .gallery-item:nth-child(6n+4) .image-container::before { background: var(--warm-gradient); } .gallery-item:nth-child(6n+5) .image-container::before { background: var(--cool-gradient); } .gallery-item:nth-child(6n+6) .image-container::before { background: var(--dark-gradient); } .image-container { position: relative; height: 250px; overflow: hidden; } .image-container::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: var(--primary-gradient); z-index: 1; } .gallery-image { width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s ease; position: relative; z-index: 2; mix-blend-mode: overlay; } .gallery-item:hover .gallery-image { transform: scale(1.1); } .image-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.7); display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s ease; z-index: 3; } .gallery-item:hover .image-overlay { opacity: 1; } .overlay-content { text-align: center; color: white; transform: translateY(20px); transition: transform 0.3s ease; } .gallery-item:hover .overlay-content { transform: translateY(0); } .overlay-icon { font-size: 3rem; margin-bottom: 1rem; opacity: 0.9; } .overlay-text { font-size: 1.2rem; font-weight: 500; } .item-content { padding: 2rem; } .item-category { display: inline-block; padding: 0.5rem 1rem; background: var(--primary-gradient); color: white; border-radius: 20px; font-size: 0.8rem; font-weight: 600; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 1rem; } .item-title { font-family: 'Playfair Display', serif; font-size: 1.5rem; font-weight: 600; color: var(--text-primary); margin-bottom: 0.5rem; } .item-description { color: var(--text-secondary); font-size: 1rem; line-height: 1.6; margin-bottom: 1.5rem; } .item-meta { display: flex; justify-content: space-between; align-items: center; padding-top: 1rem; border-top: 1px solid var(--border-light); } .item-date { color: var(--text-secondary); font-size: 0.9rem; } .item-likes { display: flex; align-items: center; gap: 0.5rem; color: #e53e3e; font-size: 0.9rem; } /* Masonry Layout for larger screens */ @media (min-width: 768px) { .gallery-grid { columns: 3; column-gap: 2rem; } .gallery-item { break-inside: avoid; margin-bottom: 2rem; display: inline-block; width: 100%; } .gallery-item:nth-child(3n+1) .image-container { height: 300px; } .gallery-item:nth-child(3n+2) .image-container { height: 200px; } .gallery-item:nth-child(3n+3) .image-container { height: 350px; } } @media (min-width: 1200px) { .gallery-grid { columns: 4; } } /* Lightbox Modal */ .lightbox { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.95); display: none; align-items: center; justify-content: center; z-index: 1000; backdrop-filter: blur(20px); } .lightbox.active { display: flex; } .lightbox-content { position: relative; max-width: 90%; max-height: 90%; animation: lightboxZoom 0.4s ease-out; } .lightbox-image { width: 100%; height: 100%; object-fit: contain; border-radius: 10px; box-shadow: var(--shadow-strong); } .lightbox-close { position: absolute; top: -60px; right: 0; background: rgba(255, 255, 255, 0.1); border: 2px solid rgba(255, 255, 255, 0.2); color: white; width: 50px; height: 50px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; transition: var(--transition); backdrop-filter: blur(10px); } .lightbox-close:hover { background: rgba(255, 255, 255, 0.2); transform: rotate(90deg); } .lightbox-nav { position: absolute; top: 50%; transform: translateY(-50%); background: rgba(255, 255, 255, 0.1); border: 2px solid rgba(255, 255, 255, 0.2); color: white; width: 60px; height: 60px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; transition: var(--transition); backdrop-filter: blur(10px); } .lightbox-nav:hover { background: rgba(255, 255, 255, 0.2); transform: translateY(-50%) scale(1.1); } .lightbox-prev { left: -80px; } .lightbox-next { right: -80px; } .lightbox-info { position: absolute; bottom: -80px; left: 0; right: 0; text-align: center; color: white; } .lightbox-title { font-size: 1.5rem; font-weight: 600; margin-bottom: 0.5rem; } .lightbox-description { font-size: 1rem; opacity: 0.8; } /* Loading Animation */ .loading { display: flex; justify-content: center; align-items: center; height: 200px; font-size: 1.2rem; color: var(--text-secondary); } .spinner { width: 40px; height: 40px; border: 3px solid var(--border-light); border-top: 3px solid #667eea; border-radius: 50%; animation: spin 1s linear infinite; margin-right: 1rem; } /* Floating Action Button */ .fab { position: fixed; bottom: 2rem; right: 2rem; width: 60px; height: 60px; background: var(--primary-gradient); border: none; border-radius: 50%; color: white; font-size: 1.5rem; cursor: pointer; box-shadow: var(--shadow-medium); transition: var(--transition); z-index: 100; } .fab:hover { transform: scale(1.1); box-shadow: var(--shadow-strong); } /* Animations */ @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } } @keyframes lightboxZoom { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @keyframes pulse { 0%, 100% { opacity: 0.6; } 50% { opacity: 1; } } /* Responsive Design */ @media (max-width: 768px) { .hero-title { font-size: 2.5rem; } .hero-subtitle { font-size: 1.2rem; } .hero-stats { flex-direction: column; gap: 1.5rem; } .container { padding: 2rem 1rem; } .gallery-grid { columns: 1; grid-template-columns: 1fr; } .gallery-item { margin-bottom: 1.5rem; } .filter-nav { gap: 0.5rem; } .filter-btn { padding: 0.75rem 1.5rem; font-size: 0.9rem; } .lightbox-nav { width: 50px; height: 50px; font-size: 1.2rem; } .lightbox-prev { left: -60px; } .lightbox-next { right: -60px; } .fab { width: 50px; height: 50px; font-size: 1.2rem; bottom: 1rem; right: 1rem; } } @media (max-width: 480px) { .hero-section { padding: 3rem 0; } .hero-title { font-size: 2rem; } .container { padding: 1.5rem 1rem; } .item-content { padding: 1.5rem; } .lightbox-close { top: -50px; width: 40px; height: 40px; font-size: 1.2rem; } .lightbox-nav { display: none; } }
JavaScript
class BeautifulGallery { constructor() { this.currentFilter = 'all'; this.currentLightboxIndex = 0; this.filteredImages = []; this.images = this.generateImages(); this.init(); } generateImages() { const categories = ['nature', 'architecture', 'portrait', 'abstract', 'landscape', 'urban']; const gradients = [ ['#667eea', '#764ba2'], ['#f093fb', '#f5576c'], ['#4facfe', '#00f2fe'], ['#43e97b', '#38f9d7'], ['#fa709a', '#fee140'], ['#a770ef', '#cf8bf3'], ['#feb692', '#ea5455'], ['#89f7fe', '#66a6ff'], ['#ffecd2', '#fcb69f'], ['#a8edea', '#fed6e3'], ['#d299c2', '#fef9d7'], ['#fda085', '#f6d365'], ['#ff9a9e', '#fecfef'], ['#a18cd1', '#fbc2eb'], ['#fad0c4', '#ffd1ff'], ['#ffecd2', '#fcb69f'], ['#ff8a80', '#ffb74d'], ['#81c784', '#aed581'], ['#64b5f6', '#42a5f5'], ['#ba68c8', '#ab47bc'], ['#ffb74d', '#ffa726'], ['#4db6ac', '#26a69a'], ['#f06292', '#ec407a'], ['#9575cd', '#7e57c2'] ]; const titles = [ 'Serene Mountain Lake', 'Modern Architecture', 'Portrait Study', 'Abstract Composition', 'Golden Hour Landscape', 'Urban Geometry', 'Forest Pathway', 'Glass Reflections', 'Artistic Portrait', 'Color Flow', 'Sunset Valley', 'City Lights', 'Ocean Waves', 'Concrete Curves', 'Natural Beauty', 'Digital Art', 'Mountain Peak', 'Street Art', 'Peaceful Moment', 'Geometric Patterns', 'Coastal View', 'Modern Design', 'Wild Nature', 'Creative Expression' ]; const descriptions = [ 'A breathtaking view of nature\'s beauty captured in perfect light', 'Modern architectural design showcasing clean lines and innovative structure', 'An intimate portrait capturing the essence of human emotion', 'Abstract art piece exploring color, form, and creative expression', 'Stunning landscape photography during the magical golden hour', 'Urban photography highlighting the geometric beauty of city life' ]; return titles.map((title, index) => { const category = categories[index % categories.length]; const gradient = gradients[index % gradients.length]; const description = descriptions[index % descriptions.length]; return { id: index + 1, title: title, category: category, description: description, image: this.createBeautifulImage(gradient[0], gradient[1], 400, 300), date: this.getRandomDate(), likes: Math.floor(Math.random() * 200) + 50 }; }); } createBeautifulImage(color1, color2, width, height) { const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const ctx = canvas.getContext('2d'); // Create beautiful gradient background const gradient = ctx.createLinearGradient(0, 0, width, height); gradient.addColorStop(0, color1); gradient.addColorStop(0.5, this.blendColors(color1, color2, 0.5)); gradient.addColorStop(1, color2); ctx.fillStyle = gradient; ctx.fillRect(0, 0, width, height); // Add artistic elements ctx.fillStyle = 'rgba(255, 255, 255, 0.1)'; // Add flowing shapes for (let i = 0; i < 6; i++) { const x = (width / 6) * i + Math.random() * 50; const y = height * 0.3 + Math.sin(i) * 50; const radius = 40 + Math.random() * 30; ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fill(); } // Add texture overlay ctx.fillStyle = 'rgba(255, 255, 255, 0.05)'; for (let i = 0; i < width; i += 3) { for (let j = 0; j < height; j += 3) { if (Math.random() > 0.8) { ctx.fillRect(i, j, 1, 1); } } } // Add subtle geometric patterns ctx.strokeStyle = 'rgba(255, 255, 255, 0.1)'; ctx.lineWidth = 2; for (let i = 0; i < 4; i++) { const startX = Math.random() * width; const startY = Math.random() * height; const endX = Math.random() * width; const endY = Math.random() * height; ctx.beginPath(); ctx.moveTo(startX, startY); ctx.lineTo(endX, endY); ctx.stroke(); } return canvas.toDataURL(); } blendColors(color1, color2, ratio) { const hex1 = color1.replace('#', ''); const hex2 = color2.replace('#', ''); const r1 = parseInt(hex1.substr(0, 2), 16); const g1 = parseInt(hex1.substr(2, 2), 16); const b1 = parseInt(hex1.substr(4, 2), 16); const r2 = parseInt(hex2.substr(0, 2), 16); const g2 = parseInt(hex2.substr(2, 2), 16); const b2 = parseInt(hex2.substr(4, 2), 16); const r = Math.round(r1 * (1 - ratio) + r2 * ratio); const g = Math.round(g1 * (1 - ratio) + g2 * ratio); const b = Math.round(b1 * (1 - ratio) + b2 * ratio); return `rgb(${r}, ${g}, ${b})`; } getRandomDate() { const start = new Date(2023, 0, 1); const end = new Date(); const date = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); return date.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }); } init() { this.renderGallery(); this.bindEvents(); this.filterImages('all'); } renderGallery() { const grid = document.getElementById('galleryGrid'); grid.innerHTML = this.images.map(image => ` <div class="gallery-item" data-category="${image.category}" data-id="${image.id}"> <div class="image-container"> <img class="gallery-image" src="${image.image}" alt="${image.title}" loading="lazy"> <div class="image-overlay"> <div class="overlay-content"> <div class="overlay-icon">?</div> <div class="overlay-text">View Image</div> </div> </div> </div> <div class="item-content"> <div class="item-category">${image.category}</div> <h3 class="item-title">${image.title}</h3> <p class="item-description">${image.description}</p> <div class="item-meta"> <span class="item-date">${image.date}</span> <div class="item-likes"> <svg width="16" height="16" fill="currentColor" viewBox="0 0 20 20"> <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" clip-rule="evenodd"/> </svg> ${image.likes} </div> </div> </div> </div> `).join(''); } filterImages(category) { this.currentFilter = category; const items = document.querySelectorAll('.gallery-item'); // Update active filter button document.querySelectorAll('.filter-btn').forEach(btn => { btn.classList.toggle('active', btn.dataset.filter === category); }); // Filter and animate items this.filteredImages = []; let delay = 0; items.forEach((item, index) => { const itemCategory = item.dataset.category; const shouldShow = category === 'all' || itemCategory === category; if (shouldShow) { this.filteredImages.push(this.images[index]); item.style.display = 'block'; item.style.animation = `fadeInUp 0.6s ease-out ${delay * 0.1}s both`; delay++; } else { item.style.display = 'none'; } }); } openLightbox(imageId) { const image = this.images.find(img => img.id === parseInt(imageId)); if (!image) return; this.currentLightboxIndex = this.filteredImages.findIndex(img => img.id === image.id); const lightbox = document.getElementById('lightbox'); const lightboxImage = document.getElementById('lightboxImage'); const lightboxTitle = document.getElementById('lightboxTitle'); const lightboxDescription = document.getElementById('lightboxDescription'); lightboxImage.src = image.image; lightboxImage.alt = image.title; lightboxTitle.textContent = image.title; lightboxDescription.textContent = image.description; lightbox.classList.add('active'); document.body.style.overflow = 'hidden'; } closeLightbox() { const lightbox = document.getElementById('lightbox'); lightbox.classList.remove('active'); document.body.style.overflow = 'auto'; } navigateLightbox(direction) { if (this.filteredImages.length === 0) return; if (direction === 'next') { this.currentLightboxIndex = (this.currentLightboxIndex + 1) % this.filteredImages.length; } else { this.currentLightboxIndex = this.currentLightboxIndex === 0 ? this.filteredImages.length - 1 : this.currentLightboxIndex - 1; } const image = this.filteredImages[this.currentLightboxIndex]; const lightboxImage = document.getElementById('lightboxImage'); const lightboxTitle = document.getElementById('lightboxTitle'); const lightboxDescription = document.getElementById('lightboxDescription'); lightboxImage.src = image.image; lightboxImage.alt = image.title; lightboxTitle.textContent = image.title; lightboxDescription.textContent = image.description; } scrollToTop() { window.scrollTo({ top: 0, behavior: 'smooth' }); } bindEvents() { // Filter buttons document.querySelectorAll('.filter-btn').forEach(btn => { btn.addEventListener('click', () => { this.filterImages(btn.dataset.filter); }); }); // Gallery items document.addEventListener('click', (e) => { const galleryItem = e.target.closest('.gallery-item'); if (galleryItem) { this.openLightbox(galleryItem.dataset.id); } }); // Lightbox controls document.getElementById('lightboxClose').addEventListener('click', () => { this.closeLightbox(); }); document.getElementById('lightboxPrev').addEventListener('click', () => { this.navigateLightbox('prev'); }); document.getElementById('lightboxNext').addEventListener('click', () => { this.navigateLightbox('next'); }); document.getElementById('lightbox').addEventListener('click', (e) => { if (e.target.id === 'lightbox') { this.closeLightbox(); } }); // Scroll to top button document.getElementById('scrollToTop').addEventListener('click', () => { this.scrollToTop(); }); // Show/hide scroll to top button window.addEventListener('scroll', () => { const scrollBtn = document.getElementById('scrollToTop'); if (window.pageYOffset > 300) { scrollBtn.style.opacity = '1'; scrollBtn.style.transform = 'scale(1)'; } else { scrollBtn.style.opacity = '0'; scrollBtn.style.transform = 'scale(0.8)'; } }); // Keyboard navigation document.addEventListener('keydown', (e) => { if (document.getElementById('lightbox').classList.contains('active')) { if (e.key === 'Escape') this.closeLightbox(); if (e.key === 'ArrowLeft') this.navigateLightbox('prev'); if (e.key === 'ArrowRight') this.navigateLightbox('next'); } }); // Touch/swipe support for lightbox let startX = 0; const lightbox = document.getElementById('lightbox'); lightbox.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; }); lightbox.addEventListener('touchend', (e) => { const endX = e.changedTouches[0].clientX; const diff = startX - endX; if (Math.abs(diff) > 50) { if (diff > 0) this.navigateLightbox('next'); else this.navigateLightbox('prev'); } }); } } // Initialize the gallery document.addEventListener('DOMContentLoaded', () => { new BeautifulGallery(); });
Output Preview
Desktop
Mobile
Fullscreen