说明
本教程面向 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
|
body { --twk-theme: #0071e3; --twk-theme-hover: #0077ed; --twk-theme-active: #006edb; --twk-on-theme: #ffffff; --twk-link: #0066cc; --twk-text: #333333; --twk-text-2: #666666; --twk-text-3: #999999; --twk-bg: #ffffff; --twk-bg-light: #f7f7f7; --twk-bg-hover: #f0f0f0; --twk-border: rgba(153, 153, 153, .32); --twk-avatar: 3.25rem; --twk-avatar-m: 2.25rem; --twk-radius: .75em; } @media (max-width: 720px) { body { --twk-avatar: var(--twk-avatar-m); } }
@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); } }
.twikoo { color: var(--twk-text); line-height: 1.75; }
.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; } .twikoo .tk-comment .tk-submit { padding: .5em .75em; margin: .75em 0 0; }
.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; }
.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 .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); } }
.twikoo .tk-submit .tk-textarea .tk-input__inner { background: transparent; border: 0; border-radius: .5em; height: auto; min-height: 8.75em; 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; }
.twikoo .tk-submit .tk-row.actions { margin: .5em 0 0 4rem; } .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 .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); }
.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; }
.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); }
.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; }
.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; }
.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; }
.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; }
.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); }
.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); }
.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); }
@media (max-width: 570px) { .twikoo .tk-extras { display: none; } } @media (max-width: 420px) { .twikoo .tk-row.actions button.tk-preview { display: none; } }
.twikoo .tk-comments-search { display: none; }
.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给的建议
- F12 选中元素 → Styles 面板,从下往上找划掉的规则;
- 盖你的是
.twikoo .xxx → 把你的选择器加长一层;
- 盖你的是 0,5,0 那种复合选择器(目前只有头像尺寸)→ 只能
!important;
- 什么都没盖但值也不对 → 变量没解析出来,检查
getComputedStyle(document.body).getPropertyValue('--twk-theme') 是不是空串。

图2 理想条件下的输出(暗色模式)
大功告成
到这里,Twikoo 的样式已经设置完成了。
本文最后更新于 2026-10-09
评论