Post

Hugo配置记录

Hugo配置记录

注意

你可以在这里看见我对主题做的改动,魔改主题

环境

MacBook M1Pro,macOS 15.1.1

hugo初始化配置

hugo安装

按照hugo官网给出的quick start进行安装。按照官网所说,我们需要安装git,goDart Sass。其中git是必备的,另外两个可以按需选择。我所选择的主题PaperMod主题似乎并没有使用Sass,所以我并没有安装,另外go我也没有安装。

我选择使用Homebrew来安装hugo(什么,作为一个mac用户你还不知道homebrew!?现在马上关闭本文去了解homebrew!)

1
brew install hugo

输入以下命令来验证hugo是否被安装成功。

1
hugo version

如果有版本提示,那么恭喜,你已经成功的安装上了hugo

创建网站目录

寻找一个合适的文件夹,在文件夹的目录下打开shell输入以下命令,创建一个站点文件夹。

1
hugo new site <blog-name> # 最后一个参数替换成你想要的站点文件夹名

接下来进入到你刚刚创建的文件夹内,后面我们将会在这个文件里面做大量的配置工作,我这里使用vscode进行编辑,你可以使用你喜爱或熟悉的文本编辑工具进行配置。

首先我们先进行git仓库的配置。

1
2
3
git init
git add .
git commit -m "first commit"

接着再添加一下.gitignore

1
touch .gitignore

将以下文件复制进去,这里是直接复制PaperMod作者的配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test

/public
.DS_Store
.hugo_build.lock
resources/_gen/


接下来,我们安装PaperMod主题,这里是官网的安装说明,我们使用推荐的git submodule

1
git submodule add https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod

这时候我们可以发现目录下的themes/PaperMod多了主题文件。如果你不熟悉git的子模块,这里还需有一些别的需要注意的地方,后面我们再提。

增加必要页面

content下创建三个文件,分别是about.mdarchives.mdsearch.md

依次往下面三个文件中写入以下内容

1
2
3
4
5
6
7
---
title: "关于"
layout: "about"
url: "/about/"
summary: about
---
这里就可以贴个人简介了
1
2
3
4
5
6
7
---
title: "Archive"
layout: "archives"
url: "/archives/"
summary: archives
---

1
2
3
4
5
6
7
8
9
---
title: "Search" # in any language you want
layout: "search" # is necessary
# url: "/archive"
# description: "Description for Search"
summary: "search"
placeholder: "请输入一些东西"
---

PaperMod主题配置

如果你想要一个最基本的配置,依然可以去阅读官网给出的案例。

我的配置就不一一详解了,大部分配置项我都写了注释,不懂的可以直接丢给GPT询问。

个人配置

相比于原主题,我加入了顶部栏常驻,数学公式渲染,代码块日夜主题适配,hover效果,盘古之白,图片放大支持,首页显示tag,最后编辑时间支持,侧边目录。

首先我们仍需要按照官网所说,将博客根目录下的hugo.toml改成hugo.yaml,这里直接使用下面的命令,因为之后我们的配置文件会覆盖的复制进去。

1
mv hugo.toml hugo.yaml

我的配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# ~~~~~~~~~
# hugo 本身的配置
# ~~~~~~~~~
baseURL: "https://hugo.forsakendelusion.online/" # 主站的 URL
title: Delusion's Blog # 站点标题
# copyright: "[©2024-2025 Delusion's Blog](https://hugo.forsakendelusion.online/)" # 网站的版权声明,通常显示在页脚,但这里我使用更改footer模板的方式实现
theme: PaperMod # 主题
languageCode: zh-cn # 语言

enableInlineShortcodes: true # shortcode,类似于模板变量,可以在写 markdown 的时候便捷地插入,官方文档中有一个视频讲的很通俗
hasCJKLanguage: true # 是否有 CJK 的字符
enableRobotsTXT: true # 允许生成 robots.txt
buildDrafts: false # 构建时是否包括草稿
buildFuture: false # 构建未来发布的内容
buildExpired: false # 构建过期的内容
enableEmoji: true # 允许 emoji
pygmentsUseClasses: true
defaultContentLanguage: zh # 顶部首先展示的语言界面
defaultContentLanguageInSubdir: false # 是否要在地址栏加上默认的语言代码
enableGitInfo: true


languages:
  zh:
    languageName: "中文" # 展示的语言名
    weight: 1 # 权重
    taxonomies: # 分类系统
      category: categories
      tag: tags
    # https://gohugo.io/content-management/menus/#define-in-site-configuration
    menus:
      main:
        - name: 首页
          pageRef: /
          weight: 2 # 控制在页面上展示的前后顺序
        - name: 归档
          pageRef: archives/
          weight: 3
        - name: 分类
          pageRef: categories/
          weight: 4
        - name: 标签
          pageRef: tags/
          weight: 5
        - name: 搜索
          pageRef: search/
          weight: 6
        - name: 关于
          pageRef: about/
          weight: 7

# ~~~~~~~~~
# 主题的配置(基本上是)
# ~~~~~~~~~
pagination.pagerSize: 8 # 每页展示的文章数量,这个没找到文档,应该是主题自己设置的

