Lightbox
Location: src/components/post-parts/Lightbox.jsx
Overview
Lightbox is a full-screen image viewer rendered via createPortal to document.body. It displays a single image at a time from an array, with navigation arrows for multi-image sets. It supports keyboard navigation, locks body scroll while open, and traps focus within its interactive elements for accessibility.
Relevant Invariants
- Invariant #2: "Calm Is the Default State" -- The lightbox is a deliberate user action, not an auto-triggered overlay.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
images | string[] | Yes | -- | Array of image URLs to display |
currentIndex | number | Yes | -- | Index of the currently displayed image |
isOpen | boolean | Yes | -- | Whether the lightbox is open |
onClose | function | Yes | -- | Callback to close the lightbox |
onPrev | function | Yes | -- | Callback to go to the previous image |
onNext | function | Yes | -- | Callback to go to the next image |
Key Behaviors
- Portal Rendering: Renders on
document.bodyviacreatePortalto ensure full-viewport coverage regardless of parent component structure. - Keyboard Navigation: Escape closes, ArrowLeft goes to previous image, ArrowRight goes to next image.
- Body Scroll Lock: Sets
document.body.style.overflow = 'hidden'while open, restoring it on close. - Focus Trap: Tab and Shift+Tab cycle through the lightbox's focusable buttons (close, prev, next) without escaping to the page behind.
- Initial Focus: The close button receives focus when the lightbox opens.
- Conditional Navigation: Previous/next buttons only appear when there are multiple images and the current index is not at the boundary.
- Image Counter: A "X / Y" counter appears when there are multiple images showing the current position.
- Backdrop Close: Clicking the dark backdrop (not the image or controls) closes the lightbox.
Usage
import Lightbox from '@/components/post-parts/Lightbox';
<Lightbox
images={imageUrls}
currentIndex={lightboxIndex}
isOpen={lightboxOpen}
onClose={() => setLightboxOpen(false)}
onPrev={() => setLightboxIndex(i => Math.max(0, i - 1))}
onNext={() => setLightboxIndex(i => Math.min(imageUrls.length - 1, i + 1))}
/>
Related
- PostImages -- Triggers the lightbox when an image is clicked
- Post -- Manages lightbox state for its images
- PostDetail -- Has its own inline lightbox implementation
Last updated: 2026-02-07