Minimal Mistakes 侧边栏导航列表(Sidebar Navigation List)配置实战指南

原创2026-09-22 17:48:281,710 阅读
文章标签:前端

Minimal Mistakes 侧边栏导航列表(Sidebar Navigation List)配置实战指南

Minimal Mistakes 主题内置了一套"自定义侧边栏导航菜单"机制:通过页面的 YAML Front Matter 挂载 sidebar.nav,配合 _data/navigation.yml 中集中定义的两级菜单数据,即可为任意页面(或整站)渲染出带分组标题、可折叠的多级导航列表。本指南以仓库中的演示文章 test/_posts/2012-03-15-layout-sidebar-nav-list.md 为切入点,讲解从数据结构定义、页面挂载到源码渲染与样式定制的完整链路,读完即可在自己的站点中复刻这一功能。

自定义侧边栏导航菜单示例,来自 Minimal Mistakes 文档站点

演示文章剖析:一段 60 行的完整配置样板

仓库在 test/_posts/ 与 docs/_posts/ 两个站点中各放置了一篇同名演示文章 2012-03-15-layout-sidebar-nav-list.md,其 Front Matter 是整个功能的"最小完整配置":

---
title: "Layout: Sidebar with Navigation List"
excerpt: "A post with a sidebar navigation list."
author_profile: false
sidebar:
  title: "Sample Title"
  nav: sidebar-sample
---

逐项拆解这四个关键字段:

字段 值 作用
title Layout: Sidebar with Navigation List 页面标题,同时是演示用途的语义说明
excerpt A post with a sidebar navigation list. 摘要文本,用于归档列表与 SEO 描述
author_profile false 显式关闭作者信息侧边栏,保证本页只展示导航列表
sidebar.title Sample Title 导航列表顶部的分组标题(渲染为 <h3 class="nav__title">)
sidebar.nav sidebar-sample 引用 _data/navigation.yml 中同名 key 的导航数据

正文部分则直接声明该页"在 Front Matter 中设置了自定义导航列表",并给出两段 YAML 配置。这里的核心模式是:页面侧边栏本身不内联任何菜单项,而是通过 nav 字段指向数据文件中的 key,从而让同一份导航数据可以被多个页面复用。

第一步:在 _data/navigation.yml 中定义导航数据

导航菜单项全部集中存放在站点的 _data/navigation.yml 数据文件中。演示文章引用的 sidebar-sample key 定义如下(原样继承自文章正文):

sidebar-sample:
  - title: "Parent Page A"
    children:
      - title: "Child Page A1"
        url: /
      - title: "Child Page A2"
        url: /
      - title: "Child Page A3"
        url: /
      - title: "Child Page A4"
        url: /
  - title: "Parent Page B"
    children:
      - title: "Child Page B1"
        url: /
      - title: "Child Page B2"
        url: /
      - title: "Child Page B3"
        url: /
      - title: "Child Page B4"
        url: /
      - title: "Child Page B5"
        url: /
  - title: "Parent Page C"
    children:
      - title: "Child Page C1"
        url: /
      - title: "Child Page C2"
        url: /
      - title: "Child Page C3"
        url: /
      - title: "Child Page C4"
        url: /
      - title: "Child Page C5"
        url: /
  - title: "Parent Page D"
    children:
      - title: "Child Page D1"
        url: /
      - title: "Child Page D2"
        url: /

数据结构语义

  • 顶级 key(sidebar-sample):即 Front Matter 中 sidebar.nav 引用的名字,二者必须完全一致(nav: "docs" 就引用 docs key)。建议简短、好记。
  • title(父级):渲染为导航列表中的分组标题(.nav__sub-title)。它可以只有 title(纯分组标题,不可点击),也可以同时带 url(变成可点击的父级链接)。
  • children(子级):父级下的链接数组,每个子项包含 title 和 url。

两份真实数据文件对比

测试站点 test/_data/navigation.yml 中的 sidebar-sample 是演示文章的完整落地版本,子项 URL 全部为真实路径而非占位符:

sidebar-sample:
  - title: "Parent Page A"
    children:
      - title: "Child Page A1"
        url: /child-page-a1/
      - title: "Child Page A2"
        url: /child-page-a2/
      - title: "Child Page A3"
        url: /child-page-a3/
      - title: "Child Page A4"
        url: /child-page-a4/
  - title: "Parent Page B"
    children:
      - title: "Child Page B1"
        url: /child-page-b1/
      - title: "Child Page B2"
        url: /child-page-b2/
      - title: "Child Page B3"
        url: /child-page-b3/
      - title: "Child Page B4"
        url: /child-page-b4/
      - title: "Child Page B5"
        url: /child-page-b5/
  - title: "Parent Page C"
    children:
      - title: "Child Page C1"
        url: /child-page-c1/
      - title: "Child Page C2"
        url: /child-page-c2/
      - title: "Child Page C3"
        url: /child-page-c3/
      - title: "Child Page C4"
        url: /child-page-c4/
      - title: "Child Page C5"
        url: /child-page-c5/
  - title: "Parent Page D"
    children:
      - title: "Child Page D1"
        url: /child-page-d1/
      - title: "Child Page D2"
        url: /child-page-d2/
      - title: "Child Page D3 (External)"
        url: https://your-domain.com