params:
  env: production # to enable google analytics, opengraph, twitter-cards and schema.
  description: "Theme PaperMod - https://github.com/adityatelange/hugo-PaperMod"
  author: Delusion
  defaultTheme: light # 默认是亮色背景
  ShowShareButtons: false # 关闭分享的按钮
  ShowReadingTime: true # 展示预估的阅读时长
  displayFullLangName: true # 展示全名
  ShowPostNavLinks: true # 展示文章导航链接,就是下一页上一页的那个
  ShowBreadCrumbs: true # 是否展示标题上方的面包屑
  ShowCodeCopyButtons: true # 是否展示复制代码的按钮
  ShowRssButtonInSectionTermList: true # RSS 相关
  ShowAllPagesInArchive: true # 在归档页面展示所有的页面
  ShowPageNums: true # 展示页面的页数
  ShowToc: true # 展示文章详情页的目录
  TocOpen: true
  comments: true # 评论
  # images: ["https://forsakendelusion.online/wp-content/uploads/2024/07/715156.png"] # 缺省的图片,比如,博客的封面
  DateFormat: "2006-01-02" # 这个时间是作者自己写的,只能这样写
  fancybox: true # 启用图片放大功能
  hideFooter: false
  footer:
    hideCopyright: false  # 控制版权信息显示

  # 首页的文章上方的一些信息
  homeInfoParams:
    # 首页的 profile 内容
    Title: "你好 👋"
    # 首页的 profile 内容
    Content: >
      Welcome to my Blog!此博客为本人的学习记录以及各种工具的使用记录,希望对您有所帮助。如果您有任何问题,欢迎在评论区留言,我会尽快回复。
      
  # 社交帐号的按钮
  socialIcons:
    - name: github
      title: Follow my Github
      url: "https://github.com/ForsakenDelusion"
    # - name: X
    #   title: Follow my Twitter
    #   url: "https://x.com/sonnycalcr"
    # - name: Bilibili
    #   title: 关注我的 B 站帐号
    #   url: "https://space.bilibili.com/3493138859559908"
    # - name: Youtube
    #   title: Follow my Youtube Channel
    #   url: "https://youtube.com/@sonnycalcr"
    # - name: Telegram
    #   title: Contact Me
    #   url: "https://t.me/sonnycalcr"

  # 搜索
  fuseOpts:
      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
  # 设置网站的标签页的图标,即 favicon
  assets:
      favicon: "img/favicon.ico"
      favicon16x16: "img/favicon-16x16.png"
      favicon32x32: "img/favicon-32x32.png"
      apple_touch_icon: "img/apple-touch-icon.png"
      safari_pinned_tab: "img/favicon.ico"
      disableFingerprinting: false    # 可选,但有时候有帮助    


  # 评论的设置
  giscus:
    repo: "ForsakenDelusion/blog"
    repoId: "R_kgDONshkTg"
    category: "Announcements"
    categoryId: "DIC_kwDONshkTs4CmKQV"
    mapping: "pathname"
    strict: "0"
    reactionsEnabled: "1"
    emitMetadata: "0"
    inputPosition: "top"
    lightTheme: "light"
    darkTheme: "dark"
    lang: "zh-CN"
    crossorigin: "anonymous"

# https://github.com/adityatelange/hugo-PaperMod/wiki/Features#search-page
outputs:
  home:
    - HTML # 生成的静态页面
    - RSS # 这个其实无所谓
    - JSON # necessary for search, 这里的配置修改好之后,一定要重新生成一下

markup:
  goldmark:
    renderer:
      unsafe: true # 可以 unsafe,有些 html 标签和样式可能需要
  extensions:
    passthrough:
      delimiters:
        block:
        - - \[
          - \]
        - - $$
          - $$
        inline:
        - - \(
          - \)
      enable: true
  highlight:
    disableHLJS: true
    anchorLineNos: false # 不要给行号设置锚标
    codeFences: true # 代码围栏
    guessSyntax: true
    pygmentsUseClasses: true
    noClasses: false # TODO: 不知道干啥的,暂时没必要了解,不影响展示
    lineNos: false # 代码行 设置成true总是有奇奇怪怪的bug
    lineNumbersInTable: false # 不要设置成 true,否则如果文章开头是代码的话,摘要会由一大堆数字(即代码行号)开头文章
    # 这里设置 style 没用,得自己加 css
    # style: "github-dark"
    # style: monokai

frontmatter:
    date:
    - date
    - publishDate
    - lastmod
    lastmod:
    - lastmod
    - :git

接着我们去这个网站去生成一下图标。将生成的东西放在根目录下的/assets/favicon/下,这样就为我们的网站添加了图标。

giscus评论系统

采用# PaperMod 集成 Giscus 评论这篇文章的方案

然后我们还需要为网站添加评论系统。我这里使用了giscus作为我的评论系统。首先在网站根目录下的layouts\partials\下新建一个comments.html文件,然后将以下代码复制进去(这里给出的是适配深浅色主题的方案),然后根据引导生成数据,填入hugo.yaml中,替换掉我的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<div id="tw-comment"></div>
<script>
    const getStoredTheme = () => localStorage.getItem("pref-theme") === "dark" ? "{{ .Site.Params.giscus.darkTheme }}" : "{{ .Site.Params.giscus.lightTheme }}";
    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",
            "async": "",
        };

        // 动态创建 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>

在 layouts/partials/extend_head.html 添加如下内容

1
{{ partial "math.html" . }}

在主页显示文章tag信息

参考# Hugo PaperMod 主题精装修# 初始化 & 设置 PaperMod 主题的基础功能 (这一步现在不用添加,在下面添加最后编辑时间支持的时候直接复制整个文件的配置就好了)修改 layouts/partials/post_meta.html 中的 author 部分如下:

