前言

  Butterfly 主题自带的本地搜索功能默认交互较为简单,样式也比较素。为了更符合博客的整体风格,对搜索框和搜索结果做了全面美化,实现了从打开搜索到查看结果的三级流畅交互,并加入了紫色主题色和毛玻璃效果。


效果图

搜索美化效果


实现功能

  • 三级交互状态:关闭 → 居中(搜索框在屏幕中央)→ 展开(搜索框移到顶部 + 结果显示)
  • Pill形搜索框:圆角 pill 形状输入框,视觉更柔和
  • 紫色主题光标:#7c3aed 紫色闪烁光标,输入时停止闪烁
  • 动态高亮条:输入框底部紫色下划线动画展开效果
  • 毛玻璃结果卡片:搜索结果卡片带 backdrop-filter 毛玻璃效果
  • 卡片悬浮动效:鼠标悬停时上移 2px + 阴影增强
  • 关键词高亮:搜索关键词使用 #F47466 橙红色加粗高亮
  • 关闭按钮:右上角圆形 X 按钮,鼠标悬停变色,手机版更易用
  • 暗黑模式适配:完全兼容深色模式

配置方法

修改搜索对话框样式

编辑 themes/butterfly/source/css/_search/index.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
// === Main Search Dialog Container ===
.search-dialog
position: fixed
top: 0
left: 0
z-index: 1001
display: none
width: 100%
height: 100%
padding: 0
background: transparent
overflow: hidden
pointer-events: none
--search-height: 100vh

// Dialog visible when in centered or expanded state
&.is-centered,
&.is-expanded
display: block

// Allow clicks on children
.search-header,
.search-results-area,
#loading-status,
#loading-database
pointer-events: auto

// ===== SEARCH HEADER =====
// Default: off-screen at bottom (hidden)
.search-header
position: absolute
left: 50%
width: 100%
max-width: 720px
padding: 0 20px
display: flex
align-items: center
justify-content: center
background: transparent
top: 100%
transform: translateX(-50%)
opacity: 0
transition: all .6s cubic-bezier(.4, 0, .2, 1)

#local-search-input
width: 100%

// Centered: header in vertical center
&.is-centered .search-header
top: 50%
transform: translateX(-50%) translateY(-50%)
opacity: 1

// Expanded: header at top
&.is-expanded .search-header
position: absolute
top: 120px
left: 50%
transform: translateX(-50%)
opacity: 1
width: 100%
max-width: 720px
padding: 0 20px

+maxWidth768()
top: 80px

// ===== RESULTS AREA =====
.search-results-area
position: absolute
top: 185px
left: 0
right: 0
bottom: 0
overflow-y: auto
-webkit-overflow-scrolling: touch
scrollbar-width: none
-ms-overflow-style: none
opacity: 0
pointer-events: none
transition: opacity .4s ease .15s

&::-webkit-scrollbar
display: none

+maxWidth768()
top: 135px

&.is-expanded .search-results-area
opacity: 1
pointer-events: auto

// ===== CLOSE BUTTON (top-right corner, mobile only) =====
#search-close-btn
display: none
position: fixed
top: 0
right: 0
z-index: 10
width: 48px
height: 48px
align-items: center
justify-content: center
color: $grey
font-size: 1.2em
cursor: pointer
transition: all .3s ease
pointer-events: auto

&:hover
color: $theme-color
background: rgba(0, 0, 0, .03)

+maxWidth768()
display: flex
width: 44px
height: 44px
font-size: 1.1em

[data-theme='dark']
#search-close-btn
color: rgba(255, 255, 255, .5)

&:hover
color: rgba(255, 255, 255, .85)
background: rgba(255, 255, 255, .05)

// ===== CLOSE ANIMATION =====
@keyframes search_close
0%
opacity: 1
100%
opacity: 0

// ===== SEARCH MASK =====
#search-mask
position: fixed
top: 0
right: 0
bottom: 0
left: 0
z-index: 1000
display: none
background: rgba($dark-black, .25)

修改搜索结果卡片样式

编辑 themes/butterfly/source/css/_search/local-search.styl,添加 pill 形输入框、紫色主题和毛玻璃卡片:

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
// =================
// Card-style search input (pill shape)
// =================
#local-search-input
.local-search-box
position: relative
margin: 0 auto
max-width: 100%
width: 100%

input
width: 100%
padding: 12px 24px
font-size: 16px
line-height: 1.4
outline: none
border: 1px solid rgba(0, 0, 0, .08)
border-radius: 28px
background: rgba(255, 255, 255, .95)
color: var(--search-input-color)
-webkit-appearance: none
transition: all .3s ease
box-shadow: 0 1px 3px rgba(0, 0, 0, .06)
// Visible blinking caret
caret-color: #7c3aed
animation: caret_blink 1s step-end infinite

&:focus
border-color: rgba(0, 0, 0, .08)
box-shadow: 0 1px 3px rgba(0, 0, 0, .06)
animation: none

