Hugo

Hugo FixIt 主题美化:非文章页

FixIt 主题支持通过多种方式自定义样式与布局:

  • 自定义样式:在 assets/css/ 下创建 _override.scss(覆盖主题变量)和 _custom.scss(添加自定义 CSS),主题会自动加载。
  • 自定义脚本:在 assets/js/ 创建 custom.js 会在每个页面末尾自动执行,其它 JS 文件需在 [params.page.library.js] 中配置路径。
  • 模板覆盖:将主题 layouts/ 下的文件复制到项目根目录相同路径,Hugo 会优先使用新模板。
  • 自定义块:在 layouts/_partials/ 下创建任意 .html 文件,并在 [params.customPartials] 中启用,主题会自动渲染到页面相应位置。

下面介绍如何利用这些能力,实现 FixIt 主题首页、归档页、分类页(含分类、合集等)的美化。

首页

摘要卡片

按最后修改时间排序,自定义卡片布局(左侧封面图 + 右侧标题 / 分类 / 合集 / 日期),移除摘要、作者、阅读全文和标签;当文章有更新时,右上角显示更新斜飘带角标。

创建以下文件:

HTMLlayouts/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
 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
{{- /* 覆盖来源:FixIt/layouts/home.html */ -}}

{{- define "title" -}}
  {{- .Site.Title -}}
  {{- if .Site.Params.indexWithSubtitle | and .Site.Params.header.subtitle.name -}}
    {{- printf " - %v" .Site.Params.header.subtitle.name -}}
  {{- end -}}
{{- end -}}

{{- define "content" -}}
  {{- $params := partial "function/params.html" -}}
  {{- $profile := .Site.Params.home.profile -}}
  {{- $posts := .Site.Params.home.posts -}}
  {{- $pages := slice -}}

  {{- /* Paginate calculation */ -}}
  {{- if ne $posts.enable false -}}
    {{- $pages = .Site.Store.Get "mainSectionPages" -}}
    {{- if .Site.Params.page.hiddenFromHomePage -}}
      {{- $pages = where $pages "Params.hiddenfromhomepage" false -}}
    {{- else -}}
      {{- $pages = where $pages "Params.hiddenfromhomepage" "!=" true -}}
    {{- end -}}

    {{- /* ======================== 修改点 1:开始 ======================== */ -}}
    {{- /* 按最后修改时间降序(如果未设置 lastmod 则回退到发布日期) */ -}}
    {{- $pages = $pages.ByLastmod.Reverse -}}
    {{- /* ======================== 修改点 1:结束 ======================== */ -}}

    {{- with $posts.paginate | default .Site.Params.paginate -}}
      {{- $pages = $.Paginate $pages . -}}
    {{- else -}}
      {{- $pages = .Paginate $pages -}}
    {{- end -}}
  {{- end -}}

  <div class="page home{{ if ne $posts.enable false }} posts{{ end }}">
    {{- /* Only show profile and content on first page */ -}}
    {{- if (not $profile.onlyFirstPage) | or (eq $.Paginator.PageNumber 1) -}}
      {{- /* Profile */ -}}
      {{- if ne $profile.enable false -}}
        {{- partial "home/profile.html" . -}}
      {{- end -}}

      {{- /* Content */ -}}
      {{- if .Content -}}
        <div class="single">
          <div class="content" id="content">
            {{- dict "Content" .Content "Ruby" $params.ruby "Fraction" $params.fraction "Fontawesome" $params.fontawesome | partial "function/content.html" | safeHTML -}}
          </div>
        </div>
      {{- end -}}
    {{- end -}}

    {{- /* Paginate rendering */ -}}
    {{- if ne $posts.enable false -}}
      {{- range $pages.Pages -}}

        {{- /* ======================== 修改点 2:开始 ======================== */ -}}
        {{- /* 日期计算(必须在卡片循环内,以便 $isUpdated 可用) */ -}}
        {{- $dateFormat := "2006/01/02" -}}
        {{- $publishDate := .PublishDate -}}
        {{- $lastmodDate := .Lastmod -}}
        {{- $displayDate := $publishDate -}}
        {{- $isUpdated := false -}}
        {{- if $lastmodDate -}}
          {{- if $lastmodDate.After $publishDate -}}
            {{- $displayDate = $lastmodDate -}}
            {{- $isUpdated = true -}}
          {{- end -}}
        {{- end -}}
        {{- /* 自定义卡片布局 */ -}}
        {{- $pageParams := .Params | merge $.Site.Params.page -}}
        <article class="custom-summary" itemscope itemtype="http://schema.org/Article">
          {{- /* 右上角更新角标 */ -}}
          {{- if $isUpdated -}}
            <div class="update-badge">
              <span>更新</span>
            </div>
          {{- end -}}      
          {{- /* 左侧封面图 */ -}}
          {{- $imagams.featuredimagepreview | default $pageParams.featuredimage -}}
          {{- $matches := slice -}}
          {{- if .Resources.GetMatch "featured-image-preview" -}}
            {{- $matches = $matches | append "featured-image-preview" -}}
          {{- else if .Resources.GetMatch "featured-image" -}}
            {{- $matches = $matches | append "featured-image" -}}
          {{- end -}}
          {{- if $image | or (gt (len $matches) 0) -}}
            <div class="featured-image-preview">
              <a href="{{ .RelPermalink }}" aria-label="{{ .Title }}">
                {{- $optim := slice
                  (dict "Process" "fill 240x160 Center webp" "descriptor" "240w")
                  (dict "Process" "fill 480x320 Center webp" "descriptor" "480w")
                  (dict "Process" "fill 720x480 Center webp" "descriptor" "720w")
                -}}
                {{- dict "Src" $image "Title" .Title "Resources" .Resources "Matches" $matches "Loading" "eager" "Width" "240" "Height" "160" "Sizes" "(max-width: 680px) 100px, 120px" "OptimConfig" $optim "Alt" (printf "Featured image for %v" .Title) | partial "plugin/image.html" -}}
              </a>
            </div>
          {{- end -}}
          {{- /* 右侧内容区 */ -}}
          <div class="summary-content" style="position: relative;">
            {{- /* 标题 */ -}}
            <h2 class="single-title{{ if $isUpdated }} has-update{{ end }}" itemprop="name headline">
              {{- with $pageParams.weight -}}
                {{- $icon := dict "Class" "fa-solid fa-thumbtack" -}}
                <span title="{{ T "single.pin" }}" class="icon-pin">{{- $icon | partial "plugin/icon.html" -}}</span>
              {{- end -}}
              {{- $repost := $pageParams.repost | default dict -}}
              {{- with $repost -}}
                {{- if eq .Enable true -}}
                  {{- $icon := dict "Class" "fa-solid fa-share" -}}
                  {{- $title := cond (hasPrefix .Url "http") (printf "%v -> %v" (T "single.repost") .Url ) (T "single.repost") -}}
                  <span title="{{ $title }}" class="icon-repost">{{- $icon | partial "plugin/icon.html" -}}</span>
                {{- end -}}
              {{- end -}}
              <a href="{{ .RelPermalink }}">{{ cond (.Param "capitalizeTitles") (title .Title) .Title }}</a>
            </h2>
            {{- /* 摘要:有内容时才显示 */ -}}
            <!--
            {{- if or .Summary .Description -}}
              <div class="content">
                {{- if .Summary -}}
                  {{- $plainify := (.Param "summaryPlainify") | default false -}}
                  {{- with .Markup "home" -}}
                    {{- with .Render -}}
                      {{- $summary := dict "Content" .Summary.Text "Ruby" $params.ruby "Fraction" $params.fraction "Fontawesome" $params.fontawesome | partial "function/content.html" | safeHTML -}}
                      {{- cond $plainify ($summary | plainify) $summary -}}
                    {{- end -}}
                  {{- end -}}
                {{- else -}}
                  {{- .Description | safeHTML -}}
                {{- end -}}
              </div>
            {{- end -}}
            -->           
            {{- /* 底部行:分类/合集 + 日期 */ -}}
            <div class="post-footer-row">
              <div class="post-categories">
                {{- $categories := .GetTerms "categories" -}}
                {{- $collections := .GetTerms "collections" -}}
                {{- if or $categories $collections -}}
                  {{- range $categories -}}
                    <a href="{{ .RelPermalink }}" class="post-category-link">
                      {{- dict "Class" "fa-regular fa-folder" | partial "plugin/icon.html" -}}
                      {{- .LinkTitle -}}
                    </a>
                  {{- end -}}
                  {{- range $collections -}}
                    <a href="{{ .RelPermalink }}" class="post-category-link">
                      {{- dict "Class" "fa-solid fa-layer-group" | partial "plugin/icon.html" -}}
                      {{- .LinkTitle -}}
                    </a>
                  {{- end -}}
                {{- end -}}
              </div>
              <div class="post-date">
                <time datetime="{{ $displayDate.Format "2006-01-02" }}" title='{{ $displayDate.Format "2006-01-02 15:04:05" }}'>
                  {{- $displayDate.Format $dateFormat -}}
                </time>
              </div>
            </div>
          </div>
        </article>
        {{- /* ======================== 修改点 2:结束 ======================== */ -}}
        
      {{- end -}}
      {{- partial "paginator.html" . -}}
    {{- end -}}
  </div>
{{- end -}}