1
2
3
4
5
6
7
{{- $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中填入

1
2
3
4
5
6
7
8
9
{{- $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 }}&nbsp;·&nbsp;{{ end -}}
  {{- end -}}
{{- end -}}

添加最后编辑时间支持

layouts/partials/post_meta.html中填入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{{/* 创建一个新的 scratch 对象用于存储元信息 */}}
{{- $scratch := newScratch }}

{{/* 获取文章的发布时间和最后修改时间 */}}
{{ $date := .Date }}
{{ $lastmod := .Lastmod }}

{{/* 添加发布日期信息 */}}
{{- if not .Date.IsZero -}}
{{- $scratch.Add "meta" (slice (printf "<span title='%s'>%s</span>" 
    (.Date) 
    (.Date | time.Format (default "January 2, 2006" site.Params.DateFormat)))) }}
{{- end }}

{{/* 添加最后修改日期信息 
     只在最后修改日期存在且与发布日期不是同一天时显示 */}}
{{- if and (not .Lastmod.IsZero) (ne ($lastmod.Format "2006-01-02") ($date.Format "2006-01-02")) -}}
{{- $scratch.Add "meta" (slice (printf "编辑于<span title='%s'>%s%s</span>" 
    (.Lastmod) 
    (i18n "updated") 
    (.Lastmod | time.Format (default "January 2, 2006" site.Params.DateFormat)))) }}
{{- end }}

{{/* 如果启用了阅读时间显示,添加预计阅读时间 */}}
{{- if (.Param "ShowReadingTime") -}}
{{- $scratch.Add "meta" (slice (i18n "read_time" .ReadingTime | default (printf "%d min" .ReadingTime))) }}
{{- end }}

{{/* 如果启用了字数统计显示,添加文章字数 */}}
{{- if (.Param "ShowWordCount") -}}
{{- $scratch.Add "meta" (slice (i18n "words" .WordCount | default (printf "%d words" .WordCount))) }}
{{- end }}

{{/* 获取作者信息和标签信息 */}}
{{- $author := (partial "author.html" .) }}
{{- $tags := (partial "tags.html" .) }}

{{/* 处理作者和标签的显示逻辑 
     - 如果不隐藏作者,显示作者信息
     - 如果有标签,显示标签信息 */}}
{{- if not (.Param "hideAuthor") -}}
    {{- if $tags }}
        {{- $scratch.Add "meta" (slice $author $tags) -}}
    {{- else}}
        {{- $scratch.Add "meta" (slice $author) -}}
    {{- end}}
{{- else}}
    {{- if $tags }}
        {{- $scratch.Add "meta" (slice $tags) -}}
    {{- end}}
{{- end }}

{{/* 使用 · 符号连接所有元信息并输出 */}}
{{- with ($scratch.Get "meta") }}
{{- delimit . "&nbsp;·&nbsp;" | safeHTML -}}
{{- end -}}

侧边目录

复制模板文件 ~/themes\PaperMod\layouts\partials\toc.html 到站点根目录下,替换内容并添加样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
{{- $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">
    <div class="toc">
            <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 := len (seq (index (findRE "[1-6]" (index $headers 0) 1) 0)) -}} -->
                        {{- $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>
    </div>
</aside>
<script>
    let activeElement;
    let elements;
    window.addEventListener('DOMContentLoaded', function (event) {
        checkTocPosition();
        elements = document.querySelectorAll('h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]');
         // Make the first header active
         activeElement = elements[0];
         const id = encodeURI(activeElement.getAttribute('id')).toLowerCase();
         document.querySelector(`.inner ul li a[href="#${id}"]`).classList.add('active');
     }, false);
    window.addEventListener('resize', function(event) {
        checkTocPosition();
    }, false);
    window.addEventListener('scroll', () => {
        // Check if there is an object in the top half of the screen or keep the last item active
        activeElement = Array.from(elements).find((element) => {
            if ((getOffsetTop(element) - window.pageYOffset) > 0 && 
                (getOffsetTop(element) - window.pageYOffset) < window.innerHeight/2) {
                return element;
            }
        }) || activeElement
        elements.forEach(element => {
             const id = encodeURI(element.getAttribute('id')).toLowerCase();
             if (element === activeElement){
                 document.querySelector(`.inner ul li a[href="#${id}"]`).classList.add('active');
             } else {
                 document.querySelector(`.inner ul li a[href="#${id}"]`).classList.remove('active');
             }
         })
     }, false);
     function checkTocPosition() {
        const width = document.body.scrollWidth;
        
        // 动态获取文章和目录的宽度
        const main = document.querySelector('.main').offsetWidth; // 获取文章的实际宽度
        const toc = document.querySelector('.toc').offsetWidth;   // 获取目录的实际宽度
        const gap = parseInt(getComputedStyle(document.body).getPropertyValue('--gap'), 15);
    
        // 判断是否有足够的空间来显示目录(TOC)
        if (width - main - toc - gap * 2 > 0) {
            document.getElementById("toc-container").classList.add("wide");
        } else {
            document.getElementById("toc-container").classList.remove("wide");
            document.getElementById("toc-container").classList.remove("logo");
        }
    }
    function getOffsetTop(element) {
        if (!element.getClientRects().length) {
            return 0;
        }
        let rect = element.getBoundingClientRect();
        let win = element.ownerDocument.defaultView;
        return rect.top + win.pageYOffset;   
    }
            // 防抖时间延长至250ms适应旋转延迟
    function debounce(fn, delay=250) {
        let timer;
        return (...args) => {
            clearTimeout(timer);
            timer = setTimeout(() => fn.apply(this, args), delay);
        };
    }

    // 单独处理旋转事件
    const onOrientationChange = debounce(() => {
        console.log('屏幕方向变化');
        // 强制触发重排
        document.body.clientWidth;
        checkTocPosition();
    });

    window.addEventListener('resize', debounce(checkTocPosition));
    window.addEventListener('orientationchange', onOrientationChange);
    
</script>
{{- end }}

assets/css/extended/toc.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
:root {
    --margin-width: 300px;
    --toc-width: 300px;
    --gap: 20px; /* 添加 gap 变量来控制两边的间距 */
    --toc-height: 70vh;
}

.main {
    max-width: 100%; /* 允许宽度填满父容器 */
    margin-inline-start: auto;
    margin-inline-end: auto;
    margin-left: var(--margin-width); /* 保持默认宽度 */
    margin-right: var(--margin-width); /* 保持默认宽度 */
    line-height: var(--header-height * 0.5);
    transition: margin 0.3s ease; /* 平滑过渡 */
    padding-left: var(--gap);
    padding-right: var(--gap);
}

.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%;
    left: calc((var(--margin-width) - var(--gap) * 0) * -1);
    top: calc(var(--gap) * 4);
    width: var(--toc-width);
}

.wide .toc {
    position: sticky;
    top: 70px ; /* 侧边目录顶部和顶部导航栏的空隙 */
    border-radius: 15px;
    width: auto;
    height: auto;
    margin: 45px var(--gap) 40px var(--gap);
}

.toc .details{
    margin-left: 10px;
    font-weight: 700;
}

.toc-container.wide .toc .inner {
    margin: 0;
}

.active {
    font-size: 110%;
    font-weight: 500;
}

.toc ul {
    list-style-type: circle;
}

.toc .inner {
    margin: 0 0 0 20px;
    padding: 0px 15px 15px 30px;
    font-size: 16px;
    max-height: var(--toc-height);
    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);
}

/* 修改媒体查询条件 */
@media (max-width: 900px), (max-height: 400px) {
    .main {
        margin-left: 10px;
        margin-right: 10px;
        transition: margin 0.1s ease; /* 缩短过渡时间 */
    }
}

/* 以下代码是为了做旋转适配 */

.toc-container {
    display: none; /* 默认隐藏 */
}

.toc-container.wide {
    display: block; 
}

顶部菜单移动端适配

assets/css/extended/header-flex.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
/* 移动端样式 */
@media (max-width: 900px) {
    .nav {
        position: relative;
    }

    /* 显示移动端按钮 */
    .mobile-menu-btn {
        display: block;
        position: absolute;
        right: 1rem;
        top: 50%;
        transform: translateY(-50%);
        font-size: 1.5rem;
        background: none;
        border: none;
        color: inherit;
        cursor: pointer;
        z-index: 1001;
    }

    /* 移动端目录遮罩层 */
    .mobile-menu-mask {
        display: none;
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background: rgba(0, 0, 0, 0.5);
        z-index: 1000;
    }

    /* 移动端目录样式 */
    .toc-container.mobile-show {
        display: block;
        position: fixed;
        top: 0;
        right: 0;
        width: 80%;
        max-width: 300px;
        height: 100%;
        background: var(--theme);
        border-radius: 2px;
        z-index: 1001;
        padding: 10px;
        box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
        overflow-y: auto;
        transform: translateX(100%);
        transition: transform 0.3s ease;
    }

    .toc-container.mobile-show.active {
        transform: translateX(0);
    }

    .mobile-menu-mask.show {
        display: block;
    }

    /* 移动端目录内部样式调整 */

    .toc-container.mobile-show .toc {
        margin: unset;
        height: auto;
        border: none;
    }

    .toc-container.mobile-show .toc .inner {
        max-height: none;
        height: 100%;
        padding: 0 10px 10px 0;
    }

    /* 移动端文章页面隐藏menu */
    .nav-menu {
        display: none !important;
    }
}

/* 桌面端隐藏按钮 */
@media (min-width: 900px) {
    .mobile-menu-btn,
    .mobile-menu-mask {
        display: none !important;
    }
}

layouts/partials/header.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
{{- /* theme-toggle is enabled */}}
{{- if (not site.Params.disableThemeToggle) }}
{{- /* theme is light */}}
{{- if (eq site.Params.defaultTheme "light") }}
<script>
    if (localStorage.getItem("pref-theme") === "dark") {
        document.body.classList.add('dark');
    }

</script>
{{- /* theme is dark */}}
{{- else if (eq site.Params.defaultTheme "dark") }}
<script>
    if (localStorage.getItem("pref-theme") === "light") {
        document.body.classList.remove('dark')
    }

</script>
{{- else }}
{{- /* theme is auto */}}
<script>
    if (localStorage.getItem("pref-theme") === "dark") {
        document.body.classList.add('dark');
    } else if (localStorage.getItem("pref-theme") === "light") {
        document.body.classList.remove('dark')
    } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
        document.body.classList.add('dark');
    }

</script>
{{- end }}
{{- /* theme-toggle is disabled and theme is auto */}}
{{- else if (and (ne site.Params.defaultTheme "light") (ne site.Params.defaultTheme "dark"))}}
<script>
    if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
        document.body.classList.add('dark');
    }

