Skip to content

Twikoo评论区仿waline样式美化教程

说明 本教程面向 Twikoo 2.0,写的是把 Twikoo 换成 Waline 的外观,绝对不是换成把评论系统换成 Waline 。基准为 Twikoo 2.0.12 + @waline/client 3.16.0,跨大版本请先自查 DOM。

前言

我的博客评论区原来照着别人的 Twikoo 美化抄,Twikoo 从 1.7 升到 2.0 后旧 CSS 全部失效。原因是 2.0 把内部 DOM 和类名整套换成了 tk- 前缀 BEM,网上多数教程还停在 1.7 的 .el-* 时代。于是我让“大肥鱼”按 Waline 官方 waline.css 重写了一遍,逐条翻译到 Twikoo 2.0 的真实 DOM 上。效果如下。

原版的waline评论输入框是没有头像的,但是我加了一个。

效果图

自定义样式

整块 CSS 不依赖任何博客主题,想换配色只改 token 段即可。

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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/* ==========================================================================
Twikoo 2.x — Waline 风格皮肤
基准:@waline/client 3.16.0 的 waline.css
作用域:.twikoo(Twikoo 自己给根元素加的类,与主题无关)
========================================================================== */

/* ---------- 1. token:想换配色只改这一段 ---------- */
body {
--twk-theme: #0071e3; /* 强调色:发送按钮底、分页当前页底 */
--twk-theme-hover: #0077ed; /* 强调色 hover */
--twk-theme-active: #006edb; /* 强调色 active */
--twk-on-theme: #ffffff; /* 压在强调色上的文字色 */
--twk-link: #0066cc; /* 文字强调色:排序激活、链接、hover */
--twk-text: #333333; /* 正文/昵称 */
--twk-text-2: #666666; /* 次级文字(引用块) */
--twk-text-3: #999999; /* 时间、计数、页脚 */
--twk-bg: #ffffff; /* 面板底色 */
--twk-bg-light: #f7f7f7; /* 输入框 focus 底、设备信息片 */
--twk-bg-hover: #f0f0f0; /* 表情项 hover */
--twk-border: rgba(153, 153, 153, .32); /* Waline 的 #ddd ≈ 32% 灰半透明 */
--twk-avatar: 3.25rem; /* 主楼头像(Waline 默认 3.25rem = 52px) */
--twk-avatar-m: 2.25rem; /* 嵌套回复 / 回复框头像(36px) */
--twk-radius: .75em; /* 面板圆角 */
}
@media (max-width: 720px) {
body { --twk-avatar: var(--twk-avatar-m); } /* Waline 窄屏收小头像 */
}

/* 暗色:两套值都写出来(比 :root 放变量更省心) */
@media (prefers-color-scheme: dark) {
body {
--twk-text: #dddddd; --twk-text-2: #bbbbbb; --twk-text-3: #808080;
--twk-bg: #181818; --twk-bg-light: #202020; --twk-bg-hover: #262626;
--twk-link: #69b9ff; --twk-border: rgba(128, 128, 128, .35);
}
}
/* 如果你的主题是「手动切换暗色」,解开下面这段并改成你主题的开关:
html[data-theme='dark'] body, body[data-color-scheme='dark'] {
--twk-text: #dddddd; --twk-text-2: #bbbbbb; --twk-text-3: #808080;
--twk-bg: #181818; --twk-bg-light: #202020; --twk-bg-hover: #262626;
--twk-link: #69b9ff; --twk-border: rgba(128, 128, 128, .35);
} */

/* ---------- 2. 全局 ---------- */
.twikoo { color: var(--twk-text); line-height: 1.75; }

/* ---------- 3. 提交面板(= .wl-panel:白底 + 细边框 + 圆角 + 无阴影) ---------- */
.twikoo .tk-comments .tk-submit {
background: var(--twk-bg);
border: 1px solid var(--twk-border);
border-radius: var(--twk-radius);
padding: .75em 1em 1em;
margin: 1.5em 0;
box-shadow: none; /* Waline 不用卡片阴影:--waline-box-shadow: none */
}
.twikoo .tk-comment .tk-submit { /* 点「回复」后弹出的小面板 */
padding: .5em .75em;
margin: .75em 0 0;
}

