欢迎来到清风客的blog
作者微信二维码

扫码添加作者微信

添加时请备注"博客读者"

点击空白处关闭

Featured image of post 我的博客美化与功能完善之路 — Hugo + Stack 主题深度定制

我的博客美化与功能完善之路 — Hugo + Stack 主题深度定制

3006 字

前言

从零搭建一个个人博客其实不难——Hugo 一条命令就能生成一个站点。但要让博客真正有"自己的味道",就得花不少心思了。

我的博客从去年冬天开始搭建,这半年多来陆陆续续加了各种功能和美化,踩了不少坑,也学到了很多东西。这篇文章就来记录一下我是怎么一步一步把这个博客折腾成现在这个样子的。

目前博客的主要特色:

  • 🎨 毛玻璃 + 纯白 + 暗夜三主题自由切换
  • ✨ 粒子动画背景 + 自定义光标
  • 🎵 底部固定音乐播放器(带进度记忆)
  • 💬 Giscus 评论区(跟随主题明暗切换)
  • 🔍 客户端全文搜索(支持 URL 参数)
  • 📱 移动端适配的汉堡菜单 + 内联搜索
  • 📑 文章页悬浮目录
  • 🔗 PJAX 无刷新页面切换
  • 📤 文章社交分享(微信/微博/QQ)

下面就一项一项来说。


一、视觉美化

1. 三主题切换 — 毛玻璃 / 纯白 / 暗夜

这是我最满意的一个功能。Stack 主题原生只支持亮色/暗色两种模式,我在此基础上增加了一个"毛玻璃“模式,作为默认主题。

1

三个主题各有特点:

主题 特点 适用场景
毛玻璃 (Glass) 半透明卡片 + 模糊背景 + 粉色粒子 日常浏览,展示个性
纯白 (Light) 纯白背景 + 高对比度 + 隐藏背景图 认真阅读,打印友好
暗夜 (Dark) GitHub 风格暗色 + #0D1117 底色 夜间阅读,保护眼睛

主题状态存储在 localStorage 中,刷新不丢失。评论组件(Giscus)和音乐播放器(APlayer)也会跟随主题自动切换配色。

实现上,核心是通过 CSS 自定义属性区分三个主题的变量值:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[data-theme="glass"] {
    --card-background: rgba(255, 255, 255, 0.45);
    --card-border: rgba(255, 255, 255, 0.8);
    backdrop-filter: blur(16px);
    /* ... */
}

[data-theme="light"] {
    --card-background: #ffffff;
    --card-border: #e0e0e0;
    /* ... */
}

[data-theme="dark"] {
    --card-background: #161B22;
    --card-border: #30363d;
    /* ... */
}

TypeScript 维护一个 ThemeSwitcher 类,通过 document.documentElement.setAttribute('data-theme', theme) 切换主题,并派发 onColorSchemeChange 事件通知其他组件。

2. 粒子动画背景

在毛玻璃主题下,全站背景会显示粉色粒子动画,用的是经典的 particles.js 库。

2