创建以下文件:

SCSSassets/css/_custom.scss
  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
// 全局样式与变量见最后一章

// ========= 首页 =========

.home.posts {
  width: 100%;
  margin-block: var(--c-space-4);
  max-width: clamp(360px, 85vw, 700px);

  .custom-summary {
    padding: 0;
    display: flex;
    overflow: hidden;
    position: relative;
    border-radius: 16px;
    margin-block: var(--c-space-8);
    box-shadow: var(--c-card-shadow);
    background-color: var(--c-card-bg);
    border: 1px solid var(--c-card-border);
    transition: all 0.3s cubic-bezier(0.2, 0, 0, 1);
    
    &:hover {
      transform: translateY(-2px);
      box-shadow: var(--c-card-shadow-hover);
      border-color: var(--c-card-border-hover);
    }

    &::before, 
    &::after {
      padding: 0;
      display: none;
      content: none;
    }

    &:last-of-type {
      margin-bottom: 0;
    }

    .update-badge {
      position: absolute;
      top: 0;
      right: 0;
      z-index: 10;
      width: 70px;
      height: 70px;
      overflow: hidden;
      pointer-events: none;

      span {
        position: absolute;
        top: 6px;
        right: -24px;
        color: #fff;
        padding: 0 28px;
        font-weight: 600;
        white-space: nowrap;
        letter-spacing: 1px;
        transform: rotate(45deg);
        text-transform: uppercase;
        font-size: var(--c-font-xs);
        box-shadow: 0 1px 3px rgba(0,0,0,0.1);
        background-color: var(--c-summary-update-bg);
      }
    }

    .featured-image-preview {
      margin: 0;
      height: auto;
      width: 200px;
      overflow: hidden;
      aspect-ratio: 2 / 1;
      border-radius: 16px 0 0 16px;
      @include phone { width: 90px; aspect-ratio: 4 / 3; }

      img {
        height: 100% !important;
        transition: transform 0.3s ease;

        &:hover {
          transform: scale(1.2);
        }        
      }
    }

    .summary-content {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      justify-content: center;
      gap: calc(var(--c-space-1) * 3);
      padding: calc(var(--c-space-1) * 3); 

      .single-title {
        margin: 0;
        width: 100%;
        line-height: 1.5;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        font-size: var(--c-font-l);

        &.has-update {
          max-width: calc(100% - 20px);
        }
      }

      /*.content {
        line-clamp: 2;
        overflow: hidden;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        text-overflow: ellipsis;
        -webkit-box-orient: vertical;
      }*/
    }

    .post-footer-row {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      justify-content: space-between;

      .post-categories {
        display: flex;
        flex-wrap: wrap;
        gap: var(--c-space-2);
      }

      a {   
        border-radius: 14px;
        align-items: center;
        transition: all 0.3s;
        display: inline-flex;
        text-decoration: none;
        gap: var(--c-space-1);
        color: var(--c-meta-2);
        font-size: var(--c-font-xs);
        background-color: var(--c-meta-bg);
        padding: var(--c-space-1) var(--c-space-2);
        @include category-color-styles;

        &:hover {
          color: var(--c-meta-hover);
          background-color: var(--c-meta-bg-hover);
        }
      }      
      
      .post-date {
        white-space: nowrap;  
        color: var(--c-meta-1);
        font-size: var(--c-font-xs);
      }        
    }    
  }    
}  

文章热力图

FixIt 2026.6.21 | 更改

按发布日与更新日合并统计,以日历热力图形式展示全年的文章活跃度;绿色方块表示当日有新文章发布或旧文章被修改,灰色表示无变动;悬停显示当日文章标题列表并支持点击跳转。1

创建以下文件:

HTMLlayouts/_partials/custom-profile.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
{{- $page := . -}}
{{- $days := 365 -}}
{{- $section := "posts" -}}

{{- $today := time.AsTime 0 -}}
{{- range $page.Site.RegularPages -}}
  {{- if eq .Section $section -}}
    {{- if gt .Lastmod.Unix $today.Unix -}}
      {{- $today = .Lastmod -}}
    {{- end -}}
    {{- if gt .Date.Unix $today.Unix -}}
      {{- $today = .Date -}}
    {{- end -}}
  {{- end -}}
{{- end -}}
{{- if eq $today.Unix 0 -}}
  {{- $today = now -}}
{{- end -}}

{{- $windowOffset := mul -1 (sub $days 1) -}}
{{- $windowStart := $today.AddDate 0 0 $windowOffset -}}
{{- $windowStartKey := $windowStart.Format "2006-01-02" -}}
{{- $windowEndKey := $today.Format "2006-01-02" -}}
{{- $startPad := mod (add $windowStart.Weekday 6) 7 -}}
{{- $visibleCellCount := add $startPad $days -}}
{{- $endPad := mod (sub 7 (mod $visibleCellCount 7)) 7 -}}
{{- $cellCount := add $visibleCellCount $endPad -}}
{{- $weekCount := div $cellCount 7 -}}

{{- $publishMap := dict -}}
{{- $updateMap := dict -}}

