Skip to content

VitePress 博客 Yohaku 风格重构:从 Skills 设计契约到 960px 长文版式

一次围绕留白、排版、文章阅读体验与可维护性的系统性改造

这次改造最终落在两个层面:仓库有了用于沉淀 SKILL.md 和设计知识的 skills/ 目录;博客则从原有的 VitePress 视觉样式,迁移为一套以留白、纸张感、克制色彩和长文阅读为核心的 Yohaku 风格主题。

它不是一次简单的“换颜色”。改动覆盖了设计令牌、字体职责、首页信息层级、文章元数据、阅读时间、正文宽度、左右导航、归档列表、代码块、深色模式和移动端布局。文章详情页还参考了 Yohaku Demo Post 的内容节奏,并使用仓库内 skills/design-system/ 的设计契约约束最终实现。

最终目标只有一个:让界面退到内容之后,让文章成为页面里最重要的东西。

一、先建立可复用的 Skills 入口

仓库根目录新增了 skills/,用于放置可以被人和 AI 同时阅读的技能说明、设计规范、参考资料与检查脚本。

当前的 skills/design-system/ 已经不只是一个说明文件,而是一套相对完整的 Yohaku 设计资产:

  • SKILL.md:描述适用场景、工作流程和必须遵守的约束;
  • CHEATSHEET.md:提供颜色、排版、组件和常见组合的速查表;
  • references/:详细说明令牌、字体、组件和反模式;
  • src/tokens.css:保存设计系统的静态设计令牌;
  • templates/showcase/:提供可直接观察和复用的页面样例;
  • scripts/check.ts:检查禁用色阶、硬编码字体和令牌漂移。

这让“Yohaku 风格”不再依赖模糊的视觉印象,而是变成一份可以执行的设计合同。博客主题中的颜色、字体、阴影和组件状态,都可以追溯到这份合同。

二、Yohaku 设计语言如何落到博客里

Yohaku 的核心不是日式装饰,而是做减法。设计系统用一句话概括就是:

一种主色,三档中性层级,剩下都是留白。

1. 纸张、暖灰与梅红

主题在 docs/.vitepress/theme/styles/vars.scss 中建立了一套新的语义令牌:

角色令牌或颜色用途
纸张背景#fefefb页面主背景,避免纯白带来的刺眼感
表面层neutral-1neutral-4卡片、代码块、悬停和弱分区
边界与辅助信息neutral-5neutral-7分割线、图标、日期、说明文字
正文与标题neutral-8neutral-10正文、强调文字和最高层级标题
主色#c56473梅红,用于焦点、品牌标记和引用线
深色主色#e095a4深色模式下提高可读性的浅梅红

正文默认使用 neutral-9,标题使用 neutral-10,辅助信息使用 neutral-7neutral-5 只承担边框或弱图标职责,不拿来承载常规文字。

主色也被严格限制在较小的视觉面积中。它出现在首页圆点、活动导航线、按钮、文章分类、二级标题短线、链接下划线和引用块左边框上,而不会铺满大块背景。这样可以保留识别度,又不会破坏页面的安静感。

浅色模式使用略带暖意的中性色阶;深色模式则切换为更纯的灰阶,并只通过纸张背景保留温度。原因是低亮度下的暖灰很容易看起来偏橄榄色,反而会让界面显脏。

2. 三种字体承担三种职责

主题把字体拆分为清晰的三层:

  • 无衬线字体负责导航、按钮和功能性界面;
  • Noto Serif SC 等衬线字体负责标题与正文阅读;
  • 等宽字体负责日期、眉题、阅读时间、代码和统计信息。

中文标题没有使用过重的粗体,而是以 400500 的字重、较紧的字距和更充足的行高建立层级。这比单纯放大、加粗更符合 Yohaku 的克制感。

3. 阴影退场,边界与留白接管层级

旧样式中的强阴影和装饰性代码块外壳被移除。新的层级主要依赖:

  • 1px 的低对比度边框;
  • 不同中性色表面;
  • 纵向留白;
  • 字体、字号与字距;
  • 极轻的图片阴影。

这种做法让首页、列表和正文在视觉上属于同一个系统,也减少了“通用 SaaS 模板感”。

三、首页从组件堆叠变成编辑式版面

首页的主要实现集中在 VPHome.vueHomePostsPreview.vuePostCard.vue

原来的 HomePreview.vue 被删除,docs/index.md 也只保留首页布局声明。首页展示逻辑全部回到 Vue 组件中,避免在 Markdown、默认主题配置和自定义组件之间重复维护。

1. 新的首页首屏

