Image carousels join the standard library — and the filename timestamp turns out to be a run stamp, not a capture stamp
`:::image-carousel` normalizes into one renderer-ready payload: alias collapsed, slides extracted, order resolved. Sequence variants sort chronologically by the ISO stamp in the filename — which works because the sort is stable, and which needed to be, because that stamp records when a batch was processed rather than when each screen was captured.
Image carousels join the standard library
Why Care?
A sequence of screenshots — a setup flow, a before/after, a step-by-step — is one thing with an order, not several things that happen to be adjacent. Markdown has never had a way to say so, and every site that wanted to say it re-implemented the same three chores: find the child images, decide what counts as one, and decide what order they go in.
:::image-carousel moves all three into the pipeline. The directive is normalized to a single data.carousel payload, so an Astro renderer, a Svelte renderer, and a plain-HTML renderer all read one contract instead of re-walking children.
What’s New?
lfmImageCarousel— normalizes:::image-carouseland:::img-carouselcontainer directives.- The alias collapses in the plugin.
img-carouselis rewritten toimage-carouselbefore the renderer ever sees it, so downstream code matches on one name. Polyglot rule: authors pick the syntax that fits their hand, the renderer sees one node. - Slide extraction accepts both syntaxes — the
::image{}leaf directive and plain— and recurses through paragraphs, because remark wraps loose inline content and directives usually arrive nested one level down. - A variant taxonomy (
filmstrip,stepper,peek,contact-sheet) withSEQUENCE_VARIANTSnaming the three whose whole point is order. Unknown variants degrade to the default rather than failing the build, per the no-hard-validation posture. - Registered in the preset alongside
remarkLinkPreview, riding ondirectives— inert on any document containing no carousel.
Ordering, and the stamp that isn’t what it looks like
Sequence variants default to sort="chronological", reading the ISO 8601 basic-format stamp the house image-prep convention appends to every filename:
Aside__Welcome-Screen_20260817T164659Z.jpg
└────────────────┘
Basic format (no separators) exists precisely so a timestamp survives being embedded in a filename or URL, where : is illegal or hostile.
The stamp is applied once per prep run, not once per image. It records when a batch was processed, not when each screen was captured — so every image from one invocation carries an identical stamp, and the stamp orders batches, not photographs.
That would be a footgun if the sort weren’t stable. It is: equal stamps — and absent ones — fall back to authoredIndex, the position the author wrote. So the ordinary case (capture a sequence, prep it in one batch) resolves entirely to authored order and is correct. Chronological only ever reorders across batches.
Which leaves exactly one failure mode, and it is the one that surfaced this: an image belonging mid-sequence, uploaded in a later batch. Against the real filenames from the fullstack-vc Aside walkthrough, where the recovery-key screen was re-uploaded after redaction:
authored Welcome > Recovery key > The agent > Done
chronological Welcome > The agent > Done > Recovery key ← wrong
one batch Welcome > Recovery key > The agent > Done
sort="authored" opts out; sort="reverse-chronological" inverts. contact-sheet is excluded from the sequence set deliberately — it renders every frame at once in a grid, so reordering would shuffle ordinal badges against no visual sequence.
The durable fix belongs upstream in the image-prep script: stamp per-image capture time from EXIF or mtime rather than per-run wall clock. Then chronological means what it says.
Naming
This plugin is lfm-image-carousel, not remark-image-carousel. The remark-* prefix belongs to the remark ecosystem’s own plugins; this is LFM-specific and contributes nothing to remark’s standard library. The existing remark-* plugins here predate the distinction and are untouched — renaming them is a public-API change and wants its own pass.
Corrected later the same day. Two things above were wrong. The count was seven, not six — it was taken from filenames and missed
og-fetcher.ts, which exportsremarkOgFetcher. And “contributes nothing to remark’s standard library” turned out to be the wrong test: the remarkjs org’s plugin list contains nothing for callouts, citations, wikilinks, code fences, OG fetching, link previews or heading ids, so the test caught every plugin in the package and distinguished between none of them.What replaced it is a three-tier rule, because the capability and the behavior are separate questions:
remark-{name}when a formal remark plugin handles it,remark-lfm-{name}when one exists and we substantially change how it works,lfm-{name}when the syntax trigger and the handling are both ours.lfm-image-carouselstays exactly as named above — tier 3, no formal prior art. The pass ran in 0.5.0; the rule is a blueprint atcontext-v/blueprints/Naming-Plugins-Against-the-Remark-Ecosystem.md. See [[2026-08-17_02]].
What’s Next?
A dedicated pass on whether the other six plugins take theDone in 0.5.0 — seven plugins, sorted into two prefixes by the three-tier rule, every old export name kept as a permanent alias.lfm-prefix, weighed against the export names being public API at 0.4.1.- Publish, so consuming sites can drop their local slide-extraction copies and read
data.carousel— including the sort, which site-local implementations do not have.