Config flows one way: site.Params into _partials/init.html, which resolves defaults once, and out to the templates. Templates read resolved values and never re-derive a default inline, so every default below lives in exactly one place in the source.

Caution

Config keys are API. Once published, a key is never renamed or repurposed without a major version bump and a changelog note. If you are reading this from a fork, that promise is yours to keep too.

Site parameters#

KeyDefaultWhat it does
description—Site description. Meta tags, the RSS channel, the home page.
colorScheme"periwinkle"periwinkle, sage, clay, plum, slate or rose. An unknown value falls back rather than rendering unstyled.
defaultAppearance"light"light or dark. Only applies when autoSwitchAppearance is off.
autoSwitchAppearancetrueFollow the reader’s operating system.
enableSearchtrueThe ⌘K modal. Needs JSON in [outputs].home.
enableCodeCopytrueCopy button on code blocks.
fingerprintAlgorithm"sha512"md5, sha256, sha384 or sha512. Changing it renames every asset at once — a cache bust, not a stale asset, but worth knowing.
disableTextInHeaderfalseLogo-only header. The home link keeps an aria-label, or it would be unlabelled on every page.
enableStyledScrollbartrueHand the scrollbar back to the operating system when off.
smartTOCHideUnfocusedChildrenfalseCollapse TOC child lists except under the current heading. Off by default: extra motion in a component whose job is to stay still.
backgroundImageWidth1600Scale target for the full-bleed background image, the largest image on any page that uses one.
hotlinkFeatureImagefalseAllow featureImageURL in front matter to point at a remote image. Off, because that is a third-party request on page view.
defaultFeaturedImage—Site-wide cover for posts that have none. A configured-but-missing file renders nothing rather than a 404 on every page.
defaultSocialImage—The OpenGraph/Twitter card image for pages without a cover of their own.
seo.metaDescriptionOrder["description", "summary", "siteDescription"]Which source fills the meta description, first non-empty wins. An unknown name is skipped rather than failing the build.
header.showSubNavfalseA second navigation row from the subnav menu. Renders only when that menu has entries.
imagePosition"center"object-position for cropped images — the avatar and card thumbnails. Covers are never cropped, so it does not affect the hero.
disableImageOptimizationfalseHand raster images to the browser unresized. For a site whose images are already optimised upstream.
layoutBackgroundBlurfalseBlurs background and hero artwork. Applied to the image, not the block, so text over it stays sharp.
layoutBackgroundHeaderSpacefalseExtra room between the header and the body.
enableLightboxfalseClick a prose image to open it, then zoom to 6x and pan around it — by the dialog’s own buttons, the scroll wheel, a double-click, a trackpad or touch pinch, or + / - / 0 and the arrow keys. Uses a <dialog>, so the modal semantics, backdrop, focus trap and Escape handling come from the browser. Images that are already links are left alone.
enableA11yfalseShows a control that underlines every link on the page. Named for what it does: a control whose effect a reader cannot predict is not an accessibility feature.
accentEmphasisfalseLets the palette reach five surfaces the theme otherwise leaves neutral: section headings, the year rules on the post index, tag chips, a card’s hover border and the blockquote edge. Invents no colours, so every palette gets it. See “More of it” on Appearance.
header.layout"fixed"fixed keeps the header sticky (the current behaviour); basic lets it scroll away.
mainSections["blog"]Which sections count as posts for the home page, index, RSS and search.
dateFormat"2 Jan 2006"Go reference layout.
rtlfalseSets dir="rtl" on <html>.
logo—Replaces the dot and the wordmark in the header.
logoDark—Optional dark counterpart. With only logo set, the same file is used in both modes.

[params.author]#

