Minimal Mistakes 侧边栏导航列表(Sidebar Navigation List)配置实战指南
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 为切入点,讲解从数据结构定义、页面挂载到源码渲染与样式定制的完整链路,读完即可在自己的站点中复刻这一功能。
演示文章剖析:一段 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"就引用docskey)。建议简短、好记。 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-samplekey)。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 负责组装,其渲染顺序为:
- 作者资料(当
page.author_profile或layout.author_profile为真时,引入author-profile.html); - YAML 定义的侧边栏块(
page.sidebar循环:image→title→text→nav); - 导航列表(
page.sidebar.nav存在时引入nav_list); - 自定义内容(
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 后,可从以下几个角度验证配置是否生效:
- 检查导航 key 是否匹配:
sidebar.nav的值必须精确对应_data/navigation.yml中的顶级 key(含引号内大小写),否则侧边栏不渲染任何菜单且不报错。 - 查看生成的 HTML:页面源码中应出现
<nav class="nav__list">结构,父级为nav__sub-title,子级为普通<a>;当前页面的子级链接应带有class="active"。 - 确认
author_profile:若同时出现作者头像,说明author_profile被默认开启(如_config.yml的 Front Matter Defaults),可在页面级用author_profile: false覆盖,正如演示文章所做。 - 数据文件位置:若使用主题 Gem 方式安装,
_data/navigation.yml应位于你的站点根目录下(而非主题目录内),这样自定义数据才能覆盖主题默认值。
按照"先定义数据、再挂载页面"的两步流程,配合 Front Matter Defaults 的批量能力,即可在 Minimal Mistakes 上快速搭建文档站式的多级侧边栏导航体系。