{{- range $page.Site.RegularPages -}}
  {{- if eq .Section $section -}}
    {{- $publishDate := .Date -}}
    {{- $lastmod := .Lastmod -}}
    {{- $isNotExpired := or .ExpiryDate.IsZero (gt .ExpiryDate.Unix $today.Unix) -}}
    {{- if and (not .Draft) $isNotExpired -}}
      {{- if le $publishDate.Unix $today.Unix -}}
        {{- $publishKey := $publishDate.Format "2006-01-02" -}}
        {{- if and (ge $publishKey $windowStartKey) (le $publishKey $windowEndKey) -}}
          {{- $article := dict "title" .LinkTitle "url" .RelPermalink "type" "发布" -}}
          {{- $existing := default slice (index $publishMap $publishKey) -}}
          {{- $publishMap = merge $publishMap (dict $publishKey ($existing | append $article)) -}}
        {{- end -}}
      {{- end -}}
      {{- $publishDateStr := $publishDate.Format "2006-01-02" -}}
      {{- $lastmodStr := $lastmod.Format "2006-01-02" -}}
      {{- if and (ne $publishDateStr $lastmodStr) (le $lastmod.Unix $today.Unix) -}}
        {{- $updateKey := $lastmod.Format "2006-01-02" -}}
        {{- if and (ge $updateKey $windowStartKey) (le $updateKey $windowEndKey) -}}
          {{- $article := dict "title" .LinkTitle "url" .RelPermalink "type" "更新" -}}
          {{- $existing := default slice (index $updateMap $updateKey) -}}
          {{- $updateMap = merge $updateMap (dict $updateKey ($existing | append $article)) -}}
        {{- end -}}
      {{- end -}}
    {{- end -}}
  {{- end -}}
{{- end -}}

{{- $mergedMap := dict -}}
{{- range $key, $articles := $publishMap -}}
  {{- $mergedMap = merge $mergedMap (dict $key $articles) -}}
{{- end -}}
{{- range $key, $articles := $updateMap -}}
  {{- $existing := default slice (index $mergedMap $key) -}}
  {{- $mergedMap = merge $mergedMap (dict $key ($existing | append $articles)) -}}
{{- end -}}

{{- $monthStarts := slice -}}
{{- range $weekIndex := seq 0 (sub $weekCount 1) -}}
  {{- $appendedMonth := false -}}
  {{- $firstVisibleInWeek := true -}}
  {{- range $dayIndex := seq 0 6 -}}
    {{- $offset := sub (add (mul $weekIndex 7) $dayIndex) $startPad -}}
    {{- if and (ge $offset 0) (lt $offset $days) -}}
      {{- $cellDate := $windowStart.AddDate 0 0 $offset -}}
      {{- if and (eq $weekIndex 0) $firstVisibleInWeek -}}
        {{- $monthLabel := printf "%d月" $cellDate.Month -}}
        {{- $monthStarts = $monthStarts | append (dict "label" $monthLabel "week" $weekIndex) -}}
        {{- $appendedMonth = true -}}
      {{- else if and (not $appendedMonth) (eq $cellDate.Day 1) -}}
        {{- $monthLabel := printf "%d月" $cellDate.Month -}}
        {{- $monthStarts = $monthStarts | append (dict "label" $monthLabel "week" $weekIndex) -}}
        {{- $appendedMonth = true -}}
      {{- end -}}
      {{- $firstVisibleInWeek = false -}}
    {{- end -}}
  {{- end -}}
{{- end -}}

<div class="hea" style="--hea-weeks: {{ $weekCount }};">
  <section class="hea-card">
    <div class="hea-chart">
      <div class="hea-months-row">
        <div class="hea-months" aria-hidden="true">
          {{- range $monthIndex, $month := $monthStarts -}}
            {{- $startWeek := int (index $month "week") -}}
            {{- $spanEnd := $weekCount -}}
            {{- if lt $monthIndex (sub (len $monthStarts) 1) -}}
              {{- $spanEnd = int (index (index $monthStarts (add $monthIndex 1)) "week") -}}
            {{- end -}}
            {{- $span := sub $spanEnd $startWeek -}}
            {{- if lt $span 1 -}}{{- $span = 1 -}}{{- end -}}
            <span class="hea-month" style="grid-column: {{ add $startWeek 1 }} / span {{ $span }};">{{ index $month "label" }}</span>
          {{- end -}}
        </div>
      </div>

      <div class="hea-grid-row">
        <div class="hea-grid">
          {{- range $weekIndex := seq 0 (sub $weekCount 1) -}}
            <div class="hea-week">
              {{- range $dayIndex := seq 0 6 -}}
                {{- $offset := sub (add (mul $weekIndex 7) $dayIndex) $startPad -}}
                {{- if and (ge $offset 0) (lt $offset $days) -}}
                  {{- $cellDate := $windowStart.AddDate 0 0 $offset -}}
                  {{- $dayKey := $cellDate.Format "2006-01-02" -}}
                  {{- $articles := default slice (index $mergedMap $dayKey) -}}
                  {{- $hasContent := gt (len $articles) 0 -}}

                  {{- $stateClass := "hea-cell--inactive" -}}
                  {{- if $hasContent -}}
                    {{- $stateClass = "hea-cell--level-3" -}}
                  {{- end -}}

                  {{- $dataJSON := $articles | jsonify -}}

                  <span class="hea-cell {{ $stateClass }}"
                        role="img"
                        data-date="{{ $cellDate.Format "2006/01/02" }}"
                        data-articles="{{ $dataJSON }}"
                  ></span>
                {{- else -}}
                  <span class="hea-cell hea-cell--empty" aria-hidden="true"></span>
                {{- end -}}
              {{- end -}}
            </div>
          {{- end -}}
        </div>
      </div>
    </div>
  </section>
</div>

创建以下文件:

JavaScriptassets/js/custom.js
  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
(function() {

  // ========== 热力图悬停卡片(仅首页) ==========

  (function() {
    if (!document.querySelector('.hea')) return;

    var card = document.createElement('div');
    card.className = 'hea-hover-card';
    card.id = 'hea-hover-card';
    document.body.appendChild(card);

    var activeCell = null;
    var hideTimer = null;

    function showCard(cell) {
      var data = cell.getAttribute('data-articles');
      if (!data || data === '[]') return;
      try {
        var articles = JSON.parse(data);
        if (!articles || articles.length === 0) return;
      } catch (e) { return; }

      var date = cell.getAttribute('data-date') || '';
      var html = '<div class="card-date">' + date + '</div>';
      articles.forEach(function(a) {
        var badgeClass = a.type === '发布' ? 'publish' : 'update';
        html += '<a href="' + a.url + '" target="_blank">';
        html += '<span class="card-badge ' + badgeClass + '">' + a.type + '</span>';
        html += a.title;
        html += '</a>';
      });
      card.innerHTML = html;

      card.style.display = 'block';
      card.style.opacity = '0';

      var isMobile = window.innerWidth <= 680;
      var delay = isMobile ? 20 : 0;

      setTimeout(function() {
        var rect = cell.getBoundingClientRect();
        var cardW = card.offsetWidth || 280;
        var cardH = card.offsetHeight || 200;

        var left = rect.left + rect.width / 2 - cardW / 2;
        var top = rect.bottom + 8;

        if (left + cardW > window.innerWidth - 10) left = window.innerWidth - cardW - 10;
        if (left < 10) left = 10;
        if (top + cardH > window.innerHeight - 10) {
          top = rect.top - cardH - 8;
        }
        if (top < 10) top = 10;

        card.style.left = left + 'px';
        card.style.top = top + 'px';
        card.style.opacity = '1';
        activeCell = cell;
      }, delay);
    }

    function hideCard() {
      card.style.display = 'none';
      activeCell = null;
    }

    document.querySelectorAll('.hea-cell[data-articles]').forEach(function(cell) {
      var data = cell.getAttribute('data-articles');
      if (!data || data === '[]') return;

      var isMobile = window.innerWidth <= 680;

      if (isMobile) {
        cell.addEventListener('touchstart', function(e) {
          e.preventDefault();
          e.stopPropagation();
          if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
          showCard(cell);
        }, { passive: false });
      } else {
        cell.addEventListener('mouseenter', function(e) {
          if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
          showCard(cell);
        });

        cell.addEventListener('mouseleave', function(e) {
          hideTimer = setTimeout(function() {
            var related = e.relatedTarget;
            if (card.contains(related)) return;
            hideCard();
          }, 150);
        });
      }
    });

    card.addEventListener('mouseenter', function() {
      if (hideTimer) { clearTimeout(hideTimer); hideTimer = null; }
    });

    card.addEventListener('mouseleave', function() {
      hideTimer = setTimeout(function() {
        hideCard();
      }, 150);
    });

    document.addEventListener('click', function(e) {
      if (card.style.display === 'block' && !card.contains(e.target)) {
        var target = e.target.closest('.hea-cell[data-articles]');
        if (target !== activeCell) {
          hideCard();
        }
      }
    });

    // 手机端滚动到最右侧
    document.addEventListener('DOMContentLoaded', function() {
      setTimeout(function() {
        var hea = document.querySelector('.hea');
        if (hea && window.innerWidth <= 680) {
          hea.scrollLeft = hea.scrollWidth;
        }
      }, 50);
    });
  })();

})();

