KKUONIDesign System

Components

Image Gallery

Beta

A grid of clickable thumbnail images that opens a full-screen lightbox. Used for hotel photo galleries, destination photography, and room detail views.

Overwater villa
Lagoon
Sunset
Restaurant
Aerial
Spa

Purpose

ImageGallery presents a collection of photos in a scannable grid and opens a full-screen lightbox when any thumbnail is clicked. It is used wherever a set of images need to be browsable beyond the primary hero photo:

  • Hotel detail pages — 6-12 photos of the property, rooms, pool, restaurant
  • Destination pages — editorial photography of landscapes and experiences
  • Room type selection — multiple angles of a specific room category
  • Activity pages — photos of what the experience looks like in practice

The lightbox supports keyboard navigation (arrow keys, Escape) and shows an image counter.

Usage

Reach for ImageGallery when you have a fixed set of standalone photos that benefit from a full-screen zoomed view — a hotel's rooms, a destination's landscapes. When images are meant to auto-advance or be browsed one-at-a-time inline within the page flow (e.g. a hero rotation), use Carousel instead; it does not open a lightbox.

ImageGallery typically sits within hotel and destination detail pages, below the hero image and description, as a dedicated "Photos" section.

Portal rendering note: The lightbox uses createPortal to render to document.body. For this reason, the <ImageGallery> component cannot be shown as a live interactive demo in this documentation — the previews above show static mockups. In your application it renders and works correctly.

Anatomy

Room
Pool
View
  1. 1
    Thumbnail grid4:3 aspect ratio thumbnails in a configurable columns grid. Hover reveals a zoom icon overlay.
  2. 2
    Zoom overlayDark overlay on hover with ZoomIn icon. Signals clickability.
  3. 3
    Lightbox backdropFull-screen black/90 overlay. Renders in a portal on document.body.
  4. 4
    Lightbox imageobject-fit: contain image centred in the viewport. Max 80vh tall.
  5. 5
    Image counter"1 / 6" in a dark pill top-left of the lightbox.
  6. 6
    Navigation buttonsCircular white/10 buttons on left and right for prev/next. Disabled at first/last image.
  7. 7
    Close buttonTop-right X button with white/10 background. Keyboard focus managed.
  8. 8
    CaptionOptional text below the lightbox image from image.caption.

Variants

2-column grid

Bedroom
Bathroom
Terrace
Living area

4-column grid

Pool
Restaurant
Beach bar
Spa
Water sports
Kids club
Library
Sunset

States

ElementStateAppearance
ThumbnailDefaultImage fills tile, rounded corners
ThumbnailHoverDark overlay + ZoomIn icon
ThumbnailFocusTeal focus ring (2px)
Lightbox prevDefaultWhite/10 circle button
Lightbox prevDisabled (first)30% opacity
Lightbox navFocusWhite focus ring

Best practices

Do

Write descriptive alt text for every image that communicates the visual content to screen reader users.

'Overwater villa with steps into the lagoon at sunrise' describes the scene, not just the subject.

Don't

Use filename or destination-name-only strings as alt text.

'Maldives photo 3' is the filename, not the content — it conveys nothing useful to a screen reader user.

Do

Curate the image set to 6-12 photos showing the hotel from different perspectives (bedroom, pool, restaurant, view).

6-12 photos is the ideal range for hotel galleries — enough variety, not overwhelming.

Don't

Load all available hotel photos (potentially 40+) into a single ImageGallery without grouping.

40+ thumbnails in a grid become a wall of imagery — consider pagination or category tabs instead.

Do

Order images with the most aspirational or representative photo first.

The leading image should be the most visually compelling — it is what users click first.

Don't

Start a hotel gallery with a floor plan or site map.

Leading with a functional room plan or technical diagram when emotional photography is available misses the primary conversion moment.

Props

PropTypeDefaultDescription
images*Array<{ src: string; alt: string; caption?: string }>Array of image objects. caption appears below the image in the lightbox.
columns2 | 3 | 43Number of columns in the thumbnail grid.
layout'grid''grid'Gallery layout type. Only grid is currently supported.
showThumbnailsbooleantrueReserved for future thumbnail strip variant inside the lightbox.
classNamestringAdditional Tailwind classes on the grid container.

Accessibility

Thumbnail buttons

Each thumbnail is a <button> with aria-label="View image N: [alt text]". The role="listitem" on each slide and role="list" on the grid provide list semantics.

The lightbox has:

<div
  role="dialog"
  aria-modal="true"
  aria-label={`Image ${index + 1} of ${total}: ${image.alt}`}
>

aria-modal="true" tells screen readers to ignore content outside the dialog.

Keyboard navigation in lightbox

KeyAction
EscapeClose lightbox
ArrowLeftPrevious image
ArrowRightNext image
TabNavigate: close button → prev → next

Focus management

When the lightbox opens, focus moves into the dialog. When closed, focus returns to the thumbnail that opened it (the trigger index is tracked in state).

Scroll lock

The lightbox does not currently lock body scroll. If your layout requires scroll lock when the lightbox is open, add document.body.style.overflow = 'hidden' via a useEffect in the consuming component.

  • Carousel — use for auto-advancing or swipeable inline image browsing rather than a clickable grid with a lightbox.