HEXO 开发笔记(11)自建主题:视觉系统重构
创建于 2026-09-06
更新于 2026-09-09
科技
hexo
theme
css
javascript
accessibility
4185 字 · 约 14 分钟

前言

前面几篇已经介绍了主题的目录、搜索、评论和文章加密等功能。最近继续调整页面时,发现各个组件的颜色和边框不太统一,日间模式还有一些配色没有处理好,侧边栏和页脚在窄屏、短窗口下也有布局问题。

所以这次把相关样式和脚本一起整理了一遍。背景仍然保留星空,补充日间配色和日月切换,同时调整文章的滚动区域。本文记录具体改法,以及布局调整中遇到的几个问题。

一、整理配色配置

1.1 减少重复的颜色配置

旧主题把背景、正文卡片、侧边栏、按钮、代码块、边框和阴影都暴露为单独的配置项。开始看上去很灵活,但实际维护时有两个问题。

修改夜间背景时,还要同步调整侧栏、卡片和按钮;新增搜索弹窗或评论组件时,又容易漏掉日间颜色。各处单独配置,也容易出现文字和背景对比度不够、相似按钮颜色不同的问题。

这次减少了对外配置项,保留外观模式、两种强调色、基础字号、正文宽度和侧栏宽度。旧版逐组件设置颜色的配置需要随之调整。

yaml
1
2
3
4
5
6
7
8
9
style: appearance: night # night | day | system accent: "#E7A63A" accent_secondary: "#73C4F5" typography: font_size: "16px" layout: content_width: "46rem" sidebar_width: "18rem"

其余颜色在主题内部通过 CSS 变量管理,按背景、正文、边框等用途区分。卡片、侧栏、搜索框引用对应变量,修改配色时就不用逐个查找组件样式。主要变量如下:

text
1
2
3
4
5
6
7
8
--dt-canvas 页面天空与底色 --dt-surface 文章等主要表面 --dt-chrome 顶栏、侧栏、页脚的低对比度表面 --dt-control 卡片内的次级按钮或 chip --dt-text / muted 正文与弱文字 --dt-border 共享边界 --dt-accent 当前状态和主操作 --dt-accent-secondary 链接与辅助定位

切换昼夜模式时,统一修改这些变量的取值,搜索、目录、评论、代码块和静态页面便能一起切换配色。

1.2 日间配色与日月切换

主题默认使用夜间模式,背景为深靛蓝,保留星光和流星,按钮与链接使用金色、冷蓝色。日间模式改用偏暖的浅色背景,加入日晕和云层光尘,并调整文字、卡片和边框颜色。

外观策略支持 nightdaysystem。前两者固定初始状态,system 读取浏览器的 prefers-color-scheme。页头的圆形日月按钮只保存浏览器本地偏好,不回写主题 YAML;这样站点默认策略和访问者个人选择可以共存。

日月本身也不再是两个始终并列的图标。背景中使用一个超出屏幕的轨道作为旋转面,切换时月亮沿轨道下沉、太阳从另一侧进入;按钮则只保留当前可见的一个符号。日间画布补充缓慢移动的云层和日晕,夜间保留星空与流星。持续动画在页面不可见、用户启用减少动效或屏幕过窄时会降低或停止,避免把装饰变成阅读负担。

二、整理模板与浏览器脚本

2.1 Pug 只描述页面结构

这一轮曾暴露出一个很典型的问题:页面模板一边负责输出 HTML,一边绑定点击事件、拼接小段脚本。短期很快,长期会让行为散落在搜索、文章、侧栏和静态页面中,难以测试,也很难判断某次样式调整是否改坏了交互。

因此模板只保留语义结构、可访问性属性和模块所需的数据属性。例如搜索区域作为页面根层的 dialog 输出,而不是被锁在 header 内;目录/站点概览切换按钮只提供当前标签和状态;加密文章只输出密码表单与错误区域。事件绑定、焦点管理和状态同步都交给浏览器模块。

搜索、外链和加密功能分别做了以下调整:

  1. 搜索对话框可以在全屏遮罩上居中,打开时隔离背景焦点,按 Escape 关闭,并把焦点还给触发按钮。
  2. 外链跳转拦截可以在全局注入脚本中一次处理,而不是在每篇文章生成时改写链接或重复绑定监听器。这样不会把正常外链在构建期改成重定向地址,避免影响文章的原始链接和 SEO。
  3. 二维码和文章解密等行为可以独立加载。模板不再内嵌密钥相关的运行时逻辑,错误提示也使用页面内的 role="alert",而不是浏览器原生弹窗。

2.2 浏览器代码按职责拆分

浏览器端仍以 main.js 为入口,但入口只负责初始化。背景、外观、header、侧栏、二维码、加密、对话框和外链处理分别位于对应模块中;通用的焦点隔离和对话框生命周期归入 utils/

text
1
2
3
4
source/js/ ├── layout/ # 背景、外观、顶栏、侧栏等页面骨架 ├── features/ # 二维码、加密等可选能力 └── utils/ # dialog、外链拦截、滚动等通用行为

调整后,页面结构在 Pug 中修改,颜色和布局在 CSS 中修改,事件绑定在浏览器 JS 中处理。Hexo 的过滤器和 injector 负责构建时的生成与注入,排查问题时也可以按这几处分别查找。

三、重新组织页面骨架

3.1 让页脚固定,文章独立滚动

