Somnia 主题架构概述
Without further ado
| markdown
提醒:本文档为 AI 生成,如需使用请自行验证其内容。
Somnia 是一个基于 Hugo 的主题,适用于内容型网站(博客、文档、个人主页)。
在保持 SEO 友好的同时借助 Swup.js 提供接近 SPA 的流畅体验。
技术栈:Hugo + Alpine.js + UnoCSS + Swup.js + Medium Zoom
设计定位
适合内容型网站或轻量级 Web 应用,在以下两个目标之间取得平衡:
- SEO 友好 — Hugo 服务端渲染输出完整 HTML,搜索引擎可直接索引
- SPA 体验 — Swup.js 拦截页面间导航,AJAX 加载内容,无刷新过渡
目录
技术架构总览
用户请求 ─→ Hugo(SSG)─→ 静态 HTML ─→ 浏览器
│
Swup.js 拦截同站导航 ←→ AJAX 加载新页面
│
Alpine.js 重新初始化组件
│
Async Alpine 按需加载第三方库text
| 层次 | 技术 | 定位 |
|---|---|---|
| 生成 | Hugo v0.158.0+ | 扩展版,服务端渲染 |
| 样式 | UnoCSS 66.x | 原子化 CSS,4 个预设 |
| 交互 | Alpine.js 3.14+ | 声明式 DOM 绑定,15 个指令 |
| 懒加载 | Async Alpine | 代码分割,按需加载组件 |
| 导航 | Swup.js | 无刷新页面过渡 |
| 滚动 | Swup Scroll Plugin | 切换时保存/恢复滚动位置 |
| 预加载 | Swup Preload Plugin | 悬停链接时预加载资源 |
| 图片缩放 | Medium Zoom | 点击放大预览 |
主题自定义机制
Hugo 查找优先级:website > theme
website/assets/js/custom.js ← 优先使用
website/themes/Somnia/assets/js/custom.js ← 仅当前者不存在时text
核心原则: 在 website/ 下创建与主题同路径的文件即可覆盖,无需修改主题源码。
覆盖的文件不会随主题更新而变化,升级时需手动 diff 合并。
Hugo 模板体系
布局文件结构
layouts/
├── _default/ # 默认布局
│ ├── baseof.html # 全局骨架(html/head/body 结构)
│ ├── home.html # 首页
│ ├── single.html # 单页面
│ └── list.html # 列表页
├── posts/ # 博客文章
├── docs/ # 文档页面
├── categories/ # 分类聚合
├── tags/ # 标签聚合
├── partials/ # 可复用组件
│ ├── head/ # head 子组件(theme.html、css.html、js.html)
│ ├── header.html # 导航栏
│ ├── footer.html # 页脚
│ ├── home/ # 首页区块
│ ├── page/ # 页面级组件(hero、article-info、copyright 等)
│ ├── toc/ # 目录组件(嵌套 TOC 生成流水线)
│ ├── comment/ # 评论系统(Artalk / Mastodon)
│ └── back-to-top.html # 回到顶部(含阅读进度)
└── shortcodes/ # 22 个短代码text
关键模板说明
- baseof.html — 定义
--highlightColorCSS 变量,#content-wrapper为 Swup 切换容器 - head/theme.html — 内联脚本,DOM 渲染前从 localStorage 读取主题,防闪烁
- data.html — 通过
data-somnia属性触发 KaTeX 等第三方库按需加载
JavaScript 架构
文件组织
assets/js/
├── variable.js # 全局变量(CDN 路径等)
├── main.js # Somnia 核心类(工具方法 + 第三方库管理)
├── components.js # Alpine.js 组件函数(10 个组件)
├── custom.js # 用户自定义,可覆盖欢迎语、CDN 路径、初始化钩子
├── alpinejs.min.js # Alpine.js 框架(v3.14+)
├── async-alpine.min.js # 异步加载 Alpine 组件适配器
├── medium-zoom.min.js # 图片缩放库
├── sw.js # PWA Service Worker(可选)
└── swup/ # Swup 无刷新导航库
├── Swup.umd.js # 核心
├── preload-plugin.js # 悬停预加载
└── scroll-plugin.js # 滚动位置管理text
加载顺序
head/theme.html内联脚本初始化主题色(防闪烁)head/js.html按顺序加载:- variable.js → 定义全局变量(如
SOMNIA_LIBS、BASE_URL) - main.js → 实例化
Somnia类,定义工具方法和第三方库管理器 - components.js → 通过
alpine:init注册 Alpine 组件和全局 Store - custom.js → 用户覆盖的 welcome 信息、CDN 路径、初始化钩子
- Swup.js + 插件 → 初始化无刷新导航,绑定
#content-wrapper - Alpine.js → 扫描 DOM 中的
x-data指令,初始化组件
- variable.js → 定义全局变量(如
模块职责
| 文件 | 职责 |
|---|---|
variable.js | 存储全局变量、配置参数,在各模块间共享 |
main.js | 主入口,初始化全局 Somnia 实例,提供 showToast、loadResource、timestampToShichen、scanLine 等工具方法 |
components.js | Alpine.store('somnia')(主题/暗色状态)、10 个组件函数:headerComponent、mainComponent、backToTopComponent、quoteComponent、heroComponent、tocComponent、somniaData、searchComponent 等 |
custom.js | 用户自定义逻辑,可覆盖 PageInitCustom() 和 swupPageInitCustom() 钩子 |
第三方库管理
Somnia.prototype.libs 实现了一套通用的库加载/运行接口,每个库有 loaded()、ok()、load()、run() 四个方法:
| 库 | 加载方式 | 触发条件 |
|---|---|---|
| Medium Zoom | 全局打包 | 始终可用,Swup 切换后自动重新绑定 |
| Pagefind | 按需 CSS + JS | 搜索页面 searchComponent() 加载 |
| KaTeX | 按需 CSS + JS | 文章 Front Matter 设 math: true |
Swup.js 与 Alpine.js 的协作
- Swup 拦截链接点击 → AJAX 获取新页面 → 替换
#content-wrapper→ 触发页面切换事件 - Alpine.js 自动检测新 DOM 中的
x-data指令并初始化 - 组件开发关键: Swup 切换后
<script>标签不会自动执行,因此 shortcode 中的x-data="fn()"需要额外处理。详见组件编写指南。
CSS 样式体系
文件组织
assets/css/
├── uno.css # UnoCSS 自动生成(运行 pnpm build 后生成)
├── main.css # 核心样式
│ ├── fade-in-up 动画 # 内容元素按序淡入(50ms/100ms/150ms 延迟链)
│ ├── KaTeX 公式样式 # 公式容器滚动、对齐
│ └── Shiki 代码块样式 # 圆角、行号、语言标签、复制按钮
├── components.css # 组件独有样式
│ ├── 导航栏滚动效果 # 粘性定位、阴影、移动端菜单动画
│ └── 主题切换按钮 # dark/light/system 图标切换
├── app.css # 应用级全局样式
├── custom.css # 用户自定义覆盖
├── medium-zoom.css # 图片缩放样式
├── tailwind.css # Tailwind 兼容层
└── code/ # 代码高亮主题text
UnoCSS 体系
Somnia 使用 UnoCSS 作为原子化 CSS 引擎,通过 uno.config.ts 配置:
- presetMini — 基础预设(必需)
- presetAttributify — 属性化模式:
<div p-4>替代传统 class - presetTypography — 排版预设:自定义
prose类,覆盖标题锚点、引用块、表格、列表、内联代码等 - themeColors — CSS 变量映射到工具类:
text-primary、bg-muted、border-border
开发时需同时运行 UnoCSS 监听进程:
pnpm unocss "layouts/**/*.html" -o ./assets/css/uno.css --watchbash
CSS 变量主题色
所有颜色通过 CSS 变量在 :root 和 .dark 中定义,通过 UnoCSS 的 themeColors 映射到工具类:
| 变量 | 用途 | 对应类 |
|---|---|---|
--primary | 主色调(高亮色) | text-primary、bg-primary |
--foreground / --background | 文字/背景 | text-foreground、bg-background |
--muted / --muted-foreground | 柔和色 | bg-muted、text-muted-foreground |
--border / --input / --ring | 边框/输入框/焦点环 | border-border 等 |
--radius | 圆角 | rounded-* |
--highlightColor | 渐变高亮色 | 内联 style |
加载方式
通过 head/css.html 用 Hugo Pipes 加载:
{{ $css := resources.Get "css/main.css" | fingerprint }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}">html
注意: CSS
@import语法需要 Hugo >= v0.158.0 支持。
短代码组件
Somnia 内置了 22 个短代码,分为四类:
| 类别 | 短代码 | 说明 |
|---|---|---|
| 基础 | card、callout、label、quote、toast | 卡片、提示框(4 种风格)、标签、引用、消息 |
| 布局 | tabs、collapse、timeline、steps | 标签页、折叠面板、时间线、步骤指示器 |
| 增强 | qrcode、link-preview、github-card、bilibili、formatted-date | 二维码、链接预览、GitHub 卡片、B站嵌入、日期 |
| 工具 | card-list、md2html、script、date | 列表容器、Markdown 转 HTML、脚本嵌入 |
完整用法和参数说明见组件文档。
配置参数
站点 hugo.toml 的 [params] 下可配置:
| 参数路径 | 说明 |
|---|---|
params.limit | 分页:blogPosts/blogCategories/blogTags 等 |
params.author | 名称、邮箱、头像、GitHub、简介 |
params.homepage | 首页文章数、一言开关 |
params.header.menu | 导航栏菜单项列表 |
params.footer | 页脚:ICP 备案、版权信息 |
params.features | 功能开关(search 等) |
params.comment | 评论系统(provider: Artalk / Mastodon) |
params.education / params.certifications | 首页教育/证书区块 |
params.websites | 首页友情网站列表 |
params.skills | 首页技能标签 |
完整配置示例见
exampleSite/config/_default/hugo.toml。
补充说明
版本要求
- Hugo extended,最低 v0.158.0(CSS
@import支持) - pnpm v10+(UnoCSS 构建)
- Node.js 20+(推荐)
- Pagefind(可选,搜索索引)