An ordered list of media, where the order is content.
A collection data type: an ordered list of library references.
Three fields wear this shape — Images' and
Files' Multiple forms, and this one — and the
difference worth a page is not the constraint. It is the choosing
mechanism:
terms, match: any|all) beside the authored
list, and the two compose. "Every photo tagged 2026" stays current
as the library grows, without re-picking.This page's field — unconstrained, mixed kinds legal — is where the dynamic lane lives today, and that is its identity going forward: reach for the named pairs when you are curating a set of one kind; reach here when the collection should follow the library or mix kinds on purpose.
{ "media": ["med_31…", "med_8c…"], "terms": { "tag": ["2026"] }, "match": "any" }
The authored ids in author order, plus the filter. Order is content
for the curated part — the editor sorts by drag, the template renders
in storage order — and the boundary enforces allowed_types when the
field declares one, plus min_items/max_items on the selection.
At render, the collection wakes to a plain array of resolved media
objects — {src, alt, type, filename, title, width, height, sizes…} —
so templates never hold raw ids:
<div class="mk-row mk-row--3up">
<% for (const item of gallery ?? []) { %>
<% if (item.type === 'image') { %>
<img src="<%= item.src %>" alt="<%= item.alt %>">
<% } else { %>
<a href="<%= item.src %>"><%= item.title || item.filename %></a>
<% } %>
<% } %>
</div>
The kind-check in the loop is the cost of mixing kinds — and the reason
the curated pairs exist: an Images → Multiple loop needs no
if.