Gallery
Arranges a set of pictures. Four layouts (a contact sheet, a masonry, a justified library and a quilt) work over one list of images and the metadata that came with them, with a viewer a click away.
import { Gallery } from 'neba';
<Gallery
items={[{ src, alt: 'A still alpine lake at dawn', title: 'Alpine lake', ratio: '3 / 2' }]}
layout="justified"
caption="hover"
preview
/>;Props
| Prop | Type | Default | Description |
|---|---|---|---|
| items * | NebaGalleryItem[] | — | The pictures, in the order they are drawn |
| layout | 'grid' | 'masonry' | 'justified' | 'quilted' | 'grid' | How the tiles are arranged. grid gives every tile one shape, masonry keeps the proportions and stacks columns, justified keeps them and fills every row to the edge, quilted lets a tile take more than one cell |
| columns | number | Partial<Record<NebaBreakpoint, number>> | { xs: 2, sm: 3, lg: 4 } | How many tiles across. Read by grid, masonry and quilted; justified decides for itself from rowHeight |
| gap | NebaSize | number | string | 'md' | The space between tiles: a step of the ladder, a number in pixels, or a CSS length |
| ratio | number | string | 1 | The shape of a tile in grid, and what an item with no ratio of its own falls back to elsewhere |
| rowHeight | number | 220 | How tall a row aims to be in justified and how tall one cell is in quilted. A justified row scales to the width it actually has, so it lands near this rather than on it |
| rounded | NebaSize | boolean | 'md' | Rounds the tiles |
| caption | 'none' | 'below' | 'overlay' | 'hover' | 'none' | Where an item's title and description go. below is under the picture, overlay is across the foot of it, hover is overlay that arrives with the pointer |
| hover | 'none' | 'lift' | 'dim' | 'zoom' | 'lift' | What a tile does under the pointer and the focus. lift is depth, dim is colour, zoom scales the photograph inside a frame that does not move |
| preview | boolean | false | Opens the picture full size on a click, with the rest of the set an arrow key away. The viewer is fetched on demand |
| onItemSelect | (item: NebaGalleryItem, index: number) => void | — | Called when a tile is chosen, whether or not there is a viewer |
| filter · frame · watermark · protect | see Image | — | Passed straight through to every tile's Image. watermark and protect follow the picture into the viewer |
| label | string | — | The list's accessible name. Defaults to the locale's word for it |
| locale | string | — | Which language the viewer's buttons are named in: a BCP 47 tag |
| empty | ReactNode | — | What is drawn when items is empty. Nothing at all by default |
| classNames | NebaSlots<'item' | 'image' | 'caption' | 'title' | 'description'> | — | Class names for the parts behind the root. The root itself is className, so there is no root key |
Every other <ul> attribute passes through to the list. The shared axes are defined in prop conventions.
The item
| Prop | Type | Default | Description |
|---|---|---|---|
| src * | string | — | Where the picture is |
| alt * | string | — | What the picture says. Required for the reason Image requires it |
| ratio | number | string | — | The picture's own proportion. masonry and justified are laid out from it, before a single file has arrived |
| title | ReactNode | — | The first line of the caption |
| description | ReactNode | — | The second, one step down the scale and muted |
| full | string | — | A larger file for the viewer, when the tile is a thumbnail. Falls back to src |
| cols · rows | number | 1 | How many cells the tile takes in quilted |
| id | string | — | A stable identity. Defaults to src |
ratio is the one worth writing down even when it feels optional. masonry and justified are laid out from it, and they are laid out before a single file has arrived: which is what makes the arrangement right in the first frame and stops a wall of forty photographs reflowing forty times as they land. Nothing is ever measured.
Examples
layout
grid gives every tile the same shape, whatever shape the files are. masonry keeps each picture's own proportion and stacks the columns, dealing each item into the shortest one so the first row is the first three pictures rather than the first three of column one. justified keeps the proportions and fills every row to the edge, scaling each row to a common height: the arrangement where nothing is cropped and no space is left over. quilted is a grid whose tiles may take more than one cell.
columns and gap
columns is how many tiles across, and it takes a breakpoint map: { xs: 2, sm: 3, lg: 4 } is the default. grid, masonry and quilted read it; justified decides for itself, row by row, from rowHeight.
gap is the space between tiles: a step of the spacing ladder, a number in pixels, or a CSS length.
caption
below puts an item's title and description under the picture, overlay writes them across the foot of it on a gradient, and hover is overlay that arrives with the pointer. none, the default, draws neither: the words are still in the picture's alt and in the viewer.
Reach for overlay or hover in justified: a caption below the picture makes a tile taller than the row it was measured into, and rows stop lining up.
hover
What a tile does under the pointer, and under the keyboard focus: both, always, so a tile is never a state only a mouse can reach.
lift raises the tile on the shadow ladder and dim darkens the picture, which is how the rest of the library answers a pointer. zoom scales the photograph inside a frame that does not move: the one place in Neba where something is scaled, and it is allowed here because a photograph carries no text to resample and the tile's own edges stay exactly where they were.
filter, frame, watermark and protect pass straight through to every tile's Image, so a gallery of greyed thumbnails or a marked proof set is one prop.
preview
Opens the picture full size, with the rest of the set an arrow key away. ← and → move, Esc closes, and the counter under the picture is announced when it changes.
An item's full is used if it has one, so a grid of thumbnails can open the file it is a thumbnail of. watermark and protect follow the picture into the viewer, because a mark that came off the moment somebody enlarged the picture would not be a mark.
The viewer is fetched on demand. A Gallery that does not offer one does not carry it.
onItemSelect
Called with the item and its index when a tile is chosen, whether or not there is a viewer. It is what makes a tile a control: a Gallery with neither preview nor this draws plain pictures with nothing to press.
Accessibility
- The list is a
role="list"named bylabel, or by thelocale's word for "Gallery". Name it after what the set is: a page with two galleries and one name on both is a page with one name. - A tile's button is named by the picture's
altand its place in the set, so a reader tabbing a wall of thumbnails is told which one of how many they are on. - Every hover treatment is also a focus treatment. A tile that only responds to a pointer responds to half the readers.
- The viewer's counter is a live region, so an arrow key says where it landed to a reader who cannot see the picture it landed on.