+maxWidth768()
font-size: 16px
padding: 10px 18px

// ===== Internal highlight bar (inside the input) =====
.search-highlight-bar
position: absolute
bottom: 10px
left: 24px
width: 0
height: 2px
background: #7c3aed
border-radius: 2px
transition: width .3s ease
pointer-events: none

&:focus-within .search-highlight-bar
width: calc(100% - 48px)

+maxWidth768()
.search-highlight-bar
left: 18px

&:focus-within .search-highlight-bar
width: calc(100% - 36px)

// =================
// Caret blink animation
// =================
@keyframes caret_blink
0%, 100%
caret-color: #7c3aed
50%
caret-color: transparent

// =================
// Search results as cards
// =================
#local-search-results
padding: 20px 0

.search-result-list
max-width: 680px
margin: 0 auto
padding: 0 20px 40px

.local-search-hit-item
margin-bottom: 16px
padding: 20px 24px
background: rgba(255, 255, 255, .95)
border: 1px solid rgba(0, 0, 0, .08)
border-radius: 12px
box-shadow: 0 1px 3px rgba(0, 0, 0, .06)
transition: all .3s ease
line-height: 1.7
-webkit-backdrop-filter: blur(8px)
backdrop-filter: blur(8px)

&:hover
box-shadow: 0 4px 16px rgba(0, 0, 0, .12)
transform: translateY(-2px)
border-color: transparent

&:last-child
margin-bottom: 0

a
display: block
color: var(--search-a-color)
text-decoration: none

&:hover
color: var(--search-a-color)

.search-result-title
display: block
font-size: 18px
font-weight: 600
color: #1a0dab
margin-bottom: 8px
line-height: 1.4

&:hover
text-decoration: underline

.search-result
margin: 4px 0 0
font-size: 14px
color: #545454
line-height: 1.6

&:first-of-type
margin-top: 8px

// =================
// Search stats
// =================
#local-search-stats-wrap
max-width: 680px
margin: 0 auto
padding: 0 20px

.search-result-stats
padding: 4px 0 8px
font-size: 14px
color: rgba(0, 0, 0, .45)

hr
display: none

// =================
// Keyword highlight
// =================
.search-keyword
background: transparent
color: $search-keyword-highlight
font-weight: bold

// =================
// Dark mode
// =================
[data-theme='dark']
#local-search-input
.local-search-box
input
border-color: rgba(255, 255, 255, .08)
background: rgba(30, 30, 30, .92)
color: rgba(255, 255, 255, .85)
box-shadow: 0 1px 3px rgba(0, 0, 0, .3)

&:focus
border-color: rgba(255, 255, 255, .08)
box-shadow: 0 1px 3px rgba(0, 0, 0, .3)

.search-highlight-bar
background: #a78bfa

.local-search-hit-item
background: rgba(30, 30, 30, .92)
border-color: rgba(255, 255, 255, .08)
box-shadow: 0 1px 3px rgba(0, 0, 0, .3)

&:hover
box-shadow: 0 4px 16px rgba(0, 0, 0, .5)
border-color: transparent

.search-result-title
color: #8ab4f8

.search-result
color: rgba(255, 255, 255, .65)

#local-search-stats-wrap
.search-result-stats
color: rgba(255, 255, 255, .35)

交互逻辑说明

搜索交互采用了三级状态管理(参考 themes/butterfly/source/js/search/local-search.js):

  1. CLOSED(关闭):搜索遮罩和对话框隐藏
  2. CENTERED(居中):点击搜索按钮 → 遮罩淡入 + 搜索框从底部滑入屏幕中央
  3. EXPANDED(展开):点击输入框或输入文字 → 搜索框移到顶部 + 结果区域淡入显示

关键交互细节:

  • 点击遮罩时:有结果且输入框非空 → 保持展开状态;输入框为空 → 回到居中;居中状态 → 关闭搜索
  • 按 ESC 键 → 关闭搜索
  • 搜索结果按包含的关键词数量排序,每个文章显示最佳匹配段落

变量配置

themes/butterfly/_config.yml 中控制本地搜索行为:

1
2
3
4
5
local_search:
enable: true # 开启本地搜索
preload: false # 是否预加载搜索数据(false表示首次打开时才加载)
top_n_per_article: 1 # 每篇文章最多显示的匹配段落数
unescape: false # 是否解码HTML实体

注意事项

  1. 文件位置:修改的是主题内的源文件(themes/butterfly/source/css/_search/),如果后续更新主题需要重新修改
  2. 搜索数据生成:依赖 hexo-generator-searchdb 插件生成 search.xml,确保已在 _config.yml 中配置
  3. 搜索按钮:只要任意搜索引擎(local/algolia/docsearch)启用,导航栏会自动显示搜索图标
  4. 移动端适配:搜索框和结果区域在 768px 以下屏幕有独立的 padding 和位置调整

本文由 AI 助手辅助编写。