更新版本:2026/2/17

1 一图流background

参考文档:https://android99.com/p/butterfly-top-image-modify/

前提为安装cherrio插件环境

1
npm install cheerio

主题配置项

1
2
3
4
5
6
index_img: transparent
footer_img: transparent
background: /assets/xxxx
index_top_img_height: 400px
mask:
header: false

同时在添加css文件的读取路径

1
2
3
inject:
head:
- <link rel="stylesheet" href="/css/modify.css">

外部引用css

Butterfly/Sourse/css/modify.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@import 'nib'

// 顶部图
#page-header
background: transparent !important

&.post-bg, &.not-home-page
height: 280px !important
#post-info
bottom: 40px !important
text-align: center
#page-site-info
top: 140px !important

@media screen and (max-width: 768px)
&.not-home-page
height: 200px !important
#post-info
bottom: 10px !important
#page-site-info
top: 100px !important

.top-img
height: 250px
margin: -50px -40px 50px
border-top-left-radius: inherit
border-top-right-radius: inherit
background-position: center
background-size: cover
transition: all .3s

@media screen and (max-width: 768px)
height: 230px
margin: -36px -14px 36px

[data-theme='dark'] &
filter: brightness(.8)

// 页脚
#footer:before
background-color: alpha(#FFF, .5)

[data-theme='dark'] &
background-color: alpha(#000, .5)

#footer-wrap, #footer-wrap a
color: #111
transition: unset

[data-theme='dark'] &
color: var(--light-grey)

注意:此文件更新不会被覆盖,但是勤备份。截止2025/2/17以上代码未做修改

外部引用JS

butterfly/scripts/modify.js

以下为代码备份。未修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
'use strict';
const { filter } = hexo.extend;
const cheerio = require('cheerio');

/**

* 在页面插入新顶部图
* @param {cheerio.Root} $ Root
*/
function insertTopImg($) {
const header = $('#page-header');
if (header.length === 0) return;
const background = header.css('background-image');
if (!background) return;
$('#post, #page, #archive, #tag, #category').prepend(`<div class="top-img" style="background-image: ${background};"></div>`);
}

// 修改 HTML
filter.register('after_render:html', (str, data) => {
const $ = cheerio.load(str, {
decodeEntities: false
});
insertTopImg($);
return $.html();
});

2 主页card竖图排版并列+微调

Themes/butterfly/source/css/_page/homepage.styl


2025/10/02
因为增加了自由tag所以信息栏的长度增加了,这版主要是拉长手机端的描述栏长度并且将字体缩小


注意,主题配置中排列模式输入:6

以下是整个文件的copy。逐条修改也是重点关注有关于6的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
$indexLayout = hexo-config('index_layout') || 1
$indexEnable = hexo-config('cover.index_enable')

#recent-posts
.recent-post-item
@extend .cardHover
position: relative
overflow: hidden
margin-bottom: 20px

if $indexLayout == 6 || ($indexLayout == 7)
display: inline-block
width: calc(100% / 2 - 12px)
height: 730px
vertical-align: top

+maxWidth768()
height: 440px
width: calc(100% / 2 - 6px)

+minWidth2000()
width: calc(100% / 2 - 6px)
height: 410px

if $indexLayout == 1 || ($indexLayout == 2 || ($indexLayout == 3))
display: flex
flex-direction: row
align-items: center
height: 16.8em

+maxWidth768()
flex-direction: column
height: auto

+minWidth2000()
height: 18.8em

&:hover
.post-bg
transform: scale(1.1)

&.ads-wrap
display: block !important
height: auto !important

.post_cover
overflow: hidden

if ($indexLayout != 5 && ($indexLayout != 7))
+maxWidth768()
width: 100%
height: 230px

if $indexLayout == 1 || ($indexLayout == 2 || ($indexLayout == 3))
width: 42%
height: 100%

&.right
order: 1

+maxWidth768()
order: 0

if $indexLayout == 4 || ($indexLayout == 5 || ($indexLayout == 6 || ($indexLayout == 7)))
width: 100%

if ($indexLayout == 5 || ($indexLayout == 7))
height: 17em
else
height: 530px

if ($indexLayout == 5 || ($indexLayout == 7)) && $indexEnable
&:before
position: absolute
z-index: 1
width: 100%
height: 100%
background-color: rgba(18, 18, 18, .4)
content: ''
backdrop-filter: blur(2px)

.post-bg
z-index: -4
@extend .imgHover

& >.recent-post-info
+maxWidth768()
padding: 12px 12px 20px
width: 100%

if $indexLayout == 1 || ($indexLayout == 2 || ($indexLayout == 3))
padding: 0 40px
width: 58%

if $indexLayout == 4 || ($indexLayout == 5 || ($indexLayout == 6 || ($indexLayout == 7)))
padding: 30px 30px 40px

if ($indexLayout == 5 || ($indexLayout == 7)) && $indexEnable
&:not(.no-cover)
position: absolute
top: 50%
z-index: 2
width: 100%
color: var(--text-highlight-color)
transform: translateY(-50%)
--text-highlight-color: rgba(255, 255, 255, 1)
--card-meta: rgba(255, 255, 255, .7)

&.no-cover
+maxWidth768()
padding: 30px 20px

if $indexLayout == 1 || ($indexLayout == 2 || ($indexLayout == 3))
width: 100%

if $indexLayout == 4 || ($indexLayout == 5 || ($indexLayout == 6 || ($indexLayout == 7)))
padding: 35px 30px

& > .article-title
@extend .limit-more-line
color: var(--text-highlight-color)
font-size: 1.55em
line-height: 1.4
transition: all .2s ease-in-out
-webkit-line-clamp: 2

.sticky
margin-right: 10px
color: $sticky-color
transform: rotate(45deg)

+maxWidth768()
font-size: 1.35em

&:hover
color: $text-hover

& > .article-meta-wrap
margin: 6px 0
color: var(--card-meta)
font-size: .82em

& > .post-meta-date
cursor: default

i
margin: 0 4px 0 0

.fa-spinner
margin: 0

.article-meta-label
if hexo-config('post_meta.page.label')
padding-right: 4px
else
display: none

.article-meta-separator
margin: 0 6px

.article-meta-link
margin: 0 4px

if hexo-config('post_meta.page.date_format') == 'relative'
time
display: none

a
color: var(--card-meta)

&:hover
color: $text-hover
text-decoration: underline

& > .content
@extend .limit-more-line
-webkit-line-clamp: 2
word-break: break-word
font-size: .82em

3 首页logo尺寸+site-name,site-slogan色彩,整体配色

Themes/butterfly/source/css/_layout/head.styl

以下需要搜索字段逐条修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.site-icon
margin-right: 6px
height: 52px
vertical-align: middle



#site-title
margin: 0
color: #fff5e9
font-size: 1.85em

+minWidth768()
font-size: 2.85em

#site-subtitle
color: #fff5e9
font-size: 1.15em

+minWidth768()
font-size: 1.72em

4 引用块的美化&配色调整

Themes/butterfly/source/css/var.styl

2026/2/17 新增日间模式的配色设计改动地方比较多,下边直接复制文件内容,包含了之前的色块调整

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// color
$bright-blue = #3f4d63
$strong-cyan = #2e4263
$light-orange = #FF7242
$light-red = #F47466
$themeColorEnable = hexo-config('theme_color') && hexo-config('theme_color.enable')
$theme-color = $themeColorEnable && hexo-config('theme_color.main') ? convert(hexo-config('theme_color.main')) : $bright-blue
$theme-paginator-color = $themeColorEnable && hexo-config('theme_color.paginator') ? convert(hexo-config('theme_color.paginator')) : $strong-cyan
$theme-text-selection-color = $themeColorEnable && hexo-config('theme_color.text_selection') ? convert(hexo-config('theme_color.text_selection')) : $strong-cyan
$theme-link-color = $themeColorEnable && hexo-config('theme_color.link_color') ? convert(hexo-config('theme_color.link_color')) : $bright-blue
$theme-hr-color = $themeColorEnable && hexo-config('theme_color.hr_color') ? convert(hexo-config('theme_color.hr_color')) : $bright-blue
$code-foreground = $themeColorEnable && hexo-config('theme_color.code_foreground') ? convert(hexo-config('theme_color.code_foreground')) : $light-red
$code-background = $themeColorEnable && hexo-config('theme_color.code_background') ? convert(hexo-config('theme_color.code_background')) : rgba(27, 31, 35, .05)
$theme-toc-color = $themeColorEnable && hexo-config('theme_color.toc_color') ? convert(hexo-config('theme_color.toc_color')) : $strong-cyan
// font
$chineseFont = $language == 'zh-CN' ? 'Microsoft YaHei' : 'Microsoft JhengHei'
$default-font-family = -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Lato, Roboto, 'PingFang SC', $chineseFont, sans-serif
$default-code-font = consolas, Menlo, monospace, 'PingFang SC', $chineseFont, sans-serif
$font-family = hexo-config('font.font_family') ? unquote(hexo-config('font.font_family')) : $default-font-family
$code-font-family = hexo-config('font.code_font_family') ? unquote(hexo-config('font.code_font_family')) : $default-code-font
$site-name-font = hexo-config('blog_title_font.font_family') && unquote(hexo-config('blog_title_font.font_family'))
// hr
$hrEnable = hexo-config('hr_icon') && hexo-config('hr_icon.enable')
$hr-icon = $hrEnable && hexo-config('hr_icon.icon') ? hexo-config('hr_icon.icon') : '\f0c4'
$hr-icon-top = $hrEnable && hexo-config('hr_icon.icon_top') ? convert(hexo-config('hr_icon.icon_top')) : -10px
// page beautify
$beautifyEnable = hexo-config('beautify.enable')
$title-prefix-icon = $beautifyEnable && hexo-config('beautify.title_prefix_icon') ? hexo-config('beautify.title_prefix_icon') : '\f0c1'
$title-prefix-icon-color = $beautifyEnable && hexo-config('beautify.title_prefix_icon_color') ? convert(hexo-config('beautify.title_prefix_icon_color')) : $light-red
// Global Variables
$font-size = hexo-config('font.global_font_size') ? convert(hexo-config('font.global_font_size')) : 14px
$code-font-size = hexo-config('font.code_font_size') ? convert(hexo-config('font.code_font_size')) : var(--global-font-size)
$font-color = #1F2D3D
$text-line-height = 2
$index_top_img_height = hexo-config('index_top_img_height') ? convert(hexo-config('index_top_img_height')) : 100vh
$index_site_info_top = hexo-config('index_site_info_top') ? convert(hexo-config('index_site_info_top')) : 43%
// Global color & SVG
$light-blue = $theme-color
$dark-black = #000000
$light-grey = #3f4d63
$grey = #858585
$dark-grey = #cacaca
$white = #e8e5dd
$whitesmoke = #909eb0
$font-black = #4C4948
$card-bg = $white
$text-highlight-color = $font-color
$text-hover = $theme-color
$text-bg-hover = $theme-color
// code
$line-height-code-block = 1.6
$blockquote-color = #7689a3bd
$blockquote-padding-color = $themeColorEnable && hexo-config('theme_color.blockquote_padding_color') ? convert(hexo-config('theme_color.blockquote_padding_color')) : #49B1F5
$blockquote-background-color = $themeColorEnable && hexo-config('theme_color.blockquote_background_color') ? alpha(convert(hexo-config('theme_color.blockquote_background_color')), .1) : alpha($blockquote-padding-color, .1)
// page
$body-bg = #fff
$a-link-color = #24354d
$sticky-color = $light-orange
$theme-meta-color = $themeColorEnable && hexo-config('theme_color.meta_color') ? convert(hexo-config('theme_color.meta_color')) : #858585
// sidebar
$sidebar-background = #d7d3c1
$sidebar-width = 330px
// aside
$toc-link-color = #666261
$toc-mobile-width = calc(100% - 80px)
$toc-mobile-maxWidth = 380px
$toc-active-color = #fff
// Button
$button-color = #fff
$button-hover-color = $themeColorEnable && hexo-config('theme_color.button_hover') ? convert(hexo-config('theme_color.button_hover')) : $light-orange
$button-bg = $theme-color
$pseudo-hover = $button-hover-color
// scrollbar
$scrollbar-color = $themeColorEnable && hexo-config('theme_color.scrollbar_color') ? convert(hexo-config('theme_color.scrollbar_color')) : $theme-color
// table
$table-thead-bg = #99a9bf
// reward
$reward-pop-up-bg = #f5f5f5
$reward-pop-up-color = #858585
// search
$search-bg = #d2d2d2
$search-input-color = $font-black
$search-color = $theme-color
$search-keyword-highlight = #F47466
$search-a-color = $font-black
// comments
$comments-switch-first-text = $bright-blue
$comments-switch-second-text = $light-orange
$comments-switch-round = #fff
$comments-switch-bg = #f6f8fa
// noticeOutdate
$noticeOutdate-bg = #ffe6e6
$noticeOutdate-color = #ff6666
$noticeOutdate-border = #ff8080
// gallery
$gallery-color = #fff
// tag-hide
$tag-hide-bg = $theme-color
$tag-hide-toggle-bg = #f0f0f0
// preloader
$preloader-bg = #37474f
$preloader-word-color = #fff
// rightside
$rightside-bottom = hexo-config('rightside_bottom') ? convert(hexo-config('rightside_bottom')) : 40px
// fireworks
$fireworks-zIndex = hexo-config('fireworks.zIndex') ? hexo-config('fireworks.zIndex') : 99999
// Tag Plugins - Note
hexo-config('note.light_bg_offset') is a 'unit' ? ($lbg = unit(hexo-config('note.light_bg_offset'), '%')) : ($lbg = 0)
$note-types = 'default' 'primary' 'info' 'success' 'warning' 'danger'
// Default
$note-default-border = #777
$note-default-bg = lighten(spin($note-default-border, 0), 94% + $lbg)
$note-default-text = $note-default-border
$note-default-icon = '\f0a9'
$note-modern-default-border = #e1e1e1
$note-modern-default-bg = lighten(spin($note-modern-default-border, 10), 60% + ($lbg * 4))
$note-modern-default-text = #666
$note-modern-default-hover = darken(spin($note-modern-default-text, -10), 32%)
// Primary
$note-primary-border = #6f42c1
$note-primary-bg = lighten(spin($note-primary-border, 10), 92% + $lbg)
$note-primary-text = $note-primary-border
$note-primary-icon = '\f055'
$note-modern-primary-border = #e1c2ff
$note-modern-primary-bg = lighten(spin($note-modern-primary-border, 10), 40% + ($lbg * 4))
$note-modern-primary-text = #6f42c1
$note-modern-primary-hover = darken(spin($note-modern-primary-text, -10), 22%)
// Info
$note-info-border = #428bca
$note-info-bg = lighten(spin($note-info-border, -10), 91% + $lbg)
$note-info-text = $note-info-border
$note-info-icon = '\f05a'
$note-modern-info-border = #b3e5ef
$note-modern-info-bg = lighten(spin($note-modern-info-border, 10), 50% + ($lbg * 4))
$note-modern-info-text = #31708f
$note-modern-info-hover = darken(spin($note-modern-info-text, -10), 32%)
// Success
$note-success-border = #5cb85c
$note-success-bg = lighten(spin($note-success-border, 10), 90% + $lbg)
$note-success-text = $note-success-border
$note-success-icon = '\f058'
$note-modern-success-border = #d0e6be
$note-modern-success-bg = lighten(spin($note-modern-success-border, 10), 40% + ($lbg * 4))
$note-modern-success-text = #3c763d
$note-modern-success-hover = darken(spin($note-modern-success-text, -10), 27%)
// Warning
$note-warning-border = #f0ad4e
$note-warning-bg = lighten(spin($note-warning-border, 10), 88% + $lbg)
$note-warning-text = $note-warning-border
$note-warning-icon = '\f06a'
$note-modern-warning-border = #fae4cd
$note-modern-warning-bg = lighten(spin($note-modern-warning-border, 10), 43% + ($lbg * 4))
$note-modern-warning-text = #8a6d3b
$note-modern-warning-hover = darken(spin($note-modern-warning-text, -10), 18%)
// Danger
$note-danger-border = #d9534f
$note-danger-bg = lighten(spin($note-danger-border, -10), 92% + $lbg)
$note-danger-text = $note-danger-border
$note-danger-icon = '\f056'
$note-modern-danger-border = #ebcdd2
$note-modern-danger-bg = lighten(spin($note-modern-danger-border, 10), 35% + ($lbg * 4))
$note-modern-danger-text = #a94442
$note-modern-danger-hover = darken(spin($note-modern-danger-text, -10), 22%)
// Tag Plugins - Button/note
$color-types = 'blue' 'pink' 'red' 'purple' 'orange' 'green'
$btn-color = #fff
$btn-default-color = #777
$tagsP-blue-color = #428bca
$tagsP-pink-color = #FF69B4
$tagsP-red-color = #FF0000
$tagsP-orange-color = #FF8C00
$tagsP-purple-color = #6f42c1
$tagsP-green-color = #5cb85c
// Tag Plugins - Tab
$tab-border-color = #f0f0f0
$tab-button-bg = #f0f0f0
$tab-button-color = $font-color
$tab-button-hover-bg = darken($tab-border-color, 8)
$tab-active-border-color = $theme-color
$tab-button-active-bg = $card-bg
$tab-to-top-color = #99a9bf
$tab-to-top-hover-color = $theme-color
// Tag Plugins - timeline
$timeline-default-color = $theme-color
// note - darkmode
// Default
$note-dark-default-border = #5a5a5a
$note-dark-default-bg = #2b2b2b
$note-dark-default-text = #b3b3b3
$note-dark-modern-default-border = #9a9a9a
$note-dark-modern-default-bg = #353535
$note-dark-modern-default-text = #c4c4c4
$note-dark-modern-default-hover = #d0d0d0
// Primary
$note-dark-primary-border = #5935a1
$note-dark-primary-bg = #2e1c3e
$note-dark-primary-text = #a47dd4
$note-dark-modern-primary-border = #9985cc
$note-dark-modern-primary-bg = #3c2d4c
$note-dark-modern-primary-text = #b693e6
$note-dark-modern-primary-hover = #c9a8f0
// Info
$note-dark-info-border = #346fa2
$note-dark-info-bg = #1f2e3b
$note-dark-info-text = #7bb3db
$note-dark-modern-info-border = #7ca8b5
$note-dark-modern-info-bg = #2b3c44
$note-dark-modern-info-text = #8fc6e0
$note-dark-modern-info-hover = #a3d4ea
// Success
$note-dark-success-border = #4a944a
$note-dark-success-bg = #202e20
$note-dark-success-text = #82c682
$note-dark-modern-success-border = #8bb087
$note-dark-modern-success-bg = #2c3d2c
$note-dark-modern-success-text = #96d196
$note-dark-modern-success-hover = #a8dca8
// Warning
$note-dark-warning-border = #c08a3e
$note-dark-warning-bg = #3e301f
$note-dark-warning-text = #e6ba6b
$note-dark-modern-warning-border = #b8a285
$note-dark-modern-warning-bg = #4b3c2b
$note-dark-modern-warning-text = #d4b373
$note-dark-modern-warning-hover = #e0c080
// Danger
$note-dark-danger-border = #b34440
$note-dark-danger-bg = #3b201f
$note-dark-danger-text = #e67572
$note-dark-modern-danger-border = #c7898c
$note-dark-modern-danger-bg = #4d2b2e
$note-dark-modern-danger-text = #d98b8e
$note-dark-modern-danger-hover = #e59fa2

【这项改动包含在上边的整文件里】改动的只有blockquote-color,改字体颜色(其实不改也没啥的)

1
2
$line-height-code-block = 1.6
$blockquote-color = #7689a3bd

Themes/butterfly/source/css/_mode/darkmode.styl

只改第一项

1
2
--blockquote-color: $blockquote-color
--blockquote-bg: lighten(#121212, 10)

Themes/butterfly/source/css/_global/index.styl

去除引用的左边色块。直接注释掉就行

1
2
3
4
5
6
7
blockquote
margin: 0 0 20px
padding: 7px 15px
//border-left: 4px solid $blockquote-padding-color
background-color: var(--blockquote-bg)
color: var(--blockquote-color)
addBorderRadius()

5 折叠块的美化及使用

其实是提醒下自己这个语法。。display替换为提示内容。color我已经懒得改了现在的白底黑字也算醒目。

1
2
3
{% hideToggle display,bg,color %}
content
{% endhideToggle %}

6 Wiki仓库页面

新增文件如下:

themes/butterfly/source/css/wiki.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#wiki-page
max-width: 1600px
margin: 0 auto
padding: 20px 0
height: calc(100vh - 80px)
overflow: hidden
position: relative

.wiki-container
display: flex
gap: 30px
height: 100%
padding: 0 20px

// ---------- 左侧目录树 ----------
.wiki-sidebar
flex: 0 0 260px
background: var(--card-bg)
border-radius: 12px
padding: 20px
box-shadow: 0 2px 8px rgba(0,0,0,0.1)
overflow-y: auto
height: 100%

.sidebar-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 15px

h3
margin: 0
border-bottom: none
padding-bottom: 0

.sidebar-close
background: transparent
border: none
color: var(--text-muted)
font-size: 1.2rem
cursor: pointer
padding: 5px
display: none
&:hover
color: #ff8c00

.category-tree
list-style: none
padding: 0
margin: 0

details
margin-bottom: 8px

summary
cursor: pointer
padding: 6px 8px
border-radius: 4px
.category-name
color: var(--text-color)
text-decoration: none
display: inline-block
&.active-category
font-weight: bold
color: #ff8c00

ul
list-style: none
padding-left: 24px
margin-top: 5px

.post-list
padding-left: 24px

.post-item
margin: 5px 0

a
color: var(--text-color)
text-decoration: none
display: block
padding: 4px 8px
border-radius: 4px
&:hover
background: var(--background)
color: #ff8c00
&.active
color: #ff8c00
background: rgba(255,140,0,0.1)
i.fa-lock
margin-right: 6px
color: #f0ad4e
font-size: 0.9em

// ---------- 右侧主内容 ----------
.wiki-main
flex: 1
height: 100%
overflow: hidden
display: flex
flex-direction: column
min-width: 0

.wiki-toolbar
padding: 10px 0
display: flex
justify-content: flex-end
border-bottom: 1px solid var(--border-color)
margin-bottom: 0

.wiki-welcome
display: block
text-align: center
color: #fff
font-size: 1.2em
margin-top: 10px
width: 100%

.wiki-reader
flex: 1
display: flex
flex-direction: column
background: transparent
width: 100%
height: 100%

.wiki-reader-toolbar
display: flex
justify-content: space-between
align-items: center
margin-bottom: 15px
padding: 0 5px
width: 100%
box-sizing: border-box

iframe
width: 100%
height: 100%
border: none
background: transparent
display: block

// 通用按钮样式
.wiki-toc-btn, .wiki-back-btn
padding: 8px 16px
background: #ff8c00
color: #fff
border: none
border-radius: 6px
cursor: pointer
font-size: 1em
display: inline-flex
align-items: center
gap: 8px
&:hover
opacity: 0.9

// ---------- 移动端样式 ----------
@media (max-width: 768px)
#wiki-page
padding: 10px 0

.wiki-container
padding: 0 10px
gap: 0

.wiki-sidebar
display: none
position: fixed
top: 60px
left: 0
right: 0
bottom: 0
z-index: 99
border-radius: 0
overflow-y: auto
width: 100%
height: auto
background: var(--card-bg)
padding: 15px
box-sizing: border-box

&.active
display: block

.sidebar-close
display: block !important // 强制显示
background: #ff8c00 // 可临时加亮色背景确认
color: white
border: none
font-size: 1.5rem
padding: 8px 16px
border-radius: 4px
cursor: pointer

.wiki-main
width: 100%
padding: 0
height: calc(100vh - 80px)

.wiki-toolbar
justify-content: center
padding: 10px 0
border-bottom: none
margin-bottom: 0
width: 100%

.wiki-toc-btn
font-size: 1.2rem
padding: 12px 24px
margin: 0 auto

.wiki-welcome
font-size: 1rem
margin-top: 15px
width: 100%

.wiki-reader
width: 100%
padding: 0
height: 100%

.wiki-reader-toolbar
padding: 10px
width: 100%
box-sizing: border-box

iframe
width: 100%
height: 100%

themes/butterfly/source/js/wiki.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
(function() {
'use strict';

const sidebar = document.getElementById('wiki-sidebar');
const reader = document.getElementById('wiki-reader');
const iframe = document.getElementById('wiki-iframe');
const welcome = document.querySelector('.wiki-welcome');
const toolbar = document.querySelector('.wiki-toolbar');
const backBtn = document.getElementById('wiki-back-to-toc');
const showTocBtn = document.getElementById('wiki-show-toc');
const sidebarClose = document.getElementById('sidebar-close');
let currentActiveLink = null;

window.loadArticle = function(url, element) {
const fullUrl = new URL(url, window.location.origin).href;
if (welcome) welcome.style.display = 'none';
if (reader) reader.style.display = 'block';
if (toolbar) toolbar.style.display = 'none';
iframe.src = fullUrl;

// 移除上一个高亮的文章
if (currentActiveLink) {
currentActiveLink.classList.remove('active');
}
// 移除上一个高亮的一级分类
const prevActiveCategory = document.querySelector('.category-name.active-category');
if (prevActiveCategory) {
prevActiveCategory.classList.remove('active-category');
}

// 高亮当前点击的文章
if (element) {
element.classList.add('active');
currentActiveLink = element;

// 找到该文章所属的一级分类并高亮
const postItem = element.closest('.post-item');
if (postItem) {
const parentDetails = postItem.closest('details');
if (parentDetails) {
const categoryName = parentDetails.querySelector('summary .category-name');
if (categoryName) {
categoryName.classList.add('active-category');
}
}
}
}

// 移动端自动隐藏侧边栏
if (window.innerWidth <= 768) {
sidebar.classList.remove('active');
}
};

function backToToc() {
if (welcome) welcome.style.display = 'flex';
if (reader) reader.style.display = 'none';
if (toolbar) toolbar.style.display = 'flex';
iframe.src = '';

// 移除所有高亮
if (currentActiveLink) {
currentActiveLink.classList.remove('active');
currentActiveLink = null;
}
const prevActiveCategory = document.querySelector('.category-name.active-category');
if (prevActiveCategory) {
prevActiveCategory.classList.remove('active-category');
}
}

if (showTocBtn) {
showTocBtn.addEventListener('click', function(e) {
e.stopPropagation();
sidebar.classList.toggle('active');
});
}

if (backBtn) {
backBtn.addEventListener('click', backToToc);
}

if (sidebarClose) {
sidebarClose.addEventListener('click', function() {
sidebar.classList.remove('active');
});
}

document.addEventListener('click', function(e) {
if (window.innerWidth <= 768) {
if (!sidebar.contains(e.target) && !showTocBtn.contains(e.target)) {
sidebar.classList.remove('active');
}
}
});
})();

themes/butterfly/scripts/wiki.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// scripts/wiki.js
hexo.extend.helper.register('build_category_tree', function(categories, posts) {
var tree = [];
var map = {};

// 第一步:以 _id 为键建立所有分类的映射
categories.forEach(function(cat) {
map[cat._id] = {
_id: cat._id,
name: cat.name,
path: cat.path,
parentId: cat.parent,
children: [],
posts: []
};
});

// 第二步:将文章分配到所属分类(使用分类的 _id 关联)
posts.forEach(function(post) {
if (post.categories && post.categories.length) {
post.categories.forEach(function(cat) {
var node = map[cat._id];
if (node) node.posts.push(post);
});
}
});

// 第三步:对每个分类下的文章按日期升序排序
for (var id in map) {
if (map.hasOwnProperty(id)) {
map[id].posts.sort(function(a, b) {
var aTime = a.date ? a.date.valueOf() : 0;
var bTime = b.date ? b.date.valueOf() : 0;
return aTime - bTime;
});
}
}

// 第四步:构建树形结构,根据 parentId 连接
for (var id in map) {
var node = map[id];
if (node.parentId) {
var parent = map[node.parentId];
if (parent) parent.children.push(node);
} else {
tree.push(node);
}
}

// 第五步:对每层分类按名称排序
function sortTree(nodes) {
nodes.sort(function(a, b) { return a.name.localeCompare(b.name); });
nodes.forEach(function(node) {
if (node.children.length) sortTree(node.children);
});
}
sortTree(tree);

return tree;
});

themes/butterfly/layout/wiki.pug

1
2
3
4
extends page

block content
include includes/page/wiki.pug

themes/butterfly/layout/includes/page/wiki.pug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//- 引入样式和脚本
link(rel='stylesheet', href=url_for('/css/wiki.css'))
script(src='https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js')

//- 获取文章数组并构建分类树
- var postsArray = site.posts ? site.posts.toArray() : []
- var categoriesTree = build_category_tree(site.categories, postsArray)

//- 分类树渲染mixin
mixin renderCategoryTree(categories)
ul.category-tree
each cat in categories
li
details(open=false)
summary
span.category-name= cat.name
if cat.children && cat.children.length
+renderCategoryTree(cat.children)
//- 仅当没有子分类时才显示文章列表
if cat.posts && cat.posts.length && cat.children.length === 0
ul.post-list
each post in cat.posts
li.post-item
a.post-link(href='javascript:void(0)' onclick=`loadArticle('${url_for(post.path)}', this)` data-post-id=post._id)
= post.title
if post.password
= ' '
i.fas.fa-lock

.page#wiki-page
.wiki-container
//- 左侧目录树
aside#wiki-sidebar.wiki-sidebar
.sidebar-header
h3 目录
button#sidebar-close.sidebar-close
i.fas.fa-times
+renderCategoryTree(categoriesTree)

//- 右侧主内容区
main.wiki-main
//- 全局工具栏(始终显示)
.wiki-toolbar
button#wiki-show-toc.wiki-toc-btn
i.fas.fa-list
| 展开目录

//- 欢迎页
.wiki-welcome
p 点击左侧目录/展开目录开始阅读

//- 阅读器
#wiki-reader.wiki-reader(style='display: none;')
.wiki-reader-toolbar
button#wiki-back-to-toc.wiki-back-btn
i.fas.fa-arrow-left
| 返回目录
iframe#wiki-iframe(src='' frameborder='0' allowtransparency='true' style='background: transparent;')

script(src=url_for('/js/wiki.js'))