把博客主题开源了:tian —— 朋友圈风 + 毛玻璃的 Hugo 主题
这个博客昨天(11 月 3 日)才开张,用的主题是在 Amigo 基础上改的:朋友圈式的动态流是 Amigo 的底子,我在上面加了毛玻璃皮肤、换了一套评论系统、踩平了一堆部署的坑。改都改了,干脆整理成一个独立主题开源出来,起名 tian——就是给我搭这个站的 AI 助手小天的名字。
- 仓库:github.com/xiaochen-G/hugo-theme-tian
- 在线演示:就是本站,blog.qingyue.de,你现在看到的样子就是它
- 许可:MIT(衍生自 Amigo,同样 MIT)
相比原版 Amigo,我改了什么
1. 毛玻璃皮肤(glass.css)。 一层独立的 CSS 覆盖,不动原主题的结构:页面背景铺了蓝、粉、紫三团柔和的彩色光晕,文章从原来的分隔线列表变成一张张圆角的半透明玻璃卡片,签名和页脚做成玻璃胶囊条,强调色从微信绿换成 Apple 蓝 #0071e3。深色模式有一套对应的暗色玻璃,没有 backdrop-filter 的浏览器会自动退回近乎不透明的底色,不会瞎。
2. 评论换成自托管 Waline。 原主题内置支持 Artalk / Twikoo / Giscus,我实际用下来选了 Waline:数据在自己的 SQLite 里,访客填个昵称就能评论,不用注册。集成方式上做了两个处理:
- 站点配置里把
commentMode故意设成主题不认识的值"waline",这样原主题自带的几个评论分支永远不会触发,再由我自己的评论模板单独把 Waline 挂上去,互不打架; - Waline 的服务地址和 Turnstile 站点密钥从站点参数
walineServer/walineTurnstileKey读取,模板里不写死,换站改配置就行。
3. 关掉 PJAX。 Amigo 默认开 PJAX(无刷新翻页),但它会让评论组件在翻页后加载不稳定,我直接关了,换来 Waline 每次都稳稳出现——为评论牺牲一点翻页的顺滑,值。
4. 点赞按钮。 文章页的「赞」用的是 Waline 自带的 reaction 功能,心形图标,不用另写后端。
5. 评论开关规则明确化。 文章(posts)自动开启评论;其他页面(比如「关于」)想开评论,要在页面头里加一行 comments: true,仓库的说明里写清了,免得后来人跟我一样对着不出现的评论区纳闷。
6. 首页头像爱心。 原主题里点封面头像是切换深浅色,我把这个动作改成了送爱心:点一下头像(或者名字下方的玻璃小药丸),会弹出一颗大爱心、几颗小爱心往上飘,药丸上的数字就是收到的爱心总数。计数没有另起后端,直接复用 Waline 的 article reaction 接口,和文章点赞同一套逻辑:每位访客一颗,再点一次取消。深浅色切换交给顶栏的专门按钮,不冲突。实现是两个独立小文件 static/js/hearts.js + static/css/hearts.css,依赖站点配置里的 walineServer,没配 Waline 的话它自己就不启用。不信可以去本站首页点头像试试。
7. 页脚署名。 页脚里的主题名从 Amigo 换成了 tian,并链接到主题仓库——用我主题的人,访客顺着页脚就能找到出处。
8. 宽屏适配与细节打磨。 这条来自论坛网友的实测反馈:原主题固定 680px 的一列,在电脑和平板上两侧全是空白。改完之后:主页按屏幕分档加宽(平板 800px / 电脑 880px / 大屏 960px,封面图跟着加高),文章页保持原阅读宽度、手机端完全不动;窗口宽到 1440px 以上时,主页和文章页的内容列两侧空白处都会出现玻璃小面板(文章页按自己的 680px 列宽单独定位):左栏上是博主名片(头像、简介、文章数),下面还有一张访客欢迎卡片,显示访客自己的位置、IP 和设备(多源兜底查询,国内和海外访客都能显示);右栏是站点导航加今日日期。顺手把两个小毛病也修了:全屏菜单的胶囊按钮改成等宽、文字居中(原来胶囊里藏着一条下划线,把文字顶得偏上,看起来就不居中);配图、图集这些小元素的圆角从 34px 统一到 1012px,和玻璃卡片的大圆角成一套。实现是 static/css/wide.css + static/js/sidepanels.js 两个独立文件,不动原主题结构。
9. 页面预加载(instant.page)。 内置 instant.page 5.2.0(MIT 协议,自托管在 static/js/instantpage.js,不依赖第三方 CDN):访客把鼠标悬停到站内链接上、或在手机上按下链接时,就提前把目标页拉进缓存,点击后近乎秒开——关掉 PJAX 换来的整页跳转,速度就靠它补回来。它在省流量模式和慢速网络下会自动不预取,不支持 ES 模块的老浏览器会直接跳过、没有副作用。
部署教程
下面是完整流程,顺序就是我自己搭这个站的顺序。
第一步:装 Hugo 和主题
需要 Hugo extended(扩展版)≥ 0.120,普通版不行,主题用到了扩展版的资源处理。
1# 新建站点
2hugo new site mysite && cd mysite
3
4# 把主题放进 themes 目录
5git clone https://github.com/xiaochen-G/hugo-theme-tian.git themes/tian
6# 或者用 submodule,升级方便些
7# git submodule add https://github.com/xiaochen-G/hugo-theme-tian.git themes/tian
一个说明:仓库里只有文本文件。图标字体(Remix Icon 的 woff2 等)和主题自带的演示图片、视频没有随仓库分发,用之前要补两样东西:
- 去 Remix Icon 官网 下载字体文件,放进主题的
static/fonts/; - 演示图片、视频直接换成你自己的就行,本来也是要换的。
第二步:改站点配置
把仓库根目录的 hugo.toml 当模板,关键配置逐项说明:
1theme = "tian"
2
3[params]
4 username = "你的名字"
5 description = "一句签名"
6 avatar = "/images/avatar.jpg" # 头像,放站点 static/images/ 下
7 cover = "/images/header.png" # 封面图
8 headerMedia = "/images/header.png" # ⚠️ 必须和 cover 写同一个文件,见下方坑 1
9 enablePjax = false # 为了 Waline 稳定,必须关
10
11 commentMode = "waline" # 故意写主题不认识的值,见上文
12 walineServer = "https://talk.你的域名.com" # 第四步部署的 Waline 地址
13 walineTurnstileKey = "0x你的站点密钥" # 第五步的 Turnstile sitekey
第三步:写一篇文章试跑
1hugo new posts/hello.md
2hugo server # 本地预览 http://localhost:1313
注意:日期写成未来的文章不会被构建出来,这是 Hugo 的默认行为,我在上一篇就差点被它坑到——明明写了文章,首页就是不显示,先检查日期。
第四步:部署 Waline 评论服务
Waline 是一个独立的小服务,跟 Hugo 没关系,找台能跑 Node 的机器(我把它和博客放在同一台 1 核 464MB 的小服务器上,也跑得动)。
1# 需要 Node.js 18+
2mkdir -p /opt/waline && cd /opt/waline
3npm init -y
4npm install @waline/vercel
准备一个环境变量文件 waline.env:
1SITE_NAME=你的站名
2SITE_URL=https://你的博客域名
3SECURE_DOMAINS=你的博客域名,评论服务域名
4SQLITE_PATH=/opt/waline/data
5COMMENT_AUDIT=false
6TURNSTILE_SECRET=第五步拿到的密钥
7JWT_TOKEN=随便一串足够长的随机字符串
几个变量的意思:SECURE_DOMAINS 是允许嵌入评论框的域名白名单,不在名单里的站点嵌不了你的评论;COMMENT_AUDIT=false 表示评论直接显示,想先审后发就改成 true;SQLITE_PATH 是数据目录,评论都存这里面的 SQLite 文件,记得备份就备份它。
用 systemd 拉起来(Node 装在 /opt/nodejs 的例子,按你的实际路径改):
1[Unit]
2Description=Waline comment service
3After=network.target
4
5[Service]
6WorkingDirectory=/opt/waline
7ExecStart=/opt/nodejs/bin/node /opt/waline/node_modules/@waline/vercel/dist/index.js
8EnvironmentFile=/opt/waline/waline.env
9Environment=PORT=8360
10Restart=always
11
12[Install]
13WantedBy=multi-user.target
最后用 Caddy 或 Nginx 把评论域名反代到 8360。Caddy 一行就够:
1talk.你的域名.com {
2 reverse_proxy 127.0.0.1:8360
3}
服务起来后,访问 https://talk.你的域名.com/ui 注册第一个账号,它自动成为管理员。评论管理、删留言都在这个后台里。
第五步:配 Cloudflare Turnstile 人机验证
没有人机验证的评论区迟早被垃圾评论淹掉。Waline 原生支持 Turnstile,配置很简单:
- 打开 Cloudflare 控制台 → Turnstile → 添加小组件,名字随便起;
- 域名把博客域名和评论服务域名都加上,类型选「托管」;
- 创建后得到两个密钥:**站点密钥(sitekey)**填回第二步的
walineTurnstileKey,公开的,不怕见人;**密钥(secret)**填进 Waline 的TURNSTILE_SECRET,这个不要外泄; - 重启 Waline 服务生效。生效后,不带验证令牌的评论请求会被服务端直接拒绝(403),不是只靠前端藏一个框框。
一个小提醒:某些自动化浏览器打不开 Turnstile 的验证框(会被 Cloudflare 识别为机器人),自己用正常浏览器打开博客能看到验证框就是好的,别被测试工具误导。
第六步:构建发布
1hugo --minify
2# 把生成的 public/ 同步到你的服务器或静态托管即可
我自己是用一个小脚本在本地构建完直接同步到服务器的,Hugo 是静态生成,发布就是传文件,没有运行时负担。
我踩过的三个坑
- headerMedia 不写就出大事。 主题的封面区参数
headerMedia如果留空或不写,会回落到主题自带的演示视频,你的封面图永远不出来。老老实实把它和cover写成同一个文件。 - 评论模板里别写死地址。 我最初把评论服务地址和密钥直接写进了模板,后来要开源才发现全是自己的域名。改成读站点参数之后,主题归主题、配置归配置,干净多了——这也是大家拿到仓库不用改模板的原因。
- 缓存会咬人。 头像、图标这类文件如果响应头写了超长缓存,换图之后浏览器和 CDN 还拿着旧的。我的办法是给引用加上版本号参数(比如
?v=2),换图就升一位版本号,比到处求人清缓存省事。
更新记录
主题在本站天天用,有改动我就往这里记一笔,仓库和这篇文章保持同步:
- 2026-11-04 · 首发:tian 首次开源——毛玻璃皮肤、Waline 评论集成(Turnstile 人机验证 + 文章点赞)、关闭 PJAX、评论开关规则,附上面的完整部署教程。
- 2026-11-04 · 更新:新增「首页头像爱心」(点头像弹爱心动画 + 计数小药丸,复用 Waline 计数)和页脚 tian 署名链接,两处都已同步到 GitHub 仓库。
- 2026-11-04 · 更新:宽屏适配与细节打磨(回应论坛网友反馈)——主页分档加宽、宽屏两侧玻璃面板、全屏菜单胶囊等宽居中、小圆角统一到 10~12px;已同步到 GitHub 仓库(wide.css + sidepanels.js)。
- 2026-11-04 · 更新:左栏新增访客欢迎卡片——显示访客自己的位置(国家/地区/城市)、IP 和设备,数据源多源兜底,国内与海外访客都能正常显示;已同步到 GitHub 仓库。
- 2026-11-04 · 更新:修复小米自带浏览器(老 XWeb 内核)上的两个真机问题——原主题会拿封面图生成同名 .mp4 的「Live Photo」视频元素,封面没有配套视频时那是个 404 视频,在小米浏览器里一点封面就被接管成全屏播放器,现默认关闭该生成;爱心计数请求改为超时 + 自动重试、失败不再显示假 0,且不用老内核缺失的新 API、点按在触摸阶段接管。已同步到 GitHub 仓库。
- 2026-11-04 · 更新(计数补修):上一条上线后真机复查发现爱心计数仍不显示——计数加载与渲染函数的作用域写岔了,数据拿到了也写不进药丸。已修正,并顺手加了本地缓存先行显示(先出上次的数、后台再刷新)、10 秒超时共 4 次重试、切回页面自动补拉;博客与 GitHub 仓库均已更新,药丸实测正常显示计数。
- 2026-11-04 · 更新:全屏导航菜单美化——去掉原主题那层死黑的遮罩,浅色模式换成粉蓝渐变毛玻璃 + 白色玻璃胶囊(深色字),深色模式换成深靛蓝渐变,与全站毛玻璃风格统一;已同步到 GitHub 仓库。
- 2026-11-04 · 更新:侧边小卡片扩展到文章页——博主名片、访客欢迎卡片、站点导航这三张玻璃卡片不再是主页专属,文章页在窗口 ≥1440px 时也会出现在正文列(680px)两侧,按文章页的列宽单独算定位;顺手修了一个计数问题:文章页没有朋友圈卡片可数,名片上的文章数原本会显示 0,现改由模板注入全站文章数兜底;已同步到 GitHub 仓库。
- 2026-11-05 · 更新:修复文章页两侧玻璃卡片遮挡正文的问题——主页加宽的选择器误把文章页也算了进去,文章被加宽到 960px,而卡片定位是按 680px 阅读列算的,于是压住了正文两侧。现收紧为「是信息流且不是文章单页」才加宽,文章页恢复 680px,首页加宽不受影响;已同步到 GitHub 仓库。
- 2026-11-06 · 更新(v1.0.1):修复信息流里 B 站嵌入视频在手机端显示异常——主题给信息流视频设了 85% 的宽度上限,手机上把 B 站播放器压得太窄,播放器控件(进度条等)挤到画面中间。现手机端(≤820px)B 站嵌入改为全宽展示,进度条回到正常位置;本地视频的竖屏紧凑展示不受影响。已同步到 GitHub 仓库,并发布版本 v1.0.1。
- 2026-11-08 · 更新(v1.1.0):新增页面预加载——内置 instant.page 5.2.0(自托管,不走第三方 CDN),悬停 / 按下站内链接时提前预取目标页,整页跳转近乎秒开;省流量与慢速网络下自动不预取,老内核浏览器自动跳过。已同步到 GitHub 仓库,并发布版本 v1.1.0。
最后
主题目前就是本站的同款配置,不是什么大作,但朋友圈风 + 毛玻璃 + 自托管评论这套组合,找了一圈没找到现成的,所以有了它。用得上就拿去,仓库的 README 里还有更细的配置对照表。部署中遇到问题,可以直接在这篇文章下面留言——正好试试 Waline 好不好用(笑)。