Lightbox with Caption
Arrow viewer with a caption under the photo — title and detail read from your HTML — that rises and fades in on opening.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Gallery has 44 effects, including 6 free. Explore the category →
3 usage examples



How it works
The caption comes from two attributes on each .clb-thumb button: data-title (the bold title) and data-desc (the grey line: place, date or detail). Without data-title, the photo's alt becomes the title; without data-desc, the second line is hidden. Changing a text in the HTML is enough; the script holds no captions.
The viewer is a <figure> inside a modal <dialog>: the photo, then a <figcaption>. On opening, the caption starts 20 px lower and transparent, then rises over 0.4 s after a 0.2 s delay, just after the image arrives. From then on, ‹ › (looping), ← → and a horizontal drag of more than 50 px on the photo change the image and its caption together.
To keep the caption visible, the photo is limited to the screen height minus 130 px (and to 80 % of its width). Close with ✕, Escape or a click on the background.
Accessibility
- Caption tied to the image:
<figure>+<figcaption>, and the caption is anaria-live="polite"region: each new caption is read when moving to the next photo. - Named buttons: thumbnails, ‹ “Previous image”, › “Next image”, ✕ “Close”.
- Modal dialog: named “Image viewer”, focus on ✕ on opening, page behind inert, focus returned to the thumbnail.
- Reduced motion: the caption appears directly, with no rise or fade, like the image.
Browser compatibility
Same base as the other viewers: modal <dialog> (Chrome 37, Firefox 98, Safari 15.4) and aspect-ratio (Chrome 88). Checked in Chromium 151, Firefox 153 and WebKit 26.5: caption changed in the HTML then read on opening, only one copy opened when two galleries share the page, script pasted twice with no skipped caption.
Without showModal(), nothing opens; your texts stay in the attributes, unseen. To have them read even without the script, repeat them in the alt.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0177-root">
<div class="clb-grid">
<button type="button" class="clb-thumb" aria-haspopup="dialog" data-title="Northern lights" data-desc="Norway, 2024"><img src="photo.jpg" alt="Indigo to violet gradient" width="600" height="400"></button>
<button type="button" class="clb-thumb" aria-haspopup="dialog" data-title="Sunset" data-desc="Cyclades, Greece"><img src="photo.jpg" alt="Fuchsia to rose gradient" width="600" height="400"></button>
<button type="button" class="clb-thumb" aria-haspopup="dialog" data-title="Mystic forest" data-desc="Amazon, Brazil"><img src="photo.jpg" alt="Violet to cyan gradient" width="600" height="400"></button>
<button type="button" class="clb-thumb" aria-haspopup="dialog" data-title="Pacific Ocean" data-desc="Hawaii, USA"><img src="photo.jpg" alt="Cyan to emerald gradient" width="600" height="400"></button>
</div>
<dialog class="clb-viewer" aria-label="Image viewer">
<figure class="clb-content">
<button type="button" class="clb-close" aria-label="Close"><svg>…</svg></button>
<button type="button" class="clb-nav clb-prev" aria-label="Previous image"><svg>…</svg></button>
<img class="clb-image" alt="">
<button type="button" class="clb-nav clb-next" aria-label="Next image"><svg>…</svg></button>
<figcaption class="clb-caption" aria-live="polite">
<strong class="clb-title"></strong>
<span class="clb-desc"></span>
</figcaption>
</figure>
</dialog>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Caption title (HTML — data-title) |
the image alt |
Bold text under the photo. |
Second line (HTML — data-desc) |
empty (line hidden) | Place, date, author… in grey under the title. |
Height left for the caption (CSS — max-height of .clb-image) |
calc(80vh - 130px) |
Increase the 130 px for captions on several lines. |
Caption rise (CSS — .clb-caption) |
20 px, 0.4 s after 0.2 s | Starting transform and transition; set them to zero for a static caption. |
Second line colour (CSS — .clb-desc) |
#8b8b93 |
A grey that stays readable on the black background. |
Thumbnails and overlay (CSS — --clb-thumb, --clb-backdrop) |
120px, rgba(0, 0, 0, 0.9) |
Width of the page buttons and viewer background. |
Large photo (HTML — data-full) |
absent | URL loaded into the viewer instead of the thumbnail. |
FAQ
<button class="clb-thumb" data-title="Corlanne viaduct" data-desc="May 2024">. The script reads them when the photo is shown; there is nothing to change in the JavaScript.textContent), which also stops code from slipping in by mistake. For a rich caption you would replace textContent with your own template.