</script>
{{- end }}



<header class="header">
    <nav class="nav">
        {{/* 只在文章页面显示汉堡菜单按钮,使用 .Kind 来精确判断是否为文章页面 */}}
        {{ if and (eq .Kind "page") (ne .Layout "search") (ne .Layout "archives") (ne .Type "page") }}
            <button class="mobile-menu-btn" aria-label="展开目录">
                <span class="menu-icon"></span>
            </button>
        {{ end }}
        <div class="logo">
            {{- $label_text := (site.Params.label.text | default site.Title) }}
            {{- if site.Title }}
            <a href="{{ "" | absLangURL }}" accesskey="h" title="{{ $label_text }} (Alt + H)">
                {{- if site.Params.label.icon }}
                {{- $img := resources.Get site.Params.label.icon }}
                {{- if $img }}
                    {{- $processableFormats := (slice "jpg" "jpeg" "png" "tif" "bmp" "gif") -}}
                    {{- if hugo.IsExtended -}}
                        {{- $processableFormats = $processableFormats | append "webp" -}}
                    {{- end -}}
                    {{- $prod := (hugo.IsProduction | or (eq site.Params.env "production")) }}
                    {{- if and (in $processableFormats $img.MediaType.SubType) (eq $prod true)}}
                        {{- if site.Params.label.iconHeight }}
                            {{- $img = $img.Resize (printf "x%d" site.Params.label.iconHeight) }}
                        {{ else }}
                            {{- $img = $img.Resize "x30" }}
                        {{- end }}
                    {{- end }}
                    <img src="{{ $img.Permalink }}" alt="" aria-label="logo"
                        height="{{- site.Params.label.iconHeight | default "30" -}}">
                {{- else }}
                <img src="{{- site.Params.label.icon | absURL -}}" alt="" aria-label="logo"
                    height="{{- site.Params.label.iconHeight | default "30" -}}">
                {{- end -}}
                {{- else if hasPrefix site.Params.label.iconSVG "<svg" }}
                    {{ site.Params.label.iconSVG | safeHTML }}
                {{- end -}}
                {{- $label_text -}}
            </a>
            {{- end }}
            <div class="logo-switches">
                {{- if (not site.Params.disableThemeToggle) }}
                <button id="theme-toggle" accesskey="t" title="(Alt + T)">
                    <svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
                    </svg>
                    <svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <circle cx="12" cy="12" r="5"></circle>
                        <line x1="12" y1="1" x2="12" y2="3"></line>
                        <line x1="12" y1="21" x2="12" y2="23"></line>
                        <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
                        <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
                        <line x1="1" y1="12" x2="3" y2="12"></line>
                        <line x1="21" y1="12" x2="23" y2="12"></line>
                        <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
                        <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
                    </svg>
                </button>
                {{- end }}

                {{- $lang := .Lang}}
                {{- $separator := or $label_text (not site.Params.disableThemeToggle)}}
                {{- with site.Home.Translations }}
                <ul class="lang-switch">
                    {{- if $separator }}<li>|</li>{{ end }}
                    {{- range . -}}
                    {{- if ne $lang .Lang }}
                    <li>
                        <a href="{{- .Permalink -}}" title="{{ .Language.Params.languageAltTitle | default (.Language.LanguageName | emojify) | default (.Lang | title) }}"
                            aria-label="{{ .Language.LanguageName | default (.Lang | title) }}">
                            {{- if (and site.Params.displayFullLangName (.Language.LanguageName)) }}
                            {{- .Language.LanguageName | emojify -}}
                            {{- else }}
                            {{- .Lang | title -}}
                            {{- end -}}
                        </a>
                    </li>
                    {{- end -}}
                    {{- end}}
                </ul>
                {{- end }}
            </div>
        </div>
         
        {{- $currentPage := . }}
        <ul id="menu" class="{{ if and (eq .Kind "page") (ne .Layout "search") (ne .Layout "archives") (ne .Type "page") }}nav-menu{{ end }}">
            {{- range site.Menus.main }}
            {{- $menu_item_url := (cond (strings.HasSuffix .URL "/") .URL (printf "%s/" .URL) ) | absLangURL }}
            {{- $page_url:= $currentPage.Permalink | absLangURL }}
            {{- $is_search := eq (site.GetPage .KeyName).Layout `search` }}
            <li>
                <a href="{{ .URL | absLangURL }}" title="{{ .Title | default .Name }} {{- cond $is_search (" (Alt + /)" | safeHTMLAttr) ("" | safeHTMLAttr ) }}"
                {{- cond $is_search (" accesskey=/" | safeHTMLAttr) ("" | safeHTMLAttr ) }}>
                    <span {{- if eq $menu_item_url $page_url }} class="active" {{- end }}>
                        {{- .Pre }}
                        {{- .Name -}}
                        {{ .Post -}}
                    </span>
                    {{- if (findRE "://" .URL) }}&nbsp;
                    <svg fill="none" shape-rendering="geometricPrecision" stroke="currentColor" stroke-linecap="round"
                        stroke-linejoin="round" stroke-width="2.5" viewBox="0 0 24 24" height="12" width="12">
                        <path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"></path>
                        <path d="M15 3h6v6"></path>
                        <path d="M10 14L21 3"></path>
                    </svg>
                    {{- end }}
                </a>
            </li>
            {{- end }}
        </ul>
       
    </nav>