修改主题配置:

TOMLconfig/_default/hugo.toml
1
2
[params.customPartials]
profile = ["custom-profile.html"]

创建以下文件:

SCSSassets/css/_custom.scss
  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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// 全局样式与变量见最后一章

// ========= 首页 =========

.home.posts {
  .home-profile {
    padding: var(--c-space-8) 0 var(--c-space-4);
    
    .home-title {
      font-size: var(--c-font-xxl);
    }
  }
  .hea {
    width: 100%;
    margin-top: var(--c-space-8);
    @include phone { overflow-x: auto; -webkit-overflow-scrolling: touch; }

    * {
      box-sizing: border-box;
    }

    .hea-card {
      margin: 0;
      border-radius: 12px;
      padding: var(--c-space-4);
      background: var(--c-card-bg);
      border: 1px solid var(--c-card-border);  
      @include phone { min-width: 700px; padding: var(--c-space-2); }

      .hea-chart {    
        width: 100%;
        display: grid;
        grid-template-rows: auto auto;
        row-gap: calc(var(--c-space-10) / 4);
        grid-template-columns: minmax(0, 1fr);

        .hea-months-row,
        .hea-grid-row {
          width: 100%;
          display: grid;
          grid-template-columns: minmax(0, 1fr);
          column-gap: calc(var(--c-space-6) / 2);
        } 
        
        .hea-months-row {
          align-items: end;
        }

        .hea-grid-row {
          align-items: stretch;
        }   
        
        .hea-months-row--with-weekdays,
        .hea-grid-row--with-weekdays {
          grid-template-columns: calc(var(--c-space-1) * 9) minmax(0, 1fr);
        }

        .hea-axis-spacer {
          display: block;
          min-height: 1px;
          width: calc(var(--c-space-1) * 9);
        }

        .hea-months {
          width: 100%;
          display: grid;
          line-height: 1;
          color: var(--c-meta-1);
          font-size: var(--c-font-s);
          column-gap: var(--c-space-1);
          min-height: var(--c-space-1);
          grid-template-columns: repeat(var(--hea-weeks), minmax(0, 1fr));
        }

        .hea-month {
          min-width: 0;
          align-self: end;
          overflow: visible;
          white-space: nowrap;
          font-size: var(--c-font-xs);
        }

        .hea-weekdays {
          height: 100%;
          display: grid;
          line-height: 1;
          font-weight: 600;
          color: var(--c-meta-1);
          gap: var(--c-space-1);
          font-size: var(--c-font-xs);
          width: calc(var(--c-space-1) * 9);
          grid-template-rows: repeat(7, 1fr);
        }

        .hea-weekday {
          height: 100%;
          min-width: 0;
          display: flex;
          align-items: center;
          justify-content: flex-end;
          padding-right: var(--c-space-1);
        }

        .hea-grid {
          width: 100%;
          display: grid;
          gap: var(--c-space-1);
          grid-template-columns: repeat(var(--hea-weeks), minmax(0, 1fr));
        }

        .hea-week {
          display: grid;
          gap: var(--c-space-1);
          grid-template-rows: repeat(7, minmax(0, 1fr));
        }

        .hea-cell {
          width: 100%;
          display: block;
          border-radius: 3px;
          aspect-ratio: 1 / 1;
          background: var(--c-meta-bg);
          border: 1px solid rgba(27, 31, 36, 0.08);
        }

        .hea-cell--empty {
          background: transparent;
          border-color: transparent;
        }

        .hea-cell--inactive {
          background: var(--c-meta-bg);
        }

        .hea-cell--level-1 {
          background: var(--c-hea-level-1);
        }

        .hea-cell--level-2 {
          background: var(--c-hea-level-2);
        }

        .hea-cell--level-3 {
          background: var(--c-hea-level-3);
        }

        .hea-cell--level-4 {
          background: var(--c-hea-level-4);
        }    
      }  
    }  
  }  
}

.hea-hover-card {
  display: none;
  position: fixed;
  z-index: 999999;
  min-width: 200px;
  max-width: 320px;
  line-height: 1.3;
  border-radius: 12px;
  pointer-events: auto;
  font-size: var(--c-font-xs);
  background: var(--c-card-bg);
  transition: opacity 0.15s ease;
  border: 1px solid var(--c-card-border);
  padding: var(--c-space-2) var(--c-space-4);
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
 
  .card-date {
    font-weight: 600;
    margin-block: var(--c-space-2);
    padding-bottom: var(--c-space-1);
    color: var(--c-global-font-color);
    border-bottom: 1px solid var(--c-card-border);
  }

  a {
    display: block;
    color: var(--c-link-color);
    text-decoration: none;
    padding-block: var(--c-space-1);

    &:hover {
      text-decoration: underline;
      color: var(--c-link-hover);
    }
  }

  .card-badge {
    font-weight: 600;
    border-radius: 4px;
    color: var(--c-meta-1);
    display: inline-block;
    background: var(--c-meta-bg);
    font-size: var(--c-font-xxs);
    margin-right: var(--c-space-1);
    padding-inline: var(--c-space-1);
    text-decoration: none !important;

    &.publish {
      color: var(--c-hea-publish-color);
      background: var(--c-hea-publish-bg);
    }

    &.update {
      color: var(--c-hea-update-color);
      background: var(--c-hea-update-bg);
    }
  }
}  

归档页

同首页卡片布局(custom-archive),按年分组显示文章列表。

创建以下文件:

HTMLlayouts/_partial/custom-post-list.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
{{- $page := . -}}
{{- $showLastmod := false -}}
{{- /* 如果页面上下文中有来自最近更新的标记,则显示更新日期 */ -}}
{{- if $page.Scratch.Get "fromRecentlyUpdated" -}}
  {{- $showLastmod = true -}}
{{- end -}}

