Venir à la salle
12 quai des Tanneurs · ouverture des portes à 19 h 30
- MLigne 2, arrêt Halle — 3 min à pied
- BBus 14 et 31, arrêt Pont-Neuf
- PParking du Quai, 420 places, ouvert jusqu'à 1 h
Plan d'accès — touchez pour agrandir
Visionneuse minimale : une vignette cliquée s'ouvre en grand dans une vraie boîte de dialogue, sur fond noir flouté ; ✕, clic à côté ou Échap la referment.
Mis à jour le
Cet effet est gratuit — la catégorie Galerie en contient 44 au total, dont 6 gratuits. Effect.Labs regroupe 840 effets vanilla. Découvrir la catégorie →
12 quai des Tanneurs · ouverture des portes à 19 h 30
Plan d'accès — touchez pour agrandir
Chaque vignette est un vrai <button class="blb-thumb"> qui contient votre <img> (avec son alt). Au clic, au toucher, ou avec Entrée / Espace au clavier, le script remplit l'image de la visionneuse avec l'attribut data-full du bouton s'il existe, sinon avec l'image de la vignette elle-même, puis ouvre le <dialog class="blb-viewer"> avec showModal().
Le fond (rgba(0, 0, 0, 0.9) et un flou de 10 px) apparaît en fondu pendant que l'image passe de 80 % à 100 % de sa taille, en 0,4 s avec un léger rebond. L'image s'affiche à sa taille naturelle, plafonnée à 80 % de la largeur et 70 % de la hauteur de la fenêtre.
Comme c'est un <dialog> modal, la visionneuse passe au-dessus de tout, même si la galerie est dans un conteneur transformé ou masqué. Tout le code est rangé sous .fx-0171-root et le préfixe blb- ; chaque racine est câblée séparément, donc plusieurs galeries cohabitent sur une page et coller le script deux fois ne double rien.
<dialog> ouvert par showModal() a le rôle dialog et le comportement modal ; il porte un nom (aria-label="Visionneuse d'images"). Le reste de la page devient inerte : le Tab passe des boutons de la visionneuse à la barre du navigateur, jamais à la page cachée derrière.alt de votre vignette est recopié sur la grande image. Écrivez-le pour décrire la photo, pas « image 1 ».prefers-reduced-motion: reduce, ni fondu ni agrandissement : l'image s'affiche et se referme d'un coup.Repose sur <dialog> et showModal() (Chrome 37, Firefox 98, Safari 15.4) et sur aspect-ratio pour les vignettes (Chrome 88, Firefox 89, Safari 15), versions lues dans @mdn/browser-compat-data. Testé dans Chromium 151, Firefox 153 et WebKit 26.5 : deux galeries sur la même page, clavier, toucher à 390 px, mouvement réduit.
Sans showModal() (navigateur plus ancien que ces versions), le script ne fait rien et les vignettes restent de simples images. Sans backdrop-filter (Firefox avant 103), le fond est noir à 90 % sans flou.
Copiez les trois blocs dans votre page. Aucune dépendance.
<div class="fx-0171-root">
<div class="blb-grid">
<button type="button" class="blb-thumb" aria-haspopup="dialog"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='400' viewBox='0 0 600 400'%3E%3ClinearGradient id='g' x2='1' y2='1'%3E%3Cstop stop-color='%236366f1'/%3E%3Cstop offset='1' stop-color='%238b5cf6'/%3E%3C/linearGradient%3E%3Crect width='600' height='400' fill='url(%23g)'/%3E%3C/svg%3E" alt="Dégradé indigo vers violet" width="600" height="400"></button>
<button type="button" class="blb-thumb" aria-haspopup="dialog"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='400' viewBox='0 0 600 400'%3E%3ClinearGradient id='g' x2='1' y2='1'%3E%3Cstop stop-color='%23d946ef'/%3E%3Cstop offset='1' stop-color='%23f43f5e'/%3E%3C/linearGradient%3E%3Crect width='600' height='400' fill='url(%23g)'/%3E%3C/svg%3E" alt="Dégradé fuchsia vers rose" width="600" height="400"></button>
<button type="button" class="blb-thumb" aria-haspopup="dialog"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='400' viewBox='0 0 600 400'%3E%3ClinearGradient id='g' x2='1' y2='1'%3E%3Cstop stop-color='%2306b6d4'/%3E%3Cstop offset='1' stop-color='%2310b981'/%3E%3C/linearGradient%3E%3Crect width='600' height='400' fill='url(%23g)'/%3E%3C/svg%3E" alt="Dégradé cyan vers émeraude" width="600" height="400"></button>
<button type="button" class="blb-thumb" aria-haspopup="dialog"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='600' height='400' viewBox='0 0 600 400'%3E%3ClinearGradient id='g' x2='1' y2='1'%3E%3Cstop stop-color='%23f59e0b'/%3E%3Cstop offset='1' stop-color='%23ef4444'/%3E%3C/linearGradient%3E%3Crect width='600' height='400' fill='url(%23g)'/%3E%3C/svg%3E" alt="Dégradé ambre vers rouge" width="600" height="400"></button>
</div>
<dialog class="blb-viewer" aria-label="Visionneuse d'images">
<div class="blb-content">
<button type="button" class="blb-close" aria-label="Fermer"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M18 6L6 18M6 6l12 12"/></svg></button>
<img class="blb-image" alt="">
</div>
</dialog>
</div>
/* fx-0171 — scoped to .fx-0171-root and the blb- prefix: two copies on one page stay independent */
.fx-0171-root,
.fx-0171-root *,
.fx-0171-root *::before,
.fx-0171-root *::after {
box-sizing: border-box;
}
.fx-0171-root {
--blb-thumb: 120px; /* thumbnail width */
--blb-gap: 10px; /* space between thumbnails */
--blb-radius: 0.5rem; /* thumbnail corners */
--blb-radius-lg: 1rem; /* large image corners */
--blb-backdrop: rgba(0, 0, 0, 0.9); /* viewer background */
--blb-accent: rgba(99, 102, 241, 0.3);
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 300px;
padding: 16px;
position: relative;
background: #0a0a0f;
color: #fff;
font-family: sans-serif;
}
/* Thumbnails: real buttons wrapping real images */
.blb-grid {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: var(--blb-gap);
width: 100%;
}
.blb-thumb {
display: block;
width: var(--blb-thumb);
max-width: 100%;
aspect-ratio: 3 / 2;
padding: 0;
border: 0;
border-radius: var(--blb-radius);
overflow: hidden;
background: #1a1a24;
cursor: pointer;
transition: transform 0.3s, box-shadow 0.3s;
}
.blb-thumb img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.blb-thumb:hover,
.blb-thumb:focus-visible {
transform: scale(1.05);
box-shadow: var(--blb-accent) 0 10px 30px;
}
.blb-thumb:focus-visible {
outline: 2px solid #fff;
outline-offset: 3px;
}
/* Viewer: native modal <dialog> (top layer, focus kept inside, Escape) */
.blb-viewer {
width: 100%;
height: 100%;
max-width: none;
max-height: none;
margin: 0;
padding: 0;
border: 0;
overflow: hidden;
background: var(--blb-backdrop);
color: #fff;
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
opacity: 0;
transition: opacity 0.4s ease;
}
.blb-viewer[open] {
display: flex;
align-items: center;
justify-content: center;
}
.blb-viewer.is-open {
opacity: 1;
}
.blb-viewer::backdrop {
background: transparent;
}
.blb-viewer button:focus-visible {
outline: 2px solid #fff;
outline-offset: 3px;
}
.blb-content {
position: relative;
max-width: 80vw;
transform: scale(0.8);
transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.blb-viewer.is-open .blb-content {
transform: scale(1);
}
.blb-image {
display: block;
width: auto;
height: auto;
max-width: 80vw;
max-height: 70vh;
border-radius: var(--blb-radius-lg);
touch-action: pan-y pinch-zoom;
-webkit-user-select: none;
user-select: none;
}
/* Close button */
.blb-close {
position: absolute;
top: -44px;
right: 0;
width: 36px;
height: 36px;
background: rgba(255, 255, 255, 0.1);
border: none;
border-radius: 50%;
color: white;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.3s ease, transform 0.3s ease;
}
.blb-close:hover {
background: rgba(255, 255, 255, 0.2);
transform: rotate(90deg);
}
/* Reduced motion: the viewer opens and closes at once, nothing scales */
@media (prefers-reduced-motion: reduce) {
.blb-thumb,
.blb-viewer,
.blb-content,
.blb-close,
.blb-nav {
transition: none;
}
.blb-content,
.blb-thumb:hover,
.blb-thumb:focus-visible {
transform: none;
}
}
(function () {
'use strict';
// Basic Lightbox — one independent instance per .fx-0171-root (pasting the script twice is harmless)
var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
var CLOSE_MS = 400; // matches the CSS closing transition
function setup(root) {
if (root.hasAttribute('data-blb-ready')) return;
var thumbs = Array.prototype.slice.call(root.querySelectorAll('.blb-thumb'));
var viewer = root.querySelector('.blb-viewer');
if (!thumbs.length || !viewer || typeof viewer.showModal !== 'function') return;
root.setAttribute('data-blb-ready', '');
var image = viewer.querySelector('.blb-image');
var index = 0, opener = null, closeTimer = 0, downOnBackdrop = false;
image.draggable = false;
// Large version: data-full on the button, otherwise the thumbnail image itself
function sourceOf(thumb) {
var small = thumb.querySelector('img');
return {
src: thumb.getAttribute('data-full') || (small ? small.currentSrc || small.src : ''),
alt: small ? small.alt : ''
};
}
function show(i) {
index = i;
var s = sourceOf(thumbs[i]);
image.src = s.src;
image.alt = s.alt;
}
function open(i) {
clearTimeout(closeTimer);
opener = thumbs[i];
if (!viewer.open) viewer.showModal();
show(i);
void viewer.offsetWidth; // start the opening transition from the closed state
viewer.classList.add('is-open');
}
function close() {
if (!viewer.open) return;
viewer.classList.remove('is-open');
clearTimeout(closeTimer);
if (reduceMotion.matches) viewer.close();
else closeTimer = setTimeout(function () { viewer.close(); }, CLOSE_MS);
}
thumbs.forEach(function (thumb, i) {
thumb.addEventListener('click', function () { open(i); });
});
viewer.querySelector('.blb-close').addEventListener('click', close);
// Click on the dark background (not on the image or a button) closes
viewer.addEventListener('pointerdown', function (e) { downOnBackdrop = e.target === viewer; });
viewer.addEventListener('click', function (e) {
if (e.target === viewer && downOnBackdrop) close();
});
viewer.addEventListener('cancel', function (e) { e.preventDefault(); close(); });
// Keyboard: Escape closes
viewer.addEventListener('keydown', function (e) {
if (e.key === 'Escape') { e.preventDefault(); close(); }
});
// Closed: focus goes back to the thumbnail that opened the viewer
viewer.addEventListener('close', function () {
clearTimeout(closeTimer);
viewer.classList.remove('is-open');
if (opener) opener.focus();
});
}
function start() {
document.querySelectorAll('.fx-0171-root').forEach(setup);
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start);
else start();
})();
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Taille des vignettes (CSS — --blb-thumb) |
120px | Largeur de chaque vignette (format 3:2). La rangée se replie toute seule sur plusieurs lignes. |
Espace entre vignettes (CSS — --blb-gap) |
10px | Écart horizontal et vertical de la grille. |
Fond de la visionneuse (CSS — --blb-backdrop) |
rgba(0, 0, 0, 0.9) |
Couleur et opacité du voile. Le bouton ✕ est blanc : gardez un fond sombre. |
Arrondis (CSS — --blb-radius / --blb-radius-lg) |
0.5rem / 1rem | Coins des vignettes, puis de la grande image. |
Ombre au survol (CSS — --blb-accent) |
rgba(99, 102, 241, 0.3) |
Couleur de l'ombre quand la vignette est survolée ou a le focus clavier. |
Grande version (HTML — data-full sur le bouton) |
absent (l'image de la vignette est réutilisée) | Adresse d'une image plus grande, chargée seulement à l'ouverture. Utile quand la vignette est une miniature légère. |
Nom de la visionneuse (HTML — aria-label du <dialog>) |
« Visionneuse d'images » | Ce que les lecteurs d'écran annoncent à l'ouverture ; à adapter au contenu (« Plan d'accès »). |
Remplacez le src et l'alt des <img> dans les boutons .blb-thumb, ajoutez ou retirez des boutons : le script les trouve tous. Pour une miniature légère et une grande image séparée, mettez l'adresse de la grande dans data-full.
Ça ne devrait pas arriver : un <dialog> ouvert avec showModal() est placé dans la couche supérieure du navigateur, au-dessus de tous les z-index. Si vous voyez ce problème, vérifiez que le script a bien tourné (sans lui, rien ne s'ouvre).
Pas avec cette version : elle montre une image à la fois et se referme. Pour des flèches, un compteur et le balayage au doigt, prenez la visionneuse avec navigation de la même catégorie.