Lavender's Blog

浩瀚众星,皆降为尘

文字很轻,思考很远

本文面向已经能够正常运行 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 与回退

  1. 新增或修改源文件后,在自己的博客目录重新生成。
  2. 如果使用 CDN,上传同一次生成的 JS/CSS 与相关资源,再发布匹配的 HTML。
  3. 基础示例的文件名固定,部署时需要刷新这两个资源的 CDN 缓存;若主题已有内容指纹流程,优先接入该流程。仅改一个手工版本号不能保证 HTML 与资源一致。
  4. 需要回退时,恢复模板中新增的两行引用,并删除自己新增的源文件,然后重新生成。原文章无需修改。
  5. 本地存储键可以保留,不会在代码移除后自行执行;若要清理,只删除 shoka-display-tutorial-v1 。

歌词编辑器、宇宙漫游等完全独立应用不应引用这两个文件。设置只在加载了对应脚本与样式的博客页面中生效,不意味着同域名下的所有独立网页都会改变。

#常见问题

为什么恢复默认后不是我想要的颜色? 默认来自当前主题。如果改了主题默认色,重新生成后才会有新的默认;浏览器中的自选色则需要先清除。

为什么 100% 看起来也不是一大块颜色? 浓度改变图案透明度,不改变点的半径或网格密度。密度、形状和颜色是另外的参数。

为什么我的 Canvas 还在动? CSS 减弱规则管不到绘制循环,要在模块自己的运行 / 销毁逻辑里订阅偏好。

能直接把本站所有新增文件覆盖进去吗? 不建议。Shoka 分支、布局结构、构建顺序可能不同;先确认依赖,再比较合并。基础示例可独立运行,不依赖本站的后台和私有配置。

#参考与致谢

  • Shoka 原主题源码:主题结构与集成基础,遵守其 MIT 许可。
  • 本站《Shoka主题:美化统计页面并新增多维图表》:本文沿用其循序渐进的教程组织方式。
  • 本站《Shoka主题:Markdown扩展语法展示与使用指南》:文章阅读工具与全站显示设置的区别及实际使用入口。

本文配套的基础示例仅包含教学代码,不含任何站点个人配置。请先在自己的本地站点完成验收,再发布到线上。

更新于 阅读次数 … 次

请我喝[茶]~( ̄▽ ̄)~*

Lavender 微信支付

微信支付

Lavender 支付宝

支付宝