/* ---------- 4. 头像(圆形 + Waline 尺寸体系) ---------- */
/* Twikoo 写了 0,5,0 的复合选择器把尺寸钉死,这两条只能 !important */
.twikoo .tk-avatar.tk-avatar {
width: var(--twk-avatar) !important;
height: var(--twk-avatar) !important;
border-radius: 50% !important;
margin-right: .75em;
background: var(--twk-bg-light);
}
.twikoo .tk-replies .tk-avatar.tk-avatar,
.twikoo .tk-comment .tk-submit .tk-avatar.tk-avatar {
width: var(--twk-avatar-m) !important;
height: var(--twk-avatar-m) !important;
}
.twikoo .tk-avatar .tk-avatar-img { width: 100%; height: 100% !important; line-height: normal; }

/* ---------- 5. meta 行(= .wl-header:透明输入 + 底部 2px 虚线) ---------- */
.twikoo .tk-submit .tk-meta-input {
display: flex;
padding: 0 .25em;
border-bottom: 2px dashed var(--twk-border);
margin-bottom: 0;
}
.twikoo .tk-submit .tk-meta-input .tk-input { background: none; border: 0; border-radius: 0; }
.twikoo .tk-submit .tk-meta-input .tk-input-group__prepend {
background: none; border: 0; border-radius: 0;
height: auto; min-width: 2.5em; justify-content: center;
padding: .75em .5em .75em 0;
color: var(--twk-text); font-size: .75em; line-height: 1.5;
}
.twikoo .tk-submit .tk-meta-input .tk-input__inner {
background: none; border: 0; border-radius: 0; height: auto;
padding: .5em .5em .5em 0;
font-size: .75em; line-height: 1.75;
transition: all .25s ease;
}
.twikoo .tk-submit .tk-meta-input .tk-input__inner:focus { background: var(--twk-bg-light); border: 0; }
@media (max-width: 767px) { /* Twikoo 窄屏把 meta 竖排 → 改成逐项虚线分隔 */
.twikoo .tk-submit .tk-meta-input { flex-direction: column; border-bottom: 0; }
.twikoo .tk-submit .tk-meta-input .tk-input { width: auto; }
.twikoo .tk-submit .tk-meta-input .tk-input:not(:last-child) { border-bottom: 2px dashed var(--twk-border); }
}

/* ---------- 6. 编辑器(= .wl-editor:无边框,focus 变浅灰底) ---------- */
.twikoo .tk-submit .tk-textarea .tk-input__inner {
background: transparent; border: 0; border-radius: .5em; height: auto;
min-height: 8.75em; /* Waline 的编辑器高度 */
margin: .75em 0 .25em; padding: .75em .5em;
font-size: .875em; line-height: 1.75;
resize: vertical; transition: all .25s ease;
}
.twikoo .tk-submit .tk-textarea .tk-input__inner:focus { background: var(--twk-bg-light); border: 0; }
.twikoo .tk-submit .tk-textarea .tk-input__count { color: var(--twk-text-3); font-size: .75em; }

/* ---------- 7. 工具行与按钮(= .wl-btn / .wl-btn.primary) ---------- */
.twikoo .tk-submit .tk-row.actions { margin: .5em 0 0 4rem; } /* 让开 3.25rem 头像 + 间距 */
.twikoo .tk-comment .tk-submit .tk-row.actions { margin-left: 3rem; }
@media (max-width: 720px) { .twikoo .tk-submit .tk-row.actions { margin-left: 3rem; } }
.twikoo .tk-submit .tk-submit-action-icon { color: var(--twk-text); }
.twikoo .tk-submit .tk-submit-action-icon:hover,
.twikoo .tk-submit .tk-submit-action-icon.__markdown:hover { color: var(--twk-link); opacity: 1; }

