阅读体验
主题在“读”这件事上做了四件事:一条进度条、一个带编号的页内目录、加工过的代码块,以及 文章卡片上的阅读时间与字数。这一页说明每一项的行为与开关。
阅读进度条
页面顶部有一条 2px 的进度条,随滚动前进。
theme: githubTheme({
progressBar: false, // 关掉
})- 它由滚动位置驱动,宽度写在一个 CSS 变量上,滚动期间不会触发 Vue 重渲染
- 滚动与尺寸变化都合并进
requestAnimationFrame,一帧最多测量一次 - 纯装饰:
aria-hidden,不进入无障碍树,也不会读给屏幕阅读器 - 换页时归零,新页面从顶部开始
只在页面够长时才看得见
文档比视口短的时候,它始终是空的 —— 因为没有可滚动的内容,进度本来就是 100% 或没有意义。
页内目录
右侧目录由页面实际渲染出来的标题生成,所以它不会列出不存在的章节。
- 深度由
sidebarDepth决定(2–6),总是从h2开始 ——h1是页面标题 - 更深的标题嵌套成树,折叠时仍保留在 DOM 中,每个条目都保留自己的锚点
- 嵌套标题默认带大纲编号:
1.、1.1.、2.1.。已经以数字开头的标题不动,页面第一个 标题也不动(GitHub 的行为) - 激活项按滚动位置计算,并把激活的深层条目滚进可视区
- 指针悬停在目录上时暂停跟随 —— 这样点击深层条目时高亮不会在脚下跳走
- 键盘聚焦同样会暂停跟随,
Tab浏览目录时不会跳
---
toc: false # 本页隐藏
numbered: false # 本页目录不编号
sidebarDepth: 3 # 本页目录深度
---theme: githubTheme({ numbered: false }) // 全站关掉编号代码块
每个代码块会在客户端被加上两样东西:
- 右上角的语言标签 —— fence 里声明的那个(
```js→js) - 右下角的复制按钮 —— 悬停或键盘聚焦时出现,触屏常显,复制后短暂变成“已复制”
theme: githubTheme({
codeBlockLanguage: false, // 关掉语言标签与复制按钮
codeBlockLabel: '复制代码',
codeBlockCopied: '已复制',
})实现细节值得知道一点,因为它解释了行为:
- 锚点是
pre > code,所以有没有代码高亮器都能用 —— 关闭 shiki 与 prismjs 时渲染器 会输出<pre><code class="language-js">(没有外层div),按钮照样在 - 没写语言的 fence 只有按钮、没有标签。渲染器会给它
language-plain或language-text, 主题不会把这两个当成真实语言显示出来 - 复制用事件委托:整页共用一个监听器,一页 200 个片段也不会多 200 个监听器
复制按钮需要安全上下文
浏览器只在
https:// 或 localhost 下提供 navigator.clipboard。主题为 http:// 站点准备了 document.execCommand 兜底,所以本地用 IP 访问也能复制。阅读时间与字数
文章卡片上,日期后面跟着预估阅读时间和字数:
2026 年 3 月 1 日 · 预计阅读 3 分钟 · 812 字两个数字都在构建期算好,因为客户端拿不到 markdown 正文(payload 只带聚合后的列表)。 它们来自同一趟统计,所以永远不会互相矛盾。
估算规则:
- 英文 200 词/分钟;中日韩文字按字计,400 字/分钟
- 代码块算进字数(一篇全是代码的页面也要读)
- HTML 标签、标签属性、
script/style内容不算 - HTML 实体算一个字符(
&不是amp三个字母)
文案全部可本地化:
theme: githubTheme({
blog: {
readingTime: '预计阅读 :minutes 分钟',
words: ':words 字', // 不设就不显示字数
},
})无障碍
阅读相关的一切都照顾了键盘与屏幕阅读器:
- 页内目录的每个条目的锚点始终在 DOM 里,不会被
display: none藏掉 - 代码复制按钮有可读名称,
Tab到即显示 - 进度条不进入无障碍树
- 所有动画与平滑滚动都尊重
prefers-reduced-motion
细节与自查清单见 无障碍。
