hugo 主题 hextra 定制记录

#hugo

2025-02-22 13:45:18

hextra 是一个漂亮的 hugo 主题,本站使用的就是它,如果没意外,本站会一直长这样,毕竟我年纪逐渐大了,没时间折腾。

hextra 主题介绍

hextra github 地址是:https://github.com/imfing/hextra, 中文文档地址是:https://imfing.github.io/hextra/zh-cn/docs/ 官方的介绍是:

ℹ️
一个现代、快速且功能齐全的 Hugo 主题,基于 Tailwind CSS 构建。专为构建美观的文档、博客和网站而设计,它提供了开箱即用的功能和灵活性,以满足各种需求。

基础功能完善,比如数学公式、代码高亮、图表 mermaid、短代码

hextra 短代码介绍

短代码有:标签页、步骤、卡片、提示框、文件树等。

标签页

举个例子:

{{< tabs items="JSON,YAML,TOML" defaultIndex="1" >}}

  {{< tab >}}
  ```json
  { "hello": "world" }
  ```
  {{< /tab >}}

  ... 类似地添加其他标签页

{{< /tabs >}}
{ "hello": "world" }
hello: world
hello = "world"

步骤条

举个例子:

{{% steps %}}

### 第一步

这是第一步。

### 第二步

这是第二步。

{{% /steps %}}

第一步

这是第一步。

第二步

这是第二步。

卡片实例

带蓝色标签的卡片
标签文本

提示框实例

🌐
Hugo 可用于创建各种类型的网站,包括博客、作品集、文档站点等。
1
2
3
{{< callout emoji="🌐" >}}
  Hugo 可用于创建各种类型的网站,包括博客、作品集、文档站点等。
{{< /callout >}}
ℹ️
请访问 GitHub 查看最新版本。
1
2
3
{{< callout type="info" >}}
  请访问 GitHub 查看最新版本。
{{< /callout >}}

