本文面向已经能够正常运行 Hexo + Shoka 的读者。先做出一个小而完整的设置面板,再理解本站增强版如何扩展;不需要服务器、数据库、API Key 或第三方账号。
#前言
有人喜欢有封面的背景,有人更喜欢安静的纯色页面;有人喜欢动态反馈,也有人需要减少运动效果。把选择权交给读者,比让所有人使用同一种显示方式更合适。
这篇文章沿用本站统计页面教程的组织方式:先说明效果和前置条件,再列文件、实现步骤,最后给出验收与排错方法。不要一开始就覆盖整套主题,也不要直接编辑 public/ 。
基础示例和本站增强版的区别:配套的两个文件实现一个独立、精简、可运行的教学版。它包含设置弹窗、动效选择、简洁背景、两种纹理、0%–100% 浓度、HEX 配色和桌面双列。本站还有六种首页排列、更多纹理、四种侧栏背景、RGB 调色盘及更完整的灵动交互,这些在后半篇说明接入结构,不能只复制两个基础文件就声称全部复刻。
#一、先确定改什么、不改什么
| 本文基础版会做的事情 | 不会做的事情 |
|---|---|
| 在右下角显示设置入口 | 不修改文章 Markdown |
| 点击打开原生对话框,支持 Esc 关闭 | 不新增评论或访问统计请求 |
| 保存当前浏览器的显示偏好 | 不跨设备同步,也不要求登录 |
| 修改背景纹理、浓度、示例强调色 | 不上传偏好或读取个人文件 |
| 桌面首页双列,窄屏保留原布局 | 不改归档、分类、标签和文章正文布局 |
| 关闭动效、恢复默认、同步其他标签页 | 不给独立应用页面注入博客代码 |
“显示与阅读设置”与“本文阅读”应分开:前者负责全站外观,后者才负责单篇文章的字号、段落间距、专注阅读和书签。不要把所有文字统一设置字号,否则代码、表格和目录也会被意外放大。
#二、准备工作与文件清单
#1. 先确认原站正常
在自己的博客目录运行:
hexo generate --bail | |
hexo server |
打开首页和一篇文章,确认原本能正常显示。下面用 themes/shoka/ 代表主题目录;如果你的主题目录不是这个名称,按自己的情况调整。
备份即将修改的模板,或先提交到自己的 Git 分支。已经装有本站增强版设置面板的站点,不要再装基础示例,否则会出现两个入口;直接参考第七节维护已有模块。
#2. 下载两个完整文件
这是公开的通用示例,没有真实域名、服务器配置、用户文章、账号或密钥。JavaScript 不请求网络,CSS 不下载字体和图片。
把它们分别放到:
你的博客/ | |
└── themes/shoka/ | |
├── layout/_partials/head/head.njk # 只追加引用,不覆盖 | |
└── source/ | |
├── js/display-settings.js # 新增 | |
└── css/display-settings.css # 新增,普通 CSS |
不要把普通 CSS 文件改成 .styl ,也不需要安装新的 npm 依赖。
#三、先接入,再检查面板能不能打开
#1. 在 head 里引用
找到主题实际输出 <head> 的模板。本例是 themes/shoka/layout/_partials/head/head.njk 。在原有主题样式引用之后追加下面两行,不要替换原内容:
<link rel="stylesheet" href="{{ url_for('/css/display-settings.css') }}"> | |
<script src="{{ url_for('/js/display-settings.js') }}"></script> |
这里刻意没有给这个小脚本添加 async 或 defer :它会先从本地存储恢复页面属性,减少“默认背景闪一下再变成已选背景”的现象;创建弹窗则等 DOM 准备好再执行。
url_for 用来处理 Hexo 的站点路径。先用同源文件跑通,再考虑 CDN。如果模板已经有 _css 、 _js 或内容指纹助手,可以使用自己主题已有且经过验证的助手,但不要把其他主题不存在的函数直接复制进来。
#2. 重新生成
hexo clean | |
hexo generate --bail | |
hexo server |
如果本机已有正在生成或提供文件的进程,先退出再清理,避免文件占用。不要为解决占用问题删除整个项目。
打开首页,右下角应该出现“显示设置”。点击后能看到面板;按 Esc 能关闭,焦点应回到入口。检查生成目录中有:
public/css/display-settings.css | |
public/js/display-settings.js |
没有这两个文件时,先修正源文件位置,而不是在浏览器里反复刷新。
#四、看懂状态保存:一个入口管理所有选项
#1. 只接受白名单,不让保存的数据变成代码
基础示例把状态保存在 shoka-display-tutorial-v1 。下面是核心设计,完整实现已经在下载文件中:
const defaults = { | |
motion: 'system', background: 'banner', layout: 'list', | |
texture: 'none', strength: 12, primary: '', secondary: '' | |
}; | |
const enums = { | |
motion: ['system', 'reduce', 'fluid'], | |
background: ['banner', 'plain'], | |
layout: ['list', 'grid'], | |
texture: ['none', 'dots', 'grid'] | |
}; |
保存值必须先校验。非法选项回到默认;浓度取整并限制在 0~100;颜色只接受 # 加六位十六进制字符。不能把本地存储中的任意字符串直接塞进 innerHTML 、CSS 或脚本。
0 是合法值,表示纹理透明。不要写成 Number(value) || 12 ,否则用户选择 0% 后会被错误恢复成 12%。空值、坏 JSON 和无法访问存储则单独处理。
#2. 修改 DOM 属性,不修改文章内容
例如选择细点纹理后,根元素会带有:
<html data-dp-texture="dots" style="--dp-strength: 0.3;"> |
CSS 根据属性选择效果。选择默认时删除相应属性,让原主题接管,而不是手工猜测每个元素“原来应该是什么样子”。
基础示例使用独立的 data-dp-* 、 --dp-* 和存储键,避免与已存在的功能重名。它只改变显式选中的部分,默认不改变正文。
#3. 保存失败也要给反馈
无痕模式、浏览器隐私设置或存储空间不足都可能使 localStorage 不可用。面板应继续在当前标签页生效,并提示“无法保存”,而不是假装已经永久保存。
恢复默认只删除本功能自己的键,不能调用 localStorage.clear() ,否则可能连评论登录、音乐进度、写作草稿一起删除。
#4. 两个标签页要同步全部状态
storage 事件只通知其他标签页;本页点击必须自己调用 apply() 。另一个标签页收到通知时,应重新读取最新值,一次应用背景、布局、纹理、浓度和配色,不能只同步一个动效字段。
还要处理 pageshow :浏览器“返回”可能恢复旧页面快照,不会重新加载脚本。基础示例已经包含这些监听,并且重复初始化不会创建第二个面板。
#五、背景、纹理与配色怎么做
#1. 背景放在内容下方
基础示例用 main::before 画纹理,不在正文上盖一个全屏蒙层:
html[data-dp-texture] main::before { | |
content: ""; | |
position: absolute; | |
inset: 0; | |
pointer-events: none; | |
opacity: var(--dp-strength, .12); | |
} | |
html[data-dp-texture] main > .inner { | |
position: relative; | |
} |
pointer-events: none 使装饰不拦截点击和选字;正文容器在装饰上方。这里依赖原版 Shoka 的 main > .inner 结构,分支主题如果改过结构,要先在开发者工具核对。若你的 main::before 已用于其他效果,给纹理新增独立装饰层,不要覆盖原功能。
基础版“简洁主题色”只替换主内容外侧背景,不删除封面与标题。本网站增强版会一起处理头图、标题对比度和滚动状态;不要只隐藏头图,却留下适用于图片背景的白色标题。
#2. 0%~100% 的滑块
<label>纹理浓度 | |
<input name="strength" type="range" min="0" max="100" step="1"> | |
<output>12%</output> | |
</label> |
监听 input 而不是只监听 change ,拖动时才能即时预览。同步更新数值、 aria-valuetext 和保存状态。无纹理时禁用滑块,但保留上次浓度,方便切回来。
100% 是最高装饰不透明度,不是“把整个页面染满”。图案仍然只出现在图案原有的线条或圆点上;正文面板保持自己的底色。
#3. 自选颜色不等于全站自动换肤
基础示例的主辅色影响面板、纹理与卡片反馈,使用 --dp-primary 和 --dp-secondary ,没有粗暴覆盖原主题所有颜色。留空或恢复默认后,重新读取主题原色。
要做本站这样的全站调色盘,需要梳理真实主题变量,例如强调色、浅色背景、边框、按钮文字与夜间色阶;由同一套函数计算并应用,不能只替换一个 --primary-color 就宣称全部完成。
尤其不要把成功、错误、警告全部变成主题色:选择题对错、连接红绿灯等需要保留语义颜色。浅色按钮要有足够文字对比度;图表图例不能只依靠颜色区分。
本站增强版相关实现是 lib/display-palette.cjs 、主题颜色辅助代码和 display-preferences.js 中的 HEX/RGB 表单;这些是本站新增文件,不是原版 Shoka 自带接口。
#六、动效和首页排版:范围越清楚,越不容易误伤
#1. 三种动效不要混成一种
- 默认:沿用主题本来的动画。
- 减弱:缩短 CSS 动画和过渡,取消平滑滚动。
- 卡片轻盈反馈:示例只在首页卡片悬停 / 键盘聚焦时增强阴影,不移动正文和侧栏。
示例尊重系统“减少动态效果”,但 CSS 不能自动停止所有 JavaScript、Canvas 或 WebGL 循环。如果站点有这些模块,要订阅状态变化事件,暂停对应循环,恢复时只启动一次:
document.addEventListener('shoka:tutorial-display-change', event => { | |
const reduced = event.detail.motion === 'reduce'; | |
// 在这里调用你已有的动画实例 pause() / resume()。 | |
// 不要每次切换都新建一个 requestAnimationFrame 循环。 | |
}); |
上面是接入位置说明,不是通用的动画引擎控制器。接入时还要合并 matchMedia('(prefers-reduced-motion: reduce)') 的系统偏好;默认恢复后不能忽略系统减少动效设置。
#2. 不给整个侧栏做 transform 动画
侧栏里可能同时有 sticky、fixed、目录高亮、弹层和滑动指示条。给整个祖先容器加 transform 、 filter 或不必要的 will-change ,可能改变定位参照,让标题、底色、图标分开。
本站的轻纱、多层彩带、极光和弧光采用独立装饰层,设置 aria-hidden="true" 与 pointer-events: none 。只对装饰子层做 transform / opacity ,菜单与正文不参与动画。简洁玻璃是静态;本站侧栏背景另有明确约定:显式选择“减弱动效”时停止,不通过这个装饰层反向改变全站其他模块的减弱策略。
#3. 首页双列只针对文章列表
基础 CSS 使用下面这个范围:
html[data-dp-layout="grid"] .index.wrap .segments.posts > .item { | |
width: auto; | |
min-width: 0; | |
} |
完整样式只在 900px 及以上启用双列,窄屏交还原布局。不要写成全站 .item { width: 50%; } ,因为轮播、音乐、相册等也可能使用 .item 。
本站更完整的六种布局还处理了封面裁切、摘要展开、More 按钮、确定性错落高度和移动端回退。每多一个布局,都要检查长标题、无封面、短摘要与分页,不能只看三张等高卡片。
#七、本站增强版的文件对应关系
如果你是在维护与本站相同的增强版,而不是安装上面的基础版,可从下面这些文件进入:
| 文件 | 职责 |
|---|---|
themes/shoka/source/js/_app/display-state.js | 统一白名单、读取、应用、跨标签页与历史恢复 |
themes/shoka/layout/_partials/head/head.njk | 首次绘制前恢复属性,范围必须与运行时一致 |
themes/shoka/source/js/display-preferences.js | 设置面板、颜色反馈、恢复默认 |
themes/shoka/source/css/display-preferences.styl | 设置面板的卡片、图标、窄屏布局 |
themes/shoka/source/css/_common/reading-textures.styl | 内置 SVG 纹理与透明度,不外连图片 |
themes/shoka/source/css/_common/home-layout.styl | 首页多布局,作用域只在文章列表 |
themes/shoka/source/css/_common/outline/sidebar/sidebar.styl | 侧栏背景与各装饰层动画 |
themes/shoka/layout/_macro/sidebar.njk | 侧栏装饰层,保留原菜单结构 |
增强版保存键是 shoka-display-v1 ,事件是 shoka:display-change ,与基础示例故意不同。需要复用时,请统一成一套状态源,不要让两个面板互相覆盖。
2026 年 10 月这一版的纹理浓度已统一为 0%~100%。改范围要同时更新滑块、运行时规范化、首屏恢复、外部控制校验和测试;如果启用了自然语言控制,后端控制提示也应同步。只改 HTML 的 max 会导致用户拖到 100%,保存后却退回旧上限。
这张表是增强版的维护地图,不是原版 Shoka 的可直接复制依赖清单。完整搬运面板还涉及弹窗基座、按需加载、主题调色、构建脚本等;零基础读者先完成本篇基础版更容易判断每个改动的效果。
#八、验收:不能只看面板出现了
| 场景 | 预期 |
|---|---|
| 第一次打开,未保存任何设置 | 原主题排版不变 |
| 选细点并拖到 0%、100% | 数字和图案立即同步,刷新保持端点 |
| 无纹理 → 有纹理 | 滑块禁用 / 恢复,原浓度保留 |
| 输错 HEX | 保留上一次有效颜色,出现明确提示 |
| 点击恢复默认 | 本功能属性清理,其他本地数据不删除 |
| 打开两个标签页,在其中一个修改 | 另一个的整套状态更新,不只更新动效 |
| 首页 → 文章 → 浏览器返回 | 偏好保持,入口不重复,页面不空白 |
| Esc、关闭按钮、点遮罩 | 正常关闭并恢复焦点,不跳到顶部 |
| 320px 手机、平板、桌面 | 面板可滚动,按钮可见,没有横向溢出 |
| 默认 / 减弱 / 增强动效切换 | 标题、正文、图片仍显示,目录不漂移 |
再打开开发者工具,看有没有 JS 异常、CSS/JS 404。检查公式、表格、代码块、图片放大和评论输入,确认装饰没有拦截操作。打印时设置入口和纹理应隐藏。
#九、发布、CDN 与回退
- 新增或修改源文件后,在自己的博客目录重新生成。
- 如果使用 CDN,上传同一次生成的 JS/CSS 与相关资源,再发布匹配的 HTML。
- 基础示例的文件名固定,部署时需要刷新这两个资源的 CDN 缓存;若主题已有内容指纹流程,优先接入该流程。仅改一个手工版本号不能保证 HTML 与资源一致。
- 需要回退时,恢复模板中新增的两行引用,并删除自己新增的源文件,然后重新生成。原文章无需修改。
- 本地存储键可以保留,不会在代码移除后自行执行;若要清理,只删除
shoka-display-tutorial-v1。
歌词编辑器、宇宙漫游等完全独立应用不应引用这两个文件。设置只在加载了对应脚本与样式的博客页面中生效,不意味着同域名下的所有独立网页都会改变。
#常见问题
为什么恢复默认后不是我想要的颜色? 默认来自当前主题。如果改了主题默认色,重新生成后才会有新的默认;浏览器中的自选色则需要先清除。
为什么 100% 看起来也不是一大块颜色? 浓度改变图案透明度,不改变点的半径或网格密度。密度、形状和颜色是另外的参数。
为什么我的 Canvas 还在动? CSS 减弱规则管不到绘制循环,要在模块自己的运行 / 销毁逻辑里订阅偏好。
能直接把本站所有新增文件覆盖进去吗? 不建议。Shoka 分支、布局结构、构建顺序可能不同;先确认依赖,再比较合并。基础示例可独立运行,不依赖本站的后台和私有配置。
#参考与致谢
- Shoka 原主题源码:主题结构与集成基础,遵守其 MIT 许可。
- 本站《Shoka主题:美化统计页面并新增多维图表》:本文沿用其循序渐进的教程组织方式。
- 本站《Shoka主题:Markdown扩展语法展示与使用指南》:文章阅读工具与全站显示设置的区别及实际使用入口。
本文配套的基础示例仅包含教学代码,不含任何站点个人配置。请先在自己的本地站点完成验收,再发布到线上。