{{- /* 自定义卡片布局 */ -}}    
<article class="custom-archive">
  <!-- 左侧封面图:从 front matter 中读取 featuredImage -->
  {{- if $page.Params.featuredImage -}}
    <div class="archive-item-image">
      <a href="{{ $page.RelPermalink }}" class="archive-image-link">
        {{- $img := $page.Params.featuredImage -}}
        {{- $imageResource := resources.GetMatch (printf "images/%s" (path.Base $img)) -}}
        {{- if $imageResource -}}
          {{- $thumbnail := $imageResource.Fill "600x400 Center" -}}
          <img src="{{ $thumbnail.RelPermalink }}" width="{{ $thumbnail.Width }}" height="{{ $thumbnail.Height }}" alt="{{ $page.Title }}" loading="lazy">
        {{- else -}}
          <img src="{{ $img | safeURL }}" alt="{{ $page.Title }}" loading="lazy">
        {{- end -}}
      </a>
    </div>
  {{- end -}}
  <!-- 左侧内容区:日期和标题 -->
  <div class="archive-item-content">
    <span class="archive-item-date" title='{{ if $showLastmod }}{{ $page.Lastmod.Format "2006-01-02 15:04:05" }}{{ else }}{{ $page.Date.Format "2006-01-02 15:04:05" }}{{ end }}'>
      {{- dict "Class" "far fa-calendar-alt me-1" | partial "plugin/icon.html" -}}
      {{- if $showLastmod -}}
        {{- $page.Lastmod | dateFormat "01-02" -}}
      {{- else -}}
        {{- $page.Date | dateFormat "01-02" -}}
      {{- end -}}
    </span>
    <a href="{{ $page.RelPermalink }}" class="archive-item-link">
      {{- $repost := $page.Params.repost | default dict -}}
      {{- if eq $repost.enable true -}}
        {{- dict "Class" "fa-solid fa-share text-success me-1" | partial "plugin/icon.html" -}}
      {{- end -}}
      {{- cond ($page.Param "capitalizeTitles") (title $page.LinkTitle) $page.LinkTitle -}}
    </a>
  </div>
</article>

创建以下文件:

HTMLlayouts/home.archives.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
{{- /* 覆盖来源:FixIt/layouts/home.archives.html */ -}}

{{- define "title" -}}
  {{- .Params.Title | default (T "archives") -}}
  {{- if .Site.Params.withSiteTitle }} {{ .Site.Params.titleDelimiter }} {{ .Site.Title }}{{- end -}}
{{- end -}}

{{- define "content" -}}
  {{- /* All Posts */ -}}
  {{- $pages := .Site.Store.Get "mainSectionPages" -}}

  <div class="page archive">
    <div class="header">
      {{- /* Title */ -}}
      <h1 class="single-title animate__animated animate__pulse animate__faster">
        {{- dict "Class" "fa-solid fa-box-archive me-1" | partial "plugin/icon.html" -}}
        {{- .Params.Title | default (T "archives") }} <sup>{{ $pages.Len }}</sup>
      </h1>
      {{- /* Total word count */ -}}
      {{- /* See https://github.com/hugo-fixit/FixIt/issues/124 */ -}}
      {{- $localData := newScratch -}}
      {{- range $pages -}}
        {{- $localData.Add "totalWordCount" .WordCount -}}
      {{- end -}}
      {{- with ($localData.Get "totalWordCount") -}}
        {{- $humanizedNum := . -}}
        {{- if ge $humanizedNum 1000 -}}
          {{- $humanizedNum = printf "%.2fK" (div $humanizedNum 1000.0) -}}
        {{- end -}}
        <p class="single-subtitle total-word-count" title='{{- T "section.totalWordCount" . }}'>
          {{- dict "Class" "fa-regular fa-keyboard" | partial "plugin/icon.html" }} {{ T "section.totalWordCount" (dict "Count" $humanizedNum) -}}
        </p>
      {{- end -}}
    </div>

    {{- /* Paginate */ -}}
    {{- if $pages -}}
      {{- $pages = $pages.GroupByDate "2006" -}}
      {{- with .Site.Params.archives.paginate | default .Site.Params.paginate -}}
        {{- $pages = $.Paginate $pages . -}}
      {{- else -}}
        {{- $pages = .Paginate $pages -}}
      {{- end -}}
      {{- partial "recently-updated.html" . -}}
      {{- range $pages.PageGroups -}}
        <h2 class="group-title">
          {{- dict "Class" "fa-regular fa-calendar me-1" | partial "plugin/icon.html" -}}
          {{- .Key -}}
        </h2>

        {{- /* ======================== 修改点 1:开始 ======================== */ -}}
        {{- /* 自定义卡片布局:全部迁移至独立 partial "custom-post-list.html */ -}}      
        {{- range .Pages -}}
          {{- partial "custom-post-list.html" . -}}
        {{- end -}}
        {{- /* ======================== 修改点 1:结束 ======================== */ -}} 

      {{- end -}}
      {{- partial "paginator.html" . -}}
    {{- end -}}
  </div>
{{- end -}}

创建以下文件:

HTMLlayouts/term.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
{{- /* 覆盖来源:FixIt/layouts/term.html */ -}}

{{- define "title" -}}
  {{- .Title }} - {{ T .Data.Singular | default .Data.Singular -}}
  {{- if .Site.Params.withSiteTitle }} {{ .Site.Params.titleDelimiter }} {{ .Site.Title }}{{- end -}}
{{- end -}}

{{- define "content" -}}
  <div class="page archive">
    {{- /* Title */ -}}
    {{- $taxonomy := .Data.Singular -}}
    {{- $pageCount := len .Pages -}}
    {{- $termIcon := "" -}}
    {{- $termTitle := .Title -}}
    {{- if eq $taxonomy "category" -}}
      {{- $termIcon = "fa-regular fa-folder-open" -}}
    {{- else if eq $taxonomy "tag" -}}
      {{- $termIcon = "fa-solid fa-tag" -}}
    {{- else if eq $taxonomy "collection" -}}
      {{- $termIcon = "fa-solid fa-layer-group" -}}
    {{- else -}}
      {{- $termTitle = printf "%v - %v" (T $taxonomy | default $taxonomy) .Title -}}
    {{- end -}}
    <h1 class="single-title animate__animated animate__pulse animate__faster">
      {{- with $termIcon -}}
        {{- dict "Class" (add . " me-1") | partial "plugin/icon.html" -}}
      {{- end -}}
      {{- $termTitle }} <sup>{{ $pageCount }}</sup>
    </h1>

    {{- /* Paginate */ -}}
    {{- if .Pages -}}
      {{- $pages := .Pages.GroupByDate "2006" -}}
      {{- with .Site.Params.list.paginate | default .Site.Params.paginate -}}
        {{- $pages = $.Paginate $pages . -}}
      {{- else -}}
        {{- $pages = .Paginate $pages -}}
      {{- end -}}
      {{- partial "recently-updated.html" . -}}
      {{- range $pages.PageGroups -}}
        <h2 class="group-title">
          {{- dict "Class" "fa-regular fa-calendar me-1" | partial "plugin/icon.html" -}}
          {{- .Key -}}
        </h2>

        {{- /* ======================== 修改点 1:开始 ======================== */ -}}
        {{- /* 自定义卡片布局:全部迁移至独立 partial "custom-post-list.html */ -}}      
        {{- range .Pages -}}
          {{- partial "custom-post-list.html" . -}}
        {{- end -}}
        {{- /* ======================== 修改点 1:结束 ======================== */ -}} 

        {{- end -}}
      {{- end -}}
      {{- partial "paginator.html" . -}}
    {{- end -}}
  </div>
{{- end -}}

创建以下文件:

HTMLlayouts/section.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
{{- define "title" -}}
  {{- title (.Params.Title | default ((T .Section) | default .Section | dict "Some" | T "allSome")) -}}
  {{- if .Site.Params.withSiteTitle }} {{ .Site.Params.titleDelimiter }} {{ .Site.Title }}{{- end -}}
{{- end -}}

