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
::gallery{folder="media" layout="grid" cols="3"}- Create
media/next to this content object'scontent.md(or another relative folder under the item). - Drop image files into that folder (
jpg,jpeg,png,webp,gif,avif,svg; video:mp4,webm). - Name files so lexicographic order matches the intended sequence (
01-…,02-…). - 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
| Attribute | Values | Default | Notes |
|---|---|---|---|
folder | item-relative path | — (required) | Resolved under the content item directory. .. and absolute URLs are rejected. |
layout | see table below | large | Presentation mode for the same folder. |
cols / columns | 2–6 | 3 when layout is grid | Only applied to layout=grid. |
lightbox | true / false | on for browse layouts; off for large, stage, story | Click opens a fullscreen dialog; ←/→ stay inside this gallery. |
intro / lede | true / false | off for browse layouts; on for large / stage / story | Optional folder content.md title + body as a lede. |
ratio | 1:1, 16/9, 4x3, square, … | — | CSS aspect-ratio for sized plates. Aliases: aspect, aspectRatio. |
width / w | CSS length (24rem, 100%, min(100%,36rem), bare number → px) | with only ratio: min(100%,36rem) | Plate width. Rejects injection-like strings. |
height / h | CSS 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.
| Form | Example | Result |
|---|---|---|
| Ratio only | ratio="1:1" | Square box; width defaults to min(100%, 36rem) |
| Ratio + width | ratio="16:9" width="100%" | Widescreen, full content width |
| Height only | height="50vh" | Fixed plate height, width 100% of content column unless width set |
| Width + height | width="32rem" height="18rem" | Explicit box; ratio optional |
::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
| Layout | Role | Typical use |
|---|---|---|
mosaic | Bento: first image 2×2, rest fill | Hero + companions |
pair | Two-up diptych (4 on desktop; stacks on phone) | Before/after, call-and-response |
grid | Equal tiles; density via cols | Editorial sets |
masonry | Column flow, natural heights | Mixed aspect ratios |
film | Tight contact sheet | Scan a whole set |
large | Full-width stack | One image per breath |
stage | Full-viewport plates + on-image overlay caption | Immersive plate sequence |
expose | Alias of stage | Same as stage (Expose-style name) |
story | Full-bleed scroll plates + on-image caption | Magazine-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
::gallery{folder="media" layout="mosaic"}One folder. One set of plates. Choose the layout in the directive.








Pair
::gallery{folder="media" layout="pair"}One folder. One set of plates. Choose the layout in the directive.








Grid (cols="3")
::gallery{folder="media" layout="grid" cols="3"}One folder. One set of plates. Choose the layout in the directive.








Grid denser (cols="4")
::gallery{folder="media" layout="grid" cols="4"}One folder. One set of plates. Choose the layout in the directive.








Masonry
::gallery{folder="media" layout="masonry"}One folder. One set of plates. Choose the layout in the directive.








Film
::gallery{folder="media" layout="film"}One folder. One set of plates. Choose the layout in the directive.








Large (lightbox off by default)
::gallery{folder="media" layout="large"}One folder. One set of plates. Choose the layout in the directive.








Large with lightbox forced
::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.
::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):
::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.
::gallery{folder="media" layout="story"}One folder. One set of plates. Choose the layout in the directive.








Lightbox
- Browse layouts (
mosaic,pair,grid,masonry,film): lightbox on unlesslightbox=false. large/stage/story: lightbox off unlesslightbox=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).
::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:
---
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:
---
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
srcvalues 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)
| Breakpoint | Browse adjustments |
|---|---|
| ≤900px | grid → 2 columns; mosaic → 2 columns; film → 4-up |
| ≤640px | pair → stack; masonry → 2 columns; film → 3-up; story drops 100vw breakout |
| ≤380px | film → 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
| Situation | Result |
|---|---|
Missing or unreadable folder | Empty gallery wrapper (data-empty), no broken <img src=""> |
Path traversal (../…) or URL folder | Rejected (invalid-folder) |
| Empty folder | Valid empty gallery, no images |
Unknown layout | Falls back to large |
Invalid ratio / width / height | Prop ignored; gallery still renders unsized |
| Runtime without gallery expand | Directive may appear unexpanded — upgrade ARC |
| Runtime without sized-plate CSS | Stage 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-storywidget. stage/storyare 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.
Related
- Immersive cover+video plates: Immersive video gallery (Expose-style)
- Introduction article: One folder of photos, several ways to look
- Markdown image (inline single image): image
- Markdown slides (different feature): Write and embed Markdown slides
- AUP
photo-storywidget (separate layer): photo-story