如何构建我的hugo博客-这是一个系列
前言
在上一篇文章“ Hugo博客搭建_基础 😊 ”中,我们讲解了如何搭建一个Hugo博客,这一篇文章将会讲解如何配置Hugo博客。
基础配置文件
我们曾经在hugo.yaml
中配置本博客的一些功能,我们将会在这一部分补充和讨论剩余的配置:
hugo.yaml
101 lines
# Basic Configuration (基础配置)
baseURL: "" # Site URL (网站 URL)(e.g. https://downmars.github.io/)
title: "" # Site title (网站标题)(e.g. DLog)
theme: "" # Theme name (主题名称)(e.g. PaperMod, in /themes/)
# Feature Toggles (功能开关)
enableEmoji: true # Enable emoji support (启用表情支持)(e.g. https://gohugo.io/quick-reference/emojis/#smileys--emotion)
enableRobotsTXT: true # Enable search engine support (启用搜索引擎支持)
hasCJKLanguage: true # Enable CJK language support (启用中日韩语言支持)
buildDrafts: false # Build draft posts (是否构建草稿文章)
buildFuture: false # Build future posts (是否构建未来日期文章)
buildExpired: false # Build expired posts (是否构建过期文章)
# Parameters Configuration (参数配置)
params:
# Theme Settings (主题设置)
defaultTheme: dark # Default theme mode (默认主题模式)
disableThemeToggle: false # Allow theme switching (允许主题切换)
# Display Features (显示功能)
ShowShareButtons: true # Show social share buttons (显示分享按钮)
ShowCodeCopyButtons: true # Show code copy buttons (显示代码复制按钮)
ShowReadingTime: true # Show reading time estimate (显示阅读时间)
ShowWordCount: true # Show word count (显示字数统计)
ShowPostNavLinks: true # Show post navigation (显示文章导航)
ShowBreadCrumbs: true # Show breadcrumb navigation (显示面包屑导航)
ShowToc: true # Show table of contents (显示目录)
TocOpen: true # TOC expanded by default (目录默认展开)
math: true
fancybox: true # Display Images (显示图片)
# Comment System (评论系统)
comments: true # Enable comments (启用评论)
giscus: # Giscus configuration (Giscus 配置)
repo: ""
repoId: ""
category: ""
categoryId: ""
mapping: "pathname"
strict: "0"
reactionsEnabled: "1"
emitMetadata: "0"
inputPosition: "bottom"
lightTheme: "light"
darkTheme: "dark"
lang: "zh-CN"
crossorigin: "anonymous"
fuseOpts: # refer: https://sonnycalcr.github.io/posts/build-a-blog-using-hugo-papermod-github-pages/#%e9%85%8d%e7%bd%ae%e6%90%9c%e7%b4%a2
isCaseSensitive: false # 是否大小写敏感
shouldSort: true # 是否排序
location: 0
distance: 1000
threshold: 0.4
minMatchCharLength: 0
# limit: 10 # refer: https://www.fusejs.io/api/methods.html#search
keys: ["title", "permalink", "summary", "content"]
includeMatches: true
# Assets Files (资源文件)
assets:
favicon: "" # Site favicon (网站图标)
favicon16x16: "" # Small favicon (小图标)
favicon32x32: "" # Medium favicon (中图标)
apple_touch_icon: "" # iOS icon (iOS 图标)
safari_pinned_tab: "" # Safari icon (Safari 图标)
# Multilingual Support (多语言支持)
languages:
zh:
languageCode: "zh-CN"
languageName: "简体中文"
contentDir: "content/zh"
weight: 1
menu:
main:
- identifier: posts
name: "Posts"
url: "/posts/"
weight: 1
# Output Settings (输出设置)
outputs:
home:
- HTML
- RSS
- JSON # Required for search (搜索功能需要)
# Rendering Configuration (渲染配置)
markup:
goldmark:
renderer:
unsafe: true # Allow HTML in markdown (允许 Markdown 中的 HTML)
highlight:
codeFences: true # Enable code highlighting (启用代码高亮)
guessSyntax: true # Guess code language (猜测代码语言)
lineNos: true # Show line numbers (显示行号)
style: dracula # Code highlight theme (代码高亮主题)
lineNumbersInTable: true
功能开关
表情支持
通过启用此功能,我们能够从 Github Emoji API 和 Unicode 完整表情符号列表中读取数据。博主显出极高兴的样子,将两个指头的敲着键盘,点头说,“对呀对呀!……表情有两样写法,你知道么?”
Shortcode: :drooling_face:
🤤
Unicode: 🤤
🤤
关于对应的表情和文档可以查询:
参数配置
图片放大
参考于: Hugo PaperMod 主题精装修↗

