Automated Nested Menus?

Either in front matter or in site configuration.

No.

This method is looking for child menu nodes, not child pages.


For nested section menus (think of a sidebar menu on a documentation site), walk the page tree to build the items, starting with:

  • A collection of one or more sections, or
  • A menu

The trick with walking the directory tree from menu entries is to use pageRef instead of url when defining the entries. This gives you access to the page within your menu template. So, on a section page, you can access .Page.Pages, then recursively walk the page tree.

Here’s a generic example of a partial to build the tree.

layouts/partials/render-section-menu.html
{{- /*
Renders a recursive section menu starting from a page collection or menu.

As it walks the tree, this partial:

    - Sets class="active" on the active list item
    - Sets aria-current="page" on the active anchor
    - Sets aria-current="true" on the ancestors of the active anchor

If you feed it a single page, you must wrap the page in a slice. See examples
below.

If you feed it a menu, menu entries defined in site configuration must use the
pageRef property, not the URL property. Everything must be a page.

By default, a home page reference in the page collection or menu will be
skipped. To override this behavior, set $skipHome to false below.

@param {page} currentPage   The page currently being rendered.
@paran {slice} nodes        A slice of top level pages or a menu.

@returns {template.HTML}

@examples

    {{ with site.Menus.main }}
      {{ partial "render-section-menu.html" (dict "currentPage" $ "nodes" .) }}
    {{ end }}

    {{ with site.Sections }}
      {{ partial "render-section-menu.html" (dict "currentPage" $ "nodes" .) }}
    {{ end }}

    {{ with slice (site.GetPage "/introduction") }}
      {{ partial "render-section-menu.html" (dict "currentPage" $ "nodes" .) }}
    {{ end }}

*/}}

{{- /* Configure. */}}
{{- $skipHome := true }}

{{- /* Get parameters. */}}
{{- $currentPage := .currentPage }}
{{- $nodes := .nodes }}

{{- /* Render. */}}
<nav>
  <ul>
    {{- range $nodes }}
      {{- if and .Page.IsHome $skipHome }}
        {{- continue }}
      {{- end }}
      {{- template "walk" (dict "node" . "currentPage" $currentPage) }}
    {{- end }}
  </ul>
</nav>

{{- /* Recurively render list items. */}}
{{- define "walk" }}
  {{- $currentPage := .currentPage }}
  {{- $node := .node }}

  {{- $linkContent := $node.Page.LinkTitle }}
  {{- with $node.Name }}
    {{- $linkContent = . }}
  {{- end }}

  {{- $ariaCurrent := "" }}
  {{- $liClass := "" }}

  {{- if in $currentPage.Ancestors $node.Page }}
    {{- $ariaCurrent = "true" }}
  {{- end }}

  {{- if $currentPage.Eq $node.Page }}
    {{- $ariaCurrent = "page" }}
    {{- $liClass = "active" }}
  {{- end }}

  <li {{- with $liClass }} class="{{ . }}" {{ end -}}>
    <a {{- with $ariaCurrent }} aria-current="{{ . }}" {{- end }} href="{{- $node.Page.RelPermalink }}">{{ $linkContent }}</a>
    {{- with $node.Page.Pages }}
      <ul>
        {{- range . }}
          {{- template "walk" (dict "node" . "currentPage" $currentPage) }}
        {{- end }}
      </ul>
    {{- end }}
  </li>
{{- end }}