首屏采用左右不对称布局:

  • 左侧是站点眉题、主标题、说明文字和两个行动入口;
  • 右侧是一张较窄的题记卡片;
  • 两部分之间依靠 80px 的间距和底部分割线建立节奏;
  • 主标题使用衬线字体,强调“在日常与代码之间,留一页纸给时间”;
  • 页面宽度控制在 960px,避免超宽屏上信息过度分散。

右侧题记没有使用卡片阴影,只保留顶部细线、编号、短句和一条梅红色短线,让它更像纸页边缘的旁注。

2. 最近文章列表

首页最多展示 10 篇文章,并按年份倒序取出最新内容。列表会根据当前年份是否已经完整展示,动态决定“继续阅读”的目标年份和提示文案。

桌面端文章卡片采用三列:

text
日期 112px | 标题与摘要 1fr | 箭头 24px

移动端则把日期移动到标题上方,隐藏不必要的横向结构。悬停时只出现非常轻的表面变化,箭头向右移动 4px,主色只用于交互反馈。

四、文章详情页成为真正的长文阅读页

文章页是本次改造最集中的部分。它先参考 Yohaku Demo Post 建立基本结构,随后又经过几轮针对阅读空间的调整:

  1. 移除左侧目录结构;
  2. 移除右侧 CONTENTS 标题导航模块;
  3. 扩大正文区域;
  4. 正文字号整体增加约 2px;
  5. 把“容器最大宽度 960px”修正为“文字内容净宽 960px”。

1. 只对真正的文章详情页启用阅读模式

新增的 docs/.vitepress/utils/page.ts 用路径判断页面类型:

ts
export const isPostDetail = (relativePath: string) =>
  relativePath.startsWith("posts/") &&
  relativePath.endsWith(".md") &&
  !isPostIndex(relativePath);

这条规则非常重要。它让 163 篇文章详情页进入 Yohaku 阅读模式,但不会误伤 11 个年份或分类索引页。索引页仍然保留原有导航能力,详情页则专注于阅读。

2. 在构建阶段补齐阅读数据

docs/.vitepress/config.ts 新增 transformPageData。VitePress 在构建页面时读取 Markdown 源文件,去除 Frontmatter、代码块、HTML 标签和 Markdown 符号,然后分别统计中文字符与拉丁单词:

ts
const minutes = Math.max(
  1,
  Math.ceil(chineseCharacters / 400 + latinWords / 200),
);

中文按每分钟约 400 字、英文按每分钟约 200 词估算。最终得到的 wordCountreadingTime 被写回页面 Frontmatter,交给文章头部和尾部展示。

同一阶段还会为文章详情页自动设置:

ts
sidebar: false;
aside: false;
outline: false;
pageClass: "yohaku-post-page";

因此左侧 Sidebar、右侧 Outline 和窄屏下的目录下拉都会一起关闭,不需要在 163 篇旧文章中逐个添加配置。

3. 文章头部与尾部

ArticleHeader.vue 重新组织了文章开头的信息层级:

  • 分类、日期和预计阅读时间位于最上方;
  • 主标题桌面端为 40px,移动端为 36px;
  • 副标题为 18px;
  • 作者区域包含头像占位、作者名、日期与站点标识;
  • 有封面图时继续支持图片和来源说明。

ArticleFooter.vue 是新增组件,用于在正文结束后展示标签、字数和预计阅读时间。它与 VitePress 原有的上一篇、下一篇导航共同组成文章收尾。

4. 正文字号与阅读节奏

最终文章正文的排版参数是:

元素桌面端移动端
正文17.5px / 1.817px / 1.78
一级标题40px36px
副标题18px18px
二级标题22px22px
三级标题18px18px

正文首段增加了首字下沉,二级标题前增加一条 14px 的梅红短线;段落、引用、列表和图片的上下间距也重新收紧,使页面在大字号下仍然保持连续,不会显得松散。

5. 960px 指的是正文净宽

最后一次布局调整解决了一个容易混淆的问题:max-width: 960px 如果设置在带有左右 padding 的外层容器上,真正留给文字的宽度会小于 960px。

现在桌面端采用两层宽度:

scss
.VPDoc.is-post-detail .container {
  max-width: 1024px;
}

.VPDoc.is-post-detail .content {
  box-sizing: border-box;
  max-width: 1024px;
  padding: 0 32px 128px;
}

.VPDoc.is-post-detail .content-container {
  width: 100%;
  max-width: 960px;
}

尺寸关系为:

text
外层内容区域 1024px
- 左侧安全留白 32px
- 右侧安全留白 32px
= 正文文字净宽 960px

