Skip to main content

MediaGallery

Implemented

Responsive image+video gallery grid (2/3/4 columns). Tiles become full-size buttons when onSelect is provided; video tiles show a centered play indicator. Empty state renders a role='status' message.

Categorymolecules
Sourcepatterns/molecules/mediagallery.html
AI HintEvent media galleries (photos + video posters). Pass onSelect for lightbox flows; omit it for static embeds. Localize galleryLabel/openLabel from the mediaGallery.* i18n keys.

Responsive image/video gallery grid (2/3/4 columns) with an optional selection mode and an announced empty state.

MediaGallery

molecules

Uniform aspect-square tile grid. Image tiles render as-is; video tiles show a centered play affordance over a scrim. With onSelect, each tile becomes a real full-size button named "${openLabel} ${alt}" — the molecule is a grid + select-handler only, preview/lightbox UX belongs to the consumer.

Click a tile — onSelect hands the item to your preview/lightbox UX (the molecule ships no lightbox).

MediaGallery (empty state)

molecules

Zero items render a dashed placeholder with role="status" aria-live="polite", so assistive tech announces it when media arrives.

Usage

import { MediaGallery } from '@epasslive/design-system'

const media = [
  { id: 'stage', src: '/stage.jpg', alt: 'Main stage at dusk' },
  { id: 'trailer', src: '/trailer.mp4', alt: 'Event trailer', type: 'video' },
]

// Select mode — tiles are buttons; open your own preview/lightbox
<MediaGallery items={media} columns={3} onSelect={openPreview} />

// Display-only grid — tiles are inert cells
<MediaGallery items={media} columns={4} />

// Localized labels (FR)
<MediaGallery
  items={media}
  emptyLabel="Aucun média"
  galleryLabel="Galerie médias"
  openLabel="Ouvrir"
  onSelect={openPreview}
/>

When to use

  • Event photo/video grids on public event pages (uniform media wall).
  • Producer media manager — select a tile to preview, reorder, or delete.
  • Anywhere a uniform grid of media thumbnails is needed — not for mixed content cards (use a card molecule instead).

Accessibility

The grid is a labelled list (role="list" with aria-label) of role="listitem" tiles. Selectable tiles are real <button type="button"> elements covering the full tile (WCAG 2.5.5 target size), named `${openLabel} ${item.alt}`, with a visible focus-visible ring. The empty state is announced via role="status" aria-live="polite". The video play affordance is decorative and aria-hidden — the button's accessible name carries the meaning. No built-in lightbox: keyboard/focus-trap UX for previews belongs to the consuming overlay component.

Props

Prop
Type
Default
Description
items*
MediaGalleryItem[]
—

Gallery items — { id, src, alt, type?: "image" | "video", thumbnail? }. alt is required by contract (pass "" consciously for decorative media).

onSelect
(item: MediaGalleryItem) => void
—

When provided, tiles render as real <button type="button"> elements; omit for inert display cells.

columns
2 | 3 | 4
3

Responsive column count. 3 → grid-cols-2 sm:grid-cols-3; 4 adds lg:grid-cols-4; 2 stays two-up.

emptyLabel
string
"No media yet"

Text shown in the empty state. Injectable for localization.

galleryLabel
string
"Media gallery"

Accessible name (aria-label) for the list. Injectable for localization.

openLabel
string
"Open"

Verb prefix for selectable tiles, rendered as `${openLabel} ${item.alt}`. Injectable for localization (e.g. "Ouvrir").

className
string
—

Additional CSS classes merged onto the root (grid or empty state).

id
string
—

Stable root id; defaults to media-gallery-<useId()>.

Props

Preview

MediaGallery

Prop
Type
Required
Default
Description
items
MediaGalleryItem[]
Required
—

Gallery items — { type: 'image' | 'video', src, alt, poster? } (MediaGalleryItem).

onSelect
(item: MediaGalleryItem) => void
Optional
—

Tiles render as full-size buttons calling this; omit for a static (non-interactive) gallery.

columns
2 | 3 | 4
Optional
3

Grid column count.

emptyLabel
string
Optional
'No media yet'

Empty-state message (DS i18n keys exist: mediaGallery.empty).

galleryLabel
string
Optional
'Media gallery'

Accessible name for the gallery list — override for localization (e.g. 'Galerie médias').

openLabel
string
Optional
'Open'

Verb prefix for selectable tiles — the button name renders as `{openLabel} {item.alt}`.

className
string
Optional
—

Additional CSS classes.

id
string
Optional
auto (useId)

Stable root id.

Quick use:<MediaGallery />

Accessibility

ARIA rolelist

ARIA

2 rules

WCAG 4.1.2

list is role='list' with aria-label (galleryLabel); tiles are role='listitem'; selectable tiles are full-size buttons named `{openLabel} {alt}`

WCAG 4.1.3

empty state is a role='status' live region announcing emptyLabel

Usage

Import

tsxSnippet
import { MediaGallery } from "@epasslive/design-system"

Example 1

tsxSnippet
<MediaGallery items={media} onSelect={openLightbox} columns={3} />

Example 2

tsxSnippet
<MediaGallery items={[]} emptyLabel="Aucun média" galleryLabel="Galerie médias" />

Anti-patterns

— Do not wrap tiles in raw <img onClick> — keyboard users cannot reach them; onSelect makes them real buttons

— Do not hand-roll the empty state — emptyLabel renders the status region

Design System v4.1.0 | ePassLive