Patterns
Patterns
Each pattern is a small, complete example. Pick your framework in any tab, and every page remembers the choice. The live example above the code runs that code.
| Pattern | Shows |
|---|---|
| Gallery | A trigger per photo and the default controls |
| One trigger for an album | Several items behind one thumbnail that follows the selection |
| Rich origins | A photo inside a card, and a scroller that reveals on close |
| Custom content | Your own component, an iframe and a video |
| Programmatic control | Open from code, read state, log events, veto navigation |
| Zoom | Zoom steps, or zoom off |
| Filmstrip | The default strip, and your own thumbnails |
| Sheet | A panel under the photo with snap points |
| Headless controls | Your own caption bar and buttons |
| Right to left | An Arabic gallery: the viewer reads right to left |
| Photo in the address | ?photo= opens a photo, and Back closes it |
The example data#
The examples import their items from ./data. Each item is a plain object. Here is that module, with the longer lists cut short:
ts
import type { FluidboxImageItem, FluidboxItem, FluidboxLabels } from "@ogdakke/fluidbox";
/** An image with its own id. The thumbnails on the page show the same file. */
const image = (
id: string,
src: string,
alt: string,
width: number,
height: number,
): FluidboxImageItem => ({ id, type: "img", src, alt, width, height });
/** Gallery, programmatic control, zoom, filmstrip, sheet, headless controls and right to left. */
export const photos: FluidboxImageItem[] = [
image("photo-aurora", "/art/aurora.svg", "Aurora over the tundra", 1100, 1650),
image("photo-ridge", "/art/ridge.svg", "Night ridge", 1200, 1500),
image("photo-fjord", "/art/fjord.svg", "Fjord in the blue hour", 1200, 1600),
image("photo-lagoon", "/art/lagoon.svg", "Lagoon after sunset", 1800, 1200),
];
/** One trigger for an album: five items behind one thumbnail. */
export const album: FluidboxImageItem[] = [
image("album-dune", "/art/dune.svg", "Dune at golden hour", 1800, 1200),
image("album-canyon", "/art/canyon.svg", "Canyon walls", 1400, 1400),
image("album-coast", "/art/coast.svg", "Coastline panorama", 2400, 1000),
// …two more
];
/** Rich origins: a product card and a horizontal scroller. Its id is its src. */
export const product: FluidboxImageItem = {
type: "img",
src: "/runner.svg",
alt: "Illustrated coral running shoe",
width: 1000,
height: 700,
};
export const strip: FluidboxImageItem[] = [
image("strip-alpine", "/art/alpine.svg", "Alpine ridge at dawn", 1920, 1080),
image("strip-meadow", "/art/meadow.svg", "Meadow hills", 1600, 1200),
// …six more
];
/** Custom content: your own renderer, a map, a video and a YouTube embed. */
export const media: FluidboxItem[] = [
{
type: "custom",
id: "media-note",
// Names the renderer the example registers.
render: "note",
title: "A note",
width: 800,
height: 600,
thumbnail: { src: "/art/meadow.svg", width: 1200, height: 800 },
},
{
type: "iframe",
id: "media-map",
src: "https://www.openstreetmap.org/export/embed.html?bbox=24.93104%2C60.16003%2C24.97396%2C60.17497&layer=mapnik&marker=60.1675%2C24.9525",
title: "Helsinki Market Square on OpenStreetMap",
width: 1000,
height: 700,
thumbnail: { src: "/map/preview.svg", width: 1000, height: 700 },
},
{
type: "video",
id: "media-video",
src: "/video/drift.webm",
title: "Drifting clouds",
width: 960,
height: 540,
poster: "/video/drift-poster.jpg",
},
{
type: "iframe",
id: "media-youtube",
src: "https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ",
title: "Rick Astley - Never Gonna Give You Up",
width: 1280,
height: 720,
allow: "autoplay; encrypted-media; picture-in-picture",
// YouTube won't play an embed that sends no referrer.
referrerPolicy: "strict-origin-when-cross-origin",
thumbnail: {
src: "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
width: 1280,
height: 720,
},
},
];
/** The value each framework passes down through its own context, read by the custom renderer. */
export const theme = "dusk";
/** The picture a trigger shows for an item: its thumbnail, a video's poster, or the image. */
export const thumbnailSrc = (item: FluidboxItem) =>
item.thumbnail?.src ??
(item.type === "video" ? item.poster : item.type === "img" ? item.src : "");
/** Right to left: the viewer's words in Arabic, with Arabic-Indic digits in the position. */
// Plain "ar" now formats with Western digits; nu-arab asks for Arabic-Indic ones.
const arabicNumber = new Intl.NumberFormat("ar-u-nu-arab");
export const arabic: Partial<FluidboxLabels> = {
viewer: "عارض الوسائط",
close: "إغلاق",
previous: "السابق",
next: "التالي",
position: (index, count) =>
`العنصر ${arabicNumber.format(index + 1)} من ${arabicNumber.format(count)}`,
slides: "عناصر المعرض",
filmstrip: "شريط الصور المصغّرة",
filmstripList: "اختر عنصرًا من المعرض",
zoomed: "صورة مكبّرة",
sheet: "التفاصيل",
error: "تعذّر تحميل الوسائط.",
retry: "إعادة المحاولة",
};Import @ogdakke/fluidbox/styles.css once in your app (see Getting started). The class names (pattern-grid, pattern-thumb, …) belong to the examples. Use your own.