</header>

<!-- 添加遮罩层 -->
<div class="mobile-menu-mask"></div>

<script>
    // 添加到文件末尾
    document.addEventListener('DOMContentLoaded', function() {
        const menuBtn = document.querySelector('.mobile-menu-btn');
        const mask = document.querySelector('.mobile-menu-mask');
        const tocContainer = document.getElementById('toc-container');

        if (menuBtn && tocContainer) {
            menuBtn.addEventListener('click', function() {
                tocContainer.classList.add('mobile-show');
                setTimeout(() => {
                    tocContainer.classList.add('active');
                    mask.classList.add('show');
                }, 10);
            });

            mask.addEventListener('click', function() {
                tocContainer.classList.remove('active');
                mask.classList.remove('show');
                setTimeout(() => {
                    tocContainer.classList.remove('mobile-show');
                }, 300);
            });
        }
    });
</script>


额外功能

以下功能都需要修改extend_head.html这个文件,所以我们放在一起说。

首先在layouts/partials/extended_head.html中填入我已经修改好的文件,后文我会逐个介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
{{- /* Head custom content area start */ -}}

<!-- >>> 数学公式 >>> -->

{{ partial "数学公式" . }}

<!-- >>> 图片放大 >>> -->

<!-- >>> 图片放大 >>> -->

{{if .Page.Site.Params.fancybox }}
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.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>
{{ end }}

<!-- <<< 图片放大 <<< -->

<!-- >>> 盘古之白 >>> -->

{{- $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 = "{{ $highlight.RelPermalink }}";  <!-- 使用Hugo变量 -->
    if (c) {
      o.addEventListener("load", function (e) {
        c(e);
      });
    }
    s.parentNode.insertBefore(o, s);
  })("{{ $highlight.RelPermalink }}", function () {
    pangu.spacingPage();
  });
</script>


<!-- <<< 盘古之白 <<< -->

{{- /*     Insert any custom code (web-analytics, resources, etc.) - it will appear in the <head></head> section of every page. */ -}}
{{- /*     Can be overwritten by partial with the same name in the global layouts. */ -}}
{{- /* Head custom content area end */ -}}


数学公式支持

采用初始化设置papermod主题的基础功能这篇文章的方案。

因为先前给的hugo,yaml中已经包含一部分数学公式渲染支持步骤,所以我们只需在layouts/partials/中添加math.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css"
  integrity="sha512-fHwaWebuwA7NSF5Qg/af4UeDx9XqUpYpOGgubo3yWu+b2IQR4UeQwbb42Ti7gVAjNtVoI/I9TEoYeu9omwcC6g=="
  crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.js"
  integrity="sha512-LQNxIMR5rXv7o+b1l8+N1EZMfhG7iFZ9HhnbJkTp4zjNr5Wvst75AqUeFDxeRUa7l5vEDyUiAip//r+EFLLCyA=="
  crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/contrib/auto-render.min.js"
  integrity="sha512-iWiuBS5nt6r60fCz26Nd0Zqe0nbk1ZTIQbl3Kv7kYsX+yKMUFHzjaH2+AnM6vp2Xs+gNmaBAVWJjSmuPw76Efg=="
  crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<script>
  document.addEventListener("DOMContentLoaded", () =>
    renderMathInElement(document.body, {
      delimiters: [
        { left: '$$', right: '$$', display: true },
        { left: '$', right: '$', display: false }
      ],
      throwOnError: false
    })
  );
</script>

盘古之白

就是为中文和英文之间加入一个空格,提升阅读体验。

这里采用# Hugo PaperMod 主题精装修这篇文章的方案。首先将pangu.min.js文件下载下来放进assets/js/目录下。如果你不会下载,请自行Google或者问ai。剩下的操作我们在上面已经完成了。

图片放大支持

参考# Hugo博客添加图片点击放大功能

在站点目录新建文件 ~/layouts/_default/_markup/render-image.html,写入内容:

1
2
3
4
5
6
7
8
{{if .Page.Site.Params.fancybox }}
<div class="post-img-view">
<a data-fancybox="gallery" href="{{ .Destination | safeURL }}">
<img src="{{ .Destination | safeURL }}" alt="{{ .Text }}" {{ with .Title}} title="{{ . }}"{{ end }} />
</a>
</div>
{{ end }}

