Full-screen media viewer with zoom, pan, and keyboard navigation
Tags: component overlay media
Lightbox presents media full-screen over a dimmed backdrop, paging through a list with the keyboard, arrows, or a swipe. Images can be zoomed and panned; video and audio play with the standard themed controls.
It is built directly on the dialog primitive, so focus trapping, Escape to close, scroll locking, and aria-modal all behave the same way as Dialog.
Import
<script>
import { Lightbox } from '@classic-homes/theme-svelte';
</script>Basic Usage
<script>
let open = $state(false);
let index = $state(0);
const photos = [
{ id: '1', kind: 'image', src: '/one.jpg', alt: 'Front elevation', caption: 'Front elevation' },
{ id: '2', kind: 'image', src: '/two.jpg', alt: 'Kitchen' },
];
</script>
<Button onclick={() => (open = true)}>View photos</Button>
<Lightbox items={photos} bind:open bind:index />Props
| Prop | Type | Default | Description |
|---|---|---|---|
items |
MediaItem[] |
required | Items to page through |
open |
boolean |
false |
Whether the viewer is open (bindable) |
index |
number |
0 |
Index of the visible item (bindable) |
loop |
boolean |
true |
Wrap from the last item back to the first |
showThumbnails |
boolean |
false |
Show the thumbnail strip along the bottom |
showCaption |
boolean |
true |
Show the caption beneath the media |
showCounter |
boolean |
true |
Show the "n of m" counter |
zoomable |
boolean |
true |
Allow zooming and panning images |
downloadable |
boolean |
false |
Show a download link for items that expose a URL |
onOpenChange |
(open: boolean) => void |
- | Called when the open state changes |
onIndexChange |
(index: number) => void |
- | Called when the visible item changes |
class |
string |
- | Additional CSS classes for the surface |
trigger |
Snippet<[{ props }]> |
- | Optional trigger; spread props on your own button. Omit when controlling open yourself |
Snippets
| Snippet | Description |
|---|---|
trigger |
Element that opens the viewer; receives { props } to spread on your button. Omit if controlling open |
Keyboard Shortcuts
| Key | Action |
|---|---|
Escape |
Close |
← / → |
Previous/next item, or pan when zoomed |
↑ / ↓ |
Pan when zoomed |
Home / End |
First/last item |
+ / - |
Zoom in/out |
0 |
Reset zoom |
Zoom and Pan
Images support wheel zoom, pinch zoom, double-click or double-tap to toggle, and drag to pan. Zoom resets whenever the item changes, so a magnified view never carries onto the next photo. Swipe navigation is only active while the image is at its natural size, so panning and paging never conflict.
With a Trigger
Use the trigger snippet for an inline trigger button. It receives { props }: spread them on your own button so it becomes the trigger, carries aria-expanded, and gets focus back when the viewer closes. A snippet that ignores them still works, wrapped in a span as before.
<Lightbox items={photos}>
{#snippet trigger({ props })}
<Button {...props} variant="outline">Open gallery</Button>
{/snippet}
</Lightbox>Mixed Media
One list can hold images, video, audio, and provider embeds together:
<script>
const items = [
{ id: '1', kind: 'image', src: '/photo.jpg', alt: 'Exterior' },
{ id: '2', kind: 'video', src: '/tour.mp4', poster: '/tour.jpg', title: 'Walkthrough' },
{ id: '3', kind: 'embed', provider: 'youtube', videoId: 'dQw4w9WgXcQ', title: 'Overview' },
];
</script>
<Lightbox {items} bind:open showThumbnails />Moving to another item unmounts the outgoing player, so a video or track never keeps playing out of sight.
Accessibility
- A modal dialog: focus is trapped inside,
Escapecloses, and the page behind is locked from scrolling. - When opened from the
trigger, focus returns to that button on close. - Named from the active item's title or alt text, and described by its caption.
- Navigation, zoom, download, and close controls are all labelled and sized to a comfortable touch target.
- The neighbouring images are preloaded, so paging does not flash an empty frame.
- Transitions are skipped entirely under
prefers-reduced-motion.
Related Components
- MediaGrid - Opens the Lightbox on selection
- MediaCarousel - Inline browsing without an overlay
- Dialog - The general-purpose modal
- Image - Renders image items