Place a menu anywhere in your markdown file via a shortcode

You can create a shortcode to put a little nav menu anywhere in a markdown file. The use case might be something like:

  • you want a positionable menu for an ad hoc group of pages, and the menu’s position will be somewhere in the body of the pages themselves (hence the shortcode)
  • you have a top and/or sidebar menu that changes depending upon media queries, and, your page grouping does not appear in these, so, you need a way to provide navigation for them
  • you don’t necessarily want to expose your page grouping in taxonomy pages (like, making a “series”)

First make a shortcode “putsubmenu.html”:

<div class="buttons">
  <a class="button" href="/maintopic"><span class="icon"><i class="fas fa-anchor"></i></span></a>
{{ $currentPage := $.Page }}
{{ range $.Site.Menus.mainsub }}
  <a class="button{{ if $currentPage.IsMenuCurrent "mainsub" . }} is-active{{ end }}" href="{{ .URL }}">{{ .Name }}</a>
{{ end }}
</div>

Use whatever html you need. The example is using Bulma’s “buttons” class in a div, but you can use whatever css library you are using or, roll your own.

It puts a single anchor icon (from fontawesome) linking to the main topic, and then loops over pages in a menu “mainsub”.

You need to use $.Page to get the calling page’s context. The if statement checks if the page in the loop is also the current page, and if so, sets an “is-active” class. You can set whatever class your css library has.

Then in markdown files, I specify they are a member of the “mainsub” menu in frontmatter, like so:

menu:
  mainsub:
    Name: Some subtopic page
    Weight: 10
    Url: /maintopic-subtopic1/

… and in the body of the markdown, specify the shortcode like:

 {{< putsubmenu >}}

If you have some pages in a group, you just add them all to the “mainsub” menu (whatever name you want), and then you’ll see a menu where you put the shortcode, with the anchor icon, followed by however many pages there are, linked to the menu.

2 Likes

You can also generify the shortcode, to make it possible to feed the shortcode the name of the menu you intend to place:

 {{< putsubmenu mainsub >}}
<div class="buttons">
  <a class="button" href="/maintopic"><span class="icon"><i class="fas fa-anchor"></i></span></a>
{{ $currentPage := $.Page }}
{{ $menuName := .Get 0 }}
{{ $rangeData := index $.Site.Menus $menuName }}
{{ range $rangeData }}
  <a class="button{{ if $currentPage.IsMenuCurrent $menuName . }} is-active{{ end }}" href="{{ .URL }}">{{ .Name }}</a>
{{ end }}
</div>

You can also do it by pulling the menu name from page params, like:

<div class="buttons">
  <a class="button" href="/maintopic"><span class="icon"><i class="fas fa-anchor"></i></span></a>
{{ $currentPage := $.Page }}
{{ $menuName := $.Page.Params.submenu }}
{{ $rangeData := index $.Site.Menus $menuName }}
{{ range $rangeData }}
  <a class="button{{ if $currentPage.IsMenuCurrent $menuName . }} is-active{{ end }}" href="{{ .URL }}">{{ .Name }}</a>
{{ end }}
</div>

It assumes you set a submenu: mainsub in your page’s frontmatter.

Note that, while the index works fine for feeding the menu name to range, you can’t do:

 ☠☠ {{ $rangeData := (printf "$.Site.Menus.%s" $menuName ) }} ☠☠

In that case, hugo says range cannot iterate over .Site.Menus.mainsub in an ERROR. I gather this is because that produces a string which is incompatible with range, which is expecting an object.

3 Likes