/* 幽灵按钮:预览这类次要按钮 */
.twikoo .tk-row.actions .tk-button:not(.tk-button--primary) {
min-width: 2.5em; padding: .5em 1em;
border: 1px solid var(--twk-border); border-radius: .5em;
background: transparent; color: var(--twk-text);
font-weight: 400; font-size: .75em; line-height: 1.5;
transition: all .4s;
}
.twikoo .tk-row.actions .tk-button:not(.tk-button--primary):not(.is-disabled):hover,
.twikoo .tk-row.actions .tk-button:not(.tk-button--primary):not(.is-disabled):focus {
border-color: var(--twk-link); background: transparent; color: var(--twk-link);
}
/* 实心主按钮(发送/搜索):双类重复是为了在权重上压过 Twikoo 的幽灵规则 */
.twikoo .tk-button--primary.tk-button--primary {
border-color: var(--twk-theme); background: var(--twk-theme); color: var(--twk-on-theme);
}
.twikoo .tk-button--primary.tk-button--primary:not(.is-disabled):hover,
.twikoo .tk-button--primary.tk-button--primary:not(.is-disabled):focus {
border-color: var(--twk-theme-hover); background: var(--twk-theme-hover); color: var(--twk-on-theme);
}
.twikoo .tk-button--primary.tk-button--primary:not(.is-disabled):active {
border-color: var(--twk-theme-active); background: var(--twk-theme-active); color: var(--twk-on-theme);
}
.twikoo .tk-button--primary.tk-button--primary.is-disabled {
opacity: 1; border-color: var(--twk-border); background: var(--twk-bg-light); color: var(--twk-text-3);
}

/* ---------- 8. 预览区(= .wl-preview:无框,虚线隔开) ---------- */
.twikoo .tk-comments .tk-preview-container {
border: 0; border-top: 2px dashed var(--twk-border); border-radius: 0;
margin-left: 0; padding: .75em .5em .25em; background: transparent;
}

/* ---------- 9. 头部:排序 / 图标(搜索行见第十三节) ---------- */
.twikoo .tk-comments .tk-sort-item { color: var(--twk-text-3); font-size: .75em; }
.twikoo .tk-comments .tk-sort-item.__active,
.twikoo .tk-comments .tk-sort-item:focus { color: var(--twk-link); }
.twikoo .tk-comments .tk-icon.__comments { color: var(--twk-text-3); }
.twikoo .tk-comments .tk-icon.__comments:hover { color: var(--twk-link); }
.twikoo .tk-comments .tk-icon.__comments.__active {
color: var(--twk-on-theme); background: var(--twk-theme); box-shadow: 0 0 0 .2rem var(--twk-theme);
}

/* ---------- 10. 评论列表(= .wl-card:扁平 + 虚线分隔,不是卡片) ---------- */
.twikoo .tk-comments-container > .tk-comment { padding: .5em; margin-top: 0; overflow-wrap: break-word; }
.twikoo .tk-comments-container > .tk-comment > .tk-main {
border-bottom: 1px dashed var(--twk-border);
padding-top: .25em; padding-bottom: .75em;
}
.twikoo .tk-comments-container > .tk-comment:last-child > .tk-main { border-bottom: 0; }

.twikoo .tk-comment .tk-meta .tk-nick { color: var(--twk-text); font-size: .875em; font-weight: 700; }
.twikoo .tk-comment a.tk-nick-link:hover { color: var(--twk-link); }
.twikoo .tk-comment .tk-meta .tk-time { color: var(--twk-text-3); font-size: .75em; }
/* 徽标:Waline 是描边空心徽章,不是实心色块 */
.twikoo .tk-comment .tk-tag { background: transparent; border-radius: 4px; padding: 0 .3em; font-size: .75em; }
.twikoo .tk-comment [class*='tk-tag-'] { background: transparent; border-color: currentColor; }

