/ Book 模式 / 页面定制

本文将介绍在 Book 模式下定制化页面以及 extensions 配置的高级用法

关于模板语法请参考 模板引擎

通过 extensions.template 可定义文章使用的 HTML 模板。默认值为 "templates/default.html",此处称之为默认模板。你可以基于默认模板进行修改、添加、删除,也可以完全重写一套自己的模板。

模板的核心是数据,数据的核心是变量,以下提供了在 Book 模式中注册的变量及其行为:

变量名类型说明
bodyString当前页面正文,即 Markdown 转换为 HTML 后的结果
sectionJson当前页面在导航树中所处位置的结构化数据,通常用于渲染侧边栏、目录树
site_titleString站点标题,对应配置中的 title 字段
site_descriptionString站点简介,对应配置中的 description 字段
site_keywordsString站点关键词,对应配置中的 keywords 字段
site_authorsString作者列表,对应配置中的 authors 字段,多个作者以顿号拼接的逗号连接
site_languageString站点主语言,对应配置中的 language 字段
faviconStringfavicon 路径,对应配置中的 favicon 字段
logoString站点 Logo 路径,对应配置中的 logo 字段
repositoryString源码仓库地址,对应配置中的 repository 字段,未配置时为空String
srcString源目录路径,对应配置中的 src 字段
destString输出目录路径,对应配置中的 dest 字段
base_urlString站点基础 URL,对应配置中的 base_url 字段
build_timeString本次构建发生的时间
versionString当前 MoPress 的版本号
breadcrumbString当前页面的面包屑路径
currentString当前页面自身的路径
prevJson?上一篇文章,包含 titlelocation 两个字段;不存在时为空字符串
nextJson?下一篇文章,包含 titlelocation 两个字段;不存在时为空字符串

其中 prevnext 在模板中需要先判断是否存在再访问其字段,例如:

$if(next)$
<a class="page-nav-btn next" href="$next.location$">
  <div class="page-nav-dir">Next →</div>
  <div class="page-nav-title">$next.title$</div>
</a>
$endif$

prefix_chapterssuffix_chaptersnumbered_chapters 分别对应导航结构中的前言章节、后记章节、主体编号章节。

prevnextsection 具体结构如下:

interface PrevAndNext {
  title: string;
  location: string;
}

interface Section {
  title: string;
  prefix_chapters: SectionItem[];
  numbered_chapters: SectionItem[];
  suffix_chapters: SectionItem[];
}

type SectionItem = SeparatorItem | PartTitleItem | LinkItem;

interface SeparatorItem {
  is_separator: true;
}

interface PartTitleItem {
  is_part_title: true;
  title: string;
}

interface LinkItem {
  is_link: true;
  title: string;
  location: string;
  source: string;
  depth: number;
  is_current: boolean;
  number?: string;
}

仅编号章节会在 LinkItem 中携带此字段,形如 11.21.2.1

一篇文章的元数据键值对,不管具体有哪些字段,都会被整体注入到该页面的模板变量中。元数据本身的键值是不固定的,自定义模板可以按需读取任意自定义字段。

默认模板对元数据中的 titledescriptionkeywordsauthorslanguage 这几个字段做了有效处理,部分字段行为是较于 site_* 变量优先选择,而另一部分字段行为是与 site_* 变量并并列处理。如:

<html lang="$if(language)$$language$$else$$site_language$$endif$">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>$if(title)$$title$ - $site_title$$else$$site_title$$endif$</title>
  <link rel="icon" href="$favicon$" type="image/x-icon">
  <link rel="stylesheet" href="/styles/index.css">
  <meta name="description" content="$if(description)$$site_description$,$description$$else$$site_description$$endif$">
  <meta name="keywords" content="$if(keywords)$$site_keywords$,$keywords$$else$$site_keywords$$endif$">
  <meta name="authors" content="$if(authors)$$site_authors$,$authors$$else$$site_authors$$endif$">
</head>

通过有限地修改 styles/index.css 样式中的根变量达到自定义配色、字体、大小等需求。所有根变量及默认值如下:

:root {
  --nav-w: 16.25rem;
  --font: "Inter", system-ui, sans-serif;
  --mono: "JetBrains Mono", "Fira Code", monospace;

  --bg: #fafaf9;
  --bg-secondary: #f4f3f0;
  --bg-code: #f0efec;
  --text: #1a1a18;
  --text-muted: #6b6b66;
  --text-faint: #a8a8a2;
  --accent: #d49a84;
  --accent-bg: #f5e8e0;
  --accent-light: #eaccbf;
  --border: rgba(0, 0, 0, 0.08);
  --border-strong: rgba(0, 0, 0, 0.14);

  --bg-dark: #141413;
  --bg-secondary-dark: #1e1e1c;
  --bg-code-dark: #252523;
  --text-dark: #e8e8e4;
  --text-muted-dark: #9a9a94;
  --text-faint-dark: #5a5a56;
  --accent-dark: #f0c4b0;
  --accent-bg-dark: #2a1e1a;
  --accent-light-dark: #3d2d26;
  --border-dark: rgba(255, 255, 255, 0.07);
  --border-strong-dark: rgba(255, 255, 255, 0.12);
}

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

不要忘记将你的新变量添加到 extensions.use-css

[extensions]
use-css = ["""
:root {
  --accent: pink;
}
"""]

当然,自行大改样式文件也可以,但不在此处的讨论范围。

主页的本质上是源目录 /index.md 渲染为 /index.html。在 MarkDown 中可自由地使用 CSS 与 HTML 定制化你的主页。

当然,若你想更自由一点,可直接在源目录重命名 /index.md 并新建 /index.html,在其中完全不受限制于既定文档书籍页面骨架。

Caution

应当强调的是,此处的 HTML 均不是模板环境而是普通的 HTML 文件,意味着模板变量、模板语法完全不可用。

不过,最重要的是应将 /index.html 添加到 extensions.assets 中,否则将不会被 MoPress 处理。

同理,新建 /404.md/404.html 即可。如本书的:

---
title: 404
---
<style>
  :root {
    --red: #ffba9c;
    --red-bright: #e7b5a0;
  }
  .wrap { padding: 2rem; }
  .code {
    font-size: 6rem !important;
    font-weight: 700 !important;
    line-height: 1 !important;
    background: linear-gradient(135deg, var(--red), var(--red-bright)) !important;
    -webkit-background-clip: text !important;
    background-clip: text !important;
    color: transparent !important;
    margin: 0 !important;
  }
  .msg {
    font-size: 1.1rem !important;
    color: var(--text-muted, #6b6b66) !important;
    margin: 0.75rem 0 1.75rem 0 !important;
  }
  .center { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; }
  .btn {
    padding: 0.6rem 1.4rem;
    border-radius: 0.5rem;
    text-decoration: none;
    font-size: 0.95rem;
    transition: opacity 0.15s;
  }
  .btn:hover { opacity: 0.85; }
  .btn-primary {
    background: var(--red);
    color: #fff;
  }
  .btn-secondary {
    background: var(--bg-secondary, #f4f3f0);
    color: var(--text, #1a1a18);
    border: 0.5px solid var(--border-strong, rgba(0,0,0,0.14));
  }
  .dark .btn-secondary {
    background: var(--bg-secondary-dark, #1e1e1c);
    color: var(--text-dark, #e8e8e4);
    border-color: var(--border-strong-dark, rgba(255,255,255,0.12));
  }
</style>

<div class="wrap">
  <p class="center code">404</p>
  <img src="https://img0.huoshen80.top/i/2026/07/11/6a523e94c0adb.png" alt="404" class="center" width="200" height="200">
  <p class="msg center">呜哇...你要找的页面她走了(ノ`Д)ノ</p>
  <div class="center">
    <a class="btn btn-primary" href="/">返回首页</a>
    <a class="btn btn-secondary" href="javascript:history.back()">返回上一页</a>
  </div>
</div>

通过 extensions.import-jsextensions.use-js 可以引入 JavaScript 代码为页面添加各种功能、效果,如鼠标点击、整页粒子效果等。

通过 Web Components 与外部协议插件可以自定义更多的 MarkDown 语法及功能。