DFD Hugo module for metadata for .Page

Basic documentation for metadata-mod-hugo-dfd, a Hugo module by Daniel F. Dickinson for handling page metadata intended to be used in generating the ‘head’ section, as well as used in page elements.

Status

build-and-verify

Demo site

https://metadata-mod.wildtechgarden.ca/

GitHub repository

https://github.com/danielfdickinson/metadata-mod-hugo-dfd

Features

  • Provides a common method of accessing page metadata for use in layouts and shortcodes.
  • The following metadata is currently available:
    • date-created — timestamp of page creation (UTC)
    • date-expired — timestamp of page expiry (UTC)
    • date-published — timestamp of published page (UTC)
    • date-modified — timestamp of last time page was modified (UTC)
    • description — A page frontmatter defined description of the page with a fallback to the page summary (below)
    • description-no-fallback — Page frontmatter description of the page with no fallback
    • keywords — Keywords for the page (generated from all terms from all taxonomies) Note 10
    • locale — Language/locale in/for which page is written
    • locale-alternate — Other available languages/locales for the page
    • media-audio-file — The first (if any) audio file associated with the page Note 1
    • media-audio-files — All (if any) audio files associated with the page Note 1
    • media-image — The first (if any) featured image, cover image, or thumbnail image associated with the page Note 2
    • media-images — All (if any) featured images, cover images, or thumbnail images associated with the page Note 2
    • media-video — The first (if any) video file associated with the page Note 1
    • media-videos — All (if any) video files associated with the page Note 1
    • opengraph-type — Open Graph Protocol type Note 3
    • reading-time — Approximate time in seconds to read page
    • section — Top-level section in which page resides
    • secure-url — Secure (HTTPS) permanent URL for page Note 4
    • see-also — Automatically generated list of likely similar pages
    • summary — A summary of the page, may be manually created or automatic Note 5
    • tags — A list of tags associated with the page Note 1
    • title-page — The title of the page (see below)
    • title-site — The title of the entire site (see below)
    • url — Permanent URL for the page (aka permalink)
    • word-count — Estimated number of words in the main page content
  • Has a unified and comprehensive means of finding page and site titles.
  • For <head> metadata, supports either self-closing or ‘void style’ empty tags Note 6
  • Partials in the helpers directory are ‘shortcode safe’ (which means they can work from layouts or shortcodes). Does not include subdirectories of helpers.
  • Logic for finding images for use by Open Graph and Twitter cards
  • Featured/cover images discovery/selection (combines with Open Graph/Twitter cards above)
  • Supports adding microformats to the page’s <head> section Note 7
  • Improved RSS feed
  • Improved Sitemap (XML)

Basic usage

Importing the module

  1. The first step to making use of this module is to add it to your site or theme. In your configuration file:

    config.toml

    [module]
        [[module.imports]]
            path = "github.com/danielfdickinson/metadata-mod-hugo-dfd"
    

    OR config.yaml

    module:
      imports:
        - path: github.com/danielfdickinson/metadata-mod-hugo-dfd
    
  2. Execute

    hugo mod get github.com/danielfdickinson/metadata-mod-hugo-dfd
    hugo mod tidy
    

Summary of configurable params

Unless otherwise noted, params may be per-page (in frontmatter), or in the site ‘params’ section in you site configuration file(s). Page params override site-wide params.

ParamDefaultDescription
datesPreserveTimezonefalseIf true in page or site params, then the timezone of the date is not adjusted. If this is not the case, all dates are converted to UTC. You can of course use .Local or other date functions to adjust the output of the resulting date.
description(nil)If present acts as the the description metadata (see Features above). Note 17
emptyElementStyle(nil)If emptyElementStyle is set to self-close in params (site or per-page), then empty tags produced by this module use ‘self-closing’ form, otherwise ‘void style’ Note 6 Note 9
internalTemplatesOverrideRobotsTXTtrueSite-level params only. When true overrides the internal template for robots.txt (robots.txt) with one from this module Note 8
internalTemplatesOverrideRSStrueSite-level params only. When true overrides the internal template for RSS feeds (rss.xml) with one from this module
internalTemplatesOverrideSitemaptrueSite-level params only. When true overrides the internal template for Sitemap protocol file (sitemap.xml) with one from this module
metadataNumSeeAlso10Number of related pages to return as the see-also metadata
microformatsEnabletrueIf true pull in the facebook, opengraph, schema, and twitter_cards microformats into <head> Note 11
microformatsOpenGraphEnabletrueIf true add the opengraph.html microformat to <head> Note 13
microformatsSchemaEnabletrueIf true add the schema.html microformat to <head> Note 14
microformatsTwitterCardEnabletrueIf true add twitter_cards.html microformat to <head> Note 15
multipleH1ErrorIgnoretrueIf false and there would be multiple <h1> elements on a page, error the build Note 19
multipleH1ErrorFixfalseIf true and there would be multiple <h1> elements on a page, make all but the first an <h2> Note 19
multipleH1ErrorWarntrueIf true and there would be multiple <h1> elements on a page, issue a warning. Note 19
openGraphType(nil)If set overrides the default type used for Open Graph microformat og:type meta tag Note 16
pageCanonicaltruePage-level params only. When false omits this page from the sitemap.xml, if applicable
rssIncludeMainArticlefalseWhen true include the pages .Content (that is the rendered content from the page’s file such as /content/posts/a-post.md) directly in the RSS feed instead of only a summary or description
summary(nil)If present acts as the summary metadata. Note 18
summaryPreserveHTMLfalseWhen true preserves any HTML in the Summary Note 18
summaryRenderMarkdowntrueWhen true render any Markdown in the Summary Note 18
tags(nil)Should be an array of tags associated with the page. Often also rendered on the page and/or listings by themes. By default is a Taxonomy
taxCanonicalfalseWhen true includes this taxonomy or term page in the sitemap.xml, if applicable
title(nil)Page param. When present is the title of the page. Note 20
title(nil)Site configuration. When present is the title of the website. Note 21
toCanonical(nil)If pageCanonical is false then sets the <link rel='canonical' href=…> href to value of toCanonical