注意最后一项 Child Page D3 (External) 使用了完整的 https:// 外部地址,说明子项 URL 既支持站内相对路径,也支持外部绝对链接。

文档站点 docs/_data/navigation.yml 中的 foo key 则演示了另一种形态——父级同时带 url,渲染为可点击的分组链接:

foo:
  - title: "Parent Link 1"
    url: /parent-1-page-url/
    children:
      - title: "Child Link 1"
        url: /child-1-page-url/
      - title: "Child Link 2"
        url: /child-2-page-url/
  - title: "Parent Link 2"
    url: /parent-2-page-url/
    children:
      - title: "Child Link 1"
        url: /child-1-page-url/
      - title: "Child Link 2"
        url: /child-2-page-url/
      - title: "Child Link 3"
        url: /child-3-page-url/

官方文档 docs/_docs/10-layouts.md 中的 "Custom sidebar navigation menu" 一节明确说明:侧边栏菜单只支持 1 层嵌套链接(即"父级 + children"两级结构),这一点在规划菜单层级时需要记住。

第二步:通过 Front Matter 把导航挂载到页面

定义好数据后,在任意页面的 Front Matter 中添加:

sidebar:
  title: "Sample Title"
  nav: sidebar-sample
  • nav 的值必须与 _data/navigation.yml 中的顶级 key 完全匹配(nav: "sidebar-sample" 引用 sidebar-sample key)。
  • title 可选,用于给导航列表加一个顶部标题;不写则不渲染标题。

批量挂载:使用 Front Matter Defaults

如果多个页面(例如整个 _docs 集合)都要使用同一个侧边栏导航,官方推荐在 _config.yml 中用 Front Matter Defaults 统一配置,避免逐页重复书写。文档站点正是用这种方式让所有文档页共享 docs 导航:

defaults:
  # _docs
  - scope:
      path: ""
      type: docs
    values:
      sidebar:
        nav: "docs"

单页挂载多个导航(v4.26.0 起)

自 v4.26.0 起,sidebar.nav 还支持传一个列表,从而在一页中同时渲染多份导航数据:

sidebar:
  nav:
    - main
    - docs

对应 docs/_docs/10-layouts.md 中 "Custom sidebar navigation menu" 一节的说明:定义多个 sidebar navs 后,即可用列表形式在单页引入多份菜单。

渲染原理:sidebar.html 与 nav_list 的源码级解析

导航列表并不是由布局直接输出,而是经由两个 include 协作完成。整个侧边栏由 _includes/sidebar.html 负责组装,其渲染顺序为:

  1. 作者资料(当 page.author_profile 或 layout.author_profile 为真时,引入 author-profile.html);
  2. YAML 定义的侧边栏块(page.sidebar 循环:image → title → text → nav);
  3. 导航列表(page.sidebar.nav 存在时引入 nav_list);
  4. 自定义内容(sidebar-custom.html,默认为空文件,供用户扩展)。

关键代码片段(_includes/sidebar.html):

{% if page.sidebar %}
  {% for s in page.sidebar %}
    {% if s.image %}
      <img src="{{ s.image | relative_url }}" alt="{% if s.image_alt %}{{ s.image_alt }}{% endif %}">
    {% endif %}
    {% if s.title %}<h3>{{ s.title }}</h3>{% endif %}
    {% if s.text %}{{ s.text | markdownify }}{% endif %}
    {% if s.nav %}{% include nav_list locale=locale nav=s.nav %}{% endif %}
  {% endfor %}
  {% if page.sidebar.nav %}
    {% include nav_list locale=locale nav=page.sidebar.nav %}
  {% endif %}
{% endif %}

nav_list(_includes/nav_list)是导航渲染的核心,整个文件只有 28 行,逻辑清晰:

{% assign locale = include.locale | default: site.locale %}
<nav class="nav__list">
  {% if page.sidebar.title %}<h3 class="nav__title" style="padding-inline-start: 0;">{{ page.sidebar.title }}</h3>{% endif %}
  <input id="ac-toc" name="accordion-toc" type="checkbox" />
  <label for="ac-toc">{{ site.data.ui-text[locale].menu_label | default: "Toggle Menu" }}</label>
  <ul class="nav__items">
    {% for navname in include.nav %}
      {% assign navigation = site.data.navigation[navname] %}
      {% for nav in navigation %}
        <li>
          {% if nav.url %}
            <a href="{{ nav.url | relative_url }}"><span class="nav__sub-title">{{ nav.title }}</span></a>
          {% else %}
            <span class="nav__sub-title">{{ nav.title }}</span>
          {% endif %}

          {% if nav.children != null %}
          <ul>
            {% for child in nav.children %}
              <li><a href="{{ child.url | relative_url }}"{% if child.url == page.url %} class="active"{% endif %}>{{ child.title }}</a></li>
            {% endfor %}
          </ul>
          {% endif %}
        </li>
      {% endfor %}
    {% endfor %}
  </ul>