{{- define "content" -}}
  <div class="page archive">
    <div class="header">
      {{- /* Title */ -}}
      <h1 class="single-title animate__animated animate__pulse animate__faster">
        {{- $titleIcon := "fa-solid fa-feather" -}}
        {{- with .Params.titleIcon -}}
          {{- $titleIcon = . -}}
        {{- end -}}
        {{- dict "Class" (add $titleIcon " me-1") | partial "plugin/icon.html" -}}
        {{- title (.Params.Title | default ((T .Section) | default .Section | dict "Some" | T "allSome")) }} <sup>{{ .Pages.Len }}</sup>
      </h1>
      {{- /* Total word count */ -}}
      {{- /* See https://github.com/hugo-fixit/FixIt/issues/124 */ -}}
      {{- $localData := newScratch -}}
      {{- range .Pages -}}
        {{- $localData.Add "totalWordCount" .WordCount -}}
      {{- end -}}
      {{- with ($localData.Get "totalWordCount") -}}
        {{- $humanizedNum := . -}}
        {{- if ge $humanizedNum 1000 -}}
          {{- $humanizedNum = printf "%.2fK" (div $humanizedNum 1000.0) -}}
        {{- end -}}
        <p class="single-subtitle total-word-count" title='{{- T "section.totalWordCount" . }}'>
          {{- dict "Class" "fa-regular fa-keyboard" | partial "plugin/icon.html" }} {{ T "section.totalWordCount" (dict "Count" $humanizedNum) -}}
        </p>
      {{- end -}}
    </div>

    {{- /* Paginate */ -}}
    {{- if .Pages -}}
      {{- $pages := .Pages.GroupByDate "2006" -}}
      {{- with .Site.Params.section.paginate | default .Site.Params.paginate -}}
        {{- $pages = $.Paginate $pages . -}}
      {{- else -}}
        {{- $pages = .Paginate $pages -}}
      {{- end -}}
      {{- partial "recently-updated.html" . -}}
      {{- range $pages.PageGroups -}}
        <h2 class="group-title">
          {{- dict "Class" "fa-regular fa-calendar me-1" | partial "plugin/icon.html" -}}
          {{- .Key -}}
        </h2>

        {{- /* ======================== 修改点 1:开始 ======================== */ -}}
        {{- /* 自定义卡片布局:全部迁移至独立 partial "custom-post-list.html */ -}}      
        {{- range .Pages -}}
          {{- partial "custom-post-list.html" . -}}
        {{- end -}}
        {{- /* ======================== 修改点 1:结束 ======================== */ -}} 

      {{- end -}}
      {{- partial "paginator.html" . -}}
    {{- end -}}
  </div>
{{- end -}}

创建以下文件:

HTMLlayouts/_partials/recently-updated.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
{{- /* 覆盖来源:FixIt/layouts/_partials/recently-updated.html */ -}}

{{- /* Recently updated pages for archives, section and term list */ -}}
{{- $kindMap := dict
  "home" "archives"
  "section" "section"
  "term" "list"
-}}
{{- $recentlyUpdatedConfig := .Site.Params.recentlyUpdated -}}
{{- $scope := index $kindMap .Kind -}}
{{- $scopeEnable := index $recentlyUpdatedConfig $scope | default false -}}
{{- if $scopeEnable | and (eq $.Paginator.PageNumber 1) -}}
  {{- $hasTitle := false }}
  {{- $postCount := 0 -}}
  {{- $maxPostCount := $recentlyUpdatedConfig.maxCount | default 10 -}}
  {{- $days := $recentlyUpdatedConfig.days | default 30 -}}
  {{- $dateformat := (index .Site.Params $scope).dateformat | default "01-02" -}}
  {{- $scopePages := .Pages -}}
  {{- if eq $scope "archives" -}}
    {{- $scopePages = .Site.Store.Get "mainSectionPages" -}}
  {{- end -}}
  {{- $scopePages = where $scopePages.ByLastmod.Reverse "Section" "!=" "" -}}
  {{- $hiddenAdapters := .Param "hiddenAdapters" | default false -}}
  {{- if $hiddenAdapters -}}
    {{- $scopePages = where $scopePages "File.IsContentAdapter" "ne" true -}}
  {{- end -}}
  {{- range first $maxPostCount $scopePages -}}
    {{- if gt (add .Lastmod.Unix (mul 86900 $days)) now.Unix -}}
      {{- if ne .Lastmod.Unix .Date.Unix }}
        {{- $postCount = add $postCount 1 -}}
        {{- if eq $hasTitle false -}}
          <h2 class="group-title">
            {{- dict "Class" "fa-regular fa-calendar-check me-1" | partial "plugin/icon.html" -}}
            {{- T "section.recentlyUpdated" -}}
          </h2>
          {{- $hasTitle = true -}}
        {{- end -}}

        {{- /* ======================== 修改点 1:开始 ======================== */ -}}
        {{- /* 自定义卡片布局:全部迁移至独立 partial "custom-post-list.html */ -}}      
        {{- .Scratch.Set "fromRecentlyUpdated" true -}}
        {{- partial "custom-post-list.html" . -}}
        {{- .Scratch.Delete "fromRecentlyUpdated" -}}
        {{- /* ======================== 修改点 1:结束 ======================== */ -}} 
        
      {{- end -}}
    {{- end -}}
  {{- end -}}
{{- end -}}

创建以下文件:

SCSSassets/css/_custom.scss
 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
// 全局样式与变量见最后一章

// ========= 归档页 =========

.special,
.archive {
  width: 100%;
  border-radius: 16px;
  box-sizing: border-box;
  padding: var(--c-space-8);
  background-color: var(--c-card-bg); 
  max-width: clamp(360px, 85vw, 800px);
  border: 1px solid var(--c-card-border); 
  box-shadow: 0 2px 10px var(--c-card-shadow); 
  margin-block: var(--c-space-4) var(--c-space-8);
  transition: background-color 0.3s ease, border-color 0.3s ease;
  @include phone { padding: var(--c-space-6); }

  .single-title {
    padding: 0;
    font-size: var(--c-font-xxl);
    margin-block: var(--c-space-6);
  }

  .single-subtitle,
  .group-title {
    font-size: var(--c-font-xl);
  }

  .custom-archive {
    display: flex;
    align-items: center;
    gap: var(--c-space-2);
    box-sizing: border-box;  
    margin: var(--c-space-4);    
    justify-content: space-between;

    &:last-of-type {
      margin-bottom: var(--c-space-6);
    }

    .archive-item-image {
      width: 140px;
      flex-shrink: 0;
      overflow: hidden;
      border-radius: 8px;
      aspect-ratio: 2 / 1;
      @include phone { width: 80px; aspect-ratio: 4 / 3; }
      @include pad { width: 110px; aspect-ratio: 5 / 3; }

      img {
        height: 100%;
        transition: transform 0.3s ease;

        &:hover {
          transform: scale(1.15);
        }
      }
    }

    .archive-item-content {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      gap: var(--c-space-1);

      .archive-item-date {
        text-align: left;       
        font-size: var(--c-font-s);
      }
        
      .archive-item-link {
        &:hover {
          color: var(--c-link-hover);
        }
      }
    }      
  }
}

合集页

手风琴折叠布局,展示全部合集,按发布时间降序排列,移除更多链接。

创建以下文件:

HTMLlayouts/taxonomies.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
{{- /* 覆盖来源:FixIt/layouts/taxonomies.html */ -}}

{{- /* Taxonomies Page */ -}}
{{- $iconMap := dict
  "category" "fa-regular fa-folder"
  "collection" "fa-solid fa-layer-group"
-}}
{{- $icon := index $iconMap .Data.Singular | default $iconMap.category -}}