A note on <head> generation

This theme includes a number of ‘helper’ partials under layouts/partials/helpers/head (called via {{ partial "helpers/head/<name-of-partial>.html" }}) which are intended to assist in populating the contents of the <head> element on your page.

An example generation of <head> using these helpers can be found in the exampleSite source code under /layouts/partials/head.html (which is in turn called by the theme’s layouts/_default/baseof.html).

A note on CSS generation

It is expected that layouts/partial/helpers/head/resources-pipes.html will not be used by consumers of this module, and that instead handling for the CSS, scripts and so on will use code more suitable for the consumer’s production and/or development situation.

That is, it is expected the module consumer will use their own version of resources-pipes.html that overrides the one from this module.

A note on metadata generation

In general metadata can be gathered and accessed by consumers of the module by issuing a partial call such as

{{- $metadataGathered := partial "helpers/return-metadata" ( dict "page" .Page "requestedData" (slice "description" "keywords") ) -}}

Where description and keywords Note 10 are the metadata types we are gathering (see Features for the list of metadata gathered by this module).

Then to use the description (for example):

{{- with $metadataGathered.description }}
    <meta name="description" content="{{ . }}">
{{- end -}}

or

{{- with (index $metadataGathered "description") }}
    <meta name="description" content="{{ . }}">
{{- end -}}

The layouts/partials/helpers/head/metadata.html in this theme gathers and directly adds to <head> description and keywords metadata Note 10. In addition it adds the generator meta tag (using the command hugo.Generator) and also pulls in layouts/partials/helpers/head/microformats.html (see below).

Notes on Date, Expiry, PublishDate, Lastmod

date-created, date-expired, date-published, and date-modified metadata items

  • For pages that do no exist in the content directory (e.g. taxonomies, terms, subdirectories/sections with no ‘_index.md’ or ‘index.md’ and/or a homepage with no ‘_index.md’ in ‘content’), then Hugo’s .PublishDate (Open Graph name ‘article:published_time’) is .IsZero, treated as false or not present depending on the method of use. This is true even with the config.toml [frontmatter] publishDate configured.
  • With this exception, the preferences for automatic values for the various date .Page variables can be configured using the frontmatter section in your Hugo config.
  • Not all themes are consistent in treating .Date as ‘dateCreated’ even though Hugo’s internal templates (like RSS feed generator) have that expectation. I’d recommend modifying the theme’s date handling if this is the case.
  • See also datesPreserveTimezone param (above)

Contributions welcome

If your issue can’t be found when searching both open and closed issues, please add it!

Please check open issues on danielfdickinson/metadata-mod-hugo-dfd for enhancements and bugs that you would like resolved, write the fix, and submit a PR!

Adding and improving documention is always handy as well.

Support and general questions

Please use GitHub Discussions for support and general questions.


End notes

Note 1

From frontmatter

Note 2

See DFDs Hugo metadata image module

Note 3

Article or website, depending on frontmatter

Note 4

Other URLs such as those for media-image may have their own secure URL as a part of the microformat

Note 5

Depending on frontmatter and site config

Note 6

  • That is a choice of either <meta name=“some-meta” content=“some-content” /> or <meta name=“some-meta” content=“some-content”>
  • Only for <head> tags generated by this module; you’ll need to implement the same logic for the rest of the items in your <head> section if you need consistency

Note 7

Microformats are used by search engines and other automated page handling software including for displaying website ‘cards’ (image and summary of a page/site in a box on other sites, such as Discourse forum).

Note 8

When using enableRobotsTXT = true and the default of overriding the robots.txt template with the one from this repo, if disableKinds includes "sitemap" (e.g. disableKinds = ["RSS","sitemap"]) then you will also need to add the following to your config.toml:

[sitemap]
    filename = ""

to avoid the generated robots.txt (/public/robots.txt from your project root in the default configuration) containing the following type of line

Sitemap: <your baseURL from config.toml>

instead of it being omitted from robots.txt.

