Galerie Lightbox Thumbnails Tailwind CSS

Extraits & Composants HTML 10/04/2026 08:00:00 angularforall.com
Tailwind Css Lightbox Galerie Thumbnails Modal Template Javascript

Galerie d'images avec lightbox et miniatures en Tailwind CSS : thème bleu moderne, navigation fluide, modal plein écran et design utility-first.

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8" />
    <meta name="copyright" content="AngularForAll" />
    <meta name="author" content="AngularForAll" />
    <meta name="robots" content="noindex, nofollow" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="Cache-Control" content="public, max-age=604800" />
    <title>Snippets Lightbox Thumbnails Tailwindcss 2026 05022043 | AngularForAll</title>
<!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    
    <!-- Configuration -->
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: {
                            50: '#eff6ff',
                            100: '#dbeafe',
                            200: '#bfdbfe',
                            300: '#93c5fd',
                            400: '#60a5fa',
                            500: '#3b82f6',
                            600: '#2563eb',
                            700: '#1d4ed8',
                            800: '#1e40af',
                            900: '#1e3a8a',
                        }
                    },
                    animation: {
                        'fade-in': 'fadeIn 0.3s ease-out',
                        'slide-up': 'slideUp 0.4s ease-out',
                        'zoom-in': 'zoomIn 0.3s ease-out',
                    },
                    keyframes: {
                        fadeIn: {
                            '0%': { opacity: '0' },
                            '100%': { opacity: '1' },
                        },
                        slideUp: {
                            '0%': { transform: 'translateY(20px)', opacity: '0' },
                            '100%': { transform: 'translateY(0)', opacity: '1' },
                        },
                        zoomIn: {
                            '0%': { transform: 'scale(0.9)', opacity: '0' },
                            '100%': { transform: 'scale(1)', opacity: '1' },
                        },
                    }
                }
            }
        }
    </script>
    
    <!-- Font Awesome -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.5.1/css/all.min.css">
