v2.6.0

<bmx-gallery>

A picture gallery with a viewer: thumbnails in a grid, in masonry columns, or in rows of equal height that fill the width (as photo libraries show them), and a full-screen viewer that zooms, pans, pinches, swipes and plays a slideshow. Videos play in the viewer, and a before/after pair is shown with a slider to compare the two.

20 properties · 7 events · 9 methods · 4 parts

Example

Open a picture to zoom (wheel, pinch, double click or + and -), swipe or use the arrows, play the slideshow or show its details. The glacier lake is a before/after pair; drag its handle.

Show markup
<bmx-gallery id="ex-gal" toolbar selectable download group-by="month" sort="date-desc" item-size="200" style="--bmx-gallery-height: 34rem"></bmx-gallery>
<p class="note" id="ex-gal-out" role="status" style="margin: 12px 0 0">Open a picture to zoom (wheel, pinch, double click or + and -), swipe or use the arrows, play the slideshow or show its details. The glacier lake is a before/after pair; drag its handle.</p>

<script type="module">
  await customElements.whenDefined('bmx-gallery');
  const gallery = document.getElementById('ex-gal');
  const out = document.getElementById('ex-gal-out');
  const base = 'assets/gallery/';
  // A fictional photo club's year: each picture with its size (so the layout is right before anything loads), a date, tags and a photographer.
  const photos = [
    ['alpine-sunset', 1500, 1000, 'Alpine sunset', '2026-09-14', 'Mara Lind', 'mountains, sunset', 'The last light over the western ridges, from the hut terrace.'],
    ['harbour-dawn', 1500, 1000, 'Harbour at dawn', '2026-09-02', 'Tom Okafor', 'sea, lighthouse', 'The lighthouse on the north arm, a few minutes before sunrise.'],
    ['dunes-noon', 1500, 940, 'Dunes at noon', '2026-08-21', 'Priya Shah', 'desert'],
    ['milky-way', 1000, 1400, 'The Milky Way', '2026-08-12', 'Mara Lind', 'night, stars', 'A 20-second exposure on the night of the Perseids.'],
    ['aurora-fjord', 1500, 1000, 'Aurora over the fjord', '2026-03-03', 'Erik Dahl', 'night, aurora'],
    ['city-dusk', 1500, 1000, 'City at dusk', '2026-07-30', 'Sam Rivera', 'city'],
    ['lantern-bokeh', 1000, 1000, 'Lanterns', '2026-07-18', 'Amira Haddad', 'abstract, night'],
    ['rolling-hills', 1500, 1000, 'Rolling hills', '2026-06-29', 'Priya Shah', 'hills, summer'],
    ['violet-ridges', 1400, 1050, 'Violet ridges', '2026-09-20', 'Erik Dahl', 'mountains'],
    ['silver-coast', 1500, 860, 'Silver coast', '2026-02-14', 'Tom Okafor', 'sea, winter'],
    ['autumn-valley', 1050, 1400, 'Autumn valley', '2026-10-04', 'Amira Haddad', 'hills, autumn'],
    ['evening-peaks', 1500, 640, 'Evening peaks, panorama', '2026-09-14', 'Mara Lind', 'mountains, panorama'],
    ['mirror-lake', 1000, 1400, 'Mirror lake', '2026-07-05', 'Sam Rivera', 'lake, reflection'],
    ['red-dunes', 1400, 1050, 'Red dunes', '2026-08-22', 'Priya Shah', 'desert'],
    ['city-night', 1000, 1400, 'City at night', '2026-07-31', 'Sam Rivera', 'city, night'],
    ['polar-night', 1000, 1250, 'Polar night', '2026-03-04', 'Erik Dahl', 'night, aurora'],
    ['garden-bokeh', 1500, 1000, 'Garden lights', '2026-06-12', 'Amira Haddad', 'abstract'],
    ['green-downs', 1500, 760, 'The downs', '2026-06-30', 'Priya Shah', 'hills, summer'],
    ['storm-ridges', 1500, 1000, 'Before the storm', '2026-10-09', 'Erik Dahl', 'mountains, weather'],
    ['desert-stars', 1500, 1000, 'Desert stars', '2026-08-23', 'Mara Lind', 'desert, night'],
    ['bay-sunset', 1500, 1000, 'Sunset over the bay', '2026-09-03', 'Tom Okafor', 'sea, sunset'],
    ['highland-mist', 1500, 1000, 'Highland mist', '2026-10-11', 'Erik Dahl', 'mountains, weather'],
    ['terracotta-hills', 1400, 1050, 'Terracotta hills', '2026-10-05', 'Amira Haddad', 'hills, autumn'],
  ];
  gallery.items = [
    ...photos.map(([name, width, height, title, date, author, tags, caption]) => ({
      id: name,
      src: `${base}${name}.jpg`,
      thumb: `${base}${name}-thumb.jpg`,
      width,
      height,
      title,
      alt: title,
      caption,
      date,
      author,
      tags: tags.split(', '),
    })),
    // A before/after pair: the same lake before and after its colour grade.
    {
      id: 'glacier-lake',
      type: 'compare',
      src: `${base}glacier-lake.jpg`,
      before: `${base}glacier-lake-before.jpg`,
      thumb: `${base}glacier-lake-thumb.jpg`,
      width: 1500,
      height: 1000,
      title: 'Glacier lake, graded',
      alt: 'A glacier lake under blue sky, mountains reflected in still water',
      caption: 'Drag the handle to compare the raw file with the graded one.',
      date: '2026-09-18',
      author: 'Mara Lind',
      tags: ['lake', 'before and after'],
      meta: { Camera: 'Field 24', Lens: '35 mm', Exposure: '1/250 s, f/8' },
    },
    // A video plays in the viewer.
    {
      id: 'induction',
      type: 'video',
      src: 'assets/media/induction.mp4',
      poster: 'assets/media/induction-poster.jpg',
      width: 1280,
      height: 720,
      title: 'Club safety talk',
      alt: 'Video: the club safety talk',
      date: '2026-09-01',
      author: 'Photo club',
      tags: ['video'],
    },
  ];
  gallery.actions = [{ id: 'favourite', label: 'Add to favourites', icon: '♡' }];
  gallery.addEventListener('bmxSelectionChange', e => (out.textContent = `bmxSelectionChange: ${e.detail.selection.length} selected.`));
  gallery.addEventListener('bmxView', e => (out.textContent = `bmxView: ${e.detail.item.title} (${e.detail.index + 1}).`));
  gallery.addEventListener('bmxAction', e => (out.textContent = `bmxAction: ${e.detail.action} on ${e.detail.item.title}.`));
  gallery.addEventListener('bmxChange', e => (out.textContent = `bmxChange: ${e.detail.reason} - layout ${e.detail.layout}, size ${e.detail.itemSize}px, search “${e.detail.query}”.`));
