Skip to main content

Web Device · media block

Folder-backed media gallery (`::gallery`)

Put a media folder next to a content item and present it with layout, density, and optional lightbox — samples for every mode live on this page.

Put image (and video) files in a folder under the content item, then declare a gallery in Markdown. The runtime expands ::gallery into a static media block. Media stays with the item; you do not publish a separate CDN catalog for the set.

Requires a Web Device runtime that includes gallery folder expand (arc#3676 / ::gallery{folder}). If the directive does not expand, upgrade the serving ARC build before treating samples as broken.

Minimal example

markdown
::gallery{folder="media" layout="grid" cols="3"}
  1. Create media/ next to this content object's content.md (or another relative folder under the item).
  2. Drop image files into that folder (jpg, jpeg, png, webp, gif, avif, svg; video: mp4, webm).
  3. Name files so lexicographic order matches the intended sequence (01-…, 02-…).
  4. Render the content object; the directive expands to a media-gallery block.

This page keeps its samples in ./media/ so the images ship with the repo (no external URLs).

Parameters

AttributeValuesDefaultNotes
folderitem-relative path— (required)Resolved under the content item directory. .. and absolute URLs are rejected.
layoutsee table belowlargePresentation mode for the same folder.
cols / columns2–63 when layout is gridOnly applied to layout=grid.
lightboxtrue / falseon for browse layouts; off for large, stage, storyClick opens a fullscreen dialog; ←/→ stay inside this gallery.
intro / ledetrue / falseoff for browse layouts; on for large / stage / storyOptional folder content.md title + body as a lede.
ratio1:1, 16/9, 4x3, square, …—CSS aspect-ratio for sized plates. Aliases: aspect, aspectRatio.
width / wCSS length (24rem, 100%, min(100%,36rem), bare number → px)with only ratio: min(100%,36rem)Plate width. Rejects injection-like strings.
height / hCSS length (50vh, 400px, …)—Plate height. Use alone or with ratio / width.

Layout aliases: expose / slideshow → stage; magazine → story; diptych / two → pair; bento / hero-grid → mosaic; contact / sheet / thumb → film.

Sized plates (ratio · width · height)

stage / expose / story (and large) default to full-viewport or full-width plate height. For an in-article demo — square Live Photo feel, fixed hero height, or a reading-column box — set any combination of ratio, width, and height. The runtime emits data-sized="true" and CSS variables; media uses object-fit: cover inside the box.

FormExampleResult
Ratio onlyratio="1:1"Square box; width defaults to min(100%, 36rem)
Ratio + widthratio="16:9" width="100%"Widescreen, full content width
Height onlyheight="50vh"Fixed plate height, width 100% of content column unless width set
Width + heightwidth="32rem" height="18rem"Explicit box; ratio optional

markdown
::gallery{folder="media" layout="stage" ratio="1:1"}
::gallery{folder="media" layout="stage" ratio="16:9" width="100%"}
::gallery{folder="media" layout="stage" height="40vh"}
::gallery{folder="media" layout="large" width="24rem" height="16rem"}

Scope: sized-plate CSS applies to stage / expose / story / large. Browse layouts (grid, mosaic, …) ignore these props for tile geometry. Invalid ratio or size strings are dropped (unsized gallery remains valid).

Requires a Web Device runtime that includes sized-plate CSS (gallery ratio / width / height). Older builds expand the gallery but keep full-viewport stage.

Layout reference

LayoutRoleTypical use
mosaicBento: first image 2×2, rest fillHero + companions
pairTwo-up diptych (4 on desktop; stacks on phone)Before/after, call-and-response
gridEqual tiles; density via colsEditorial sets
masonryColumn flow, natural heightsMixed aspect ratios
filmTight contact sheetScan a whole set
largeFull-width stackOne image per breath
stageFull-viewport plates + on-image overlay captionImmersive plate sequence
exposeAlias of stageSame as stage (Expose-style name)
storyFull-bleed scroll plates + on-image captionMagazine-style scroll

Cover + short video pairs (same stem or sidecar video: / poster:) turn stage/expose/story plates into muted looping immersion when the runtime includes arc#3764. Dedicated authoring notes and a live sample: Immersive video gallery (Expose-style).

Live samples (same folder)

All samples below use folder="media" on this document.

Mosaic

markdown
::gallery{folder="media" layout="mosaic"}

One folder. One set of plates. Choose the layout in the directive.

Pair

markdown
::gallery{folder="media" layout="pair"}

One folder. One set of plates. Choose the layout in the directive.

Grid (cols="3")

markdown
::gallery{folder="media" layout="grid" cols="3"}

One folder. One set of plates. Choose the layout in the directive.

Grid denser (cols="4")

markdown
::gallery{folder="media" layout="grid" cols="4"}

One folder. One set of plates. Choose the layout in the directive.

Masonry

markdown
::gallery{folder="media" layout="masonry"}

One folder. One set of plates. Choose the layout in the directive.

Film

markdown
::gallery{folder="media" layout="film"}

One folder. One set of plates. Choose the layout in the directive.

Large (lightbox off by default)

markdown
::gallery{folder="media" layout="large"}

One folder. One set of plates. Choose the layout in the directive.

Large with lightbox forced

markdown
::gallery{folder="media" layout="large" lightbox="true"}

One folder. One set of plates. Choose the layout in the directive.

Stage (immersive plates)

Full-viewport plates. Caption text is drawn on the image (not under it), from per-file sidecars (title, body, overlay, tone). Lightbox defaults off — the plate already owns the viewport.

markdown
::gallery{folder="media" layout="stage"}

One folder. One set of plates. Choose the layout in the directive.

Stage sized (ratio="1:1")

Same folder and overlay captions, constrained to a square reading-column plate (no full-viewport scroll tax):

markdown
::gallery{folder="media" layout="stage" ratio="1:1"}

One folder. One set of plates. Choose the layout in the directive.

Story (full-bleed scroll)

Tall plates in a continuous scroll. Same on-image overlay captions as stage.

markdown
::gallery{folder="media" layout="story"}

One folder. One set of plates. Choose the layout in the directive.

  • Browse layouts (mosaic, pair, grid, masonry, film): lightbox on unless lightbox=false.
  • large / stage / story: lightbox off unless lightbox=true.
  • Open: click a tile. Close: Esc, backdrop, or ×.
  • Navigate: ← / → (or on-screen controls) within the same gallery only.
  • Progressive enhancement via the site SSR runtime bundle; tiles keep their layout CSS (no DOM rewrite of the grid).
markdown
::gallery{folder="media" layout="grid" lightbox="false"}

Folder content.md and sidecars

Optional media/content.md (and locale variants such as content.zh.md) can supply a short lede for immersive modes:

markdown
---
title: Coastal walk
---

A short coastal walk at dusk.

Per-file sidecars (e.g. 01-ridge.md or 01-ridge.jpg.md) set the plate label. For stage / story, front matter also places the caption on the image:

markdown
---
title: Ridge at dusk
overlay: bottom-left   # top-left | top | top-right | center | bottom-left | bottom | bottom-right
tone: dark             # dark | light
---

Optional second line on the plate.

Browse layouts (grid, mosaic, …) still show the caption under the tile when present. Sidecars and content.md are never treated as media members. Names starting with _ or . are skipped.

Self-contained media

  • Keep files under the content item. Relative src values stay item-local after expand.
  • Prefer shipping demo images in the repo next to the content (this page does). External hotlinks break when the host changes.
  • Pre-render copies item-local media into the web cache so relative paths resolve on the built site.

Responsive behavior (summary)

BreakpointBrowse adjustments
≤900pxgrid → 2 columns; mosaic → 2 columns; film → 4-up
≤640pxpair → stack; masonry → 2 columns; film → 3-up; story drops 100vw breakout
≤380pxfilm → 2-up; masonry → 1 column

Stage captions respect safe-area insets on notched phones. Details and screenshots: site issue #349 / design pack under reference/design/gallery-responsive-evidence/.

Errors and empty states

SituationResult
Missing or unreadable folderEmpty gallery wrapper (data-empty), no broken <img src="">
Path traversal (../…) or URL folderRejected (invalid-folder)
Empty folderValid empty gallery, no images
Unknown layoutFalls back to large
Invalid ratio / width / heightProp ignored; gallery still renders unsized
Runtime without gallery expandDirective may appear unexpanded — upgrade ARC
Runtime without sized-plate CSSStage stays full-viewport; ratio / width / height have no visual effect

Boundaries

  • Gallery is a content-item media block, not a site-wide media library UI and not the AUP photo-story widget.
  • stage / story are presentation modes of the same folder model; they are not separate content types.
  • Do not document price, token value, or unimplemented overlay products as part of this block.