KeyDefaultWhat it does
name—Article meta line and the home byline. Omit it and both disappear.
headline—One line under the name on the home page.
bio—A paragraph, rendered as Markdown, shown on the profile home layout. The headline says what you do; this says who you are.
email—Used by the reply-by-email link. Nothing renders unless article.replyByEmail is also on.
imageQuality85JPEG/WebP quality for the avatar. Lossless formats ignore it — a PNG is byte-identical at any value; only the resize applies there.
image—Square avatar, 160px or larger. Looked up in assets/ then static/; a missing file renders nothing rather than a broken image.
links—Array of single-key tables. Supported: linkedin, github, rss, link.
id—The @id of the Person node your main site publishes, e.g. https://example.com/#person. Emitted in JSON-LD so both properties resolve to one entity rather than two people with the same name.
url—The author’s main site, emitted as Person.url in JSON-LD.
sameAs—Array of profile URLs corroborating the identity, emitted as Person.sameAs. Only list profiles you control.

Several authors#

[params.author] is the single, site-wide author, and a site that only ever has one needs nothing else. For more than one, put a file per person in data/authors/ and name the keys in a post’s front matter:

# data/authors/ada.toml
name = "Ada Example"
headline = "Invented the demo co-author"
image = "images/ada.png"
links = [{ link = "https://example.com" }]
# in the post
authors: ["morten", "ada"]

A post that says nothing about authors falls back to [params.author], which is what keeps this backward compatible: an existing site renders exactly what it rendered before.

A key with no matching file fails the build. A post crediting somebody who then does not appear is the kind of bug nobody notices until the person asks why their name is missing.

Registering author = "authors" under [taxonomies] gives each author a page listing what they wrote; showAuthorsBadges then links the byline names to them.