</head>
<body class="bg-gradient-to-br from-blue-50 via-white to-blue-100 min-h-screen flex items-center justify-center p-4 sm:p-8">

    <!-- Gallery Container -->
    <div class="w-full max-w-4xl bg-white rounded-2xl shadow-2xl p-6 sm:p-8 border border-blue-100">
        
        <!-- Header -->
        <div class="flex items-center justify-between mb-6">
            <h2 class="text-2xl font-bold text-blue-900 flex items-center gap-2">
                <i class="fas fa-images text-blue-500"></i>
                Galerie Produit
            </h2>
            <span class="bg-blue-500 text-white px-4 py-1.5 rounded-full text-sm font-semibold shadow-lg shadow-blue-200">
                8 Photos
            </span>
        </div>
        
        <!-- Main Image Container -->
        <div id="mainImageContainer" class="relative bg-gray-100 rounded-xl overflow-hidden mb-6 aspect-[16/10] cursor-zoom-in group">
            <img id="mainImage" src="" alt="Image principale" 
                 class="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105">
            
            <!-- Navigation Arrows -->
            <button onclick="navigateImage(-1)" 
                    class="absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/90 hover:bg-blue-500 hover:text-white text-blue-700 rounded-full flex items-center justify-center shadow-lg transition-all duration-300 hover:scale-110 backdrop-blur-sm z-10">
                <i class="fas fa-chevron-left text-lg"></i>
            </button>
            
            <button onclick="navigateImage(1)" 
                    class="absolute right-4 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/90 hover:bg-blue-500 hover:text-white text-blue-700 rounded-full flex items-center justify-center shadow-lg transition-all duration-300 hover:scale-110 backdrop-blur-sm z-10">
                <i class="fas fa-chevron-right text-lg"></i>
            </button>
            
            <!-- Zoom Indicator -->
            <div id="zoomIndicator" class="absolute top-4 right-4 w-10 h-10 bg-white/90 text-blue-600 rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 shadow-lg">
                <i class="fas fa-search-plus"></i>
            </div>
            
            <!-- Image Counter -->
            <div id="imageCounter" class="absolute bottom-4 right-4 bg-white/90 text-blue-800 px-3 py-1.5 rounded-full text-sm font-semibold backdrop-blur-sm shadow-lg">
                1 / 8
            </div>
        </div>
        
        <!-- Thumbnails -->
        <div class="relative">
            <button id="prevThumbBtn" onclick="scrollThumbnails(-1)" disabled
                    class="absolute -left-3 top-1/2 -translate-y-1/2 w-9 h-9 bg-white hover:bg-blue-500 hover:text-white text-blue-600 rounded-full flex items-center justify-center shadow-lg transition-all duration-300 z-10 border border-blue-200 disabled:opacity-30 disabled:cursor-not-allowed">
                <i class="fas fa-chevron-left text-sm"></i>
            </button>
            
            <div class="overflow-hidden rounded-xl mx-4" id="thumbnailsWrapper">
                <div id="thumbnailsTrack" class="flex gap-3 transition-transform duration-500 ease-out"></div>
            </div>
            
            <button id="nextThumbBtn" onclick="scrollThumbnails(1)"
                    class="absolute -right-3 top-1/2 -translate-y-1/2 w-9 h-9 bg-white hover:bg-blue-500 hover:text-white text-blue-600 rounded-full flex items-center justify-center shadow-lg transition-all duration-300 z-10 border border-blue-200 disabled:opacity-30 disabled:cursor-not-allowed">
                <i class="fas fa-chevron-right text-sm"></i>
            </button>
        </div>
    </div>
    
    <!-- Lightbox Modal -->
    <div id="lightboxModal" class="fixed inset-0 z-50 hidden items-center justify-center bg-black/95 backdrop-blur-sm">
        <div class="relative max-w-[90vw] max-h-[90vh]">
            <!-- Close Button -->
            <button onclick="closeLightbox()" 
                    class="absolute -top-12 right-0 w-11 h-11 bg-white/10 hover:bg-red-500 text-white rounded-full flex items-center justify-center transition-all duration-300 hover:rotate-90 border border-white/20">
                <i class="fas fa-times text-xl"></i>
            </button>
            
            <!-- Lightbox Navigation -->
            <button onclick="navigateLightbox(-1)" 
                    class="absolute left-4 sm:-left-16 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/10 hover:bg-blue-500 text-white rounded-full flex items-center justify-center transition-all duration-300 border border-white/20 text-xl">
                <i class="fas fa-chevron-left"></i>
            </button>
            
            <img id="lightboxImage" src="" alt="Lightbox" 
                 class="max-w-[90vw] max-h-[85vh] object-contain rounded-lg shadow-2xl animate-fade-in">
            
            <button onclick="navigateLightbox(1)" 
                    class="absolute right-4 sm:-right-16 top-1/2 -translate-y-1/2 w-12 h-12 bg-white/10 hover:bg-blue-500 text-white rounded-full flex items-center justify-center transition-all duration-300 border border-white/20 text-xl">
                <i class="fas fa-chevron-right"></i>
            </button>
            
            <!-- Counter -->
            <div id="lightboxCounter" class="absolute -bottom-10 left-1/2 -translate-x-1/2 text-white font-semibold text-sm">
                1 / 8
            </div>
        </div>
    </div>

    <script>
        // Images
        const images = [
            { src: 'https://picsum.photos/800/500?random=1', alt: 'Produit vue 1' },
            { src: 'https://picsum.photos/800/500?random=2', alt: 'Produit vue 2' },
            { src: 'https://picsum.photos/800/500?random=3', alt: 'Produit vue 3' },
            { src: 'https://picsum.photos/800/500?random=4', alt: 'Produit vue 4' },
            { src: 'https://picsum.photos/800/500?random=5', alt: 'Produit vue 5' },
            { src: 'https://picsum.photos/800/500?random=6', alt: 'Produit vue 6' },
            { src: 'https://picsum.photos/800/500?random=7', alt: 'Produit vue 7' },
            { src: 'https://picsum.photos/800/500?random=8', alt: 'Produit vue 8' },
        ];
        
        let currentIndex = 0;
        let isZoomed = false;
        let thumbScrollPos = 0;
        
        function init() {
            renderThumbnails();
            updateMainImage(0);
            setupZoom();
            setupKeyboard();
        }
        
        function renderThumbnails() {
            const track = document.getElementById('thumbnailsTrack');
            track.innerHTML = images.map((img, i) => `
                <div class="thumbnail-item flex-shrink-0 w-[calc(20%-0.6rem)] sm:w-[calc(16.66%-0.75rem)] aspect-[16/10] rounded-lg overflow-hidden cursor-pointer border-2 transition-all duration-300 hover:-translate-y-1 hover:shadow-lg relative ${i === currentIndex ? 'border-blue-500 shadow-lg shadow-blue-200' : 'border-transparent hover:border-blue-300'}" 
                     onclick="updateMainImage(${i})">
                    <img src="${img.src}" alt="${img.alt}" class="w-full h-full object-cover" loading="lazy">
                    ${i === currentIndex ? '<div class="absolute inset-0 bg-blue-500/20"></div>' : ''}
                </div>
            `).join('');
        }
        
        function updateMainImage(index) {
            currentIndex = index;
            isZoomed = false;
            
            const mainImg = document.getElementById('mainImage');
            const container = document.getElementById('mainImageContainer');
            
            mainImg.style.opacity = '0';
            mainImg.src = images[index].src;
            mainImg.alt = images[index].alt;
            container.classList.remove('zoomed');
            
            setTimeout(() => mainImg.style.opacity = '1', 150);
            
            document.getElementById('imageCounter').textContent = `${index + 1} / ${images.length}`;
            
            renderThumbnails();
            scrollToThumb(index);
        }
        
        function navigateImage(dir) {
            let newIdx = currentIndex + dir;
            if (newIdx < 0) newIdx = images.length - 1;
            if (newIdx >= images.length) newIdx = 0;
            updateMainImage(newIdx);
        }
        
        function setupZoom() {
            const container = document.getElementById('mainImageContainer');
            container.addEventListener('click', (e) => {
                if (e.target.closest('button')) return;
                isZoomed = !isZoomed;
                const img = document.getElementById('mainImage');
                img.style.transform = isZoomed ? 'scale(2)' : 'scale(1)';
                img.style.cursor = isZoomed ? 'zoom-out' : 'zoom-in';
                container.style.overflow = isZoomed ? 'scroll' : 'hidden';
                
                const indicator = document.getElementById('zoomIndicator');
                indicator.innerHTML = isZoomed ? '<i class="fas fa-search-minus"></i>' : '<i class="fas fa-search-plus"></i>';
            });
            
            container.addEventListener('dblclick', (e) => {
                if (!e.target.closest('button')) openLightbox();
            });
        }
        
        function scrollThumbnails(dir) {
            const wrapper = document.getElementById('thumbnailsWrapper');
            const scrollAmount = wrapper.offsetWidth * 0.6;
            thumbScrollPos += dir * scrollAmount;
            
            const track = document.getElementById('thumbnailsTrack');
            const maxScroll = track.scrollWidth - wrapper.offsetWidth;
            thumbScrollPos = Math.max(0, Math.min(thumbScrollPos, maxScroll));
            
            track.style.transform = `translateX(-${thumbScrollPos}px)`;
            updateThumbButtons(maxScroll);
        }
        
        function scrollToThumb(index) {
            const wrapper = document.getElementById('thumbnailsWrapper');
            const thumbWidth = wrapper.offsetWidth / 5;
            thumbScrollPos = index * thumbWidth - thumbWidth;
            
            const track = document.getElementById('thumbnailsTrack');
            const maxScroll = track.scrollWidth - wrapper.offsetWidth;
            thumbScrollPos = Math.max(0, Math.min(thumbScrollPos, maxScroll));
            
            track.style.transform = `translateX(-${thumbScrollPos}px)`;
            updateThumbButtons(maxScroll);
        }
        
        function updateThumbButtons(maxScroll) {
            document.getElementById('prevThumbBtn').disabled = thumbScrollPos <= 0;
            document.getElementById('nextThumbBtn').disabled = thumbScrollPos >= maxScroll - 1;
        }
        
        function openLightbox() {
            document.getElementById('lightboxModal').classList.remove('hidden');
            document.getElementById('lightboxModal').classList.add('flex');
            document.body.style.overflow = 'hidden';
            updateLightbox();
        }
        
        function closeLightbox() {
            document.getElementById('lightboxModal').classList.add('hidden');
            document.getElementById('lightboxModal').classList.remove('flex');
            document.body.style.overflow = '';
        }
        
        function updateLightbox() {
            document.getElementById('lightboxImage').src = images[currentIndex].src;
            document.getElementById('lightboxCounter').textContent = `${currentIndex + 1} / ${images.length}`;
        }
        
        function navigateLightbox(dir) {
            let newIdx = currentIndex + dir;
            if (newIdx < 0) newIdx = images.length - 1;
            if (newIdx >= images.length) newIdx = 0;
            updateMainImage(newIdx);
            updateLightbox();
        }
        
        function setupKeyboard() {
            document.addEventListener('keydown', (e) => {
                const modal = document.getElementById('lightboxModal');
                if (modal.classList.contains('flex')) {
                    if (e.key === 'Escape') closeLightbox();
                    if (e.key === 'ArrowLeft') navigateLightbox(-1);
                    if (e.key === 'ArrowRight') navigateLightbox(1);
                }
            });
        }
        
        document.getElementById('lightboxModal').addEventListener('click', (e) => {
            if (e.target === document.getElementById('lightboxModal')) closeLightbox();
        });
        
        document.addEventListener('DOMContentLoaded', init);
    </script>
</body>
</html>

Télécharger le fichier source

Partager