一个基于 Hexo+Butterfly 的便捷自定义主题生态系统(MSS)
本说明文档面向两类使用者:
- 使用者:只想通过配置项切换不同皮肤风格
- 开发者:希望在保持 Butterfly 主题结构不变的前提下,新增或修改皮肤
本文只关注“皮肤层”的能力:即所有可以通过 Stylus/CSS 覆盖实现的视觉定制,包括但不限于:
- 全局背景与纹理
- 顶部 Banner 区域
- 首页文章卡片
- 右栏卡片
- 页脚区域
- 滚动条、按钮、右侧悬浮按钮
- 文章版权卡片结构与样式(新增 Pug 定制)
- 代码块主题配色(新增变量覆盖与样式强制)
- Markdown 表格样式(新增局部覆盖)
- 多页面统一背景逻辑(新增容器覆盖)
- 其他能通过选择器选中并装饰的部分
皮肤生态整体设计
不改主题结构,不动业务逻辑
- 保持 Hexo + Butterfly 的模板结构和功能不变
- 只在样式层(Stylus)做“换皮肤”
通过配置切换皮肤
- 使用
skin.preset指定当前皮肤 - 每个皮肤对应一份独立的
.styl文件
- 使用
皮肤之间互相隔离
- 每个皮肤文件只负责自己的配色和装饰
- 切换
skin.preset≈ 一键切换整站视觉方案
深度定制与进阶能力
- 超越纯 CSS:支持通过 Pug 模板分支实现结构级定制(如版权卡片)。
- 模式隔离:支持为亮色/暗色模式定义完全不同的视觉逻辑。
- 组件级接管:支持接管 Banner、背景、代码块配色、表格样式、标签样式等核心组件。
配置入口与目录结构
配置入口:skin.preset
文件:themes/butterfly/_config.yml
skin: |
皮肤生态整体设计
配置驱动,一键切换
- 通过
skin.preset配置项,一键切换整站视觉方案(配色、背景、组件样式、交互动画)。 - 每个皮肤对应一份独立的
.styl文件,互相隔离,互不干扰。
- 通过
不改主题结构,不动业务逻辑
- 保持 Hexo + Butterfly 的模板结构和功能不变,只在样式层(Stylus)做“换皮肤”。
- 即使是 Pug 结构的修改,也是通过配置项开关控制的,确保原主题逻辑的纯净。
深度定制与进阶能力
- 超越纯 CSS:支持通过 Pug 模板分支实现结构级定制(如版权卡片)。
- 模式隔离:支持为亮色/暗色模式定义完全不同的视觉逻辑。
- 组件级接管:支持接管 Banner、背景、代码块配色、标签样式等核心组件。
配置入口与目录结构
配置入口:skin.preset
文件:themes/butterfly/_config.yml
skin: |
说明:
skin.preset是当前启用皮肤的名字(自定义字符串,大小写敏感)- 推荐命名规则:
- 英文:
red,Spring_festival,Dark_ink等 - 或者中文:
锦绣,墨色等,只要和 Stylus 中的判断一致即可
- 英文:
样式入口:index.styl
文件:themes/butterfly/source/css/index.styl
示意:
$skinPreset = hexo-config('skin.preset') ? hexo-config('skin.preset') : '' |
说明:
- 使用
hexo-config('skin.preset')读取_config.yml中的配置值 - 根据不同
preset名称,@import不同的皮肤文件 - 你可以按需增加
if $skinPreset == 'XXX'分支,每个分支对应一个皮肤
皮肤文件目录
文件目录:themes/butterfly/source/css/skins/
每个皮肤是一份 Stylus 文件,例如:
skins/ |
约定:
- 一个皮肤 = 一个
.styl文件 - 每个文件内部按模块组织:背景、Banner、卡片、右栏、页脚、滚动条等
皮肤能定制的能力清单
本节总结这套生态在不改 Pug 模板前提下,能通过 Stylus 直接定制的所有重要部分,并说明对应的选择器和修改方式。
3.1 全局背景与纹理(包括图片叠加)
目标:控制页面整体氛围:纸张颜色、光晕、纹理图片等。
推荐修改位置:皮肤文件中的 body 选择器。
示例(简化):
body |
可做的事:
- 用
background-color控制整体基底色(冷暖调) - 用多层
radial-gradient、linear-gradient制作光晕、阴影、纸面质感 - 在最后叠加一张或多张图片(城市、纹理、插画等)
- 通过半透明白色渐变模拟“再淡一点”的蒙版效果
与 Butterfly 自带的 theme.background 的关系:
- 如果你只在皮肤文件中控制
body背景:- 可以不使用
theme.background
- 可以不使用
- 如果你需要保留
theme.background:- 可以在皮肤中只画渐变或纹理,不强制覆盖
#web_bg背景图片
- 可以在皮肤中只画渐变或纹理,不强制覆盖
顶部 Banner(覆盖 default_top_img 限制)
目标:让每个皮肤拥有独立的 Banner 图与光效,而不是所有皮肤共用一张配置里的图片。
相关结构:
- Banner 容器:
#page-header - 由 Butterfly 在 Pug 中注入默认背景图(
default_top_img、index_img等)
在皮肤文件中,你可以通过以下方式接管 Banner 背景:
#page-header |
可做的事:
- 使用
background-image url(...) !important为当前皮肤指定自己的 Banner 图 - 使用
::before叠加光效(白光、云雾、色块等) - 在不同皮肤中使用不同的 Banner 图片和光效组合,实现“换皮肤 = 换 Banner”
注意:
!important用于覆盖 Butterfly 在行内样式中设置的背景图片- 如果你想继续尊重主题配置里的
default_top_img,可以不写background-image,只在::before上做装饰
首页文章卡片(纸张、挑染、边框、印章等)
目标:在不增加额外 DOM 的前提下,为首页文章列表增加质感和装饰。
典型结构:
- 卡片容器:
.recent-posts .recent-post-item - 卡片标题:
.recent-posts .article-title
你可以在皮肤中定义:
.recent-posts .recent-post-item |
可做的事:
- 用
background控制卡片的纸张基底色 - 用
__pattern内的渐变做卡片内部挑染 - 用
::before、::after画金线、角落折线、印章、纹章等 - 用
article-title控制标题颜色与悬停色
说明:
__pattern这个类名只要和模板结构对应,即可在皮肤文件中用来承载纹理与光点- 所有装饰都通过伪元素绘制,无需增加额外 HTML 标签
右栏卡片(顶部线条、卡片底色、标题风格)
目标:让侧栏卡片与首页卡片共享同一套视觉语言。
相关结构:
- 右栏卡片容器:
.card-widget - 右栏卡片标题:
.card-widget .item-headline
示例:
.card-widget |
可做的事:
- 为每个右栏卡片顶部增加一条渐变横线(可做成流动金线)
- 调整右栏卡片的底色、边框、阴影,使之与首页卡片统一
- 调整右栏标题颜色、字距,打造一致的文字风格
页脚区域(底色、挑染、链接高亮)
目标:让页脚看上去是整套设计的一部分,而不是单独的条款信息。
相关结构:
- 页脚容器:
#footer - 页脚其他信息区:
#footer .footer-other - 页脚链接:
#footer .footer-other a
示例:
#footer |
可做的事:
- 统一调整页脚底色和挑染,保证与首页卡片风格相连
- 设置页脚链接 hover 时的颜色(如统一金色)
- 若你在 Pug 中调整了页脚文案结构,也可以通过这里补充装饰(背景、分割线等)
滚动条、按钮、右侧悬浮按钮
目标:让细节部分也跟随皮肤风格变化。
相关结构示例:
::-webkit-scrollbar |
可做的事:
- 控制滚动条粗细、颜色、圆角,让其更轻、更符合整体配色
- 控制右下角按钮的底色和 hover 色
- 为不同皮肤设置不同按钮风格
暗色模式适配
目标:确保皮肤在暗色模式下不仅可用,还能自动切换为专属的夜间视觉。
相关结构:
- Butterfly 使用
[data-theme="dark"]属性标记暗色模式。 - 皮肤文件中可以通过该属性嵌套样式,覆盖默认的背景图或颜色。
示例:
// 暗色模式背景设置 |
可做的事:
- 为暗色模式准备一套独立的“夜间背景图”(如
beijingdark.png) - 调整遮罩透明度,保证文字在深色背景上的对比度
- 覆盖特定组件的颜色(如果默认反色效果不理想)
交互细节优化
目标:提升用户体验,增加精致感。
相关结构:
- CSS 伪类
:hover用于悬停效果。 transition属性用于平滑过渡。- 强制样式覆盖(
!important)用于修正主题默认行为。
示例:
// 强制右下角按钮始终显示 |
可做的事:
- 修复亮色模式下某些图标颜色不明显的问题
- 为重要按钮添加细腻的悬停动画(如缩放、阴影、渐变背景)
- 强制覆盖某些交互组件的显示逻辑(如始终显示右下角工具栏)
文章底部标签(标签云与分享区)
目标:让文章末尾的标签展示区与皮肤风格融合,支持磨砂、光晕等高级质感。
相关结构:
- 标签容器:
#post .tag_share .post-meta__tags - 标签文字:
.post-meta__tags(也是容器本身)
在皮肤文件中,我们可以利用 !important 覆盖默认的边框和背景,甚至添加 backdrop-filter 实现毛玻璃效果。
示例(亮色与暗色模式区分):
// 亮色模式:柔和色调 + 磨砂 |
可做的事:
- 磨砂质感:通过
backdrop-filter: blur()让标签背景模糊背后的元素(如纸张纹理)。 - 光晕交互:在 hover 状态下添加带颜色的
box-shadow,模拟发光。 - 模式隔离:利用
[data-theme="dark"]为夜间模式设计完全不同的交互逻辑(如夜间发光,白天变色)。
代码块风格(全局展开与局部配色)
目标:控制代码块的默认交互状态(折叠/展开)以及与皮肤主题一致的配色。
1. 全局交互配置
在 themes/butterfly/_config.yml 中控制代码块是否默认折叠:
code_blocks: |
2. 皮肤局部配色(CSS 变量覆盖)
Butterfly 使用 --hl-* 系列 CSS 变量定义代码块颜色。你可以在皮肤文件中覆盖这些变量,使其完美适配亮色/暗色模式。
示例:
// 亮色模式:自定义高亮配色 |
3. 进阶:强制覆盖 highlight.js 语法颜色
如果 CSS 变量无法满足需求(例如:浅色背景下默认的高亮颜色太淡,看不清),可以直接通过 CSS 选择器强制修改 highlight.js 的类名颜色。
示例:
// 强制覆盖 highlight.js 注释颜色为传统绿色,去除斜体 |
Markdown 表格样式定制
目标:自定义文章内 Markdown 表格的边框颜色,使其适应不同的背景色(如浅色纸张背景需要深色边框)。
注意: Butterfly 的代码块内部也使用了 table 标签。为了避免误伤代码块,必须将选择器限定在 .table-wrap 容器内。
示例:
// 亮色模式:深色边框 |
多页面统一背景逻辑
目标:将文章页(Post)的特殊背景(如纸张纹理、阴影)应用到其他独立页面(如归档、标签、分类、友链等),保持视觉统一。
Butterfly 的页面容器 ID 各不相同,常见的有:
- 文章页:
#post - 归档页:
#archive - 标签/分类/独立页:
#page,#tag,#category
在皮肤文件中,可以将这些选择器组合起来,统一应用样式:
// 统一应用背景与磨砂效果 |
通过这种方式,只需写一次样式,即可让整站所有页面(除首页外)拥有一致的“纸张”体验。
超出纯 CSS 的进阶定制(Pug 修改)
有时仅靠 CSS 无法满足复杂的结构需求(例如:想把版权信息做成三列卡片,或者去掉硬编码的括号符号)。此时,我们需要通过“配置驱动”的方式修改 Pug 模板。
原则
- 配置驱动:不在 Pug 中写死逻辑,而是通过
_config.yml中的配置项开关来控制。 - 动态类名:将
theme.skin.preset注入到 HTML 的class中,让 CSS 能针对不同皮肤应用不同样式。 - 数据纯净:HTML 结构只负责输出数据(如作者名),不包含装饰符号(如
[ ]),将装饰权交给 CSS。
案例:文章版权卡片(post-copyright)
目标:实现一个“亚克力悬浮卡片”样式的版权栏,且不带原有的 [ ] 符号。
1. 配置项
在 _config.yml 中新增 style 字段:
post_copyright: |
2. Pug 模板改造
文件:themes/butterfly/layout/includes/post/post-copyright.pug
我们增加了一个 if theme.post_copyright.style === 'acrylic' 的分支,并注入了 theme.skin.preset 作为类名:
if theme.post_copyright.enable && page.copyright !== false |
3. 皮肤文件适配
在皮肤文件(如 red.styl)中,针对该结构编写样式:
// 针对 acrylic 结构 + _red 皮肤的特定样式 |
通过这种方式,我们实现了:
- 结构可配:通过
style: acrylic开启新结构。 - 样式隔离:只有
red皮肤会应用上述 CSS,其他皮肤可以定义完全不同的外观。 - 符号控制:Pug 中不写括号,皮肤 CSS 决定是否加括号,实现了真正的样式与内容分离。
如何新增一个皮肤
以新增皮肤 My_new_skin 为例。
步骤 1:创建 Stylus 文件
在 themes/butterfly/source/css/skins/ 下新建:
my_new_skin.styl |
建议在文件内部按模块组织:
// 全局背景与纹理 |
步骤 2:在 index.styl 中引入
编辑 themes/butterfly/source/css/index.styl:
$skinPreset = hexo-config('skin.preset') ? hexo-config('skin.preset') : '' |
步骤 3:在配置中启用
编辑 themes/butterfly/_config.yml:
skin: |
步骤 4:重新生成博客
hexo clean |
刷新浏览器,即可看到新皮肤生效。
在现有皮肤上进行修改
如果你已经有一个皮肤文件,并且想在其基础上进行调整:
- 找到对应
.styl文件(例如skins/red.styl或其他) - 根据上文的能力清单,定位你要改的模块:
- 背景 →
body - Banner →
#page-header、#page-header::before - 首页卡片 →
.recent-posts .recent-post-item等 - 右栏卡片 →
.card-widget等 - 页脚 →
#footer等 - 滚动条 / 按钮 → 相关选择器
- 版权卡片 →
.post-copyright.acrylic.YourSkinName
- 背景 →
- 在皮肤文件里直接调整颜色、渐变、图片路径等
- 执行
hexo clean && hexo g重新生成
注意事项与建议
命名一致性
_config.yml中的preset字符串必须与index.styl中的判断相同- 文件名不一定必须相同,但建议保持一致,便于维护
样式隔离
- 尽量把视觉逻辑写在皮肤文件中,不要散落到其他通用 Stylus 文件
- 避免使用过于泛化的选择器(例如直接
a { ... }),以免影响第三方组件
与主题配置的关系
- 如果皮肤已经接管了某块区域(例如 Banner 背景图),可以适当简化主题配置,减少冲突来源
- 若要兼容旧配置,可以只在皮肤中做“装饰层”(如
::before),不去改行内背景图
回退方式
- 如遇某个皮肤出现样式问题,可以在
_config.yml中暂时改回其他稳定皮肤 - 或者临时注释掉
index.styl中对应@import分支,恢复默认主题样式
- 如遇某个皮肤出现样式问题,可以在
进阶示例:让皮肤“动起来”的关键部位
本节不依赖任何特定皮肤,只说明“生态层面”允许你修改的关键区域,以及推荐的做法。你可以自由组合这些能力,设计完全不同的视觉方案。
文章正文纸张背景(独立于全局背景)
典型需求:只想让“文章正文区域”看起来像铺在宣纸上,而不改变首页 / 侧栏 / 页脚的背景。
通用思路:
- 找到文章正文的外层容器(常见选择器:
#post、#article-container或.post-block等) - 在皮肤文件中,只对这个容器设置多层背景和纹理
- 用伪元素
::before、::after叠加宣纸噪点、网格等效果 - 用
z-index确保文字和图片浮在纹理之上
示例(以 #post 为例):
#post |
你可以把上面的颜色、渐变位置、纹理强度全部替换掉,只保留“结构”:
- 外层容器:负责纸张底色和整体渐变
::before:负责噪点 / 纸纹::after:负责网格 / 装饰线- 子元素提高 z-index:保证内容清晰可读
右侧卡片交互(网站信息 / 分类 / 归档 / 最新文章)
目标:在不改 Pug 结构的前提下,为右侧卡片的每一行加上:
- 悬停时的背景渐变(例如左侧金色光晕向右扩散)
- 左侧竖线(类似书页边侧的装饰线或书签)
- 细微的缩进或扩展动画(hover 动画幅度可控)
典型结构(以 Butterfly 为例,实际以主题为准):
- 网站信息项:
.card-widget.card-webinfo .webinfo-item - 最新文章项:
.card-widget.card-recent-post .aside-list .aside-list-item - 分类项:
.card-widget.card-categories .card-category-list-item > a - 归档项:
.card-widget.card-archives .card-archive-list-item > a
可以在皮肤文件中为这些“行”统一定义悬停效果,例如:
// 行内左侧装饰竖线 |
对于“分类 / 归档 / 最新文章”模块,只要换成对应选择器即可,实现统一或差异化的交互风格。
建议:
- 保持 hover 动画的位移值适中(如
6px以内),避免内容感觉“弹出过头” - 竖线的
left位置与卡片 padding 配合,确保不会被卡片裁剪掉
目录(TOC)当前章节高亮与悬停样式
目标:在文章页右侧“目录”卡片中:
- 高亮当前阅读的小节(active 状态)
- 调整鼠标悬停时的文字颜色、背景、是否放大等
典型结构:
- 目录容器:
#card-toc.card-widget - 每一项链接:
.toc-link - 当前激活项:
.toc-link.active(或主题定义的当前项类)
示例:
// 当前阅读章节的高亮 |
这样,皮肤可以独立决定:
- active 项用什么颜色和背景突出当前章节
- hover 时是否带背景、是否带缩放动画
全局文本 hover 背景与文字颜色
很多主题会在根节点(如 :root 或 body)上定义一些 CSS 变量,用于控制链接 / 文本 hover 时的背景色等。皮肤可以通过覆盖这些变量,统一全站交互风格。
示例(假设存在 --text-bg-hover 变量):
:root |
此时,所有使用该变量的组件(包括正文链接、某些按钮等)都会在 hover 时的文字颜色,可以在皮肤文件中写更具体的选择器,例如:
// 分类 / 归档列表内的文字在 hover 时变为某主色 |
通过变量 + 局部覆盖的方式,你可以在“全局统一”与“局部差异化”之间取得平衡。
通过以上机制,这套皮肤生态可以:
- 在不改动 Butterfly 模板结构的前提下(除特殊功能分支外),
- 为任意视觉元素增加丰富的装饰、纹理和动画,
- 并通过一个简单的配置项
skin.preset实现整站换肤。
