Skip to main content

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

PropTypeRequiredDefaultDescription
imagesstring[]Yes--Array of image URLs to display
currentIndexnumberYes--Index of the currently displayed image
isOpenbooleanYes--Whether the lightbox is open
onClosefunctionYes--Callback to close the lightbox
onPrevfunctionYes--Callback to go to the previous image
onNextfunctionYes--Callback to go to the next image

Key Behaviors

  • Portal Rendering: Renders on document.body via createPortal to 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))}
/>
  • 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