Note 9

If emptyElementStyle is set to self-close in params (site or per-page), then empty tags produced by this module use ‘self-closing’ form (see above), otherwise ‘void style’ (see above). (Note 6)

For example, with toml for the site as

[params]
     emptyElementStyle = "self-close"

you get

    <meta name="generator" content="Hugo 0.91.2" />
    <meta name="keywords" content="Placeholder" />

while not configuring emptyElementStyle produces

    <meta name="generator" content="Hugo 0.91.2">
    <meta name="keywords" content="Placeholder">

in the ‘Test of metadata for <head>’ section of the page from exampleSite/docs/placeholder.md as the generated /docs/placeholder.

Note 10

  • For all pages except the homepage, any taxonomy terms (but not taxonomies themselves; so not ’tags’ and ‘categories’ for the default taxonomies configuration, but the terms you specify under ’tags’ and/or under ‘categories’ in page frontmatter) in the page frontmatter are used to fill in the ‘keywords’ <meta> tag.
  • For the homepage all taxonomy terms (but not taxonomies) are used to fill in the ‘keywords’ <meta> tag.
  • The ‘keywords’ meta tag uses a comma separated list of terms, with no space added before or after the comma, as a flat string as defined in the HTML5 spec.
  • Note that the ‘keywords’ meta tag will likely not be used by search engines due to historical misuse of this tag to mislead naive processors.

Note 11

The layouts/partials/helpers/head/microformats.html in this theme is wrapped in a check for a page or site-level microformatsEnable param which defaults true and pulls in layouts/partials/lib-output/microformats/<microformat_type> where <microformat_type> are each the following four types:

Note 12

Facebook microformat

This is only used if .Site.Social.facebook_admin is defined (via the [social] configuration option in config.toml).

Note 13

Open Graph microformat

This outputs Open Graph protocol microformat meta tags. There are too many to list here, so for details see layouts/partials/helpers/lib-output/microformats/opengraph.html in the source.

Note 14

Schema microformat

This outputs a limited subset of Schema microformat meta tags. There are too many to list here, so for details see layouts/partials/helpers/lib-output/microformats/schema.html in the source.

Note 15

Twitter cards microformat

This outputs Twitter Card microformat meta tags. There are too many to list here, so for details see layouts/partials/helpers/lib-output/microformats/schema.html in the source.

Note 16

Open Graph type (og:type meta tag) defaults to article for most pages except the home page or pages with no PublishDate is article. For the exceptions the the default is type website. See Object Types in the Open Graph Protocol.

Note 17

  • For a page (including sections, taxonomies, and terms, but not the home page): Uses .Description if present, otherwise defaults to the summary (if present).
    • For the found description: Turn into plain text, replace newlines and carriage-returns with spaces, and condense all multiple space sequences to single spaces.
  • For the home page: defaults to .Description if present, otherwise the home page frontmatter description, otherwise the site .Description, otherwise summary from .Site.Params
    • For the found description: Render markdown if present, turn into plain text, replace newlines and carriage-returns with spaces, then condense all multiple space sequences to single spaces.
    • description-no-fallback follows the same logic except does not default to summary (instead defaults to the empty string)

Note 18

  • If summaryRenderMarkdown is true in Params (site or per-page) then we allow setting a summary in the frontmatter that contains Markdown, which we render.
  • If summaryPreserveHtml is true in Params (site or per-page) then we return a summary that will render any contained HTML. Note that HTML is only preserved using the manual split method of generating a summary (that is using <!--more--> as described in the Hugo docs for Content Summaries), or when we render Markdown (above).
  • Also note that if you render Markdown but don’t preserve HTML the generated HTML is converted to plain text (i.e. will have no formatting).

Note 19

  • In the event of H1 issues:
    1. If site or per-page multipleH1ErrorFix is not false do nothing; indicates that the content handling logic fixes up the H1 issues.
    2. Otherwise, if multipleH1ErrorWarn, emit a build warning if there are H1 issues.
    3. Finally, if multipleH1ErrorIgnore is not true, fatally error the build on H1 issues.

Note 20

Page title (name/title/title-page) discovery and use

  • Uses the following order of preference for discovering title:
    1. title frontmatter (that is, .Page.Title)
    2. The contents of the first H1 element (if there is at least one)
    3. Either:
      1. For the home page the Site Title
      2. For any other page which has a content file, the ContentBaseName
    4. If not title has been found to this point, the title is ‘Untitled’.
  • If a non-content title was found and there are one or more H1 elements in the content, use the fix, warn, or error logic Note 19.
  • If a content title was found and there are more than one H1 elements in the content, use fix, warn, or error logic Note 19.

Note 21

Site title (site_name/title-site) title discovery and use

  • Uses the following order of preference for discovering site title:
    1. title configuration item for the site (or language-specific site) Hugo Docs for Multilingual and Hugo Docs for Site Config
    2. .Site.Params.Title
    3. Try steps 1 and 2 for the ’name’/Title of the homepage
    4. If not title found in 1-3, use a title of ‘Untitled Site’