传统文档流中,页脚会跟随文章内容向下移动。对长文来说没有问题,但在这个主题里,顶栏、侧栏、阅读进度和回到顶部都已经是页面框架的一部分;继续让整个 body 滚动,容易造成组件各自监听不同滚动源。

这里把主容器高度设为 100dvh,顶栏和页脚固定在页面上下两端,#content-wrapper 使用 flex: 1min-height: 0 填满剩余空间,文章在这个区域内滚动。阅读进度、目录高亮和回顶的事件源也一并改到内容区域。

text
1
2
3
4
5
6
7
8
┌──────────────────────────────────────────┐ │ header │ ├──────────────┬───────────────────────────┤ │ sidebar │ content-wrapper(滚动) │ │ │ └── article stage │ ├──────────────┴───────────────────────────┤ │ footer │ └──────────────────────────────────────────┘

这里要特别区分宽度与高度。屏幕窄不等于窗口矮:小于桌面阈值时,侧栏转换为抽屉或隐藏入口;高度较短时,则只压缩顶栏、页脚和辅助内容,不能误把桌面窗口切成手机布局。触摸设备通过 pointer: coarse 增大操作目标,而不是只凭宽度猜测输入方式。

3.2 目录不再用符号模拟层级

文章目录曾在最左边使用 >> 作为前导符。它有明显的旧式终端感,但放在新的低对比度轨道中显得过于突兀,也无法自然表达当前阅读位置。

新目录使用一条弱对比度的垂直轨道和圆点节点:普通节点保持弱文字颜色,hover 和当前章节使用强调色与轻微的光晕。层级仍由缩进和编号配置表达,不再依赖重复的装饰符号。桌面侧栏中的目录与站点概览共享同一个切换入口,按钮文字随当前视图变化,避免“按钮写着站点概览,下面却显示文章目录”的状态错位。

3.3 给日月背景留出空间

背景右侧的日月天体需要空间,但内容卡片不能因此看起来向侧边栏倾斜。这个问题在宽屏上尤其明显:第一次实现使用了安全区加 translateX 左移,虽然右侧空出来了,文章舞台却不再处于主容器中心。

后来去掉了左移,改为计算正文可用宽度。设当前主容器可用宽度为 W,主题可读宽度为 S,单侧天体安全距离为 C,则桌面正文区域宽度为:

text
1
stage = min(S, W - 2C - gutter)

左右对称扣除 C 后,卡片仍使用 margin: auto 居中。右侧不会贴近天体,左侧也不会被推向侧边栏;在侧栏隐藏的窄屏断点,安全距离归零,恢复普通单列宽度。4K 宽度下再设置单独的舞台上限,避免长文一行过长。

四、检查搜索、评论和静态页面

首页和文章页往往最先得到关注,但主题真正容易出问题的地方是低频页面和可选功能。这次样式令牌与页面骨架调整后,也逐项检查了以下内容:

范围 本轮关注点
本地/Algolia 搜索 对话框置于全局层;两种后端共享触发入口、结果表面、背景遮罩、模糊、焦点隔离与语言包文案,不让本地搜索残留中文固定文案
评论 Gitment、Valine、Twikoo 等第三方容器使用统一表面、文字与控制器令牌
加密文章 密码表单、错误提示和解密后的内容延续文章表面层级
404、隐私、条款与重定向 使用相同的标题、空状态和弱文字规则,不再像独立页面
二维码与赞赏 提示文案允许回退到当前语言,避免主题默认中文覆盖英文页面
滚动条 内容区与侧栏分别使用昼夜令牌,而不是浏览器默认白色滚动条

国际化同样不能只翻导航菜单。页面标题、搜索占位符、空结果、倒计时、加密提示、二维码和赞赏提示都应该优先读取语言包;配置留空时才回退到该语言的默认文本。这样站点可以用一份主题配置切换语言,而不会在英文页面里突然出现“本地搜索”几个中文字。

五、构建与页面检查

主题包含 Pug、Stylus、浏览器 ES Module、Hexo filter、injector 和 generator。直接执行某个 JS 文件,无法证明 Hexo 的配置合并、页面生成、资源注入和最终选择器仍然正确。因此这轮验证一直以 Hexo CLI 为核心。

bash
1
2
3
4
5
6
# 在博客根目录 npm run clean npm run build # 在主题目录 node tests/theme-contract.test.js

契约测试覆盖了子路径资源、外观策略、搜索与静态页面开关、加密、站点地图、robots、本地搜索、语言包和 themeinit 配置初始化等场景。它不替代浏览器测试,但可以防止模板/配置/注入层在重构后悄悄失配。

浏览器端则分别检查首页、长文、带目录文章、归档、标签/分类、搜索、404、加密和重定向页面,并覆盖夜间、日间和系统跟随模式。重点视口包括常规桌面、短高度窗口、横向平板、手机和 4K 宽屏。对于天体安全区这类几何问题,还需要在真实构建页面中检查卡片中心线、侧栏距离和天体边界,而不能只相信 CSS 的字面公式。

六、小结

这次主要补齐了日间配色,整理了颜色配置和脚本位置,并把文章滚动、目录进度和回顶统一到内容区域。修改主题布局时,短窗口和宽屏都需要单独检查,尤其是日月背景与正文之间的留白,只看常规桌面尺寸容易漏掉问题。

手机扫码阅读
本文作者: 有次元袋的 tiger
本文链接: https://www.superheaoz.top/2026/09/52265/
版权声明: 本站点所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 我的个人天地