配置文件在 assets/background/particlesjs-config.json,设置了 120 个粉色(#f3afca)粒子,粒子之间有连线,鼠标可以抓取和排斥粒子。粒子动画只在毛玻璃主题下显示,切换到纯白或暗夜时会自动隐藏——毕竟阅读的时候不需要这些花里胡哨的东西。

3. 自定义光标

我给博客的三种指针状态换了定制光标:

  • 默认光标 → 青色圆形箭头(1.png
  • 文本选择 → 同系列 I 型光标(2.png
  • 链接悬停 → 同系列手型光标(3.png

光标的实现很简单,CSS 的 cursor 属性配合 url() 即可:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
body, html, img {
    cursor: url('/mouse/1.png'), auto;
}

input, textarea, p, .article-content {
    cursor: url('/mouse/2.png'), text;
}

a, button, .copyCodeButton {
    cursor: url('/mouse/3.png'), pointer;
}

4. 自定义字体 — 宋体

@font-face 加载了一个中文字体文件(assets/font/宋.ttf),并通过 Hugo 的资源管道 resources.Get 获取文件的 URL。然后将 --base-font-family--code-font-family 两个 CSS 变量都覆盖为这个字体,让正文和代码块都有统一的风格。

5. 卡片悬浮效果

我借鉴了华为商城的设计风格——鼠标悬浮时卡片微微上浮(translateY(-5px)),阴影加深。这个效果应用到了文章列表、侧边栏菜单、侧边栏个人信息卡片、标签云等几乎所有可点击的区域:

1
2
3
4
5
6
7
.article-list--compact article:hover,
.widget:hover,
.sidebar-profile-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
    transition: all 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

这个 0.35 秒的三次贝塞尔缓动动画让整个网站的交互感提升了一个档次——不会太慢让人不耐烦,也不会太快没感觉。


二、交互体验

1. PJAX 无刷新页面切换 + 进度条

这可能是对用户体验提升最大的一个功能。PJAX(PushState + Ajax)让你在页面之间切换时不需要整页刷新——只有 .main-container 的内容被替换,Header、Sidebar、Footer 等保持不变。

再配合 topbar.js 进度条,页面切换时有类似 YouTube 的顶部彩色进度条动画,从白色渐变到蓝色(rgba(0, 149, 234, 1)),体验非常流畅。

实现的时候踩了一个大坑——PJAX 替换内容时不会执行新页面中的 <script> 标签。这意味着评论组件(Giscus)在页面切换后就消失了。解决方案是在 pjax:complete 事件中手动重建 Giscus:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function reloadGiscus() {
    var giscusDiv = document.getElementById('giscus');
    if (!giscusDiv) return;
    giscusDiv.innerHTML = '';
    var script = document.createElement('script');
    script.src = 'https://giscus.app/client.js';
    script.setAttribute('data-repo', 'xuewexin/xuewexin.github.io');
    // ... 其他属性
    document.head.appendChild(script);
}

2. 悬浮目录 (TOC)

对于长文章,目录导航是刚需。Stack 主题默认的目录在文章内顶部,滚动后就看不到了。于是我写了一个悬浮目录按钮

  • 文章页面右侧中间固定一个圆形按钮
  • 点击后从右侧滑出一个 300px 宽的目录面板
  • 通过轮询同步 #TableOfContents 的高亮状态,实现滚动跟踪

3

支持 ESC 关闭、点击遮罩关闭,移动端自动缩小宽度。目录内容太少(少于 100 字符)时自动隐藏,避免只有一个标题的尴尬。

3. 回到顶部按钮

当页面滚动超过 400px 时,右下角出现一个圆形回到顶部按钮。用 requestAnimationFrame 做了滚动监听的节流优化,避免高频触发影响性能。为了不和底部的音乐播放器重叠,在移动端会抬高按钮位置。

4. 代码块复制按钮

每个代码块右上角自动添加一个「Copy」按钮,点击后复制代码到剪贴板,按钮文字短暂变为「Copied!」。代码从 Stack 主题的 main.ts 中实现。


三、功能增强

1. APlayer 音乐播放器

博客底部固定了一个 APlayer 音乐播放器,预设了 8 首歌:

4

实现了播放位置记忆——关闭页面前把当前歌曲索引、播放进度、暂停状态存进 localStorage,下次打开自动恢复。切换歌曲时有 500ms 的延迟等待缓冲,避免切歌卡顿。

2. Giscus 评论区

用 GitHub Discussions 驱动的 Giscus 评论系统。最大的改进是暗色主题跟随——通过 postMessage API 与 Giscus 的 iframe 通信,当博客切换主题时,评论区也同步切换:

1
2
3
4
5
6
7
function setGiscusTheme(theme) {
    var iframe = document.querySelector('.giscus-frame');
    if (!iframe) return;
    iframe.contentWindow.postMessage({
        giscus: { setConfig: { theme: theme } }
    }, 'https://giscus.app');
}

3. 客户端全文搜索

这个功能花了不少心思。搜索页面会用 Hugo 的 JSON 输出格式生成一个全站文章索引,然后通过 TypeScript 实现客户端全文搜索:

  • 支持多关键词(空格分隔)匹配
  • 在标题和正文中分别匹配,加权评分
  • 显示匹配片段(前后各 20 个字符的上下文)
  • 搜索结果同步到 URL/search/?keyword=你的搜索词,可以通过链接分享搜索结果

4. 微信联系弹窗

侧边栏菜单增加了一个「联系作者」入口,点击弹出微信二维码。弹窗支持:

  • 点击空白处关闭
  • ESC 键关闭
  • 阻止背景滚动
  • 暗色主题适配

每篇文章底部也有一个微信联系按钮,绿色微信品牌配色。


四、移动端适配

移动端体验是衡量一个博客好坏的重要标准。我重写了移动端的顶部导航栏,包括:

  • 汉堡菜单:带动画旋转效果的汉堡图标
  • 站点标题:“欢迎来到清风客的blog”
  • 内联搜索:点击搜索图标在顶栏内展开搜索输入框,自动聚焦

5

移动端视口 ≤ 767px 时自动切换到这个布局。侧边栏菜单项在移动端也做了优化——去掉圆角,全宽居中排列。


五、社交分享

每篇文章底部有四个分享按钮:

  1. 复制链接 — 一键复制文章 URL,兼容旧浏览器的 document.execCommand('copy') 回退方案
  2. 微信分享 — 通过 api.qrserver.com 动态生成文章二维码
  3. 微博分享 — 跳转微博分享页面
  4. QQ 分享 — 跳转 QQ 分享页面

分享弹窗同样支持暗色适配和 ESC 关闭。


六、部署流水线

博客托管在 GitHub Pages,通过 GitHub Actions 自动部署。推送代码到 main 分支后,工作流会自动:

  1. 用 Hugo Extended 构建站点(包含草稿内容)
  2. 将生成的静态文件推送到 xuewexin.github.io 仓库

站点地址:https://xuewexin.github.io


结语

从一个默认的 Hugo 主题,到如今这个功能齐全的个性化博客,这半年来我学到了很多前端知识——CSS 变量、TypeScript 构建、PJAX 原理、浏览器 API(localStoragepostMessageclipboard)等等。

当然,还有一些想做的功能没来得及加:

  • 图片懒加载优化(目前图片太多的话首屏加载会慢)
  • 文章分类的 RSS 订阅
  • 更丰富的文章封面图展示

写博客这件事本身就很有意思——你不仅要会写代码,还得会设计、会排版、会考虑用户体验。但对于每个程序员来说,拥有一个自己亲手搭建的博客,绝对是一件很酷的事情。

感谢各位的观看,如有不足的地方请见谅 (●’◡’●) (~ ̄▽ ̄)~


本文首发于「清风客」个人博客,欢迎来访交流。

分享:
微信扫码分享

打开微信"扫一扫"
分享文章给好友

5201314
使用 Hugo 构建
主题 StackJimmy 设计