把博客主题开源了:tian —— 朋友圈风 + 毛玻璃的 Hugo 主题

这个博客昨天(11 月 3 日)才开张,用的主题是在 Amigo 基础上改的:朋友圈式的动态流是 Amigo 的底子,我在上面加了毛玻璃皮肤、换了一套评论系统、踩平了一堆部署的坑。改都改了,干脆整理成一个独立主题开源出来,起名 tian——就是给我搭这个站的 AI 助手小天的名字。

相比原版 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,配置很简单:

  1. 打开 Cloudflare 控制台 → Turnstile → 添加小组件,名字随便起;
  2. 域名把博客域名和评论服务域名都加上,类型选「托管」;
  3. 创建后得到两个密钥:**站点密钥(sitekey)**填回第二步的 walineTurnstileKey,公开的,不怕见人;**密钥(secret)**填进 Waline 的 TURNSTILE_SECRET,这个不要外泄;
  4. 重启 Waline 服务生效。生效后,不带验证令牌的评论请求会被服务端直接拒绝(403),不是只靠前端藏一个框框。

一个小提醒:某些自动化浏览器打不开 Turnstile 的验证框(会被 Cloudflare 识别为机器人),自己用正常浏览器打开博客能看到验证框就是好的,别被测试工具误导。

第六步:构建发布

1hugo --minify
2# 把生成的 public/ 同步到你的服务器或静态托管即可

我自己是用一个小脚本在本地构建完直接同步到服务器的,Hugo 是静态生成,发布就是传文件,没有运行时负担。

我踩过的三个坑

  1. headerMedia 不写就出大事。 主题的封面区参数 headerMedia 如果留空或不写,会回落到主题自带的演示视频,你的封面图永远不出来。老老实实把它和 cover 写成同一个文件。
  2. 评论模板里别写死地址。 我最初把评论服务地址和密钥直接写进了模板,后来要开源才发现全是自己的域名。改成读站点参数之后,主题归主题、配置归配置,干净多了——这也是大家拿到仓库不用改模板的原因。
  3. 缓存会咬人。 头像、图标这类文件如果响应头写了超长缓存,换图之后浏览器和 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 好不好用(笑)。