{{- /* ======================== 修改点 1:开始 ======================== */ -}}
{{- /* 手风琴折叠布局:每个项是一个折叠面板,默认收起,点击展开 */ -}}
{{- range .Data.Terms.ByCount -}}
  {{- $term := .Term -}}
  {{- $pages := .Pages -}}
  {{- with $.Site.GetPage "taxonomy" (printf "%v/%v" $.Type $term) -}}
  <div class="custom-taxonomy">
    {{- /* 折叠控制:使用 checkbox + label 实现互斥折叠(JS 确保同时只能展开一个) */ -}}
    <input type="checkbox" id="taxonomy-{{ $term | urlize }}" class="taxonomy-toggle">
    <label class="taxonomy-header" for="taxonomy-{{ $term | urlize }}">
      <div class="taxonomy-title">
        {{- .Page.Title -}}
        <span class="taxonomy-count">({{ len $pages }})</span>
      </div>
      <div class="taxonomy-icon">
        <i class="fas fa-chevron-down"></i>
      </div>
    </label>
    {{- /* 文章列表排序与显示 */ -}}
    <div class="taxonomy-content">
      {{- /* 按发布时间降序排序 */ -}}
      {{- $pages = sort $pages "PublishDate" "desc" -}}
      {{- /* 遍历该合集下的全部文章 */ -}}
      {{- range $pages -}}
        <a href="{{ .RelPermalink }}" class="taxonomy-post-link">
          <span class="taxonomy-post-title">
            {{- $repost := .Params.repost | default dict -}}
            {{- if eq $repost.enable true -}}
              {{- dict "Class" "fa-solid fa-share text-success me-1" | partial "plugin/icon.html" -}}
            {{- end -}}
            {{- cond (.Param "capitalizeTitles") (title .LinkTitle) .LinkTitle -}}
          </span>
          {{- $dateFormat := .Site.Params.dateFormat | default "2006-01-02" -}}
          {{- $displayDate := .PublishDate -}}
          <span class="taxonomy-post-date" title='{{ $displayDate.Format "2006-01-02 15:04:05" }}'>
            {{- $displayDate.Format $dateFormat -}}
          </span>
        </a>
      {{- end -}}
      {{- /* 已删除更多链接 */ -}}
    </div>
  </div>
  {{- end -}}
{{- end -}}
{{- /* ======================== 修改点 1:结束 ======================== */ -}}

{{- /* EOF */ -}}

创建以下文件:

JavaScriptassets/js/custom.js
 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
(function() {

  // ========== 手风琴折叠(仅 taxonomy 页) ==========

  (function() {
    if (!document.querySelector('.custom-taxonomy')) return;

    setTimeout(function() {
      var headers = document.querySelectorAll('.custom-taxonomy .taxonomy-header');
      var checkboxes = document.querySelectorAll('.custom-taxonomy .taxonomy-toggle');
      var currentOpen = null;

      for (var i = 0; i < checkboxes.length; i++) {
        checkboxes[i].checked = false;
      }

      for (var i = 0; i < headers.length; i++) {
        headers[i].addEventListener('click', function(e) {
          e.preventDefault();
          var checkboxId = this.getAttribute('for');
          var checkbox = document.getElementById(checkboxId);
          var item = this.closest('.custom-taxonomy');
          if (!checkbox || !item) return;

          if (checkbox.checked) {
            checkbox.checked = false;
            currentOpen = null;
            return;
          }

          if (currentOpen) {
            var oldCheckbox = currentOpen.querySelector('.taxonomy-toggle');
            if (oldCheckbox) oldCheckbox.checked = false;
          }

          checkbox.checked = true;
          currentOpen = item;
        });
      }
    }, 50);
  })();

)();    

创建以下文件:

SCSSassets/css/_custom.scss
  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
// 全局样式与变量见最后一章

// ========= 合集页 =========

.custom-taxonomy {
  display: flex;
  overflow: hidden;
  gap: var(--c-space-2);
  flex-direction: column;
  margin-block: var(--c-space-2);
  border-bottom: 1px solid var(--c-card-border);

  &:last-of-type {
    border-bottom: none;
    margin-bottom: var(--c-space-6);
  }

 .taxonomy-toggle {
    display: none;

    &:checked + .taxonomy-header + .taxonomy-content {
      opacity: 1;
      max-height: none;
    }

    &:checked + .taxonomy-header {
      .taxonomy-title {
        color: var(--c-link);
      }

      .taxonomy-icon i {
        transform: rotate(180deg);
        font-size: var(--c-font-s);
      }
    }        
  }   

  .taxonomy-header {
    display: flex;
    cursor: pointer;
    user-select: none;
    border-radius: 8px;
    align-items: center;
    padding: var(--c-space-2);
    justify-content: space-between;
    transition: background 0.3s ease;
    
    &:hover {
      background: var(--c-meta-bg);
    }
   
    .taxonomy-title {
      font-weight: 600;
      color: var(--c-meta-alt);
      font-size: var(--c-font-l); 
    }

    .taxonomy-count {
      font-weight: normal;
      color: var(--c-meta);
      font-size: var(--c-font-s);
      margin-left: var(--c-space-2);
    }

    .taxonomy-icon i {
      color: var(--c-meta);
      transition: transform 0.3s ease-out;
    }
  }   

  .taxonomy-content {
    opacity: 0;
    max-height: 0;
    overflow: hidden;
    padding-inline: var(--c-space-4);

    .taxonomy-post-link {
      display: flex;
      line-height: 1.7;
      align-items: center;
      color: var(--c-meta-alt);       
      justify-content: space-between;

      &:hover {
        color: var(--c-link) !important;
      }

      &:last-of-type {
        margin-bottom: var(--c-space-4);
      }
    }

    .taxonomy-post-title {
      flex: 1; 
      min-width: 0; 
      overflow: hidden;
      white-space: nowrap;
      text-overflow: ellipsis;   
    }

    .taxonomy-post-date {
      flex-shrink: 0;
      white-space: nowrap;
      color: var(--c-meta);
      font-size: var(--c-font-xs);
      margin-left: var(--c-space-2);
    }    
  }       
}

全局样式与变量

创建以下文件:

SCSSassets/css/_override.scss
  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
:root {
  // 字号
  --c-font-base: clamp(0.96875rem, 0.85rem + 0.2vw, 1rem);
  --c-font-xxs: calc(0.625 * var(--c-font-base));
  --c-font-xs: calc(0.75  * var(--c-font-base));
  --c-font-s: calc(0.875 * var(--c-font-base));
  --c-font-m: var(--c-font-base); 
  --c-font-l: calc(1.125 * var(--c-font-base));
  --c-font-xl: calc(1.25 * var(--c-font-base));
  --c-font-xxl: calc(1.5 * var(--c-font-base));
  --c-font-xxxl: calc(1.75 * var(--c-font-base));

  // 边距
  --c-space-1: 0.25rem;   /* 1 unit = 4px */
  --c-space-2: calc(var(--c-space-1) * 2);    /* 0.5rem */
  --c-space-4: calc(var(--c-space-1) * 4);    /* 1rem   */
  --c-space-6: calc(var(--c-space-1) * 6);    /* 1.5rem */
  --c-space-8: calc(var(--c-space-1) * 8);    /* 2rem   */
  --c-space-10: calc(var(--c-space-1) * 10);  /* 2.5rem */
  --c-space-12: calc(var(--c-space-1) * 12);  /* 3rem   */

  @media (max-width: 680px) { --c-space-1: 0.22rem; }
  @media (min-width: 681px) and (max-width: 1200px) { --c-space-1: 0.23rem; }

  // 颜色
  --c-global-font-color: #333;

  --c-card-bg: #fefefe;
  --c-card-border: #e5e7eb; 
  --c-card-shadow: 0 2px 8px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
  --c-card-shadow-hover: 0 12px 24px -8px rgba(0, 0, 0, 0.12);
  --c-card-border-hover: rgba(0, 0, 0, 0.08);

  --c-header-bg: rgba(255, 255, 255, 0.68);
  --c-header-border: rgba(0, 0, 0, 0.06);
  --c-header-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);

  --c-meta-1: #8b949e;
  --c-meta-2: #3a3a44;
  --c-meta-hover: #000;
  --c-meta-bg: rgba(0, 0, 0, 0.05);
  --c-meta-bg-hover: rgba(0, 0, 0, 0.1);

  --c-tech-color: #2376b7; 
  --c-tech-hover: #1a5a8a;
  --c-tech-bg: rgba(35, 118, 183, 0.12);
  --c-tech-bg-hover: rgba(35, 118, 183, 0.2);  

  --c-work-color: #28a050;
  --c-work-hover: #1e6e3a;
  --c-work-bg: rgba(40, 160, 80, 0.12);
  --c-work-bg-hover: rgba(40, 160, 80, 0.2);
  
  --c-life-color: #e6781e;
  --c-life-hover: #b85c12;
  --c-life-bg: rgba(230, 120, 30, 0.12);
  --c-life-bg-hover: rgba(230, 120, 30, 0.2);

  --c-postinfo-bg: #fafbfc;
  --c-postinfo-border: rgba(0, 0, 0, 0.06);
  --c-avatar-bg: #fefefe;
  --c-avatar-border: #e0e4e8;
  --c-avatar-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  --c-avatar-shadow-hover: 0 8px 20px rgba(0, 0, 0, 0.12);
  --c-reward-btn-bg: #e65c3c;
  --c-reward-btn-hover: #c94b2c;
  --c-subscribe-btn-bg: #45b37a;
  --c-subscribe-btn-hover: #3ab47a;

  --c-link-color: #2376b7;
  --c-link-hover: #ea517f;  
  --c-toc-color: #161209;
  --c-collection-bg: #fefefe;
  --c-collection-border: #f0f0f0;
  --c-tooltip-bg: #fefefe;
  --c-tooltip-color: #0f172a;
  --c-summary-update-bg: #ff5722;

  --c-book-corner-bg: #f99b01;
  --c-book-corner-shadow: rgba(0, 0, 0, 0.2);
  --c-book-corner-border: #e68900;
  --c-book-star-full: #ffac2d;
  --c-book-star-empty: #ddd;

  --c-hea-level-1: #9be9a8;
  --c-hea-level-2: #40c463;
  --c-hea-level-3: #30a14e;
  --c-hea-level-4: #216e39;  
  --c-hea-publish-bg: #dbeafe;
  --c-hea-publish-color: #1d4ed8;
  --c-hea-update-bg: #fef3c7;
  --c-hea-update-color: #b45309;  
}