/* 操作按钮(赞/踩/回复):正文色,hover 才变强调色 */
.twikoo .tk-comment .tk-action .tk-action-link { color: var(--twk-text); }
.twikoo .tk-comment .tk-action .tk-action-link:hover { color: var(--twk-link); }
.twikoo .tk-comment .tk-action .tk-action-icon { color: inherit; }
.twikoo .tk-comment .tk-action .tk-action-count {
color: var(--twk-text-3); font-size: .75em; height: auto; line-height: inherit;
}

/* 正文(= .wl-content:0.875em / 行高 2) */
.twikoo .tk-comment .tk-content { font-size: .875em; line-height: 2; margin-top: .625em; color: var(--twk-text); }
.twikoo .tk-content p { margin: .25em 0; }
.twikoo .tk-content a { color: var(--twk-link); overflow-wrap: anywhere; }
.twikoo .tk-content a:hover { color: var(--twk-theme-hover); }
.twikoo .tk-content blockquote {
margin: .5em 0; padding: .25em 0 .25em 1em;
border-inline-start: 4px solid var(--twk-border); color: var(--twk-text-2);
}
.twikoo .tk-content code {
padding: .2em .4em; border-radius: 3px; background: var(--twk-bg-light); font-size: 85%;
}
.twikoo .tk-content pre { padding: 10px; border-radius: 6px; background: #282c34; }
.twikoo .tk-content img { max-width: 100%; height: auto; }

/* 设备/浏览器/地区(= .wl-meta:小圆角灰片,无边框无阴影) */
.twikoo .tk-comment .tk-extras { color: var(--twk-text-3); font-size: .75em; }
.twikoo .tk-comment .tk-extra {
margin-top: .5em; margin-right: .5em; padding: 2px 6px;
border-radius: .25em; background: var(--twk-bg-light); color: var(--twk-text-3);
}

/* 嵌套回复(= .wl-quote:虚线左边界) */
.twikoo .tk-comment .tk-replies {
margin-top: .5em; padding-inline-start: .75em; border-inline-start: 1px dashed var(--twk-border);
}

/* 加载更多(= 居中幽灵小按钮,不是通栏色条) */
.twikoo .tk-comments .tk-expand {
display: block; width: auto; margin: 1em auto; padding: .5em 1em;
border: 1px solid var(--twk-border); border-radius: .5em;
background: transparent; color: var(--twk-text);
font-size: .75em; line-height: 1.5; transition: all .4s;
}
.twikoo .tk-comments .tk-expand:hover { border-color: var(--twk-link); background: transparent; color: var(--twk-link); }
.twikoo .tk-comments .tk-expand:active { background: var(--twk-bg-light); }

/* 分页 / 空状态 / 页脚 */
.twikoo .tk-pagination .tk-pagination-pager { color: var(--twk-text); }
.twikoo .tk-pagination .tk-pagination-pager.__current {
border-radius: .25em; background: var(--twk-theme); color: var(--twk-on-theme);
}
.twikoo .tk-comments .tk-comments-no { padding: 1.25em 0; color: var(--twk-text-3); }
.twikoo div.tk-footer { color: var(--twk-text-3); }
.twikoo .tk-footer a { color: var(--twk-text-3); }
.twikoo .tk-footer a:hover { color: var(--twk-link); }

/* ---------- 11. 表情弹层(= .wl-emoji-popup:面板同色 + 6px 圆角) ---------- */
.twikoo .OwO .OwO-body {
color: var(--twk-text); background-color: var(--twk-bg);
border: 1px solid var(--twk-border); border-radius: .375em;
}
.twikoo .OwO .OwO-items .OwO-item:hover { background-color: var(--twk-bg-hover); box-shadow: none; }
.twikoo .tk-lightbox-image { background: var(--twk-bg-light); }

/* ---------- 12. 窄屏收敛 ---------- */
@media (max-width: 570px) { .twikoo .tk-extras { display: none; } } /* 设备信息太长,藏掉 */
@media (max-width: 420px) { .twikoo .tk-row.actions button.tk-preview { display: none; } }

/* ---------- 13. 可选:隐藏 Twikoo 2.0 新增的「搜索评论」行(Waline 没有搜索) ---------- */
.twikoo .tk-comments-search { display: none; }

/* ---------- 14. 可选:修正人机验证(Turnstile)位置 ----------
Twikoo 默认把它 absolute 到工具行下方 75px,实测会压住下面「N 条评论」那一行。
改成:工具行允许换行,验证码独占一行右对齐。 */
.twikoo .tk-submit .tk-row.actions { flex-wrap: wrap; }
.twikoo .tk-turnstile-container {
position: static; right: auto; bottom: auto;
flex: 0 0 100%; display: flex; justify-content: flex-end; margin-top: 12px;
}
.twikoo .tk-turnstile { align-items: flex-end; }

如何使用

在自己的主题 CSS 里注入一次即可:

你的主题 放哪喵
Hexo Butterfly source/css/_custom/custom.css(或 inject.head 里塞 <style>)
Hexo Fluid 「自定义 CSS」配置项
Hexo Volantis / Solitude / Redefine / Stellar 各自的自定义 CSS / inject 配置
其他所有 主题的「自定义代码 / 注入 body 末尾」处塞一个 <style>

注入位置尽量靠后,body 末尾优于 head。如果只能塞 head,就把选择器再补一层重复类提权,比如 .twikoo .tk-submit → .twikoo .tk-submit.tk-submit。

Twikoo 2.0 的样式是运行时注入的,会插在你的自定义 CSS 后面。如果样式被盖,优先检查权重和加载顺序。也可以使用 twikoo.nocss.js + 官方 twikoo.css,或干脆不用它的 CSS,这样选择器可以写得简单些。

验收

改完样式一定一定一定要量计算样式,靠肉眼是不好的。权重被盖、变量没解析出来这种静默失败,肉眼是几乎看不太出来的。

这里给出console脚本,粘到浏览器 F12 控制台里回车即可打印出各个关键元素的计算样式,和预期值对比。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 粘到控制台即可
const cs = (sel, props) => {
const el = document.querySelector(sel); if (!el) return null;
const s = getComputedStyle(el); return Object.fromEntries(props.map(p => [p, s[p]]));
};
console.table({
'面板': cs('.twikoo .tk-comments .tk-submit', ['backgroundColor', 'borderTopWidth', 'borderRadius', 'boxShadow']),
'头像': cs('.twikoo .tk-avatar', ['width', 'height', 'borderRadius']),
'输入框': cs('.twikoo .tk-submit .tk-meta-input .tk-input__inner', ['borderTopWidth', 'backgroundColor']),
'编辑器': cs('.twikoo .tk-submit .tk-textarea .tk-input__inner', ['borderTopWidth', 'minHeight']),
'发送钮': cs('.twikoo .tk-row.actions .tk-button--primary', ['backgroundColor', 'color']),
'分隔线': cs('.twikoo .tk-comments-container > .tk-comment > .tk-main', ['borderBottomStyle']),
});

预期:面板白底 / 1px 边框 / 12px 圆角 / 无阴影;头像 52px 圆形;输入框 0 边框透明;编辑器 min-height ≥ 100px;发送钮接近 rgb(0, 113, 227);评论之间 dashed。

如果某一项没生效,按这个顺序查说不定能解决:

这段是deepseek给的建议

  1. F12 选中元素 → Styles 面板,从下往上找划掉的规则;
  2. 盖你的是 .twikoo .xxx → 把你的选择器加长一层;
  3. 盖你的是 0,5,0 那种复合选择器(目前只有头像尺寸)→ 只能 !important;
  4. 什么都没盖但值也不对 → 变量没解析出来,检查 getComputedStyle(document.body).getPropertyValue('--twk-theme') 是不是空串。

理想条件下的输出(暗色模式)

图2 理想条件下的输出(暗色模式)

大功告成

到这里,Twikoo 的样式已经设置完成了。

本文最后更新于 2026-10-09

About this Post

This post is written by xjhjtzac, licensed under CC BY-NC-SA 4.0.

#Twikoo #Website #css

评论