Frontmatter Field Classification & params.mt Migration Proposal
Generated 2026-06-21. Companion file: yaml-migration-map.xlsx. Source inventory: yaml-frontmatter-audit.*.
Every field currently used across the 267 index files, classified against Hugo’s reserved front-matter fields (verified against the Hugo 0.163.3 documentation), with a proposed target for everything that isn’t Hugo’s.
How Hugo decides what’s “its own”
Hugo reserves a fixed set of front-matter field names (title, date, type, layout, etc.). Reserved names are case-insensitive — Hugo lowercases keys on read, so lastMod resolves to Hugo’s lastmod and Layout resolves to layout. Any name not on that list is a custom field, and since Hugo 0.123 custom fields are only exposed to templates if they live under the params key. That single rule explains both buckets below: legitimate Hugo fields stay at the top level, and everything else belongs under params.mt.
1. Hugo reserved fields — keep at top level (14)
These are genuine Hugo fields and should remain exactly where they are, as siblings of params.
| Field | Type seen | Keep / action |
|---|---|---|
title | string | keep |
description | string / null | keep |
keywords | string / null | keep (Hugo expects a list, but tolerates a string) |
draft | bool | keep |
date | datetime / date | keep — normalise to one format (audit conflict) |
lastMod | date / datetime | keep — resolves to lastmod; normalise (audit conflict) |
publishDate | datetime / null | keep |
expiryDate | null only | keep — Hugo field, currently always empty; omit when unused |
type | string | keep — blog / page / section / briefings (drives templates) |
layout | string / null | keep |
linkTitle | string | keep |
slug | string | keep |
url | null only | keep — Hugo field, currently always empty; omit when unused |
params | map | keep — this is the container that will hold params.mt |
One fix in this group: Layout (capital L) on tags/_index.md is the same field as layout (Hugo lowercases it). Rename it to lowercase to remove the duplicate.
2. Custom fields sitting at the top level — in error (4 blocks)
These are not Hugo fields, yet they sit at the top level where, under current Hugo, templates can’t reliably read them. They should move under params.mt.
| Current | → Proposed | Where it occurs |
|---|---|---|
seoTitle | params.mt.seoTitle | 217 files |
hero.* | params.mt.hero.* | about/_index.md |
videoSpotlight.* | params.mt.videoSpotlight.* | root _index.md |
showVideo | params.mt.showVideo | root _index.md |
3. Custom data already under params — move into params.mt.* (the bulk)
This is the real mariothomas.com data. The proposal dissolves the redundant params.content wrapper — since params.mt now is the content namespace — promoting its children up one level, while keeping logical sub-groups (author, thumb, audio, links) intact.
| Current path | → Proposed |
|---|---|
params.content.type | params.mt.type |
params.content.category | params.mt.category |
params.content.featured | params.mt.featured |
params.content.file / .mime / .time | params.mt.file / .mime / .time |
params.content.url / .url_title | params.mt.url / .url_title |
params.content.x / .x_title | params.mt.x / .x_title |
params.content.location / .location_title | params.mt.location / .location_title |
params.content.thumb.* | params.mt.thumb.* (src, alt, caption, type, width, height, version) |
params.author.{name,email} | params.mt.author.{name,email} |
params.audio.{source,duration,size,text} | params.mt.audio.* |
params.links[] + .links_title / .links_description | params.mt.links[] + .links_title / .links_description |
params.abstract | params.mt.abstract |
params.hide | params.mt.hide |
params.tags / params.categories | params.mt.tags / params.mt.categories — see note |
4. Drop (2)
Always-null, and duplicate the Hugo top-level fields of the same name:
params.url→ drop (use Hugo’s top-levelurlif a URL override is ever needed)params.resources→ drop (Hugo’sresourcesis a reserved top-level field for page-resource metadata)
One decision to make: tags & categories
params.tags and params.categories are the one ambiguous case. If you want them to remain plain data (labels you render yourself), params.mt.tags / params.mt.categories is correct. But if you want Hugo’s taxonomy system — term pages, /tags/... listings, .GetTerms — those keys must sit at the top level (tags: / categories:), not under params at all. You already have a tags taxonomy and a tags/ section, so this is worth resolving deliberately rather than defaulting.
Summary of the move
Of the 71 distinct key paths: 14 stay as Hugo fields (plus the Layout→layout casing fix), 2 are dropped, and the remaining 54 consolidate under params.mt.* — whether they were mis-placed at the top level (seoTitle, hero, videoSpotlight, showVideo) or already living in params (content.*, author, audio, links, etc.). The proposed params.mt tree is laid out on the second sheet of the workbook.
The colour-coded, file-count-weighted version of this mapping — every path, its classification, and its target — is in yaml-migration-map.xlsx.