文件树实例

    • _index.md
      • _index.md
      • introduction.md
      • introduction.fr.md
  • hugo.toml
  • Markdown
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    {{< filetree/container >}}
      {{< filetree/folder name="content" >}}
        {{< filetree/file name="_index.md" >}}
        {{< filetree/folder name="docs" state="closed" >}}
          {{< filetree/file name="_index.md" >}}
          {{< filetree/file name="introduction.md" >}}
          {{< filetree/file name="introduction.fr.md" >}}
        {{< /filetree/folder >}}
      {{< /filetree/folder >}}
      {{< filetree/file name="hugo.toml" >}}
    {{< /filetree/container >}}

    个性化定制

    支持搜索 keywords

    ℹ️
    首先,不要将网站语言调整为中文,否则搜索体验非常差,英文搜索不了,应该是中文搜索的 js 问题。

    在 assets 目录下新建 json 目录,然后新建一个 search-data.json 文件,具体目录结构如下:

      • search-data.json
  • 内容如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    {{/* FlexSearch Index Data */}}
    {{- $indexType := site.Params.search.flexsearch.index | default "content" -}}
    
    {{- if not (in (slice "content" "summary" "heading" "title" "keywords" ) $indexType) -}}
      {{- errorf "unknown flexsearch index type: %s" $indexType -}}
    {{- end -}}
    
    {{- $pages := where .Site.Pages "Kind" "in" (slice "page" "section") -}}
    {{- $pages = where $pages "Params.excludeSearch" "!=" true -}}
    {{- $pages = where $pages "Content" "!=" "" -}}
    
    {{- $output := dict -}}
    
    {{- range $index, $page := $pages -}}
      {{- $pageTitle := $page.LinkTitle | default $page.File.BaseFileName -}}
      {{- $pageLink := $page.RelPermalink -}}
      {{- $data := partial "utils/fragments" (dict "context" $page "type" $indexType) -}}
      {{- $keywords := $page.Keywords -}}
      {{- $result := delimit $keywords " " -}}
      {{ $data = merge $data (dict "" $result) }}
      {{- $output = $output | merge (dict $pageLink (dict "title" $pageTitle "data" $data)) -}}
    {{- end -}}
    
    {{- $output | jsonify -}}

    第 18-20 行为我新增代码,其余均是主题文件源码。

    首页展示 blog

    hextra 主题的首页展示的是自定义内容,但我更希望直接展示 blog,在 layouts 下新增:hextra-home.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
    
    {{ define "main" }}
    {{- $readMore := (T "readMore") | default "Read more →" -}}
    <div class="hx-mx-auto hx-flex {{ partial `utils/page-width` . }}">
      <!-- {{ partial "sidebar.html" (dict "context" . "disableSidebar" true "displayPlaceholder" true) }} -->
      <article
        class="hx-w-full hx-break-words hx-flex hx-min-h-[calc(100vh-var(--navbar-height))] hx-min-w-0 hx-justify-center hx-pb-8 hx-pr-[calc(env(safe-area-inset-right)-1.5rem)]">
        <main class="hx-w-full hx-min-w-0 hx-max-w-6xl hx-px-6 hx-pt-4 md:hx-px-12">
          <br class="hx-mt-1.5 hx-text-sm" />
          {{ if .Title }}<h1
            class="hx-text-center hx-mt-2 hx-text-4xl hx-font-bold hx-tracking-tight hx-text-slate-900 dark:hx-text-slate-100">
            {{ .Title }}</h1>{{ end }}
          <div class="content">{{ .Content }}</div>
          {{ $paginator := .Paginate (sort (where .Site.RegularPages "Section" "blog") "Date" "desc") }}
          {{- range $paginator.Pages }}
          <div class="hx-mb-10">
            <h3><a style="color: inherit; text-decoration: none;" class="hx-block hx-font-semibold hx-mt-8 hx-text-2xl "
                href="{{ .RelPermalink }}">{{ .Title }}</a></h3>
            {{- if site.Params.blog.list.displayTags -}}
            {{ with .Params.tags }}
            <p class="hx-opacity-50 hx-text-sm hx-leading-7">
              {{- range . }}<a class="hx-inline-block hx-mr-2" href=/tags/{{ . }}/ target="_blank">#{{ . }}</a>{{ end -}}
            </p>
            {{ end -}}
            {{- end -}}
            <p class="hx-opacity-80 hx-mt-4 hx-leading-7">{{- partial "utils/page-description" . -}}</p>
            <p class="hx-opacity-80 hx-mt-1 hx-leading-7">
              <a class="hx-text-[color:hsl(var(--primary-hue),100%,50%)] hx-underline hx-underline-offset-2 hx-decoration-from-font"
                href="{{ .RelPermalink }}">
                {{- $readMore -}}
              </a>
            </p>
            <p class="hx-opacity-50 hx-text-sm hx-mt-4 hx-leading-7">{{ partial "utils/format-date" .Date }}</p>
          </div>
          {{ end -}}
          {{ partial "partials/pagination.html" . }}
        </main>
      </article>
      <!-- <div class="max-xl:hx-hidden hx-h-0 hx-w-64 hx-shrink-0"></div> -->
    </div>
    {{- end -}}

    该文件来自主题 layouts/blog/list.html 我只改了第 13,14 行以及新增第 35 行,另外,在 layouts/blog 下,新建 single.html 复制官方 layouts/blog 下的 single.html 但注释掉面包屑导航,因为面包屑导航会再次导航到 /blog 下面去。

    partials/pagination.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
    
    {{ $pag := .Paginator }}
    {{ $tps := $pag.TotalPages }}
    
    {{ $begin := sub $pag.PageNumber  4}}
    {{ $.Scratch.Set "begin" $begin }}
    {{ $end := add $pag.PageNumber  4}}
    {{ $.Scratch.Set "end" $end }}
    
    {{ if lt $begin 0}}
      {{ $end := sub $end $begin }}
      {{ $.Scratch.Set "end" $end }}
    {{ end}}
    {{ $end := $.Scratch.Get "end"}}
    
    {{ $over := sub  $tps $end }}
    
    {{ if lt $over 0}}
      {{ $begin := add $begin $over}}
      {{ $.Scratch.Set "begin" $begin }}
    {{ end }}
    {{ $begin := $.Scratch.Get "begin"}}
    <nav class="pagination">
      {{ if $pag.HasPrev }}
      <a class="extend prev" rel="prev" href="{{ $pag.Prev.URL  }}">
        <i class="fa fa-angle-left"></i>
      </a>
      {{ end }}
      {{ range $p := $pag.Pagers }}
          {{ if and (ge $p.PageNumber $begin) (le $p.PageNumber $end) }}
          {{ if eq $p $pag }}
            <span class="page-number current">{{ $p.PageNumber }}</span>
          {{ else }}
            <a class="page-number" href="{{ $p.URL  }}">{{ $p.PageNumber }}</a>
          {{ end }}
      {{ end }}
      {{ if $pag.HasNext }}
      <a class="extend next" rel="next" href="{{ $pag.Next.URL }}">
        <i class="fa fa-angle-right"></i>
      </a>
      {{ end }}
    </nav>

    该文件来自 hugo-next-theme 主题,assets/css/custom.css 里需要新增:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    .pagination {
        border-top: 1px solid #eee;
        margin: 120px 0 0;
        text-align: center;
    }
    
    .algolia-pagination .current .page-number, .pagination .page-number.current {
        background: #ccc;
        border-color: #ccc;
        color: var(--content-bg-color);
    }
    
    .pagination .prev, .pagination .next, .pagination .page-number {
        border-bottom: 0;
        border-top: 1px solid #eee;
        transition: border-color 0.2s ease-in-out;
    }
    
    .pagination .space, .pagination .prev, .pagination .next, .pagination .page-number {
        display: inline-block;
        margin: -1px 10px 0;
        padding: 0 10px;
    }

    官方版本博客不支持分页展示,仅展示前 10 条,这里做了优化。

    汉化

    在 i18n 目录下新建 en.yaml 文件,内容如下:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    backToTop: "返回顶部"
    changeLanguage: "切换语言"
    changeTheme: "切换主题"
    copyright: "© 2025 有一个程序员。"
    dark: "深色"
    editThisPage: "在 GitHub 上编辑此页 →"
    lastUpdated: "最后更新于"
    light: "浅色"
    noResultsFound: "无结果"
    onThisPage: "此页上"
    poweredBy: "由 Hextra 驱动"
    readMore: "更多 →"
    searchPlaceholder: "搜索。.."

    日期格式调整

    修改 hugo.yaml,新增:

    1
    2
    
    params:
      dateFormat: "2006-01-02 15:04:05"

    本站完整的 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
    
    baseURL: 'https://example.org/'
    languageCode: 'zh-cn'
    title: '有一个程序员'
    
    module:
      imports:
        - path: github.com/imfing/hextra
    
    enableEmoji: true
    hasCJKLanguage: true
    # 不要启用,启用后搜索不准确
    #defaultContentLanguage = "zh-cn"
    enableRobotsTXT: true
    enableInlineShortcodes: true
    
    markup:
      highlight:
        anchorLineNos: true
        codeFences: true
        guessSyntax: true
        lineNoStart: 1
        lineNos: true
        lineNumbersInTable: true
        noClasses: false
        # style = 'github'
        tabWidth: 2
      goldmark:
        renderer:
          unsafe: true
    
    params:
      page:
        width: wide #wide, full, normal
      navbar:
        width:  wide
        displayLogo: false
      footer:
        displayCopyright: false
        displayPoweredBy: true
      search:
        enable: true
        type: flexsearch
        flexsearch:
          index: heading # content | summary | heading | title
          tokenize: full # full | forward | reverse | strict
      displayUpdatedDate: true
      dateFormat: "2006-01-02 15:04:05"
      blog:
        list:
          displayTags: true
          sortBy: date
          sortOrder: desc
    
    menu:
      main:
        - name: 搜索
          params:
            type: search
          weight: 1020
    
    permalinks:
      page:
        golang: /docs/:sections/:contentbasename/
        java: /docs/:sections/:contentbasename/
        linux: /docs/:sections/:contentbasename/
        blog: /:slug/
      section:
        golang: /docs/:sections/
        java: /docs/:sections/
        linux: /docs/:sections/
    
    outputs:
      home: 
        - HTML
      page:
        - HTML
      section:
        - HTML
        - RSS
    最后更新于