This is a caption for the image
我们使用引入 fancybox↗ 来实现:
在layouts/shortcodes/figure.html
中加入:
layouts/shortcodes/figure.html
38 lines
<script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css" />
<script src="https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js"></script>
<a data-fancybox="gallery" href="{{ .Get "src" }}">
<figure{{ if or (.Get "class") (eq (.Get "align") "center") }} class="
{{- if eq (.Get "align") "center" }}align-center {{ end }}
{{- with .Get "class" }}{{ . }}{{- end }}"
{{- end -}}>
{{- if .Get "link" -}}
<a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }}>
{{- end }}
<img loading="lazy" src="{{ .Get "src" }}{{- if eq (.Get "align") "center" }}#center{{- end }}"
{{- if or (.Get "alt") (.Get "caption") }}
alt="{{ with .Get "alt" }}{{ . }}{{ else }}{{ .Get "caption" | markdownify| plainify }}{{ end }}"
{{- end -}}
{{- with .Get "width" }} width="{{ . }}"{{ end -}}
{{- with .Get "height" }} height="{{ . }}"{{ end -}}
/> <!-- Closing img tag -->
{{- if .Get "link" }}</a>{{ end -}}
{{- if or (or (.Get "title") (.Get "caption")) (.Get "attr") -}}
<figcaption>
{{ with (.Get "title") -}}
{{ . }}
{{- end -}}
{{- if or (.Get "caption") (.Get "attr") -}}<p>
{{- .Get "caption" | markdownify -}}
{{- with .Get "attrlink" }}
<a href="{{ . }}">
{{- end -}}
{{- .Get "attr" | markdownify -}}
{{- if .Get "attrlink" }}</a>{{ end }}</p>
{{- end }}
</figcaption>
{{- end }}
</figure>
</a>
数学公示
参考于: Hugo博客添加LaTeX语法支持↗ 、 Hugo PaperMod 主题精装修↗ 、 MathJax 与 Markdown 的究极融合↗
上述博客中提到了相同的问题即 ”对于mathjax与markdown格式中,hugo在渲染的过程中将_
渲染为了<em>
标签,导致mathjax在渲染的时候找不到原来正确的公示“。
首先,我们在hugo.yaml
中添加:
hugo.yaml
2 lines
params:
math: true
接着,我们在layouts/partials/mathjax.html
中添加:
layouts/partials/mathjax.html
25 lines
<script type="text/javascript"
async
src="https://cdn.bootcss.com/mathjax/2.7.3/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [['$','$'], ['\\(','\\)']],
displayMath: [['$$','$$'], ['\[\[','\]\]']],
processEscapes: true,
processEnvironments: true,
skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
TeX: { equationNumbers: { autoNumber: "AMS" },
extensions: ["AMSmath.js", "AMSsymbols.js"] }
}
});
MathJax.Hub.Queue(function() {
// Fix <code> tags after MathJax finishes running. This is a
// hack to overcome a shortcoming of Markdown. Discussion at
// https://github.com/mojombo/jekyll/issues/199
var all = MathJax.Hub.getAllJax(), i;
for(i = 0; i < all.length; i += 1) {
all[i].SourceElement().parentNode.className += ' has-jax';
}
});
</script>
接着,在layouts/partials/extend_footer.html
中添加:
layouts/partials/extend_footer.html
25 lines
<script>
(function () {
var i, text, code, codes = document.getElementsByTagName("code");
for (i = 0; i < codes.length; ) {
code = codes[i];
if (code.parentNode.tagName !== "PRE" && code.childElementCount === 0) {
text = code.textContent;
if (/^\$[^$]/.test(text) && /[^$]\$$/.test(text)) {
text = text.replace(/^\$/, "\\(").replace(/\$$/, "\\)");
code.textContent = text;
}
if (
/^\\\((.|\s)+\\\)$/.test(text) ||
/^\\\[(.|\s)+\\\]$/.test(text) ||
/^\$(.|\s)+\$$/.test(text) ||
/^\\begin\{([^}]+)\}(.|\s)+\\end\{[^}]+\}$/.test(text)
) {
code.outerHTML = code.innerHTML; // remove <code></code>
continue;
}
}
i++;
}
})();
</script>
layouts/partials/extend_head.html
中添加来判断hugo.yaml
中是否启用来决定是否渲染:layouts/partials/extend_head.html
3 lines
{{ if or .Params.math .Site.Params.math }}
{{- partial "mathjax.html" .}}
{{ end }}
本处引用的方法通过将带有公式部分使用代码block装饰起来,避免内容被修改,再将代码block去除,完整将其送给渲染工具。
没做处理之前,以下代码无法渲染:
$$ \frac{\partial E(\boldsymbol{w})}{\partial z_j} = \sum\limits_{k}\frac{\partial E(\boldsymbol{w})}{\partial y_{k}}\frac{\partial y_k}{\partial z_{j}}= \sum\limits_{k} (y_{k}- \hat{y}_{k}) w_{kj}^{(2)} \tag{5.11} $$
现有几个问题待解决:1、白色黑色主题字体颜色未翻转;2、字体大小没有自动渲染正确,在中间的一个步骤,使能够正常匹配主题的颜色和字体大小,但是完全渲染过后就会出现刚才提及的问题,可能的解决方法参考于 解决 mathjax 数学公式渲染的字体大小问题↗
目录配置
对于自带的目录,只显示在文章最上面,既不能让读者随时掌握到阅读进度,也不够优雅,所以我在参考了 在PaperMod中引入侧边目录和阅读进度显示↗ 基础上做了一些改动
- 自动编号
- 当前浏览章节下划线显示
创建
layout/partials/toc.html
,
layouts/partials/toc.hmtl
265 lines
<!-- 目录侧边栏 -->
{{- $headers := findRE "<h[1-6].*?>(.|\n])+?</h[1-6]>" .Content -}}
{{- $has_headers := ge (len $headers) 1 -}}
{{- if $has_headers -}}
<aside id="toc-container" class="toc-container wide">
<div class="toc">
<details {{if (.Param "TocOpen") }} open{{ end }}>
<summary accesskey="c" title="(Alt + C)">
<span class="details">{{- i18n "toc" | default "Table of Contents" }}</span>
</summary>
<div class="inner">
{{- $largest := 6 -}}
{{- range $headers -}}
{{- $headerLevel := index (findRE "[1-6]" . 1) 0 -}}
{{- $headerLevel := len (seq $headerLevel) -}}
{{- if lt $headerLevel $largest -}}
{{- $largest = $headerLevel -}}
{{- end -}}
{{- end -}}
{{- $firstHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers 0) 1) 0)) -}}
{{- $.Scratch.Set "bareul" slice -}}
<ul>
{{- range seq (sub $firstHeaderLevel $largest) -}}
<ul>
{{- $.Scratch.Add "bareul" (sub (add $largest .) 1) -}}
{{- end -}}
{{- range $i, $header := $headers -}}
{{- $headerLevel := index (findRE "[1-6]" . 1) 0 -}}
{{- $headerLevel := len (seq $headerLevel) -}}
{{/* get id="xyz" */}}
{{- $id := index (findRE "(id=\"(.*?)\")" $header 9) 0 }}
{{- /* strip id="" to leave xyz, no way to get regex capturing groups in hugo */ -}}
{{- $cleanedID := replace (replace $id "id=\"" "") "\"" "" }}
{{- $header := replaceRE "<h[1-6].*?>((.|\n])+?)</h[1-6]>" "$1" $header -}}
{{- if ne $i 0 -}}
{{- $prevHeaderLevel := index (findRE "[1-6]" (index $headers (sub $i 1)) 1) 0 -}}
{{- $prevHeaderLevel := len (seq $prevHeaderLevel) -}}
{{- if gt $headerLevel $prevHeaderLevel -}}
{{- range seq $prevHeaderLevel (sub $headerLevel 1) -}}
<ul>
{{/* the first should not be recorded */}}
{{- if ne $prevHeaderLevel . -}}
{{- $.Scratch.Add "bareul" . -}}
{{- end -}}
{{- end -}}
{{- else -}}
</li>
{{- if lt $headerLevel $prevHeaderLevel -}}
{{- range seq (sub $prevHeaderLevel 1) -1 $headerLevel -}}
{{- if in ($.Scratch.Get "bareul") . -}}
</ul>
{{/* manually do pop item */}}
{{- $tmp := $.Scratch.Get "bareul" -}}
{{- $.Scratch.Delete "bareul" -}}
{{- $.Scratch.Set "bareul" slice}}
{{- range seq (sub (len $tmp) 1) -}}
{{- $.Scratch.Add "bareul" (index $tmp (sub . 1)) -}}
{{- end -}}
{{- else -}}
</ul>
</li>
{{- end -}}
{{- end -}}
{{- end -}}
{{- end }}
<li>
<a href="#{{- $cleanedID -}}" aria-label="{{- $header | plainify -}}">{{- $header | safeHTML -}}</a>
{{- else }}
<li>
<a href="#{{- $cleanedID -}}" aria-label="{{- $header | plainify -}}">{{- $header | safeHTML -}}</a>
{{- end -}}
{{- end -}}
{{- $firstHeaderLevel := $largest }}
{{- $lastHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers (sub (len $headers) 1)) 1) 0)) }}
</li>
{{- range seq (sub $lastHeaderLevel $firstHeaderLevel) -}}
{{- if in ($.Scratch.Get "bareul") (add . $firstHeaderLevel) }}
</ul>
{{- else }}
</ul>
</li>
{{- end -}}
{{- end }}
</ul>
</div>
</details>
</div>
</aside>
<script>
let activeElement;
let elements;
let headerCounters = {};
// 重置所有计数器
function resetHeaderCounters() {
headerCounters = {};
}
// header计数器逻辑
function getHeaderNumber(element, headerLevel) {
// 获取之前的所有标题元素
let prevElements = Array.from(document.querySelectorAll('h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]'));
let currentIndex = prevElements.indexOf(element);
let counters = new Array(6).fill(0); // 初始化6级计数器
let numbers = [];
for (let i = 0; i <= currentIndex; i++) {
let currentElement = prevElements[i];
let currentLevel = parseInt(currentElement.tagName.substring(1)) - 1; // 转为0-based
// 重置子级计数器
for (let l = currentLevel + 1; l < 6; l++) {
counters[l] = 0;
}
// 递增当前级计数器
counters[currentLevel]++;
// 如果是目标元素则记录编号
if (currentElement === element) {
for (let l = 0; l <= currentLevel; l++) {
if (counters[l] > 0) {
numbers.push(counters[l]);
}
}
break;
}
}
return numbers.join('.');
}
// 更新目录项的显示
function updateTocDisplay(element, tocLink) {
const headerLevel = parseInt(element.tagName.substring(1));
const currentNumber = getHeaderNumber(element, headerLevel);
// 移除之前可能存在的编号
let linkText = tocLink.textContent;
linkText = linkText.replace(/^\d+(\.\d+)*\s+/, '');
// 添加新的编号
tocLink.textContent = `${currentNumber} ${linkText}`;
}
// 获取元素的顶部偏移
function getOffsetTop(element) {
if (!element.getClientRects().length) {
return 0;
}
let rect = element.getBoundingClientRect();
let win = element.ownerDocument.defaultView;
return rect.top + win.pageYOffset;
}
// 检查TOC位置
function checkTocPosition() {
const width = document.body.scrollWidth;
const main = parseInt(getComputedStyle(document.body).getPropertyValue('--article-width'), 10);
const toc = parseInt(getComputedStyle(document.body).getPropertyValue('--toc-width'), 10);
const gap = parseInt(getComputedStyle(document.body).getPropertyValue('--gap'), 10);
if (width - main - (toc * 2) - (gap * 4) > 0) {
document.getElementById("toc-container").classList.add("wide");
} else {
document.getElementById("toc-container").classList.remove("wide");
}
}
// 初始化时的处理
document.addEventListener('DOMContentLoaded', function (event) {
checkTocPosition();
elements = document.querySelectorAll('h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]');
if (elements.length > 0) {
// 设置第一个标题为活动状态
activeElement = elements[0];
const id = encodeURI(activeElement.getAttribute('id')).toLowerCase();
document.querySelector(`.inner ul li a[href="#${id}"]`).classList.add('active');
}
// 初始化所有标题的编号
if (elements && elements.length > 0) {
elements.forEach(element => {
const id = encodeURI(element.getAttribute('id')).toLowerCase();
const tocLink = document.querySelector(`.inner ul li a[href="#${id}"]`);
if (tocLink) {
updateTocDisplay(element, tocLink);
}
});
}
// 添加返回顶部链接的事件监听
const topLink = document.getElementById('top-link');
if (topLink) {
topLink.addEventListener('click', (event) => {
event.preventDefault();
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}
}, false);
// 窗口大小改变时的处理
window.addEventListener('resize', function(event) {
checkTocPosition();
}, false);
// 滚动时的处理
window.addEventListener('scroll', () => {
const scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
if (scrollPosition === 0) {
return;
}
if (elements && elements.length > 0) {
// 重置计数器
resetHeaderCounters();
// 查找当前可见的标题
activeElement = Array.from(elements).find((element) => {
if ((getOffsetTop(element) - scrollPosition) > 0 &&
(getOffsetTop(element) - scrollPosition) < window.innerHeight / 2) {
return element;
}
}) || activeElement;
// 更新所有目录项的显示
elements.forEach(element => {
const id = encodeURI(element.getAttribute('id')).toLowerCase();
const tocLink = document.querySelector(`.inner ul li a[href="#${id}"]`);
if (tocLink) {
updateTocDisplay(element, tocLink);
if (element === activeElement) {
tocLink.classList.add('active');
tocLink.style.textDecoration = "underline";
// 确保当前激活的标题在目录中可见
const tocContainer = document.querySelector('.toc .inner');
const linkOffsetTop = tocLink.offsetTop;
const containerHeight = tocContainer.clientHeight;
const linkHeight = tocLink.clientHeight;
// 计算滚动位置,将当前目录项居中显示
const scrollPosition = linkOffsetTop - (containerHeight / 2) + (linkHeight / 2);
tocContainer.scrollTo({ top: scrollPosition, behavior: 'smooth' });
} else {
tocLink.classList.remove('active');
tocLink.style.textDecoration = "none";
}
}
});
}
}, false);
</script>
{{- end }}
创建assets/css/extended/toc.css
,根据阅读内容滚动并加粗相应标题就由其实现。
assets/css/extended/toc.css
88 lines
/*目录侧边栏*/
:root {
--nav-width: 1380px;
--article-width: 650px;
--toc-width: 300px;
}
.toc {
margin: 0 2px 40px 2px;
border: 1px solid var(--border);
background: var(--entry);
border-radius: var(--radius);
padding: 0.4em;
}
.toc-container.wide {
position: absolute;
height: 100%;
border-right: 1px solid var(--border);
left: calc((var(--toc-width) + var(--gap)) * -1);
top: calc(var(--gap) * 2);
width: var(--toc-width);
}
.wide .toc {
position: sticky;
top: var(--gap);
border: unset;
background: unset;
border-radius: unset;
width: 100%;
margin: 0 2px 40px 2px;
}
.toc details summary {
cursor: zoom-in;
margin-inline-start: 20px;
padding: 12px 0;
}
.toc details[open] summary {
font-weight: 500;
}
.toc-container.wide .toc .inner {
margin: 0;
}
.active {
font-size: 110%;
font-weight: 600;
}
.toc ul {
list-style-type: circle;
}
.toc .inner {
margin: 0 0 0 20px;
padding: 0px 15px 15px 20px;
font-size: 16px;
/*目录显示高度*/
max-height: 83vh;
overflow-y: auto;
}
.toc .inner::-webkit-scrollbar-thumb { /*滚动条*/
background: var(--border);
border: 7px solid var(--theme);
border-radius: var(--radius);
}
.toc li ul {
margin-inline-start: calc(var(--gap) * 0.5);
list-style-type: none;
}
.toc li {
list-style: none;
font-size: 0.95rem;
padding-bottom: 5px;
}
.toc li a:hover {
color: var(--secondary);
}
/*目录侧边栏*/
为正文添加章节序号,参考于
初始化 & 设置 PaperMod 主题的基础功能↗
,我们在assets/css/common/post-single.css
中添加以下内容:
assets/css/common/post-single.css
56 lines
main {
counter-reset: h1-cnt h2-cnt h3-cnt h4-cnt h5-cnt h6-cnt;
}
.post-content h1 {
counter-increment: h1-cnt;
counter-reset: h2-cnt h3-cnt h4-cnt h5-cnt h6-cnt; /* Reset lower levels */
}
.post-content h2 {
counter-increment: h2-cnt;
counter-reset: h3-cnt h4-cnt h5-cnt h6-cnt; /* Reset lower levels */
}
.post-content h3 {
counter-increment: h3-cnt;
counter-reset: h4-cnt h5-cnt h6-cnt; /* Reset lower levels */
}
.post-content h4 {
counter-increment: h4-cnt;
counter-reset: h5-cnt h6-cnt; /* Reset lower levels */
}
.post-content h5 {
counter-increment: h5-cnt;
counter-reset: h6-cnt; /* Reset lower levels */
}
.post-content h6 {
counter-increment: h6-cnt;
}
.post-content h1::before {
content: counter(h1-cnt) '. ';
}
.post-content h2::before {
content: counter(h2-cnt) '. ';
}
.post-content h3::before {
content: counter(h2-cnt) '.' counter(h3-cnt) '. ';
}
.post-content h4::before {
content: counter(h2-cnt) '.' counter(h3-cnt) '.' counter(h4-cnt) '. ';
}
.post-content h5::before {
content: counter(h2-cnt) '.' counter(h3-cnt) '.' counter(h4-cnt) '.' counter(h5-cnt) '. ';
}
.post-content h6::before {
content: counter(h2-cnt) '.' counter(h3-cnt) '.' counter(h4-cnt) '.' counter(h5-cnt) '.' counter(h6-cnt) '. ';
}
同时,由于一号标题有
40px
大小,推荐从二号标题开始排序。评论支持
对于评论支持,我选择了
Giscus↗
。由于本博客也是部署在Github上,在减少额外操作的同时 Giscus
也显得足够的优雅简单。
此处操作参考了 Hugo 博客引入 Giscus 评论系统↗ 。
- Giscus↗ -> 进入配置栏
- 配置 -> 仓库 -> 填入仓库名,如 Downmars/Downmars.github.io↗ -> 若不满足条件,分别查看:对应仓库是否公开、giscus app 是否安装、 Discussions 功能在对应仓库是否启用
- 配置 -> Discussions 分类 -> 选中Announcements
- 配置 -> 启用giscus -> 复制相应字段到配置中
同时需要创建 layouts/partials/comments.html
,此处参考了
Hugo + PaperMod + Github Pages 搭建一个完善的个人博客(以 Windows11 为例)↗
。
layouts/partials/comments.html
53 lines
{{- /* Comments area start */ -}}
{{- /* to add comments read => https://gohugo.io/content-management/comments/ */ -}}
<div id="tw-comment"></div>
<script>
// 默认是暗色,根目录下的配置中的主题默认也是暗色
const getStoredTheme = () => localStorage.getItem("pref-theme") === "light" ? "{{ .Site.Params.giscus.lightTheme }}" : "{{ .Site.Params.giscus.darkTheme }}";
const setGiscusTheme = () => {
const sendMessage = (message) => {
const iframe = document.querySelector('iframe.giscus-frame');
if (iframe) {
iframe.contentWindow.postMessage({giscus: message}, 'https://giscus.app');
}
}
sendMessage({setConfig: {theme: getStoredTheme()}})
}
document.addEventListener("DOMContentLoaded", () => {
const giscusAttributes = {
"src": "https://giscus.app/client.js",
"data-repo": "{{ .Site.Params.giscus.repo }}",
"data-repo-id": "{{ .Site.Params.giscus.repoId }}",
"data-category": "{{ .Site.Params.giscus.category }}",
"data-category-id": "{{ .Site.Params.giscus.categoryId }}",
"data-mapping": "{{ .Site.Params.giscus.mapping }}",
"data-strict": "{{ .Site.Params.giscus.strict }}",
"data-reactions-enabled": "{{ .Site.Params.giscus.reactionsEnabled }}",
"data-emit-metadata": "{{ .Site.Params.giscus.emitMetadata }}",
"data-input-position": "{{ .Site.Params.giscus.inputPosition }}",
"data-theme": getStoredTheme(),
"data-lang": "{{ .Site.Params.giscus.lang }}",
"data-loading": "lazy",
"crossorigin": "anonymous",
};
// 动态创建 giscus script
const giscusScript = document.createElement("script");
Object.entries(giscusAttributes).forEach(
([key, value]) => giscusScript.setAttribute(key, value));
document.querySelector("#tw-comment").appendChild(giscusScript);
// 页面主题变更后,变更 giscus 主题
const themeSwitcher = document.querySelector("#theme-toggle");
if (themeSwitcher) {
themeSwitcher.addEventListener("click", setGiscusTheme);
}
const themeFloatSwitcher = document.querySelector("#theme-toggle-float");
if (themeFloatSwitcher) {
themeFloatSwitcher.addEventListener("click", setGiscusTheme);
}
});
</script>
{{- /* Comments area end */ -}}
网站图标
咱们一个自己的博客肯定得需要有自己的一个网站图标,可以将图标放在/static/images/
,如favicon: "/images/blog.png"
。我在这里推荐两个网站供大家用来查找符合自己图标。
多语言 & 界面布置
这部分参考来自于 Hugo 多语言博客搭建,如何优雅地管理多语言 md 内容-贤民↗ 。
我认为对于编写博客的我们来说,以时间的形式来分隔是很好的一种方式来存储以及管理我们的博客,使用统一文件命名规范YYYYMMDD-title.md
。此外,有兴趣的可以做一下多语言的准备,即使现在没有推出多语言的打算,之后可以集中找个时间来使用ai
工具来统一做一下。
$ tree .
.
├── en
│ ├── archives
│ │ └── archives.md
│ ├── posts
│ │ └── 2025_01_19-hugo_build_1.md
│ └── search
│ └── search.md
└── zh
├── archives
│ └── archives.md
├── posts
│ ├── 2025_01_19-hugo_build_1.md
│ └── 2025_01_20-hugo_build_2.md
└── search
└── search.md
可以参考我的示例结构,在这里我将zh/
和en/
作为post/
的直接子目录,这一步需在对应的语言下添加设置对应的文档目录,如contentDir: "content/zh"
。
我们在此处同时需要设置页面布局,创建zh/archives/archives.md
和zh/search/search.md
,
zh/archives/archives.md
5 lines
---
title: "时间轴"
layout: "archives"
summary: archives
---
zh/search/search.md
5 lines
---
title: "搜索" # in any language you want
layout: "search" # is necessary
summary: "search"
---
layout
字段指定了这个页面使用的模板,默认使用的是themes\<hugo_theme>\layouts\_default\archives.html&search.html
,所以我们使用默认字段search
和archives
即可以让归档内容正常显示。同时,参考 Hugo + PaperMod + Github Pages 搭建一个完善的个人博客(以 Windows11 为例)↗ 搜索需要额外加入如下配置:
hugo.yaml
20 lines
params:
# 搜索
fuseOpts: # 个性化配置 refer: https://sonnycalcr.github.io/posts/build-a-blog-using-hugo-papermod-github-pages/#%e9%85%8d%e7%bd%ae%e6%90%9c%e7%b4%a2
isCaseSensitive: false # 是否大小写敏感
shouldSort: true # 是否排序
location: 0
distance: 1000
threshold: 0.4
minMatchCharLength: 0
# limit: 10 # refer: https://www.fusejs.io/api/methods.html#search
keys: ["title", "permalink", "summary", "content"]
includeMatches: true
# ......
outputs:
home:
- HTML
- RSS
- JSON # Required for search (搜索功能需要)
接着,我们在配置文件中加入时间轴与搜索的布局即可:
hugo.yaml
13 lines
# Multilingual Support (多语言支持)
languages:
zh:
languageCode: "zh-CN"
languageName: "简体中文"
contentDir: "content/zh"
weight: 1
menu:
main:
- identifier: posts
name: "Posts"
url: "/posts/"
weight: 1
渲染配置
代码配置
我在后面换了个代码渲染方式。PaperMod
主题使用的代码高亮工具为
Chroma↗
,可能不太聪明,但是对于我的日常使用暂时没有太大问题。
Chroma
自带的配色方案预览:
https://xyproto.github.io/splash/docs/longer/all.html↗
参考于: 深入探究 Hugo 代码高亮↗ 、 代码块语法高亮及复制↗
hugo.yaml
7 lines
markup:
highlight:
codeFences: true
guessSyntax: true
lineNos: true
style: dracula
lineNumbersInTable: true
我这里参考了 Hugo PaperMod 主题精装修↗ 的代码渲染方式,在其中选择了 atom-one-dark/light↗ 与 atom-one-dark.css↗ 。
为了覆盖掉原主题对于代码渲染的设置,我们需要创建assets/css/hljs/an-old-hope.min.css
,并且在其中复制进去黑色主题和白色主题的配置,白色主题直接复制进去皆可,黑色主题需要以以下形式进行限定:
assets/css/hljs/an-old-hope.min.css
7 lines
body.dark {
.hljs {
color: #abb2bf;
background: #282c34;
}
...
}
接着,我们需要修改白色与黑色主题的背景色,同时由于我参考的博主使用了Consolas 和霞鹜文楷(注释的中文字体),字体可以从 博主的仓库↗ 中获取。
我们在assets/css/extended/blank.css
中添加:
assets/css/extended/blank.css
29 lines
@font-face {
font-family: "Consolas";
src: url("/fonts/Consolas.woff2");
}
code {
font-family: "Consolas", "LXGWWenKaiScreenR";
}
.post-content code {
margin: auto 4px;
padding: 4px 6px;
font-size: 0.8em;
line-height: 1.5;
background: var(--code-bg);
}
.post-content pre code {
display: block;
margin: auto 0;
padding: 10px;
background: var(--hljs-bg) !important;
color: var(--content);
border-radius: var(--radius);
overflow-x: auto;
word-break: break-all;
font-family: "Consolas", "LXGWWenKaiScreenR";
font-size: 15px;
}
并且,在PaperMod
中的颜色变量配置assets/css/core/theme-vars.css
中添加配置:
assets/css/core/theme-vars.css
12 lines
/* 省略的内容请拷贝原先主题对应的文件 */
:root {
...
--hljs-bg: #f7f7f7;
--code-bg: rgb(245, 245, 245);
}
.dark {
...
--hljs-bg: rgb(46, 46, 51);
--code-bg: rgb(55, 56, 62);
}
layouts/_default/baseof.html
30 lines
{{- if lt hugo.Version "0.125.7" }}
{{- errorf "=> hugo v0.125.7 or greater is required for hugo-PaperMod to build " }}
{{- end -}}
<!DOCTYPE html>
<html lang="{{ site.Language }}" dir="{{ .Language.LanguageDirection | default "auto" }}">
<head>
+ <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css">
{{- partial "head.html" . }}
</head>
<body class="
{{- if (or (ne .Kind `page` ) (eq .Layout `archives`) (eq .Layout `search`)) -}}
{{- print "list" -}}
{{- end -}}
{{- if eq site.Params.defaultTheme `dark` -}}
{{- print " dark" }}
{{- end -}}
" id="top">
{{- partialCached "header.html" . .Page -}}
<main class="main">
{{- block "main" . }}{{ end }}
</main>
+ {{ partialCached "footer.html" . .Layout .Kind (.Param "hideFooter") (.Param "ShowCodeCopyButtons") -}}
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
</body>
</html>
PaperMod
默认的代码复制键的在对应语言下是不同的文字表示复制粘贴,我觉的不够优雅,于是打算使用图标来代替。关于复制符号的配置在layouts/partials/footer.html
中:layouts/partials/footer.html
8 lines
copybutton.innerHTML = '{{- i18n "code_copy" | default "copy" }}';
function copyingDone() {
copybutton.innerHTML = '{{- i18n "code_copied" | default "copied!" }}';
setTimeout(() => {
copybutton.innerHTML = '{{- i18n "code_copy" | default "copy" }}';
}, 2000);
}
修改为:
layouts/partials/footer.html
26 lines
copybutton.innerHTML = `
<img src="/images/copy.svg"
alt="复制"
class="copy-icon"
width="16"
height="16">
`;
function copyingDone() {
copybutton.innerHTML = `
<img src="/images/check.svg"
alt="已复制"
class="copied-icon"
width="16"
height="16">
`;
setTimeout(() => {
copybutton.innerHTML = `
<img src="/images/copy.svg"
alt="复制"
class="copy-icon"
width="16"
height="16">
`;
}, 2000);
}
我这里在存放的图片位于static/images/
,大家可以选择自己的路径。由于编译为静态博客之后,static/images/
变为public/images/
,同时以public/
当作根目录,所以我在代码中使用/images/check.svg©.svg
。
接下来,我们配置复制图标样式,在assets/css/extend.css
中添加以下代码:
assets/css/extend.css
20 lines
/*copy_button*/
.copy-code {
background: transparent;
border: none;
cursor: pointer;
padding: 4px;
position: absolute;
right: 10px;
top: -2px;
}
.copy-code img {
opacity: 0.6;
transition: opacity 0.3s, filter 0.3s; /* 添加 filter 过渡效果 */
}
.copy-code:hover img {
opacity: 1;
filter: invert(100%); /* 颜色翻转效果 */
}
我这里使用的是底色为黑色的svg
图标,所以我这里做了反色处理。
接下来我们配置代码折叠,对于过长的代码段,十分影响阅读者的感受,参考于
Hugo PaperMod 主题精装修↗
,我们需要在layouts/shortcodes/collapse.html
中加入以下内容:
layouts/shortcodes/collapse.html
41 lines
{{/* 参数处理逻辑 */}}
{{ $threshold := default 15 (.Get "collapseThreshold") | int }}
{{ $forceCollapse := eq (.Get "forceCollapse") "true" }}
{{ $openByDefault := eq (.Get "openByDefault") "true" }}
{{/* 内容处理逻辑 */}}
{{ $rawContent := .Inner }}
{{ $content := $rawContent | markdownify }}
{{/* 精确行数计算(排除代码块标记和空行) */}}
{{ $cleanedContent := replaceRE `(?s)<pre.*?>\n?` "" $content }}
{{ $cleanedContent := replaceRE `(?s)</pre>\n?` "" $cleanedContent }}
{{ $lines := split $cleanedContent "\n" }}
{{ $lineCount := -1 }}
{{ range $line := $lines }}
{{ if ne (trim $line " ") "" }}
{{ $lineCount = add $lineCount 1 }}
{{ end }}
{{ end }}
{{/* 自动折叠判断逻辑 */}}
{{ $shouldOpen := cond $forceCollapse
false
(or $openByDefault (lt $lineCount $threshold))
}}
{{/* 错误处理 */}}
{{ if not (.Get "summary") }}
{{ warnf "missing value for param 'summary': %s" .Position }}
{{ end }}
<p><details class="custom-collapse" {{ if $shouldOpen }}open{{ end }}>
<summary markdown="span">
<span>{{ .Get "summary" | markdownify }}</span>
<span class="line-count">{{ $lineCount }} lines</span>
</summary>
<div class="content">
{{ $content }}
</div>
</details></p>
额外配置
正文宽度
我们复制对应目录assets/css/core/theme-vars.css
,并修改以下行:
assets/css/core/theme-vars.css
2 lines
root:
--main-width: 740px;
文章封面图移至侧边
当我们文章存在封面的时候,在archives
中文章的排布中,图片占了大部分空间,十分影响我们的观感。通过参考:
Hugo博客文章封面图片缩小并移到侧边 | PaperMod主题↗
,我们可以这样配置:
layouts/_default/list.html
33 lines
<article class="{{ $class }}">
- {{- $isHidden := (.Param "cover.hiddenInList") | default (.Param "cover.hidden") | default false }}
- {{- partial "cover.html" (dict "cxt" . "IsSingle" false "isHidden" $isHidden) }}
+ <div class="post-info">
<header class="entry-header">
<h2 class="entry-hint-parent">
{{- .Title }}
{{- if .Draft }}
<span class="entry-hint" title="Draft">
<svg xmlns="http://www.w3.org/2000/svg" height="20" viewBox="0 -960 960 960" fill="currentColor">
<path
d="M160-410v-60h300v60H160Zm0-165v-60h470v60H160Zm0-165v-60h470v60H160Zm360 580v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q9 9 13 20t4 22q0 11-4.5 22.5T862.09-380L643-160H520Zm300-263-37-37 37 37ZM580-220h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z" />
</svg>
</span>
{{- end }}
</h2>
</header>
{{- if (ne (.Param "hideSummary") true) }}
<div class="entry-content">
<p>{{ .Summary | plainify | htmlUnescape }}{{ if .Truncated }}...{{ end }}</p>
</div>
{{- end }}
{{- if not (.Param "hideMeta") }}
<footer class="entry-footer">
{{- partial "post_meta.html" . -}}
</footer>
{{- end }}
+ </div>
+ {{- $isHidden := (.Param "cover.hiddenInList") | default (.Param "cover.hidden") | default false }}
+ {{- partial "cover.html" (dict "cxt" . "IsSingle" false "isHidden" $isHidden) }}
<a class="entry-link" aria-label="post link to {{ .Title | plainify }}" href="{{ .Permalink }}"></a>
</article>
{{- end }}
assets/css/common/post-entry.css
20 lines
/* F2 make the cover in the side on blogs page */
.post-entry {
display: flex;
flex-direction: row;
align-items: center;
}
.post-info {
display: inline-block;
overflow: hidden;
width: 90%;
}
.post-entry .entry-cover {
overflow: hidden;
padding-right: 18px;
height: 80%;
width: 40%;
margin-bottom: unset;
}
字体配置
我已经忍这个默认字体很久了,现在我们就在这里干掉他。我们在这里选择的中文是 霞鹜文楷↗ ,英文是 Apple 的字体 SF Pro Text Regular 来渲染,方法参考于: Hugo PaperMod 主题精装修↗ 。
在assets/css/extended/blank.css
中加入:
assets/css/extended/blank.css
19 lines
@font-face {
font-family: "LXGWWenKaiScreenR";
src: url("/fonts/lxgwwenkaiscreen.subset.v1.235.standard.woff2");
}
/* https://www.webfontfree.com/cn/download/SFProText-Regular */
@font-face {
font-family: "SFProText-Regular";
src: url("/fonts/SFProText-Regular.woff2");
}
body {
font-family: "SFProText-Regular", "LXGWWenKaiScreenR";
font-size: 16px;
line-height: 1.6;
word-break: break-word;
background: var(--theme);
font-display: swap;
}
字体可以从我上面提及博主的 github仓库↗ 处获取。
知识共享协议
参考于: Hugo+PaperMod 双语博客搭建 Home-Info+Profile Mode↗ 、 Hugo ʕ•ᴥ•ʔ Bear Blog↗
我们在
Creative Commons许可证↗
中选择使用的知识共享协议,我这里选择的是CC-BY-NC-4
协议,并添加一个协议模组来自动呈现:
首先创建文件data/licenses.html
并添加
data/licenses.html
14 lines
# data/licenses.yml
CC-BY-NC-4.0:
name: "CC BY-NC 4.0"
url: "https://creativecommons.org/licenses/by-nc/4.0/"
icons: &cc_icons # 锚点复用图标列表
- cc.svg
- by.svg
- nc.svg
# 其他协议
CC-BY-SA-4.0:
name: "CC BY-SA 4.0"
url: "https://creativecommons.org/licenses/by-sa/4.0/"
icons: *cc_icons # 复用图标列表
其次,创建文件layouts/partials/license.html
并添加:
layouts/partials/license.html
35 lines
<hr> <!-- 添加分割线 -->
{{ $license := index site.Data.licenses .Params.license }}
{{ if $license }}
<div class="license-declaration" vocab="https://schema.org/" typeof="CreativeWork">
{{/* 自动检测主题模式 */}}
{{ $theme := "light" }}
{{ if or (eq .Site.Params.colorTheme "dark") (in .Site.Params.colorTheme "auto") }}
{{ $theme = "dark" }}
{{ end }}
<meta property="name" content="{{ .Title }}">
<link property="copyrightHolder" href="{{ .Site.BaseURL }}#author" />
<p>
<span property="license" content="{{ $license.url }}">本文采用</span>
<a
href="{{ $license.url }}"
target="_blank"
rel="license noopener noreferrer"
class="license-badge"
data-theme="{{ $theme }}"
>
{{ $license.name }}
{{ range $icon := $license.icons }}
<img
src="https://mirrors.creativecommons.org/presskit/icons/{{ $icon }}"
alt="{{ $icon | replaceRE `\.svg$` `` | upper }} 图标"
class="license-icon"
loading="lazy"
decoding="async"
>
{{ end }}
</a>
</p>
</div>
{{ end }}
接下来,创建文件assets/extended/custom.css
并添加:
assets/extended/custom.css
60 lines
/* licenses.css */
.license-badge {
--bg-light: #f8f9fa;
--bg-dark: #2b2d32;
--text-light: #212529;
--text-dark: #f8f9fa;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
border-radius: 6px;
font-size: 0.9em;
transition: all 0.3s ease;
/* 默认浅色主题 */
background: var(--bg-light);
color: var(--text-light);
border: 1px solid rgba(0,0,0,0.1);
}
.license-icon {
height: 24px;
width: auto;
transition: filter 0.3s ease;
}
/* 深色主题检测 */
@media (prefers-color-scheme: dark) {
.license-badge:not([data-theme="light"]) {
background: var(--bg-dark);
color: var(--text-dark);
border-color: rgba(255,255,255,0.1);
& .license-icon {
filter: invert(1) hue-rotate(180deg); /* 更自然的反色 */
}
}
}
/* 强制深色模式 */
.license-badge[data-theme="dark"] {
background: var(--bg-dark);
color: var(--text-dark);
border-color: rgba(255,255,255,0.1);
& .license-icon {
filter: invert(1) hue-rotate(180deg);
}
}
/* 悬停效果 */
.license-badge:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
&[data-theme="dark"]:hover {
box-shadow: 0 2px 8px rgba(255,255,255,0.1);
}
}
最后,在archetypes/default.md
中添加以下代码段即可实现一个自动添加的开源协议呈现模组。
archetypes/default.md
1 lines
license: "CC-BY-NC-4.0" # 你对应使用的协议
呈现效果如下:

但是,由于我对于前端不是很了解,黑夜模式下的样式调整未能正确实现,待解决。
超链接
hugo
的默认超链接是以当前页面打开,这对于我们的阅读体验是很糟糕的,参考于
Hugo 设置外部链接用新窗口打开↗
,我们将外部链接使用新窗口打开并在链接后面添加一个小箭头,而本博客的内容使用当前窗口打开并不添加后缀:
layouts/_default/_markup/render-link.html
10 lines
<a href="{{ .Destination | safeURL }}"
{{ with .Title }} title="{{ . }}"{{ end }}
{{ if strings.HasPrefix .Destination "http" }}
target="_blank" rel="noopener"
{{ end }}>
{{ .Text | safeHTML }}
{{- if strings.HasPrefix .Destination "http" -}}
<span class="external-link">↗</span>
{{- end -}}
</a>
同时在添加小尖头样式:
assets/extended/custom.css
7 lines
.external-link {
display: inline-block;
margin-left: 0.2em;
font-size: 0.8em;
text-decoration: none;
vertical-align: super;
}
外部链接: 我的github主页↗
内部链接: Hugo博客搭建_基础 😊
修改时间
原本主题并没有显示「修改时间」的功能,我在这里参考了 Hugo PaperMod 主题精装修↗ 。
在layouts/partials/post_meta.html
中添加以下内容,参考的博客中$scratch.Add
错误被包裹在()
中,并且行末还有一个多余的$
,我在此做了修改:
layouts/partials/post_meta.html
5 lines
{{- if (.Param "ShowLastMod") -}}
{{- if ne (.Lastmod.Format "2006-01-02") (.Date.Format "2006-01-02") -}}
{{- $scratch.Add "meta" (slice (printf "Updated: %s" (.Lastmod.Format (.Site.Params.dateFormat | default "January 2, 2006")))) -}}
{{- end -}}
{{- end -}}
并且,在hugo.yaml
中加入:
hugo.yaml
2 lines
params:
showLastMod: true
同时,我们在archetypes/default.md
中的配置如下:
archetypes/default.md
5 lines
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
lastmod: {{ .Date }}
---
MarginNote
在这里十分感谢 Yunpeng Tai↗ 的博客,我从上面学习和摘选了很多内容在本文中。
我在此处对于样式做了部分修改,在assets/extended/custom.css
:
assets/extended/custom.css
75 lines
.sidenote {
float: right;
clear: right;
position: relative;
margin-top: 1rem;
max-width: 300px;
padding: 8px 16px; /* 增加内边距以便有足够空间 */
border: 2px solid transparent; /* 默认透明边框 */
border-radius: var(--radius); /* 圆角 */
box-shadow: 0 2px 4px var(--sidenote-shadow1); /* 默认轻微阴影 */
transition: all 0.3s ease; /* 增加平滑过渡效果 */
}
/* Wide viewport */
@media (min-width: 1400px) {
.sidenote {
float: right;
clear: right;
margin-right: -20vw;
text-align: left;
top: -3rem;
width: 20vw;
margin-top: 1rem;
}
}
/* Narrow viewport */
@media (max-width: 1400px) {
.sidenote {
float: right;
text-align: left;
width: 100%;
margin: 1rem 0;
padding-left: 15%;
}
}
/* 定义数字编号的外观 */
.sidenote-number {
counter-increment: sidenote-counter;
position: relative;
}
/* 给每个sidenote添加#符号 */
.sidenote::before {
content: "# ";
position: relative;
font-size: 0.9em;
font-weight: 700;
color: red; /* 默认红色 */
transition: all 0.3s ease; /* 增加过渡效果 */
}
/* 给sidenote-number添加后缀# */
.sidenote-number::after {
content: "#";
vertical-align: super;
font-size: 0.8em;
font-weight: 700;
color: #409dff; /* 默认蓝色 */
transition: all 0.3s ease; /* 增加过渡效果 */
}
/* 悬停时,数字#和注释的高亮效果 */
.sidenote-number:hover::after {
color: red; /* 悬停时改变#符号的颜色 */
}
/* 悬停时,增加sidenote的背景和阴影 */
.sidenote-number:hover .sidenote {
background-color: var(--sidenote-bg-hover); /* 悬停时背景色变化 */
box-shadow: 0 4px 8px var(--sidenote-shadow2); /* 强化阴影效果 */
}
同时,对于shortcode
也有相应的修改,在layouts/shortcodes/sidenote.html
中添加以下内容:
layouts/shortcodes/sidenote.htm
1 lines
<span class="sidenote-number"><small class="sidenote">{{ .Inner | markdownify }}</small></span>
这是示例 这是示例的侧边注解
盘古之白
「盤古之白」↗ 一文中讨论到,所有的中文字和半形的英文、數字、符號之間应该存在的空白,被漢學家稱為「盤古之白」,因為它劈開了全形字和半形字之間的混沌。
我们需要在layouts/partials/extend_footer.html
中加入:
layouts/partials/extend_footer.html
18 lines
{{- $highlight := resources.Get "js/pangu.min.js" -}}
<script>
(function (u, c) {
var d = document,
t = "script",
o = d.createElement(t),
s = d.getElementsByTagName(t)[0];
o.src = u;
if (c) {
o.addEventListener("load", function (e) {
c(e);
});
}
s.parentNode.insertBefore(o, s);
})("{{ $highlight.RelPermalink }}", function () {
pangu.spacingPage();
});
</script>
同时,需要创建assets/js/pangu.min.js
,网站加载时,盘古之白自动加载,大家可以从此处下载。
Blockquote
参考于: Hugo PaperMod 主题精装修↗
为什么要演奏春日影!
在 layouts/shortcodes/quote.html
加入以下内容:
layouts/shortcodes/quote.html
8 lines
<blockquote class="quote{{ range .Params }} {{ . }}{{ end }}">
{{- $content := .Inner | markdownify -}}
{{- if not (strings.HasPrefix $content "<p>") -}}
{{ printf "<p>%s</p>" $content | safeHTML }}
{{- else -}}
{{ $content }}
{{- end -}}
</blockquote>
我们在assets/css/extended/quote.css
中加入以下内容:
assets/css/extended/quote.css
32 lines
blockquote.quote {
position: relative;
margin: 1em auto;
padding-left: 3em;
border: none;
}
blockquote.quote::before {
position: absolute;
left: 0;
content: "“";
font-size: 3em;
font-weight: bold;
line-height: 1;
}
blockquote.quote-copyright {
position: relative;
margin: 2em auto;
padding-left: 3em;
border: none;
background-color: aliceblue;
}
blockquote.quote-copyright::before {
position: absolute;
left: 0;
content: "“";
font-size: 3em;
font-weight: bold;
line-height: 1;
}
Admonition
layouts/shortcodes/admonition.html
26 lines
{{- $type := .Get "type" | default "note" -}}
{{- $title := .Get "title" | default (humanize $type) -}}
{{- $icon := .Get "icon" -}}
{{- $collapsible := .Get "collapsible" | default false -}}
{{/* 默认图标映射(可自定义) */}}
{{- $defaultIcons := dict
"note" "fas fa-info-circle"
"tip" "fas fa-lightbulb"
"warning" "fas fa-exclamation-triangle"
"danger" "fas fa-skull-crossbones"
-}}
{{- if not $icon -}}
{{- $icon = index $defaultIcons $type -}}
{{- end -}}
<div class="admonition {{ $type }} {{ if $collapsible }}collapsible{{ end }}">
<div class="admonition-header">
{{ if $icon }}<i class="{{ $icon }}"></i>{{ end }}
<span>{{ $title }}</span>
{{ if $collapsible }}<i class="toggle-icon fas fa-chevron-down"></i>{{ end }}
</div>
<div class="admonition-content">
{{ .Inner | markdownify }}
</div>
</div>
assets/extended/admonition.css
75 lines
/* 基础变量 */
:root {
--admonition-border-width: 4px;
--admonition-radius: 8px;
--admonition-shadow: 0 3px 10px rgba(0,0,0,0.05);
--transition-speed: 0.3s;
}
.admonition {
margin: 2rem 0;
border-left: var(--admonition-border-width) solid;
border-radius: var(--admonition-radius);
background: white;
box-shadow: var(--admonition-shadow);
transition: transform var(--transition-speed) ease, box-shadow var(--transition-speed) ease;
}
/* 悬停动画 */
.admonition:hover {
transform: translateY(-2px);
box-shadow: 0 6px 15px rgba(0,0,0,0.1);
}
/* 头部样式 */
.admonition-header {
display: flex;
align-items: center;
gap: 0.8rem;
padding: 0.8rem 1.2rem 0.8rem 1.2rem;
font-size: 1.2rem; /* 标题字体大小 */
font-weight: 600;
border-radius: var(--admonition-radius) var(--admonition-radius) 0 0;
}
/* 内容区域 */
.admonition-content {
font-size: 1rem; /* 内容区域字体大小 */
font-weight: normal;
padding: 0rem 1.2rem 0.8rem 1.2rem;
line-height: 1.6;
color: rgba(0,0,0,0.8);
}
/* 类型配色(更柔和的现代色) */
.admonition.note {
border-color: #4A90E2;
background: linear-gradient(to right, #f8fcff 1%, white 10%);
}
.admonition.tip {
border-color: #00C781;
background: linear-gradient(to right, #f2fff9 1%, white 10%);
}
.admonition.warning {
border-color: #FFB800;
background: linear-gradient(to right, #fff9e6 1%, white 10%);
}
.admonition.danger {
border-color: #FF4757;
background: linear-gradient(to right, #fff6f5 1%, white 10%);
}
/* 折叠功能 */
.admonition.collapsible .admonition-content {
display: none;
}
.admonition.collapsible.active .admonition-content {
display: block;
}
.admonition.collapsible .toggle-icon {
margin-left: auto;
transition: transform var(--transition-speed) ease;
}
.admonition.collapsible.active .toggle-icon {
transform: rotate(180deg);
}
assets/js/admonition.js
6 lines
document.querySelectorAll('.admonition.collapsible').forEach(admonition => {
const header = admonition.querySelector('.admonition-header');
header.addEventListener('click', () => {
admonition.classList.toggle('active');
});
});
layouts/_default/baseof.html
35 lines
{{- if lt hugo.Version "0.125.7" }}
{{- errorf "=> hugo v0.125.7 or greater is required for hugo-PaperMod to build " }}
{{- end -}}
<!DOCTYPE html>
<html lang="{{ site.Language }}" dir="{{ .Language.LanguageDirection | default "auto" }}">
<head>
<link rel="stylesheet" href="{{ "an-old-hope.min.css" | relURL }}">
{{- partial "head.html" . }}
+ <!-- 添加 Font Awesome CDN 链接 -->
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body class="
{{- if (or (ne .Kind `page` ) (eq .Layout `archives`) (eq .Layout `search`)) -}}
{{- print "list" -}}
{{- end -}}
{{- if eq site.Params.defaultTheme `dark` -}}
{{- print " dark" }}
{{- end -}}
" id="top">
{{- partialCached "header.html" . .Page -}}
<main class="main">
{{- block "main" . }}{{ end }}
</main>
{{ partialCached "footer.html" . .Layout .Kind (.Param "hideFooter") (.Param "ShowCodeCopyButtons") -}}
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
+<!-- admonition -->
+{{ $js := resources.Get "js/admonition.js" | minify | fingerprint }}
+<script src="{{ $js.RelPermalink }}" defer></script>
</body>
</html>
友链
友链也是博客中不得不品的一个环节,我在这里参考的是 sulv-hugo-papermod|xyming108↗ ,当鼠标浮动到友链所在的位置,头像就会开始旋转并放大。首先,创建一个friend的shortcode:
layouts/shortcodes/friends.html
13 lines
{{- if .IsNamedParams -}}
<a target="_blank" href={{ .Get "url" }} title={{ .Get "name" }} class="friendurl">
<div class="frienddiv">
<div class="frienddivleft">
<img class="myfriend" src={{ .Get "logo" }} />
</div>
<div class="frienddivright">
<div class="friendname">{{- .Get "name" -}}</div>
<div class="friendinfo">{{- .Get "word" -}}</div>
</div>
</div>
</a>
{{- end }}
assets/css/extended/friends.css
99 lines
.friendurl {
text-decoration: none !important;
color: black;
box-shadow: none !important;
}
.myfriend {
width: 56px !important;
height: 56px !important;
border-radius: 50% !important;
padding: 2px;
margin-top: 20px !important;
margin-left: 14px !important;
background-color: #fff;
}
.frienddiv {
overflow: auto;
height: 100px;
width: 49%;
display: inline-block !important;
border-radius: 5px;
background: none;
-webkit-transition: all ease-out 0.3s;
-moz-transition: all ease-out 0.3s;
-o-transition: all ease-out 0.3s;
transition: all ease-out 0.3s;
}
.dark .frienddiv:hover {
background: var(--code-bg);
}
.frienddiv:hover {
background: var(--theme);
transition: transform 1s;
webkit-transform: scale(1.1);
-moz-transform: scale(1.2);
-ms-transform: scale(1.2);
-o-transform: scale(1.2);
transform: scale(1.1);
}
.frienddiv:hover .frienddivleft img {
transition: 0.9s !important;
-webkit-transition: 0.9s !important;
-moz-transition: 0.9s !important;
-o-transition: 0.9s !important;
-ms-transition: 0.9s !important;
transform: rotate(360deg) !important;
-webkit-transform: rotate(360deg) !important;
-moz-transform: rotate(360deg) !important;
-o-transform: rotate(360deg) !important;
-ms-transform: rotate(360deg) !important;
}
.frienddivleft {
width: 92px;
float: left;
margin-right: -5px;
}
.frienddivright {
margin-top: 18px;
margin-right: 18px;
}
.friendname {
text-overflow: ellipsis;
font-size: 100%;
margin-bottom: 5px;
color: var(--primary);
}
.friendinfo {
text-overflow: ellipsis;
font-size: 70%;
color: var(--primary);
}
@media screen and (max-width: 600px) {
.friendinfo {
display: none;
}
.frienddivleft {
width: 84px;
margin: auto;
}
.frienddivright {
height: 100%;
margin: auto;
display: flex;
align-items: center;
justify-content: center;
}
.friendname {
font-size: 18px;
}
}
首先,需要在hugo.yaml中添加以下内容:
hugo.yaml
8 lines
languages:
zh:
menu:
main:
+ - identifier: friends
+ name: "友链"
+ url: "/friends/"
+ weight: 4
$ mkdir content/zh/friends/
$ vim content/zh/friends/index.md
在其中添加:
content/zh/friends/index.md
11 lines
---
title: "友链"
layout: "friends"
summary: "这是我的友链页面"
---
{{</* friend
name="Downmars"
url="https://downmars.github.io/zh/posts/"
logo="https://raw.githubusercontent.com/Downmars/images-PicGo/main/img/miku.jpg"
word="Enjoy your life!"
*/>}}
这边的友链格式为:{{< friend name="downmars" url="https://downmars.github.io/zh/posts/" logo="https://raw.githubusercontent.com/downmars/images-picgo/main/img/miku.jpg" word="enjoy your life!" >}}
,上述的/* */
是为了能够顺利转义我的shortcode而不被markdown转义为友链。
接着,我们为这个导览页添加html样式:
layouts/_default/friends.html
14 lines
{{ define "main" }}
<main class="main">
<article class="friend-container">
<header class="page-header">
<h1>{{ .Title }}</h1>
{{ with .Params.summary }}<p class="summary">{{ . }}</p>{{ end }}
</header>
<section class="friend-grid">
{{ .Content }}
</section>
</article>
</main>
{{ end }}
这样我们应该就可以在页面的对应位置看到我们的友链链接了。
Mermaid
大家如果有一些复杂的关系图,需要一些示意图、流程图的地方,那么Mermaid可能会是你需要的。我们能够以代码的格式书写我们的Mermaid图,这能够帮我们以纯文本的方式保存我们的文件内容,这对于内容的传播性、开放性和可读性都有很好的诠释。这里的创建方法参照了
Mermaid图|Yunpeng Tai↗
。
layouts/_default/_markup/render-codeblock-mermaid.html
5 lines
<!-- 因为正常写会有 ```meraid ... ``` -->
<pre class="mermaid">
{{- .Inner | htmlEscape | safeHTML }}
</pre>
{{ .Page.Store.Set "hasMermaid" true }}
layouts/partials/mermaid.html
66 lines
{{ if .Page.Store.Get "hasMermaid" }}
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
<script>
const elementCode = ".mermaid";
const loadMermaid = function (theme) {
mermaid.initialize({ theme });
mermaid.init({
theme,
themeVariables: { // 这里设置字体跟正文一致
fontFamily: ["SFProText-Regular", "LXGWWenKaiScreenR"]
}}, document.querySelectorAll(elementCode));
};
const saveOriginalData = function () {
return new Promise((resolve, reject) => {
try {
var els = document.querySelectorAll(elementCode),
count = els.length;
els.forEach((element) => {
element.setAttribute("data-original-code", element.innerHTML);
count--;
if (count == 0) {
resolve();
}
});
} catch (error) {
reject(error);
}
});
};
const resetProcessed = function () {
return new Promise((resolve, reject) => {
try {
var els = document.querySelectorAll(elementCode),
count = els.length;
els.forEach((element) => {
if (element.getAttribute("data-original-code") != null) {
element.removeAttribute("data-processed");
element.innerHTML = element.getAttribute("data-original-code");
}
count--;
if (count == 0) {
resolve();
}
});
} catch (error) {
reject(error);
}
});
};
saveOriginalData().catch(console.error);
// 不要用 localStorage.getItem("pref-theme"),因为有些时候会为 null
let isdark = document.body.className.includes("dark");
if (isdark) {
resetProcessed().then(loadMermaid("dark")).catch(console.error);
} else {
resetProcessed().then(loadMermaid("neutral")).catch(console.error);
}
document.getElementById("theme-toggle").addEventListener("click", () => {
resetProcessed();
document.body.className.includes("dark")
? loadMermaid("neutral")
: loadMermaid("dark").catch(console.error);
});
</script>
{{ end }}
layouts/_default/single.html
4 lines
<article>
<!-- 省略上面的 -->
+ {{- partial "mermaid.html" . }}
</article>
Shortcodes大赏
hugo
采用markdown
的格式进行内容创作,但是当我们需要一些特殊功能的时候,markdown
并不能满足我们的需求。我们在这个时候可能需要插入一些html
代码,这对于整个markdown
的格式与可读性都是很糟糕的。幸运的是,hugo
给我们提供了简码(Shortcodes
)功能,在我们准备好对应的简码模板与简码,这能够在使用简码的时候,将hugo
自动转换为html
语言,我们就能够使用很简码的样式来实现需要的特殊功能。
关于Shortcodes
的相关知识可以参考
Shortcodes templates↗
,大家可以自行前往查看。
图像放大
{{< figure src="/path/to/image.jpg" alt="A beautiful image" title="Image" caption="This is a caption for the image" align="center" width=600px height=300px >}}

This is a caption for the image
自动折叠代码
{{< collapse summary="test" >}}
{代码段}{{< /collapse >}}
test
1 lines
test
侧边注解
这是示例 {{< sidenote >}} 这是示例的侧边注解 {{< /sidenote >}}
这是示例 这是示例的侧边注解aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
引用
{{< quote >}}
{引用段}{{< /quote >}}
为什么要演奏春日影!
转义渲染
参考于:
如何在代码块里内嵌 HUGO 的简码 (SHORTCODES) | 消夏錄↗
,由于Shortcodes
代码会被hugo
直接转义为对应的功能,所以我们需要将Shortcodes
的括号内加入/* */
防止转义。我们需要在markdown
中输入:
{{</* myshortcode */>}}
渲染结果:
{{< myshortcode >}}
Asciinema
{{< asciinema ID >}}
Admonition
{{< admonition type=“danger” title=“严重警告!” collapsible=“true” >}}
可折叠的警告区块,点击头部展开/收起
支持 Markdown 和自动图标⚡
{{< /admonition >}}
支持 Markdown 和自动图标⚡
Mermaid
我在这边单开了一篇关于Mermaid的内容,可以参考此内容: Mermaid使用例
总结
关于hugo的配置暂时就告一段落了,之后可能会有所增减,希望能帮助到大家。😄