Photo galleries with zero JavaScript
A gallery component with a full lightbox — expand, close, prev/next — shipping not a single byte of JS.
This site now has a <Gallery> component, and it cheats in the best way:
the lightbox — click to expand, click outside or press Esc to
close, arrows to move between photos — is handled almost entirely by the
browser’s native Popover API.
Total JavaScript shipped: one ~200-byte snippet.
One photo
A single photo renders full width and keeps its natural aspect ratio. Click it:

Several photos
Multiple photos snap into a grid — click any of them, then use the arrows (or Tab + Enter) to flip through:






How it works
Each thumbnail is a <button popovertarget="photo-3">, and each full-size
photo is a <div popover>. The browser does the rest: open on click, light
dismiss, focus handling. The prev/next arrows are just buttons targeting the
neighboring photo’s popover. The fade-in is CSS @starting-style, and the
images themselves are resized and converted to modern formats at build time
by Astro.
The one place the browser needs help: when you open photo 2 from inside
photo 1, the Popover API considers photo 1 an ancestor and stacks the two
instead of swapping them. A tiny script listens for beforetoggle and
closes the previous photo, adds ←/→ navigation, and
turns horizontal swipes into prev/next on touch screens — that’s the entire
JavaScript budget of this component.
import Gallery from '../../components/Gallery.astro';
import dusk from '../../assets/photos/dusk.jpg';
<Gallery photos={[{ src: dusk, alt: 'Warm gradient at dusk', caption: 'Dusk.' }]} />