如果不想要这个功能只需要在hugo.yaml修改fancybox的配置 true 改为 false 即可。

如果想一直使用,header 或 footer 中 {{if .Page.Site.Params.fancybox }} 和 {{ end }} 去掉即可。

个性化设置

前面提到过留了一个问题后面说,我们的PaperMod是以子模块的方式被安装的,这就会导致一个问题,当我们想个性化主题的时候,会发现一个非常难受的点,因为远程仓库关联的是官方的PaperMod,所以你修改完了同步不过去,当然解决办法也很多,比如新建一个复制于官方仓库的个人仓库维护,或者添加两个远程仓库之类的方法。但我这里所说的是较为模块化的方法,那就是将themes/PaperMod/下的对应文件复制到博客根目录下的相同文件夹内进行修改的方法。比如原本的文件在themes/PaperMod/assets/css/common/footer.css,我们就将其复制到css/common/footer.css这样。这里我直接给出我的个人仓库修改的东西。大家也可以来我的github仓库进行查看

深浅色主题适配

css/core/theme-vars.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
:root {
    --gap: 24px;
    --content-gap: 20px;
    --nav-width: 1024px;
    --main-width: 720px;
    --header-height: 60px;
    --footer-height: 60px;
    --radius: 10px;
    --theme: rgb(255, 255, 255);
    --entry: rgb(255, 255, 255);
    --primary: rgb(30, 30, 30);
    --secondary: rgb(108, 108, 108);
    --tertiary: rgb(214, 214, 214);
    --content: rgb(31, 31, 31);
    --code-block-bg: #faf4ed;
    --code-bg: rgb(245, 245, 245);
    --border: rgb(238, 238, 238);

    .post-content pre code {
        color:rgb(125, 102, 102);
    }
}

.dark {
    --theme: rgb(29, 30, 32);
    --entry: rgb(29, 30, 32);
    --primary: rgb(218, 218, 219);
    --secondary: rgb(155, 156, 157);
    --tertiary: rgb(65, 66, 68);
    --content: rgb(196, 196, 197);
    --code-block-bg: #282a36;
    --code-bg: rgb(55, 56, 62);
    --border: rgb(51, 51, 51);

    .post-content pre code {
        color:rgb(213, 213, 214);
    }
}

.list {
    background: var(--code-bg);
}

.dark.list {
    background: var(--theme);
}

hover效果

css/extended/hover.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/* 以下是新增的hover效果 */

/* Light theme hover effects */
.logo a:hover {
    transition: 0.15s;
    color: #666666;  /* 浅灰色用于亮色主题 */
}

svg:hover {
    transition: 0.15s;
}

.social-icons a[href*='github']:hover svg {
    color: #666666 !important;  /* 浅灰色用于亮色主题 */
}

.social-icons a[href*='linkedin']:hover svg {
    color: #0a66c2 !important;  /* LinkedIn 蓝色保持不变 */
}

/* Dark theme hover effects */
body.dark .logo a:hover {
    transition: 0.15s;
    color: #a0a0a0;  /* 更亮的灰色用于暗色主题 */
}

body.dark svg:hover {
    transition: 0.15s;
}

body.dark .social-icons a[href*='github']:hover svg {
    color: #a0a0a0 !important;  /* 更亮的灰色用于暗色主题 */
}

body.dark .social-icons a[href*='linkedin']:hover svg {
    color: #3b82f6 !important;  /* 暗色主题下稍微更亮的蓝色 */
}

#moon:hover {
    transition: 0.15s;
    color: #1772b4;
  }
  
  #sun:hover {
    transition: 0.15s;
    color: #f4a83a;
  }

  #menu a:hover {
    transition: 0.15s;
    color: grey;
  }

顶部导航栏常驻效果

css/extended/nav-fixed.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
.nav {
    max-width: unset; 
    line-height: var(--header-height * 0.5); /* 适配移动端的高度 */
}

.header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 99;
    background-color: var(--theme);  /* 使用主题默认背景色 */
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);  /* 添加轻微阴影效果 */
    background-color: rgba(255, 255, 255, 0.3); /* 半透明背景 */
    backdrop-filter: blur(5px); /* 毛玻璃效果 */
    -webkit-backdrop-filter: blur(5px); /* 兼容 Safari */
}

/* 为了防止内容被固定导航栏遮挡,需要为 main 元素添加上边距 */
.main {
    padding-top: 100px;  /* 根据你的导航栏实际高度调整这个值 */
}

body.dark {
    .header {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 99;
        box-shadow: 0 2px 2px rgba(255, 255, 255, 0.1);  /* 添加轻微阴影效果 */
        background-color: rgba(29, 30, 32, 0.3); /* 半透明背景 */
        backdrop-filter: blur(5px); /* 毛玻璃效果 */
        -webkit-backdrop-filter: blur(5px); /* 兼容 Safari */
    }
}

css/includes/chroma-styles.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/* Generated using: hugo gen chromastyles --style=rose-pine-dawn */

