Media gallery
A grid of pictures that fits whatever box it is in, and a full-screen viewer with swipe, pinch to zoom and keyboard control. Three pieces you can use apart: the behavior with no markup, a grid, and a viewer. Choose a picture to open it.
Last change: None yet. In the viewer, the keyboard button (or ?) lists the keys. On a phone: swipe, pinch, double tap.
fandry-media-gallery {
--fd-media-columns: 4;
--fd-media-gap: 0.75rem;
--fd-media-aspect-ratio: 1 / 1;
--fd-media-fit: cover;
}
Install
Link to section InstallLWR / LWC OSS
npm install fandryui <!-- nothing to copy: your bundler ships only what you use --> <fandry-media-gallery></fandry-media-gallery>
Salesforce DX
npx fandry add media-gallery # adds the block and everything it is built from # to your package directory, as source you own
Usage
Link to section Usage<fandry-media-gallery
items={photos}
loading={loading}
onchange={handleChange}
></fandry-media-gallery>
An item is plain data:
photos = [
{
id: 'lake', // unique and stable
src: '/media/lake.jpg', // the full file, for the viewer
thumbnail: '/media/lake-400.jpg',
alt: 'A pine forest above a calm bay.',
caption: 'Above the bay',
width: 1280,
height: 960,
loading: 'eager' // only for pictures on screen at page load
},
...
];
Props and methods
Link to section Props and methods| Prop | Type | Default | Description |
|---|---|---|---|
items | item[] | [] | What the gallery shows (see the item table below). Controlled: the block never changes it. |
index | number | 0 | The current item. Set it to choose where the gallery starts; the block keeps it current as the user moves and reports each move through change. |
loading | boolean | false | While the items are being fetched: the grid shows placeholders and says it is busy. |
loading-count | number | 8 | How many placeholders. |
messages | object | {} | What the gallery says (the viewer's name, Close, Keyboard shortcuts, Previous, Next, "3 of 12", what a screen reader hears on moving, and the name of a tile whose picture has no alt text), e.g. to translate it: any key of DEFAULT_MEDIA_VIEWER_MESSAGES (fandry/mediaViewerState) or DEFAULT_MEDIA_GRID_MESSAGES (fandry/mediaGrid). |
keys (slot) | slot | an English list | The keyboard shortcuts the viewer's keyboard button (or ?) shows. To translate it, slot a fandry-media-keys here with its text slots filled (title, previous-next, first-last, zoom, reset-zoom, pan, page, toggle, close, and the keys named with words: key-home, key-end, key-page-up, key-page-down, key-escape), or a list of your own. |
show(index?) · close() | methods | — | Open the viewer (on index, or on the current item) and close it. |
next() · previous() · goTo(index) | methods | — | Move the current item, with or without the viewer open. |
Item
Link to section Item| Key | Type | Description |
|---|---|---|
id | string | Unique and stable: the key each piece renders by. |
src | string | The full-size file, shown in the viewer. |
alt | string | Describes the picture for people who can't see it. '' for a decorative one. |
thumbnail | string | A smaller file for the grid. Without one the grid uses src (with srcset / sizes). |
srcset · sizes | string | As on a native <img>, for the viewer. |
width · height | number | The intrinsic size, so the browser reserves the space before the file arrives. |
caption | string | Shown under the tile and under the picture in the viewer. Leave it out for none. |
loading | 'lazy' | 'eager' | Defaults to lazy. Set eager on the pictures that are on screen when the page opens. |
Events
Link to section Events| Event | detail | Description |
|---|---|---|
change | { index, item } | The current item changed: a tile was chosen, or the user moved in the viewer. |
toggle | boolean | The viewer opened (true) or closed (false). |
Tokens
Link to section TokensLayout and look are tokens, not props: set them on :root, on a section, or on one gallery.
| Token | Default | Description |
|---|---|---|
--fd-media-columns | 4 | The most tiles in a row. |
--fd-media-gap | --fd-space-3 | The space between tiles. |
--fd-media-min-width | 8rem | The narrowest a tile gets. A narrower box gets fewer columns, so the grid fits wherever it is put, with no breakpoints. |
--fd-media-aspect-ratio | 1 / 1 | The shape every tile is cut to. |
--fd-media-fit | cover | cover fills the tile and crops; contain shows the whole picture. |
--fd-bg · --fd-text | the page's | The viewer's surface and its text. Set them on the gallery for a dark viewer. |
Parts
Link to section Parts| Part | Description |
|---|---|
base | The grid (a <ul>). |
item · button · frame · image · caption | One tile, its fandry-button's <button>, the box the picture is cut to, the picture, its caption. |
placeholder | A tile's placeholder while loading. |
viewer · backdrop · panel | The viewer, and the fandry-dialog it is drawn in. |
toolbar · status · close-button | The bar at the top, the "3 of 12", the close button. |
keys-button · keys | The keyboard button, and the box its list of shortcuts shows in. |
stage · viewer-image · viewer-caption | Where the pictures slide, the current picture, its caption. |
previous · next | The arrows (with disabled at the first and last item). |
Make it yours: a product gallery
Link to section Make it yours: a product galleryA store's product page wants one big picture and a strip of thumbnails, not a grid. This one extends fandry/mediaGalleryState and writes only its own markup: the current picture, focus coming back after the viewer, the events and the full-screen viewer with swipe and zoom all come from the block.
// productGallery.js -- your markup, the block's behavior
import FdMediaGalleryState from 'fandry/mediaGalleryState';
export default class ProductGallery extends FdMediaGalleryState {
// `current` is undefined while there are no items.
get main() {
return { src: this.current?.src ?? '', alt: this.current?.alt ?? '' };
}
handleThumb(event) {
this.goTo(Number(event.currentTarget.dataset.thumb));
}
}
<!-- productGallery.html -->
<fandry-button variant="ghost" data-index={currentIndex} onclick={handleSelect}>
<img src={main.src} alt={main.alt} />
</fandry-button>
<ul>
<template for:each={thumbs} for:item="thumb">
<li key={thumb.key}>
<fandry-button variant="ghost" data-thumb={thumb.index} onclick={handleThumb}>...</fandry-button>
</li>
</template>
</ul>
<fandry-media-viewer
items={items}
index={currentIndex}
open={viewerOpen}
messages={messages}
onchange={handleViewerChange}
ontoggle={handleViewerToggle}
></fandry-media-viewer>
The pieces
Link to section The piecesfandry/mediaGalleryState has the behavior and no template. fandry/mediaGallery is that class plus a grid and a viewer. fandry/mediaGrid and fandry/mediaViewer each work on their own and know nothing of each other; what they share is the item shape in fandry/mediaItem. The viewer is split the same way: fandry/mediaViewerState has its keyboard, swipe and zoom and no template. fandry/mediaGestures is the swipe and pinch math, with no template. To change the markup, extend a state class, or, on Salesforce, edit the copy fandry add put in your project. Built from:dialogbuttonicontextskeleton