本文其实是配置和自定义 hugo 主题的第一章,从 papermod 的部署开始,记录整个基于 papermod 进行功能拓展和定制化的过程;
Setup PaperMod 安装和设置 PaperMod
Init Hugo Project 初始化 Hugo 项目
通过 hugo 指令新建一个 hugo 项目并制定使用 yaml 格式的配置进行设置;
hugo new site {your-proj-name} --format yaml
进入目录并通过 git clone
安装一个主题到 theme/ 目录下,如果想要使用 git 进行一些版本管理进行自己的修改,可能需要使用 submodule 的方式添加;
如果想直接对主题的内容也进行修改的话,建议先 fork 一下原仓库,将 fork 的仓库作为 submodule 和 hugo proj 一起进行版本管理和开发;
cd {your-proj-name}
git init # [optional] for develop
git clone https://github.com/adityatelange/hugo-PaperMod themes/PaperMod --depth=1
git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod # [optional] for develop
如果是自己的仓库,记得使用 git 的方式拉取,如果用 http 的方式拉取后,可能无法提交后续的修改,可以用下列命令去修改 submodule 对应的 url How to change the remote repository for a git submodule? - Stack Overflow
git submodule set-url themes/PaperMod git@github.com:{your-proj}.git
后续部署的分支需要转换为 http 的形式,这里可以参见后面部署的文章。
Manage Your Configurations 配置文件管理
推荐使用 config 文件夹切分基础配置和主题配置,这样方便在多个主题之间进行切换;
cd your-prj-name
mkdir -p config/_default
mkdir -p config/papermod
touch config/paermod/hugo.yaml
配置文件相关的目录结构如下:
.
├── hugo.yaml
├── config
│ ├── _default
│ │ └── hugo.yaml
│ └── papermod
│ └── hugo.yaml
此处使用根目录的 hugo.yaml
作为默认的配置文件,papermod/hugo.yaml
设置主题特有的配置项,启动特定主题时使用 --environment {config-dif}
指定使用特定的配置文件:
hugo --environment papermod server
该部分具体的配置切分目前还没有完全确定,暂时还没有深入尝试使用别的主题,但是结构上应该是没问题的;
Hugo’s Organization 博客的文件夹组织逻辑
在进行后续的修改和迭代之前,了解一下 hugo 目录结构对应的作用 目录结构 | Hugo官方文档 是相当重要的,此外由于本文对 PaperMod 主题进行修改,也要对 PaperMod 的覆盖逻辑有一定的了解,包括模版覆盖 hugo-PaperMod Wiki-override-theme-template 和样式覆盖 hugo-PaperMod Wiki-bundling-custom-css-with-themes-assets ,由于官方文档中的介绍都比较详细,这里就不在赘述。
这里有一个特殊功能界面 _index.md
可以通过内容组织 | Hugo官方文档
简单了解并尝试一下,后面对组织文档和界面可能会有用武之地。
PaperMod Setting 主题的基础页面配置
一、设置自己的文章模版
Archetypes 文件夹中可以注册不同的文章模版 archetypes/{template.md}
,可以使用下列命令来基于指定的模版新建文章,此处由于大多时候都是直接使用 obsidian 编写后迁移过来的,该部分就不做太多说明;
hugo new --kind template {post-name}.md
但是官方模版中页面
包含了大量的元数据信息,如果不是需要经常修改的属性,可以直接丢到 Theme/Hugo.yaml
中,避免每个 markdown 的元信息都十分冗长,等需要修改的时候再用指定的 markdown
上新增去覆盖即可;
二、启用 Archive 、 Search 、About页面等
此外 PaperMod 的很多页面是没有默认开启的包括 Search,Archive ,这些参考官方 wiki 或者中文的话 PaperMod主题配置 | 🚀 田少晗的个人博客 简单配置一下这些页面,并启动 server 看一下这些页面是否正常生成;
about 界面可以仿照 archive 界面直接创建一个 about.md 在 content 目录中,最终其呈现的 URL 如下:http://localhost:1313/about/
,content 中存放文件的最终路径均类似,会包含对应的文件夹路径。创建完成后将其新增到导航栏中 FAQs · adityatelange/hugo-PaperMod Wiki :
三、参考官方wiki 完成自己需要的基本模式和参数设置。
完成上面三步设置之后,添加一下博文测试一下各方面效果是否符合预期,没问题的话就可以开始折腾了👹
Extra Basic Function Support 额外基础功能支持
现在编写 Markdown 的时候事实上大多都额外支持或者使用了 Html 的部分语法来使得 Markdown 更加美观,或者是 Latex 来记录一些数学推导,但是博客本身这些功能要么没有默认开启或者是没有很好的支持,因此首先拓展这些基础功能;
Render Html 支持对 html 的渲染
基于安全性考虑,默认的 Goldmark 并不会渲染混合在 markdown 中的 html Configure markup | Hugo ,要打开的话修改/添加如下设置即可:
markup:
goldmark:
renderer:
unsafe: true
这里请确保自己的 html 内容是安全的
Render Latex 支持渲染 Latex
这一部分相关的资料和文章还是比较多的,主要会遇到的问题都是由于 markdown 渲染和 latex 渲染之间的冲突导致的,被 $
或者 $$
包裹的内容需要如何被匹配和被谁渲染,以及一些其他的特殊字符之间的问题;
Error
没有特别处理过的话,最终会导致一些复杂的 Latex 或者是一些内联公式导致最后的样式混乱;
经过多种尝试后,这里最终使用的如下方案 使用 Mathematics in Markdown | Hugo 中的分隔符处理 使用 How to enable latex on PaperMod | terakoya 该博主的 katex 方案 How to enable Math Typesetting in PaperMod? · Issue #236 · adityatelange/hugo-PaperMod 也提到了这种混合解法 :
修改 hugo.yaml
:
markup:
goldmark:
extensions:
passthrough:
delimiters:
block:
- - \[
- \]
- - $$
- $$
inline:
- - \(
- \)
enable: true
params:
math: true
在 layouts/partials
中添加 math.html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css" integrity="sha384-MlJdn/WNKDGXveldHDdyRP1R4CTHr3FeuDNfhsLPYrq2t0UBkUdK2jyTnXPEK1NQ" crossorigin="anonymous">
<!-- The loading of KaTeX is deferred to speed up page rendering -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.js" integrity="sha384-VQ8d8WVFw0yHhCk5E8I86oOhv48xLpnDZx5T9GogA/Y84DcCKWXDmSDfn13bzFZY" crossorigin="anonymous"></script>
<!-- To automatically render math in text elements, include the auto-render extension: -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/contrib/auto-render.min.js" integrity="sha384-+XBljXPPiv+OzfbB3cVmLHf4hdUFHlWNZN5spNQ7rmHTXpd7WvJum6fIACpNNfIR" crossorigin="anonymous"
onload="renderMathInElement(document.body);"></script>
<!-- for inline -->
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "$", right: "$", display: false}
]
});
});
</script>
在 layouts/partials/extend_head.html
的末尾添加如下内容:
{{ if or .Params.math .Site.Params.math }}
{{ partial "math.html" . }}
{{ end }}
此外,针对有时因为三个 {{{
的出现导致渲染公式异常的情况Hugo博客添加LaTeX语法支持 | 🚀 田少晗的个人博客,可以通过脚本在 publish 时在代码块外侧添加 <div></div>
避免 markdown 干涉公式的渲染,使其正确渲染:
这里提供我的处理脚本,以供参考:
并不一定需要处理,可以视自己配置后的具体情况而定
def _surround_latex_by_tag(self, content:str) -> str:
# *. need to match those inline latex & block latex & ignore those $ in ``` block
# 1. read code block and ignore it.
# 非贪婪匹配
code_block_pattern = re.compile(r'```.*?```', re.DOTALL)
preserved_code_blocks = {}
for i, match in enumerate(code_block_pattern.finditer(content)):
placeholder = f"__CODE_BLOCK_{i}__"
preserved_code_blocks[placeholder] = match.group(0)
content = content.replace(match.group(0), placeholder)
# 2. add space surround the inline latex sentence
inline_latex_pattern = re.compile(r'(?<!\ $)(\$ .*?\ $)(?!\$ )')
content = inline_latex_pattern.sub(lambda match: self._latex_add_space_inline(match, content), content)
# 3. add newline between the `\ $\$ ` block or `\ $\$ \$` block
block_latex_pattern = re.compile(r'(?<!\S)(\ $\$ .*?\ $\$ |(?<!\S)\ $\$ \ $.*?\$ \ $\$ )(?!\S)', re.DOTALL)
content = block_latex_pattern.sub(lambda match: self._latex_add_div_tags(match, content), content)
# 4. restore the code block
for placeholder, code_block in preserved_code_blocks.items():
content = content.replace(placeholder, code_block)
return content
其他参考资料 Math Typesetting | PaperMod
Rss Setting for Follow 为 follow 认证设置 rss
Ref: Follow 中如何 Claim 自己的博客 | Rokcso’s blog
参考上述文章,在 rss.xml
中设置相关的 RSS Tag 即可,感谢博主的分享;
CopyRight Setting 在 footer 设置 License
参考:Hugo+PaperMod 双语博客搭建 Home-Info+Profile Mode - YUNYI BLOG ,从 Choose a License 按自己的需求选择一个协议,并设置自己的知识共享协议;
我这里直接硬编码进 layouts/partials/footer.html
中 如下:
<footer class="footer">
{{- if not site.Params.footer.hideCopyright }}
{{- if site.Copyright }}
<span>{{ site.Copyright | markdownify }}</span>
{{- else }}
<span>© {{ now.Year }} <a href="{{ "" | absLangURL }}">{{ site.Title }}</a></span>
<span xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/">
Licensed under
<a
href="https://creativecommons.org/licenses/by-nc/4.0/?ref=chooser-v1"
target="_blank"
rel="license noopener noreferrer"
style="display:inline-block;"
>CC BY-NC 4.0 </a
></span
>
{{- end }}
{{- print " · "}}
{{- end }}
Basic Style Change 基础样式修改
PaperMod 主题本身有一些基础样式个人不是很喜欢,例如内容部分的宽度比较窄,在全屏或者宽屏观看的时候比较变扭。因此这里也会针对主题的一些基础样式做针对的调整。
Get Wider Space for Content 拓宽正文区域
根据自己的布局修改 css
中如下属性即可 Change width of the content hugo-PaperMod · Discussion #442 :
:root {
--post-width: max(60vw, 100vh);
--main-width: max(60vw, 100vh);
}
Header Counter 为 Title 添加章节序号
直接利用 CSS 的 counter
和 before
属性为正文中的小标题添加对应的章节序号,实现如下:
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) '. ';
}
需要注意的是,这里由于笔者习惯在正文中仅使用>=2 级的标题,因此对 H1 是没有做显示的,如果需要从 H1 开始需要对上述 css 进行简单调整;
将该 css 存放在 assets/css/extended
中即可生效。
Pagination Update 分页拓展
当博客多起来以后,如果在 post 界面不显示页码,以及不能快速的回到首页的话,十分破坏体验,因此简单的添加了回到首页和去到尾页的按钮,方便翻页操作,同时将页码打开,效果如下:
一、在 hugo.yml
中开启页码
params:
ShowPageNums: true
二、添加跳转至首页和尾页的 button 参数参考分页 | Hugo官方文档
在 layouts/_default/list.html
中的 prev 和 next 处添加 button 如下:
<footer class="page-footer">
<nav class="pagination">
{{- if $paginator.HasPrev }}
<a class="last-icon" href="{{ $paginator.First.URL | absURL }}">
<!-- <ion-icon name="play-back-circle-outline"></ion-icon> -->
<span><<</span>
</a>
<a class="prev" href="{{ $paginator.Prev.URL | absURL }}">
« {{ i18n "prev_page" }}
{{- if (.Param "ShowPageNums") }}
{{- sub $paginator.PageNumber 1 }}/{{ $ paginator.TotalPages }}
{{- end }}
</a>
{{- end }}
{{- if $paginator.HasNext }}
<a class="next" href="{{ $paginator.Next.URL | absURL }}">
{{- i18n "next_page" }}
{{- if (.Param "ShowPageNums") }}
{{- add 1 $paginator.PageNumber }}/{{ $ paginator.TotalPages }}
{{- end }} »
</a>
<a class="last-icon" href="{{ $paginator.Last.URL | absURL }}">
<!-- <ion-icon name="play-forward-circle-outline"></ion-icon> -->
<span>>></span>
</a>
{{- end }}
</nav>
</footer>
三、简单设置其样式即可,可以在 assets/css/extended
中随意添加一个 css 文件对默认样式进行覆盖,当然也可以考虑写在主题的 css 中。
.pagination a.last-icon {
background: unset;
border: unset;
font-size: 1.3rem;
color: #001e1d;
/* padding-top: 2px; */
font-weight: bold;
}
.pagination a.last-icon {
color: var(--primary);
}
虽然没有按照该方法实现,但是如果想要更好的分页,可以参考自定义 Hugo 的分页导航栏 | DSRBLOG ;
PanGu JS 引入盘古之白
為什麼你們就是不能加個空格呢?vinta/pangu.js: Paranoid text spacing in JavaScript
如果你跟我一樣,每次看到網頁上的中文字和英文、數字、符號擠在一塊,就會坐立難安,忍不住想在它們之間加個空格。這個外掛(支援 Chrome 和 Firefox)正是你在網路世界走跳所需要的東西,它會自動替你在網頁中所有的中文字和半形的英文、數字、符號之間插入空白。
漢學家稱這個空白字元為「盤古之白 ,因為它劈開了全形字和半形字之間的混沌。另有研究顯示,打字的時候不喜歡在中文和英文之間加空格的人,感情路都走得很辛苦,有七成的比例會在 34 歲的時候跟自己不愛的人結婚,而其餘三成的人最後只能把遺產留給自己的貓。畢竟愛情跟書寫都需要適時地留白。
感谢 pangu.js
的作者,以及分享异步加载方案的博主 盘古之白 - 中英文之间自动加空格 - Yihui Xie | 谢益辉 Hugo:中英文之间自动加空格 | Blog , 将该脚本异步引入 hugo 中。
在 layouts\partials\footer.html
中添加即可。
<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);
})('//cdn.bootcss.com/pangu/3.3.0/pangu.min.js', function() {
pangu.spacingPage();
});
</script>
Font Setting 字体修改
本文使用的中文字体为霞鹜文楷 ,感谢开源分享,英文字体可以去 google font 中找一个顺眼的,参考上述开源字体的指引,通过 cdn 引入对应的 html 和 css 即可直接在 font-family 中调用。
- html 插入到:
layouts/partials/extend_head.html
中 - (如有)css 插入到
assets/css/extended/blank.css
中
然后直接在 css 中使用 font-family
调用即可:
body {
font-family: "Open Sans", "LXGW WenKai", sans-serif;
}
Tags on Post Page 文章 widget 中显示 tag 信息
当 post 数量较多时,文章是比较零散的,希望能在 widget 界面展示文章的 Tag 提供更多文章的信息,来建立文章和文章之间的关联。
找到主题中定义 widget 结构和样式的地方 /layouts/_default/list.html
中的 post-entry
部分 How to display tags in the post list? · adityatelange/hugo-PaperMod · Discussion #606 主要样式和实现参考:novikov-ai/novikov-ai.github.io ,使用 go-template 语法遍历&获取具体的 tag 参考 Params | Hugo官方文档
获取 tag ,然后将其添加到对应的 meta 模块后面如下:
<footer class="entry-footer">
{{- partial "post_meta.html" . -}}
</footer>
{{- if .Params.tags }}
<div class="tags" style="padding: 2px;">
<div style="display: flex; flex-wrap: wrap; justify-content: flex-end; margin-top: 5px;">
{{ range .Params.tags }}
<a href="/tags/{{ . | urlize }}" style="margin-right: 5px;
color: white;
background-color: rgba(53, 174, 128, 0.7);
border-radius: 6px;
padding: 1px 10px;
font-size: 13px;
z-index: 999; ">
#{{ . }}
</a>
{{ end }}
</div>
</div>
{{- end }}
- 这里添加 z-index 将 tag 标签移至外层,避免元素置于其余元素下方导致跳转失效。
- 此外为了使得 tag 和 meta 信息处于同一行而非另起一行,添加如下样式:
.tags {
/* display: inline-block; */
float: right;
}
Tag on Meta info 文章的元信息中显示 Tag
在上述的讨论区中 mgopsill
提供了解决方案,感谢其分享,具体操作是通过修改 layouts/partials/post_meta.html
中的 author
部分如下:
{{- $author := (partial "author.html" .) }}
{{- $tags := (partial "tags.html" .) }}
{{- if $tags }}
{{- $scratch.Add "meta" (slice $ author $tags) -}}
{{- else}}
{{- $scratch.Add "meta" (slice $ author) -}}
{{- end}}
并添加对应的 layouts/partials/tags.html
如下:
{{- $tags := .Params.tags -}}
{{- if $tags -}}
{{- $lastIndex := sub (len $ tags) 1 -}}
{{- range $index, $ tag := $tags -}}
<a href="/tags/{{ $tag | urlize }}"> {{ $ tag }}</a>
{{- if ne $index $ lastIndex }} · {{ end -}}
{{- end -}}
{{- end -}}
Post Widget’s Image to left 将文章封面图移至侧边
在 post 页面,如果封面图片处于信息块的上方,会导致其对页面空间过度占用,同时不同 size 也会导致呈现效果好坏参差不齐,因此为了使得页面更加美观且高效,参考 Hugo博客文章封面图片缩小并移到侧边 | PaperMod主题-素履coder 的文章,将文章的封面移至侧边;
按照上述博主说的用 div clss=post-info
将整个 post-entry 卡片的内容包括 content、meta、footer 等包裹起来后,将下列两行移至 post-info 的上一行即可,最终结构如下:
<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">
...
</div>
该方法会导致同时影响了文章顶部的图片展示,笔者不同于原文的解决方案,这里通过更具体的 css 选择器做区分即可,还有一些其他的调整如下 assets/css/common/post-entry.css
:
/* 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;
}
Something more 一些别的
一个多图片居中失败的例子
以往使用如下代码使得多张图片同一行居中显示如下图所示,此次在该主题上失效:
源码:
<p align="center">
<a href=" https://aikenh.cn"><img alt="github" title="AikenD" src=" https://custom-icon-badges.demolab.com/badge/-aiken%20blog-palegreen?style=for-the-badge&logo=package&logoColor=black"></a>
<a href=" https://twitter.com/aiken_h97"><img alt="twitter" title="Twitter" src=" https://custom-icon-badges.demolab.com/badge/-twitter%20aikenh97-plum?style=for-the-badge&logo=package&logoColor=black"></a>
...
</p>
可以考虑使用 div 或者内联样式去覆盖主题或 hugo 的样式修复该问题如下:
使用 div:
<div style="display: flex; justify-content: center; align-items: center;">
<a href="https://aikenh.cn"><img alt="github" title="AikenD" src="https://custom-icon-badges.demolab.com/badge/-aiken%20blog-palegreen?style=for-the-badge&logo=package&logoColor=black"></a> <a href="https://twitter.com/aiken_h97"><img alt="twitter" title="Twitter" src="https://custom-icon-badges.demolab.com/badge/-twitter%20aikenh97-plum?style=for-the-badge&logo=package&logoColor=black"></a>
...
</div>
使用 inline-style in p:
<p align="center">
<a href="https://aikenh.cn" style="display: inline-block; margin: 0 5px;"><img alt="github" title="AikenD" src="https://custom-icon-badges.demolab.com/badge/-aiken%20blog-palegreen?style=for-the-badge&logo=package&logoColor=black"></a>
<a href="https://twitter.com/aiken_h97" style="display: inline-block; margin: 0 5px;"><img alt="twitter" title="Twitter" src="https://custom-icon-badges.demolab.com/badge/-twitter%20aikenh97-plum?style=for-the-badge&logo=package&logoColor=black"></a>
...
</p>
一些资源或参考
- 网页图标:favicon.io
- 一种毛玻璃的实现方式:Achieving backdrop blur without ‘backdrop-filter’ - DEV Community
- 给网站添加 loading 页面: How to Quickly Add a Loading Screen onto your website! - DEV Community
- 对 hugo 的理解:风月
FI
文中有任何错误、版权使用不当之处、或者问题欢迎指正和交流,可以留言也可以发邮件,本博客的源码位于:
AikenH/hugoblog: my blog’s hugo variant. with submodule AikenH/papermod-sidebar: my sidebar & transparnet background variant of papermod
一些处理脚本后续可能会分享到:
AikenH/ManipulateMarkdownNotes: manipulate markdown file for publish or some other reason
目前在 dev 分支完善中。