/* Background */ .bg { color:#575279;background-color:#faf4ed; }
/* PreWrapper */ .chroma { color:#575279;background-color:#faf4ed; }
/* Other */ .chroma .x {  }
/* Error */ .chroma .err { color:#b4637a }
/* CodeLine */ .chroma .cl {  }
/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit }
/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; }
/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; }
/* LineHighlight */ .chroma .hl { background-color:#e1dbd5 }
/* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f }
/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f }
/* Line */ .chroma .line { display:flex; }
/* Keyword */ .chroma .k { color:#286983 }
/* KeywordConstant */ .chroma .kc { color:#286983 }
/* KeywordDeclaration */ .chroma .kd { color:#286983 }
/* KeywordNamespace */ .chroma .kn { color:#907aa9 }
/* KeywordPseudo */ .chroma .kp { color:#286983 }
/* KeywordReserved */ .chroma .kr { color:#286983 }
/* KeywordType */ .chroma .kt { color:#286983 }
/* Name */ .chroma .n { color:#d7827e }
/* NameAttribute */ .chroma .na { color:#d7827e }
/* NameBuiltin */ .chroma .nb { color:#d7827e }
/* NameBuiltinPseudo */ .chroma .bp { color:#d7827e }
/* NameClass */ .chroma .nc { color:#56949f }
/* NameConstant */ .chroma .no { color:#ea9d34 }
/* NameDecorator */ .chroma .nd { color:#797593 }
/* NameEntity */ .chroma .ni { color:#d7827e }
/* NameException */ .chroma .ne { color:#286983 }
/* NameFunction */ .chroma .nf { color:#d7827e }
/* NameFunctionMagic */ .chroma .fm { color:#d7827e }
/* NameLabel */ .chroma .nl { color:#d7827e }
/* NameNamespace */ .chroma .nn { color:#d7827e }
/* NameOther */ .chroma .nx {  }
/* NameProperty */ .chroma .py { color:#d7827e }
/* NameTag */ .chroma .nt { color:#d7827e }
/* NameVariable */ .chroma .nv { color:#d7827e }
/* NameVariableClass */ .chroma .vc { color:#d7827e }
/* NameVariableGlobal */ .chroma .vg { color:#d7827e }
/* NameVariableInstance */ .chroma .vi { color:#d7827e }
/* NameVariableMagic */ .chroma .vm { color:#d7827e }
/* Literal */ .chroma .l { color:#ea9d34 }
/* LiteralDate */ .chroma .ld { color:#ea9d34 }
/* LiteralString */ .chroma .s { color:#ea9d34 }
/* LiteralStringAffix */ .chroma .sa { color:#ea9d34 }
/* LiteralStringBacktick */ .chroma .sb { color:#ea9d34 }
/* LiteralStringChar */ .chroma .sc { color:#ea9d34 }
/* LiteralStringDelimiter */ .chroma .dl { color:#ea9d34 }
/* LiteralStringDoc */ .chroma .sd { color:#ea9d34 }
/* LiteralStringDouble */ .chroma .s2 { color:#ea9d34 }
/* LiteralStringEscape */ .chroma .se { color:#286983 }
/* LiteralStringHeredoc */ .chroma .sh { color:#ea9d34 }
/* LiteralStringInterpol */ .chroma .si { color:#ea9d34 }
/* LiteralStringOther */ .chroma .sx { color:#ea9d34 }
/* LiteralStringRegex */ .chroma .sr { color:#ea9d34 }
/* LiteralStringSingle */ .chroma .s1 { color:#ea9d34 }
/* LiteralStringSymbol */ .chroma .ss { color:#ea9d34 }
/* LiteralNumber */ .chroma .m { color:#ea9d34 }
/* LiteralNumberBin */ .chroma .mb { color:#ea9d34 }
/* LiteralNumberFloat */ .chroma .mf { color:#ea9d34 }
/* LiteralNumberHex */ .chroma .mh { color:#ea9d34 }
/* LiteralNumberInteger */ .chroma .mi { color:#ea9d34 }
/* LiteralNumberIntegerLong */ .chroma .il { color:#ea9d34 }
/* LiteralNumberOct */ .chroma .mo { color:#ea9d34 }
/* Operator */ .chroma .o { color:#797593 }
/* OperatorWord */ .chroma .ow { color:#797593 }
/* Punctuation */ .chroma .p { color:#797593 }
/* Comment */ .chroma .c { color:#9893a5 }
/* CommentHashbang */ .chroma .ch { color:#9893a5 }
/* CommentMultiline */ .chroma .cm { color:#9893a5 }
/* CommentSingle */ .chroma .c1 { color:#9893a5 }
/* CommentSpecial */ .chroma .cs { color:#9893a5 }
/* CommentPreproc */ .chroma .cp { color:#9893a5 }
/* CommentPreprocFile */ .chroma .cpf { color:#9893a5 }
/* Generic */ .chroma .g {  }
/* GenericDeleted */ .chroma .gd { color:#b4637a }
/* GenericEmph */ .chroma .ge { font-style:italic }
/* GenericError */ .chroma .gr {  }
/* GenericHeading */ .chroma .gh {  }
/* GenericInserted */ .chroma .gi { color:#56949f }
/* GenericOutput */ .chroma .go {  }
/* GenericPrompt */ .chroma .gp {  }
/* GenericStrong */ .chroma .gs { font-weight:bold }
/* GenericSubheading */ .chroma .gu { color:#907aa9 }
/* GenericTraceback */ .chroma .gt {  }
/* GenericUnderline */ .chroma .gl {  }
/* TextWhitespace */ .chroma .w {  }



body.dark {
    
/* Generated using: hugo gen chromastyles --style=dracula */

/* Background */ .bg { color:#f8f8f2;background-color:#282a36; }
/* PreWrapper */ .chroma { color:#f8f8f2;background-color:#282a36; }
/* Other */ .chroma .x {  }
/* Error */ .chroma .err {  }
/* CodeLine */ .chroma .cl {  }
/* LineLink */ .chroma .lnlinks { outline:none;text-decoration:none;color:inherit }
/* LineTableTD */ .chroma .lntd { vertical-align:top;padding:0;margin:0;border:0; }
/* LineTable */ .chroma .lntable { border-spacing:0;padding:0;margin:0;border:0; }
/* LineHighlight */ .chroma .hl { background-color:#3d3f4a }
/* LineNumbersTable */ .chroma .lnt { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f }
/* LineNumbers */ .chroma .ln { white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f }
/* Line */ .chroma .line { display:flex; }
/* Keyword */ .chroma .k { color:#ff79c6 }
/* KeywordConstant */ .chroma .kc { color:#ff79c6 }
/* KeywordDeclaration */ .chroma .kd { color:#8be9fd;font-style:italic }
/* KeywordNamespace */ .chroma .kn { color:#ff79c6 }
/* KeywordPseudo */ .chroma .kp { color:#ff79c6 }
/* KeywordReserved */ .chroma .kr { color:#ff79c6 }
/* KeywordType */ .chroma .kt { color:#8be9fd }
/* Name */ .chroma .n {  }
/* NameAttribute */ .chroma .na { color:#50fa7b }
/* NameBuiltin */ .chroma .nb { color:#8be9fd;font-style:italic }
/* NameBuiltinPseudo */ .chroma .bp {  }
/* NameClass */ .chroma .nc { color:#50fa7b }
/* NameConstant */ .chroma .no {  }
/* NameDecorator */ .chroma .nd {  }
/* NameEntity */ .chroma .ni {  }
/* NameException */ .chroma .ne {  }
/* NameFunction */ .chroma .nf { color:#50fa7b }
/* NameFunctionMagic */ .chroma .fm {  }
/* NameLabel */ .chroma .nl { color:#8be9fd;font-style:italic }
/* NameNamespace */ .chroma .nn {  }
/* NameOther */ .chroma .nx {  }
/* NameProperty */ .chroma .py {  }
/* NameTag */ .chroma .nt { color:#ff79c6 }
/* NameVariable */ .chroma .nv { color:#8be9fd;font-style:italic }
/* NameVariableClass */ .chroma .vc { color:#8be9fd;font-style:italic }
/* NameVariableGlobal */ .chroma .vg { color:#8be9fd;font-style:italic }
/* NameVariableInstance */ .chroma .vi { color:#8be9fd;font-style:italic }
/* NameVariableMagic */ .chroma .vm {  }
/* Literal */ .chroma .l {  }
/* LiteralDate */ .chroma .ld {  }
/* LiteralString */ .chroma .s { color:#f1fa8c }
/* LiteralStringAffix */ .chroma .sa { color:#f1fa8c }
/* LiteralStringBacktick */ .chroma .sb { color:#f1fa8c }
/* LiteralStringChar */ .chroma .sc { color:#f1fa8c }
/* LiteralStringDelimiter */ .chroma .dl { color:#f1fa8c }
/* LiteralStringDoc */ .chroma .sd { color:#f1fa8c }
/* LiteralStringDouble */ .chroma .s2 { color:#f1fa8c }
/* LiteralStringEscape */ .chroma .se { color:#f1fa8c }
/* LiteralStringHeredoc */ .chroma .sh { color:#f1fa8c }
/* LiteralStringInterpol */ .chroma .si { color:#f1fa8c }
/* LiteralStringOther */ .chroma .sx { color:#f1fa8c }
/* LiteralStringRegex */ .chroma .sr { color:#f1fa8c }
/* LiteralStringSingle */ .chroma .s1 { color:#f1fa8c }
/* LiteralStringSymbol */ .chroma .ss { color:#f1fa8c }
/* LiteralNumber */ .chroma .m { color:#bd93f9 }
/* LiteralNumberBin */ .chroma .mb { color:#bd93f9 }
/* LiteralNumberFloat */ .chroma .mf { color:#bd93f9 }
/* LiteralNumberHex */ .chroma .mh { color:#bd93f9 }
/* LiteralNumberInteger */ .chroma .mi { color:#bd93f9 }
/* LiteralNumberIntegerLong */ .chroma .il { color:#bd93f9 }
/* LiteralNumberOct */ .chroma .mo { color:#bd93f9 }
/* Operator */ .chroma .o { color:#ff79c6 }
/* OperatorWord */ .chroma .ow { color:#ff79c6 }
/* Punctuation */ .chroma .p {  }
/* Comment */ .chroma .c { color:#6272a4 }
/* CommentHashbang */ .chroma .ch { color:#6272a4 }
/* CommentMultiline */ .chroma .cm { color:#6272a4 }
/* CommentSingle */ .chroma .c1 { color:#6272a4 }
/* CommentSpecial */ .chroma .cs { color:#6272a4 }
/* CommentPreproc */ .chroma .cp { color:#ff79c6 }
/* CommentPreprocFile */ .chroma .cpf { color:#ff79c6 }
/* Generic */ .chroma .g {  }
/* GenericDeleted */ .chroma .gd { color:#f55 }
/* GenericEmph */ .chroma .ge { text-decoration:underline }
/* GenericError */ .chroma .gr {  }
/* GenericHeading */ .chroma .gh { font-weight:bold }
/* GenericInserted */ .chroma .gi { color:#50fa7b;font-weight:bold }
/* GenericOutput */ .chroma .go { color:#44475a }
/* GenericPrompt */ .chroma .gp {  }
/* GenericStrong */ .chroma .gs {  }
/* GenericSubheading */ .chroma .gu { font-weight:bold }
/* GenericTraceback */ .chroma .gt {  }
/* GenericUnderline */ .chroma .gl { text-decoration:underline }
/* TextWhitespace */ .chroma .w {  }
}

优化下一篇文章阴影

assets/css/extended/disable-paginav-underline.css

1
2
3
4
5
.paginav .prev,
.paginav .next {
    box-shadow: 0 1px 2px 1px rgba(0, 0, 0, 0.1);
    width: 50%;
}

更改链接颜色

layouts/_default/_markup/render-link.html

1
2
<a href="{{ .Destination | safeURL }}"{{ with .Title}} title="{{ . }}"{{ end }}{{ if strings.HasPrefix .Destination "http" }} target="_blank" rel="noopener" style="color:#42b983";{{ end }}>{{ .Text | safeHTML }}</a>

部署到Github Pages(已弃用)

这里就不详细赘述了,按照 Hugo 的文档指导来操作即可。

部署到Vercel

因为GitHub Pages在国内访问速度过于慢的原因,我将静态托管迁移到Vercel。因为Vercel自身域名被墙的原因,建议有自己域名的同学们更换。

首先关闭掉GitHub Pages,然后注册vercel账户,直接导入你的blog仓库,然后再部署页面的框架选择hugo,这里注意一下,需要设置环境变量HUGO_VERSION为你构建时的hugo版本,我的为0.141.0。然后再按照vercel给你的提示设置域名解析。

使用Vercel构建,不仅访问速度快,发布构建的速度也远超GitHub Pages

换到Vercel就可以关掉Github Pages然后删除.github里面的工作流了。

结尾

支持配置应该就结束了,如果有疑问或者错误,欢迎评论。后续还会分享一些配合obsidian的工作流。一天后发现Jekyll才是最适合(for me)Obsidian的。

感谢以下文章

Hugo + PaperMod + Github Pages 搭建一个完善的个人博客(以 Windows11 为例) Hugo 主题配置 初始化 & 设置 PaperMod 主题的基础功能 Hugo+PaperMod 双语博客搭建 Home-Info+Profile Mode Hugo PaperMod 主题精装修 Hugo Stack 主题添加「最后修改于」 Hugo 部署方案 PaperMod 集成 Giscus 评论 Hugo博客添加图片点击放大功能

This post is licensed under CC BY 4.0 by the author.