Navigation menu
How an agent adds header and footer menus to a Meridian site with navigation slots in site.yaml, a seeded navigation set and the nav tag in the layout, marking the current page and hiding an empty menu.
Menus are navigation sets that fill slots. The project declares the slots; editors fill them in the admin’s Navigation screen; you seed the first version so the site works from day one. A menu item that points at a document drops out by itself when the document is unpublished.
flowchart LR slot["site.yaml nav: main<br/>(the slot)"] --> set["seed/navigation/main.yaml<br/>(the set, assigned)"] set --> admin["Editors change it<br/>in the admin"] set --> tag["base.html<br/>nav tag on nav.main"] admin --> tag
Declare the slots
# config/sites/main/site.yaml
nav:
main: { label: { en: Header }, depth: 2 }
footer: { label: { en: Footer }, depth: 1 } engine-dmo check --fix check refuses a slot without an id (nav slot "main" has no id); check --fix writes nav_ ids. depth is how deep items may nest, 1 to 5.
Seed the menus
# seed/navigation/main.yaml
site: main
slot: main
name: Header
assigned: true
items:
- label: { en: Walks }
index: walk
- label: { en: Contact }
document: page/contact # seed/navigation/footer.yaml
site: main
slot: footer
name: Footer
assigned: true
items:
- label: { en: Accessibility }
document: page/accessibility
- label: { en: Search }
url: /search | Item key | Links to |
|---|---|
document: <type>/<seed key> | A seeded document, in the visitor’s locale |
index: <type> | The type’s index page |
url: /path or url: https://... | A path on the site (locale-prefixed) or an external address |
asset: <seed key> | A library file |
none, with children: | A heading grouping its children |
Each item may add suffix: "#anchor" and children: (up to the slot’s depth). Every document an item names must exist in the seed.
engine-dmo seed verify
engine-dmo seed reset --confirm
engine-dmo seed apply seed verify loads the navigation files with the rest and fails on an item that names a document not in the seed (seed/navigation/main.yaml: document page/nosuch is not in the seed), or a slot site.yaml does not declare. Its summary line counts documents and assets only, so a menu that loads is silent there. Only seed apply counts the menus: it reports navigation: 2 sets, 4 items. Without a database, a clean seed verify is the evidence that the menus will load.
On a multilingual site give each item’s label every locale (label: { en: Walks, cy: Teithiau }); check does not warn about a missing one, and the item shows its source-locale label.
Render the menus in the layout
{# in templates/_shared/base.html #}
<nav aria-label="{{ t('nav.main') }}">
<ul>{% nav item in nav.main %}<li><a href="{{ item.url|url }}"{% if item.isCurrent %} aria-current="page"{% endif %}>{{ item.label }}</a>{% ifchildren %}<ul>{% children %}</ul>{% endifchildren %}</li>{% endnav %}</ul>
</nav>
...
<footer>
{% if nav.footer %}<nav aria-label="{{ t('nav.footer') }}">
<ul>{% for item in nav.footer %}<li><a href="{{ item.url|url }}">{{ item.label }}</a></li>{% endfor %}</ul>
</nav>{% endif %}
</footer> # config/strings/en.yaml
strings:
nav.main: Main
nav.footer: Footer {% nav %}renders a tree:{% ifchildren %}renders only when the item has children and{% children %}repeats the body one level down. A flat menu needs only{% for %}.- Mark the current page with
aria-current="page"fromitem.isCurrent;item.isActiveis true on the trail above it. - Wrap a menu that may be empty in
{% if nav.<slot> %}so the page has no empty landmark. - A heading item (
item.kind == "heading") has an emptyurl: print it as text.
Verify
engine-dmo render /walks --json The html holds both menus, the Walks item carries aria-current="page", and a11y is empty. See Navigation for every item property and breadcrumbs.
Listing page with filters and pagination
How an agent builds a type's index page in Meridian with route facets for filtering, a translated result count, cards and an accessible pager, and verifies the first, last and filtered pages with render.
Responsive images
How an agent declares image transforms in config/images.yaml, prints asset fields with img(), picture() and figure() so every image has srcset, sizes, dimensions, alt text and its credit, seeds real photographs, and reads the image report from render.