Introduction aux lightboxes
Une lightbox est un composant d'interface qui affiche des images ou du contenu en superposition sur la page, avec un fond assombri. C'est un pattern UX incontournable pour les galeries photos, portfolios et sites e-commerce.
Bien que de nombreuses librairies existent (Fancybox, Lightgallery, PhotoSwipe...), creer sa propre lightbox en JavaScript vanilla offre plusieurs avantages :
- Zero dependance : pas de librairie externe a charger
- Performance optimale : code leger et sur mesure
- Controle total : personnalisation illimitee du comportement
- Apprentissage : comprendre les mecanismes sous-jacents
Notre lightbox finale fera moins de 100 lignes de JavaScript et supportera la navigation clavier, le swipe tactile, et les animations CSS.
Structure HTML de base
Commencons par definir la structure HTML. Notre lightbox se compose de trois elements principaux : le conteneur overlay, l'image affichee, et les controles de navigation.
<!-- Galerie d'images -->
<div class="gallery">
<img src="photo-1.jpg" alt="Description 1" data-lightbox/>
<img src="photo-2.jpg" alt="Description 2" data-lightbox/>
<img src="photo-3.jpg" alt="Description 3" data-lightbox/>
</div>
<!-- Lightbox container -->
<div class="lightbox" id="lightbox" role="dialog" aria-modal="true">
<div class="lightbox-content">
<!-- Bouton fermer -->
<button class="lightbox-close" aria-label="Fermer">
<svg width="24" height="24" viewBox="0 0 24 24">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
<!-- Navigation -->
<button class="lightbox-nav lightbox-prev" aria-label="Precedent">‹</button>
<button class="lightbox-nav lightbox-next" aria-label="Suivant">›</button>
<!-- Image -->
<img class="lightbox-image" id="lightbox-img" src="" alt=""/>
<!-- Compteur -->
<div class="lightbox-counter"><span id="current">1</span> / <span id="total">3</span></div>
</div>
</div>
Points importants
- L'attribut
data-lightboxpermet d'identifier les images cliquables - Les attributs ARIA (
role,aria-modal,aria-label) ameliorent l'accessibilite - La structure est semantique avec des
<button>pour les controles
Styles CSS et animations
Le CSS est crucial pour creer une experience fluide. Nous allons utiliser des transitions pour l'apparition/disparition et des transforms pour les animations.
/* Overlay de fond */
.lightbox {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.95);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
/* Animation d'entree */
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.lightbox.active {
opacity: 1;
visibility: visible;
}
/* Conteneur de l'image */
.lightbox-content {
position: relative;
max-width: 90vw;
max-height: 90vh;
/* Animation de zoom */
transform: scale(0.9);
transition: transform 0.3s ease;
}
.lightbox.active .lightbox-content {
transform: scale(1);
}
/* Image principale */
.lightbox-image {
max-width: 100%;
max-height: 85vh;
border-radius: 12px;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
object-fit: contain;
}
/* Bouton fermer */
.lightbox-close {
position: absolute;
top: -50px;
right: 0;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.1);
border: none;
border-radius: 50%;
color: white;
cursor: pointer;
transition: all 0.2s ease;
}
.lightbox-close:hover {
background: #6366f1;
transform: rotate(90deg);
}
/* Boutons navigation */
.lightbox-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 50px;
height: 50px;
background: rgba(255, 255, 255, 0.1);
border: none;
border-radius: 50%;
color: white;
font-size: 24px;
cursor: pointer;
transition: all 0.2s ease;
}
.lightbox-prev { left: -70px; }
.lightbox-next { right: -70px; }
.lightbox-nav:hover {
background: #6366f1;
}
Astuces CSS avancees
Quelques techniques interessantes utilisees dans ce CSS :
- inset: 0 : raccourci pour top/right/bottom/left a 0
- visibility + opacity : permet des transitions fluides (display: none ne peut pas etre anime)
- transform: scale() : animation de zoom performante (GPU accelere)
JavaScript vanilla
Le coeur de notre lightbox : le JavaScript qui gere l'ouverture, la fermeture et la navigation entre les images.
class Lightbox {
constructor() {
this.lightbox = document.getElementById('lightbox');
this.image = document.getElementById('lightbox-img');
this.images = [...document.querySelectorAll('[data-lightbox]')];
this.currentIndex = 0;
this.init();
}
init() {
// Clic sur les images de la galerie
this.images.forEach((img, index) => {
img.addEventListener('click', () => this.open(index));
img.style.cursor = 'pointer';
});
// Boutons de controle
this.lightbox.querySelector('.lightbox-close')
.addEventListener('click', () => this.close());
this.lightbox.querySelector('.lightbox-prev')
.addEventListener('click', () => this.prev());
this.lightbox.querySelector('.lightbox-next')
.addEventListener('click', () => this.next());
// Fermer en cliquant sur le fond
this.lightbox.addEventListener('click', (e) => {
if (e.target === this.lightbox) this.close();
});
// Navigation clavier
document.addEventListener('keydown', (e) => this.handleKeyboard(e));
}
open(index) {
this.currentIndex = index;
this.updateImage();
this.lightbox.classList.add('active');
document.body.style.overflow = 'hidden';
}
close() {
this.lightbox.classList.remove('active');
document.body.style.overflow = '';
}
prev() {
this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
this.updateImage();
}
next() {
this.currentIndex = (this.currentIndex + 1) % this.images.length;
this.updateImage();
}
updateImage() {
const img = this.images[this.currentIndex];
this.image.src = img.src;
this.image.alt = img.alt;
// Mise a jour du compteur
document.getElementById('current').textContent = this.currentIndex + 1;
document.getElementById('total').textContent = this.images.length;
}
handleKeyboard(e) {
if (!this.lightbox.classList.contains('active')) return;
switch (e.key) {
case 'Escape': this.close(); break;
case 'ArrowLeft': this.prev(); break;
case 'ArrowRight': this.next(); break;
}
}
}
// Initialisation
new Lightbox();
L'utilisation d'une classe JavaScript permet d'encapsuler toute la logique et facilite la reutilisation. Vous pouvez facilement avoir plusieurs instances de lightbox sur une meme page.
Navigation entre images
La navigation est un element cle de l'experience utilisateur. Notre lightbox supporte plusieurs modes de navigation.
Navigation par boutons
Les fleches precedent/suivant permettent une navigation intuitive. L'utilisation du modulo (%) permet une navigation circulaire infinie.
// Navigation circulaire
prev() {
// Si index = 0, on passe a la derniere image
this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
this.updateImage();
}
next() {
// Si index = dernier, on revient a 0
this.currentIndex = (this.currentIndex + 1) % this.images.length;
this.updateImage();
}
Navigation au clavier
Le support des touches fleches et Escape est essentiel pour l'accessibilite et le confort d'utilisation.
handleKeyboard(e) {
// Ne reagir que si la lightbox est ouverte
if (!this.lightbox.classList.contains('active')) return;
switch (e.key) {
case 'Escape':
this.close();
break;
case 'ArrowLeft':
e.preventDefault(); // Empecher le scroll
this.prev();
break;
case 'ArrowRight':
e.preventDefault();
this.next();
break;
}
}
Demo interactive
Voici notre lightbox en action. Cliquez sur une image pour l'ouvrir, puis utilisez les fleches ou le clavier pour naviguer.
Escape : Fermer la lightbox
Fleche gauche : Image precedente
Fleche droite : Image suivante
Accessibilite et bonnes pratiques
Une lightbox accessible est essentielle pour tous les utilisateurs. Voici les points cles a ne pas negliger.
Attributs ARIA
- role="dialog" : indique aux lecteurs d'ecran qu'il s'agit d'une boite de dialogue
- aria-modal="true" : signale que le contenu derriere est inactif
- aria-label : description textuelle des boutons
Focus trap
Pour une accessibilite complete, il faut "pieger" le focus dans la lightbox quand elle est ouverte.
trapFocus() {
const focusableElements = this.lightbox.querySelectorAll(
'button, [href], [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
this.lightbox.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
});
}
Respectez la preference prefers-reduced-motion pour les utilisateurs sensibles aux animations.
@media (prefers-reduced-motion: reduce) {
.lightbox,
.lightbox-content {
transition: none;
}
}
Conclusion
Vous disposez maintenant d'une lightbox complete et professionnelle en JavaScript vanilla. Cette implementation offre :
- Zero dependance : aucune librairie externe requise
- Accessibilite : navigation clavier et attributs ARIA
- Animations fluides : transitions CSS performantes
- Code maintenable : architecture en classe ES6
N'hesitez pas a personnaliser les styles et ajouter des fonctionnalites supplementaires comme le zoom, le swipe tactile ou le chargement lazy des images.
Decouvrez notre collection d'effets de galerie dans la bibliotheque Effects.lab, avec des variations animees et des templates prets a l'emploi.