</script>
<bmx-gallery id="photos" layout="justified" toolbar selectable></bmx-gallery>
<script>
  photos.items = [{ src: 'harbour.jpg', thumb: 'harbour-small.jpg', width: 1600, height: 1000, alt: 'The harbour at dawn' }];
</script>

Only the thumbnails in view are in the page, worked out from each picture's size before any of them has loaded, so ten thousand pictures scroll as smoothly as twenty. Pictures without a size are laid out as squares and settle into their shape as they load. Grouping by day, month, year or the page's own groups puts headings in, one kept at the top while its pictures scroll by. With has-more, bmxLoadMore asks the page for the next page of items as the reader nears the end.

Accessibility: the thumbnails are a list (a listbox when selectable) with each picture's position in the whole set; arrow keys move to the picture really above, below or beside, Page Up and Down a screen at a time. The viewer is a modal dialog: Escape closes it and focus returns to the thumbnail; every control is a labelled button, the before/after handle is a slider, and the picture shown is announced.

Properties

PropertyAttributeTypeDefaultDescription
actions actions readonly BmxGalleryAction[] | string [] Buttons added to the viewer's toolbar; a click fires bmxAction.
captions captions 'hover' | 'below' | 'none' 'hover' Where thumbnails show their titles: over the picture on hover and focus, under it always, or not at all.
cellAspect cell-aspect number 1 A grid cell's shape, width over height.
download download boolean false The viewer offers a download of each picture.
fit fit 'cover' | 'contain' 'cover' How a picture sits in its grid cell: filling it (cropped) or wholly inside it.
gap gap number 8 The space between thumbnails, in pixels.
groupBy group-by BmxGalleryGroupBy 'none' Headings by the items' group, or by the day, month or year of their date.
hasMore has-more boolean false More items can be loaded: bmxLoadMore fires as the reader nears the end.
itemSize item-size number 220 The size aimed for, in pixels: the row height for justified, the column width for grid and masonry. Mutable.
items items readonly (Partial<BmxGalleryItem> | string)[] | string [] The pictures: objects (src, thumb, width, height, alt, title, caption, date, tags, type, before, poster...) or plain addresses, or JSON of either.
label label string — The gallery's accessible name. Default: "Gallery".
layout layout BmxGalleryLayout 'justified' How the thumbnails are arranged. Mutable: the toolbar changes it.
loop loop boolean true The viewer goes from the last picture round to the first.
query query string '' Shows only items matching these words (#tag for a tag). Mutable: the search box changes it.
selectable selectable boolean false Pictures can be selected: click with Ctrl or Shift, the toolbar's Select, or Space.
selection property only string[] [] The ids selected. Mutable.
slideshowInterval slideshow-interval number 5 Seconds between pictures in the slideshow.
sort sort BmxGallerySort 'none' The order shown. Mutable.
strings strings Partial<BmxGalleryStrings> | string — Words to show instead of the English ones, as an object or JSON.
toolbar toolbar boolean false Show the toolbar: search, layout, thumbnail size, sort and (with selectable) selection.

Events

EventDetailDescription
bmxAction BmxGalleryActionDetail Fired when one of actions is used in the viewer.
bmxChange BmxGalleryChangeDetail Fired when the toolbar changes the layout, the size, the search or the order.
bmxClose BmxGalleryItemDetail Fired when the viewer closes.
bmxLoadMore void Fired as the reader nears the end while has-more is set: append the next items to items.
bmxOpen BmxGalleryOpenDetail Fired before an item opens in the viewer. Cancel to show it some other way.
bmxSelectionChange BmxGallerySelectionDetail Fired when the selection changes.
bmxView BmxGalleryItemDetail Fired when the viewer moves to another item.

Methods

MethodSignatureDescription
clearSelection clearSelection() => Promise<void> Clears the selection.
close close() => Promise<void> Closes the viewer.
getItems getItems() => Promise<BmxGalleryItem[]> The items in the order shown, after the search.
next next() => Promise<void> The viewer's next item.
open open(item: string | number) => Promise<void> Opens an item (by id, or its place in the order shown) in the viewer.
previous previous() => Promise<void> The viewer's previous item.
scrollToItem scrollToItem(id: string, focus?: boolean) => Promise<void> Scrolls an item into view (and gives it focus with focus).
selectAll selectAll() => Promise<void> Selects every item shown (after the search).
setFocus setFocus() => Promise<void> Puts keyboard focus on the current thumbnail.

Slots

SlotDescription
empty Shown when there are no items at all.

CSS shadow parts

PartDescription
heading A group's heading.
item A thumbnail; also item-selected.
toolbar The search, layout, size and sort controls.
viewer The viewer dialog.

CSS custom properties

PropertyDescription
--bmx-gallery-accent The selection ring and check, and the focus of a thumbnail.
--bmx-gallery-height The gallery's height (it scrolls inside it); auto lets it grow with its content.
--bmx-gallery-placeholder The colour a thumbnail shows while it loads (an item's color overrides it).
--bmx-gallery-radius Corner radius of a thumbnail.
--bmx-gallery-viewer-background The viewer's background.
--bmx-gallery-viewer-color The viewer's text and icons.
--bmx-gallery-viewer-panel The viewer's bars and its details panel.