</nav>

值得注意的实现细节:

  • site.data.navigation[navname]:通过 Front Matter 传入的 nav 名动态查表取数——这就是 nav 值与数据文件 key 必须一致的底层原因;若 key 不存在,navigation 为 nil,循环体不输出任何内容,侧边栏会静默留空。
  • 父级链接分支:nav.url 存在时父级 title 被包裹成 <a> 链接,否则输出纯文本 <span class="nav__sub-title">。
  • 当前页高亮:子级链接通过 child.url == page.url 判定并追加 class="active",让读者始终知道自己在菜单中的位置。
  • 相对路径处理:所有 url 都经过 relative_url 过滤器,因此数据文件里既可写 /child-page-a1/ 这样的站内绝对路径,也可写 https://your-domain.com 这样的外链。
  • 移动端折叠:<input id="ac-toc" type="checkbox" /> 与 <label> 组合实现纯 CSS 的折叠开关,标签文本取自 site.data.ui-text<a href="https://link.gitcode.com/i/5864150875556b40116ff4e5099beac9" target="_blank">locale].menu_label(可在 [_data/ui-text.yml 中自定义),默认回退为 "Toggle Menu"。

样式定制:_navigation.scss 中的导航视觉体系

导航列表的样式集中在 _sass/minimal-mistakes/_navigation.scss:

  • .nav__list(约第 340 行):外层 <nav> 容器;
  • .nav__list .nav__items(约第 439 行):菜单项列表;
  • .nav__list input:checked ~ .nav__items(约第 468 行):checkbox 勾选后展开子菜单的联动规则;
  • .nav__title(约第 481 行):Front Matter 中 sidebar.title 渲染出的分组标题;
  • .nav__sub-title(约第 489 行):父级分组标题的样式。

此外,nav_list 输出的结构配合主题已有的 sidebar 排版(_sidebar.scss 中的 .sidebar.sticky)即可实现吸顶效果——这是文档站点侧边栏在长页面滚动时保持可见的原因。若需调整标题字号、分组间距或移动端折叠行为,直接修改对应规则即可,无需改动 Liquid 模板。

实战扩展:与侧边栏其他能力自由组合

导航列表并不是侧边栏的唯一内容。sidebar 在 Front Matter 中支持多种挂件(见 docs/_docs/10-layouts.md 的 "Custom sidebar content" 一节):

名称 说明
title 标题或标题文本
image 图片路径(/images/ 下或外部 URL)
image_alt 图片替代描述
text 文本内容,支持 Markdown
nav 引用 _data/navigation.yml 中的导航 key

例如在一个页面里同时叠加标题、图片、文本与导航:

sidebar:
  - title: "Title"
    image: /assets/images/bio-photo.jpg
    image_alt: "image"
    text: "Some text here."
  - title: "Another Title"
    text: "More text here."
  - title: "Navigation"
    nav: sidebar-sample

组合规则(源自 docs/_docs/10-layouts.md):

  • 若同时开启 author_profile: true,上述自定义块会显示在作者资料下方;
  • 更进阶的定制可创建 _includes/sidebar-custom.html,其内容会被追加到侧边栏最底部(作者资料、YAML 块、导航菜单之后),默认文件为空。

验证与排查清单

本地运行 jekyll serve 后,可从以下几个角度验证配置是否生效:

  1. 检查导航 key 是否匹配:sidebar.nav 的值必须精确对应 _data/navigation.yml 中的顶级 key(含引号内大小写),否则侧边栏不渲染任何菜单且不报错。
  2. 查看生成的 HTML:页面源码中应出现 <nav class="nav__list"> 结构,父级为 nav__sub-title,子级为普通 <a>;当前页面的子级链接应带有 class="active"。
  3. 确认 author_profile:若同时出现作者头像,说明 author_profile 被默认开启(如 _config.yml 的 Front Matter Defaults),可在页面级用 author_profile: false 覆盖,正如演示文章所做。
  4. 数据文件位置:若使用主题 Gem 方式安装,_data/navigation.yml 应位于你的站点根目录下(而非主题目录内),这样自定义数据才能覆盖主题默认值。

按照"先定义数据、再挂载页面"的两步流程,配合 Front Matter Defaults 的批量能力,即可在 Minimal Mistakes 上快速搭建文档站式的多级侧边栏导航体系。

登录后查看全文
minimal-mistakes