[data-theme="dark"] {
  // 颜色
  --c-global-font-color: #c0c0c0;
  
  --c-card-bg: #35373c;
  --c-card-border: #46494f;
  --c-card-shadow: 0 4px 12px rgba(0, 0, 0, 0.2), 0 1px 2px rgba(0, 0, 0, 0.1);
  --c-card-shadow-hover: 0 16px 28px -8px rgba(0, 0, 0, 0.35);
  --c-card-border-hover: rgba(255, 255, 255, 0.1);  

  --c-header-bg: rgba(46, 47, 51, 0.8);
  --c-header-border: rgba(255, 255, 255, 0.04); 
  --c-header-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);

  --c-meta-1: #7d8792;
  --c-meta-2: #c0c0c8;
  --c-meta-hover: #ffffff;
  --c-meta-bg: rgba(255, 255, 255, 0.08);
  --c-meta-bg-hover: rgba(255, 255, 255, 0.15);

  --c-tech-color: #1781b5;
  --c-tech-hover: #2a8fbf;
  --c-tech-bg: rgba(23, 129, 181, 0.2);
  --c-tech-bg-hover: rgba(23, 129, 181, 0.3);

  --c-work-color: #8ad6a8;
  --c-work-hover: #6ec08a;
  --c-work-bg: rgba(60, 180, 100, 0.2);
  --c-work-bg-hover: rgba(60, 180, 100, 0.3);
  
  --c-life-color: #f5bc7a;
  --c-life-hover: #e09c5a;
  --c-life-bg: rgba(240, 150, 60, 0.2);
  --c-life-bg-hover: rgba(240, 150, 60, 0.3);

  --c-postinfo-bg: #2f3136;
  --c-postinfo-border: rgba(255, 255, 255, 0.06);
  --c-avatar-bg: #2f3136;
  --c-avatar-border: #5a5e66;
  --c-avatar-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  --c-avatar-shadow-hover: 0 8px 20px rgba(0, 0, 0, 0.5);
  --c-reward-btn-bg: #a5452a;
  --c-reward-btn-hover: #c55a3a;
  --c-subscribe-btn-bg: #1e6d3a;
  --c-subscribe-btn-hover: #155a2e;

  --c-link-color: #1781b5;
  --c-link-hover: #cc5595;
  --c-toc-color: #b1b1ba;
  --c-collection-bg: #292a2e;
  --c-collection-border: #383838;
  --c-tooltip-bg: #39393c;
  --c-tooltip-color: #e2e2e6;  
  --c-summary-update-bg: #cc4a1a;

  --c-book-corner-bg: #b84b00;
  --c-book-corner-shadow: rgba(0, 0, 0, 0.4);
  --c-book-corner-border: #963b00;
  --c-book-star-full: #ffac2d;
  --c-book-star-empty: #ddd;

  --c-hea-level-1: #2a5a40;
  --c-hea-level-2: #3a7a58;
  --c-hea-level-3: #4a9a70;
  --c-hea-level-4: #5aba88;
  --c-hea-publish-bg: #2a4a7a;
  --c-hea-publish-color: #8ab4f8;
  --c-hea-update-bg: #5a3a1a;
  --c-hea-update-color: #f5b342;
}

创建以下文件:

SCSSassets/css/_custom.scss
 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
// ========= 全局 =========

// 字体
body { font-size: var(--c-font-m); color: var(--c-global-font-color); }
h1 { font-size: var(--c-font-xxl); }
h2 { font-size: var(--c-font-xl); }
h3, h4 { font-size: var(--c-font-l); }

// 暗黑模式
[data-theme="dark"] { img:not(.no-darken) { filter: brightness(0.95); } }
[data-theme="dark"] { .single .content { b, strong { color: #c8c8c8; } } }

// 设备响应使用方法:@include phone { } @include pad { } @include pc { } */
@mixin phone { @media (max-width: 680px) { @content; } } 
@mixin pad { @media (min-width: 681px) and (max-width: 1200px) { @content; } } 
@mixin pc { @media (min-width: 1201px) { @content; } } 

// 分类标签使用方法:@include category-color-styles;
@mixin category-color-styles {
  $cats: tech, work, life;
  @each $cat in $cats {
    &[href*="/#{$cat}/"] {
      color: var(--c-#{$cat}-color);
      background-color: var(--c-#{$cat}-bg);

      &:hover {
        color: var(--c-#{$cat}-hover);
        background-color: var(--c-#{$cat}-bg-hover);
      }
    }
  }
}
留言交流