Writing a bio in TOML has one trap. A """ string keeps its indentation, and Markdown reads four leading spaces as a code block, so a bio indented to line up with the keys around it renders as a grey <pre> slab rather than as prose. Keep the continuation lines flush left — exampleSite/hugo.toml shows the shape, and the test suite asserts the bio never renders as a code block.

[params.home]#

KeyDefaultWhat it does
layout"stack"Which homepage arrangement to render: stack, page, profile, hero, card, background, split, gallery, archive or custom. An unknown value fails the build. The Appearance page shows each one.
showFeaturedtrueLarge card for the newest post.
recentCount3 or 6Recent posts below. Layouts that lead with a feature (stack, hero) default to 3; the rest to 6.
backgroundImage—Only read by layout = "background". Without it that layout renders the ordinary intro.

[params.article]#

KeyDefaultWhat it does
showBreadcrumbstrueTrail above the title. Ancestors only.
showAuthortrueAuthor name and avatar in the meta line.
showDatetruePublication date.
showDateUpdatedfalseUpdated date. Only renders when lastmod is genuinely later than date.
showReadingTimetrueEstimated reading time.
showWordCounttrueWord count.
showHerotrueThe cover image.
heroStyle"basic"basic, big, background or thumbAndBackground. Overridable per post. An unknown value falls back to basic. Every style keeps the cover uncropped — see below.
showTableOfContentstrueSticky TOC rail on desktop, a card above the article on mobile.
showProgresstrue2px reading-progress bar.
showTaxonomiestrueTag row at the foot of a post.
showRelatedtrue“Read next” block. Needs the [related] config.
relatedLimit3How many related posts.
showPaginationtrueOlder and newer post links.
sharingLinks["linkedin", "reddit"]Share buttons, rendered in the order you list them. See the eleven supported names below. An unknown name warns at build time and renders nothing.
mastodonInstance—The Mastodon instance to post to, e.g. mastodon.social. Required if sharingLinks includes mastodon.
showCommentsfalseComments. Also needs [params.comments.giscus].
showAuthorsBadgesfalseLink each byline name to its author page. Needs author = "authors" under [taxonomies].
showAuthorBottomfalseAuthor card at the foot of a post: avatar, name, headline, links. Adds to the byline rather than replacing it.
showDateOnlyInArticlefalseDate on the article page but not on listings.
showHeadingAnchorstrueAnchor links beside headings. Overridable per page.
invertPaginationfalseSwap which side older and newer land on. Labels swap with the links.
externalLinkForceNewTabtrueExternal links open in a new tab. rel="noopener" is kept either way.
showZenModefalseA control that hides the header, the TOC rail and both footers, leaving the prose. Escape leaves. Not persisted.
showViewsfalseView counter. Needs [params.firebase].
showLikesfalseLike button. Needs [params.firebase].
seriesOpenedfalseWhether the series navigation starts expanded. Collapsed by default: the summary line already says which part you are on.
replyByEmailfalseA “reply by email” link at the foot of each post. Needs [params.author].email; renders nothing without it. No third party, no script, and it works with JavaScript off.
showEditfalse“Edit this page” link.
editURL—Base URL for that link. Renders nothing when unset.
editAppendPathtrueAppend the page’s own path to editURL.

The edit link on the page you are reading is live. It points at this file in the theme’s own repository, built from editURL plus the content path.

Eleven services are supported. Any post on this site shows the row live at the foot of the article.

NameGoes to
linkedinLinkedIn’s share dialogue
redditReddit’s submit page, with the title prefilled
mastodonYour configured instance’s compose page
blueskyBluesky’s compose intent
hackernewsHacker News’ submit page
emailThe reader’s own mail client, via mailto:
xX’s post intent
facebookFacebook’s sharer
telegramTelegram’s share URL
whatsappWhatsApp’s share URL
pinterestPinterest’s pin builder

Every one of these is a plain link. No script, no SDK and no widget is loaded, so nothing is requested from any of these services until a reader actually clicks. That is why the list is limited to services with a documented share URL — a provider needing a script would put a third-party request on every article page for a button most readers never press.

mastodon needs mastodonInstance. Mastodon is federated, so there is no central host to post to: the share URL belongs to an instance. A static site cannot know the reader’s own instance, and routing through a third-party instance picker would be a call to somebody else’s server on every click, so the site names the instance instead. Listing mastodon without setting mastodonInstance fails the build rather than quietly dropping the button.

email opens a mail client, not a website, so it is the one entry with no target="_blank" — opening a mailto: in a new tab leaves an empty tab behind in most browsers.

[params.list] and [params.footer]#

KeyDefaultWhat it does
list.groupByYeartrueYear headings in the post index, grouped within each page of results.
list.orderByWeightfalseSort the section index by weight instead of date. Replaces the date sort entirely; unweighted pages go last.
footer.showMenutrueThe footer menu.
footer.showAppearanceSwitchertrueThe light/dark control.
footer.showScrollToToptrueThe back-to-top button.
sitemap.excludedKinds["taxonomy", "term"]Which page kinds stay out of sitemap.xml. Replaces the list rather than adding to it.
list.cardViewfalseRender the section index as a card grid instead of a list. Year grouping does not apply to a grid, so this wins over list.groupByYear.
taxonomy.showTermCounttrueArticle count beside each term.
taxonomy.cardViewtrueTerm pages as a card grid. Set false for the same row list the section index uses.
list.showSummaryfalseOn listings, fall back to the post’s summary when it has no description. A description always wins where one exists. Off by default, because turning it on changes every listing on an existing site.
footer.showCopyrighttrueCopyright line.
footer.showThemeAttributiontrueThe “built with Hugo and the Northlight theme” line.
footer.themeURL—Links the theme name in that line to a URL. Unset, the name is plain text. The theme cannot default this to its own repository, because that URL contains its author’s name and no theme file is allowed to carry one.

[params.verification]#

Public ownership tags. Each renders only when its key is set, so a site that verifies nothing emits nothing.

KeyWhat it does
googlegoogle-site-verification
bingmsvalidate.01
pinterestp:domain_verify
yandexyandex-verification
fediversefediverse:creator, as @you@instance.tld

Tip

fediverse is the one most worth setting. Mastodon shows a verified author byline on a link preview when the page points back at your profile, which is the difference between a shared post looking like yours and looking like an unattributed repost.

A menu entry with children renders as a disclosure panel rather than a hover dropdown, because hover menus are unreachable by touch and awkward by keyboard. The Docs entry in the header above is one; open it and look at how it behaves with the keyboard alone.

hugo.toml
[[menu.main]]
  identifier = "docs"
  name = "Docs"
  pageRef = "docs"
  weight = 20

[[menu.main]]
  parent = "docs"
  name = "Configuration"
  pageRef = "docs/configuration"
  weight = 2

One level only. A blog that needs three wants a landing page, not a bigger menu.