也就是说,最终测量中的 .content-container.vp-doc 都是 960px,960px 不包含左右 Padding。安全留白由外层 1024px 容器承担。

在小屏幕上不会强制保留 960px,而是继续使用视口内边距和 width: 100%,因此不会产生横向滚动。

五、归档与年份索引同步统一

如果只改文章详情页,用户从首页进入归档时仍然会感到像到了另一个站点。因此 Archives.vueYearIndex.vue 也同步迁移到 Yohaku 语言。

文章归档

归档页按照年份分组:

  • 年份在桌面端保持粘性定位;
  • 每篇文章显示月日、标题和箭头;
  • 行之间使用轻边框,而不是独立卡片;
  • 页面头部展示文章总数和浏览说明。

年份索引

年份页使用 COLLECTION · YEAR 作为眉题,展示标题、说明和文章数量。列表中的日期、标题和箭头沿用首页文章卡片的排版逻辑,同时保留上一年、下一年的导航。

这样首页、总归档、年份索引和文章详情形成了一条连续的阅读路径。

六、代码块也回到“内容优先”

代码高亮主题从单一的 nord 改为:

ts
theme: {
  light: "github-light",
  dark: "github-dark",
}

code.scss 同时进行了简化:

  • 移除强阴影和类似 macOS 交通灯的装饰;
  • 隐藏全屏装饰按钮;
  • 使用 Yohaku 边框、圆角和表面色;
  • 保留语言标签、行号和高亮行;
  • 行内代码使用轻背景与 1px 边框;
  • 深浅模式自动使用对应的 Shiki 配色。

代码仍然醒目,但不会比文章正文更抢眼。

七、深色模式与响应式不是附加项

这次主题从设计令牌层就同时定义了浅色和深色,而不是在页面完成后再补一层颜色覆盖。

深色模式下:

  • 背景切换为 #181715
  • 中性色阶改为纯灰反转;
  • 主色提高亮度;
  • 导航背景保留半透明与轻微模糊;
  • 代码块、分割线和图片阴影使用各自的深色令牌。

响应式方面则重点检查了首页双栏、文章卡片、归档年份、文章头部和 960px 阅读容器。桌面端扩大阅读空间,移动端重新排列信息,而不是简单地按比例缩小。

八、改动文件与职责

主题改造涉及 19 个已有文件的调整,并新增了 3 个主题支持文件、删除了 1 个不再使用的首页组件。

范围主要文件
设计令牌styles/vars.scss
全局 Yohaku 规则styles/yohaku.scssstyles/index.scss
导航与文档样式styles/layouts/nav.scssstyles/layouts/content.scss
代码块styles/components/code.scss
页面分类与构建数据utils/page.tsconfig.ts
文章结构Layout.vueVPDoc.vueArticleHeader.vueArticleFooter.vue
首页VPHome.vueHomePostsPreview.vuePostCard.vue
归档Archives.vueYearIndex.vue
文章数据posts.data.ts
Skills 设计契约skills/design-system/

实现全部建立在现有 Vue、VitePress 和 Sass 能力之上,没有为了视觉改造增加新的运行时依赖。

九、验证结果

完成样式与布局调整后,执行了完整的生产构建:

bash
npm run docs:build

验证范围包括:

  • 163 篇文章详情页全部进入无左侧栏、无右侧目录的阅读模式;
  • 11 个年份或分类索引页继续保留正常导航;
  • 桌面端正文测量结果为 960px,不包含左右各 32px 的安全留白;
  • 移动端正文没有横向溢出;
  • 首页、归档、年份页和文章页均支持浅色与深色模式;
  • 新增阅读时间、字数、标签与文章导航可以正常生成;
  • VitePress 生产构建通过。

十、这次重构留下的维护原则

这次改造最重要的产物不是某个页面,而是一套后续可以继续遵守的规则:

  1. 先改设计令牌,再改局部组件;
  2. 让字体角色表达信息层级,不依赖大面积颜色;
  3. 主色只负责焦点,不负责填满页面;
  4. 用边框和留白建立层级,避免硬阴影;
  5. 文章详情与索引页面分开判断,避免全局样式误伤;
  6. 宽度需求必须明确是外层宽度、内容盒宽度,还是文字净宽;
  7. 每次版式调整都同时验证桌面端、移动端和深色模式;
  8. skills/design-system/ 成为设计决策的来源,而不是把规则散落在不同组件里。

Yohaku 的“余白”并不是空,而是主动为文字让出空间。经过这次重构,首页负责邀请,列表负责引导,文章页负责安静地承载内容;设计本身则退到背景中,只在需要的时候出现。

Released under the CC BY-NC-ND 3.0