ImageGalleryLightbox
A contact sheet that opens into a lightbox you can arrow, swipe or play through.
Options
-
labelPlates -
Accessible name for the sheet and for the overlay. Keep it a noun. It is read out as the name of the whole gallery.
-
closeLabelClose -
Text on the close control. There is a visible one as well as Escape, because Escape is not discoverable and on a touch device it does not exist.
-
columns3 -
1 to 6
Columns at the widest the container gets. The sheet steps down on its own below that, because it fits its container rather than the viewport.
-
autoplay0 -
0 to 12
Seconds between plates once the overlay is open. Zero is off, and off is the default: a gallery that starts moving on its own is rarely what the reader wanted. Any value above zero renders a pause control, and reduced motion turns it off entirely.
-
playLabelPlay -
Text on the autoplay control when it is stopped.
-
pauseLabelPause -
Text on the autoplay control when it is running.
What it needs
- Category
- Surfaces, a component
- Dependencies
- react@>=18
- Browser
- No WebGL
- Frame budget
- ~0.2 ms · 0 context
- Pinned to
- v0.13.0
- Uses <dialog>.showModal(), so the focus trap, Escape, top layer, inerting and focus restoration are the browser's
- The fade uses transition-behavior: allow-discrete and @starting-style; where those are unsupported the overlay appears instantly, which is a graceful loss
- No carousel library, no focus-trap library, no portal, no scroll-lock package
- Swipe is Pointer Events, and is ignored for mouse input, where a small drag is the start of a click rather than a gesture
- The space around the plate closes the overlay, as an addition to the close button rather than a replacement: Nielsen Norman find that many readers never discover a click outside and others use it by accident, and a lightbox is the right place for it because a mistake costs one click to reopen
- The usual event.target === dialog test does nothing for a full-bleed overlay, because there is no backdrop to click and every pixel is inside the dialog. What the press was not on is the question that survives, so it is a closest() test against the plate and the controls
- Press and release, both ends checked, so a drag off the plate or a caption being selected does not throw the overlay away. The listener is on the dialog rather than the stage, so a gesture that starts beside the plate is heard wherever it ends
- closedby=any is deliberately not used. Safari has it in preview only, and native light dismiss fires when press and release both land on the dialog, which is what a horizontal swipe beside the plate does: on a phone the gallery would close instead of moving on
- No JavaScript runs per frame. The enter is one CSS animation on the image
- Thumbnails below the first row are lazy, and the two neighbouring full-size files are decoded while you are looking at the current one
- Intrinsic width and height are required on every plate so the sheet reserves its shape and does not reflow as the files arrive