目录

折腾 | Hugo 博客目录(TOC)样式改造

最近在看自己的网站时,觉得文章右侧的目录(Table of Contents)样式有些单调。它虽然能用,但在视觉上还有很大的提升空间。

https://photos.philohao.com/picimpact/20250915203443489.png

可以看到,它只用了简单的圆点和缩进,层级关系不够直观,交互感也比较弱。我的目标是:在不修改任何 JavaScript 的前提下,只通过 CSS (SCSS) 来让它变得更美观、更实用。

第一版改造:四大优化方向

经过分析,我确定了四个主要的优化方向,并进行了第一轮的样式修改。

1. 增强层级感

为了让目录结构一目了然,我做了两处关键改动:

  • 区分标记点:用不同的符号来表示不同级别的标题。一级目录用实心圆 ●,二级目录用空心圆 ○,三级目录则用更细的 ◌。
  • 增加引导线:在次级目录的左侧,增加一条垂直的引导线。这条线能非常清晰地将子标题“收纳”在父标题之下,结构感瞬间增强。

2. 增加交互反馈

一个好的界面应该能对用户的操作做出反馈,之前的目录,鼠标放上去没有任何变化。

  • 悬停效果 (:hover): 为每个目录链接添加了 :hover 伪类。当鼠标悬停在链接上时,会改变文字颜色并出现一个淡淡的背景色。这个简单的改动能明确告诉用户“这里可以点击”。

3. 优化容器外观

无论是浮动目录还是静态目录,我都想让它们看起来更像一个精心设计的“卡片”,而不是简单的文字列表。

  • 浮动目录 (#toc-auto): 为其增加了半透明的背景和右侧的圆角。这样即使页面背景复杂,目录依然清晰可读。
  • 静态目录 (#toc-static): 将其从类似代码块的深色背景,改为了带有圆角和细边框的简洁样式,更好地融入文章内容。

4. 调整布局方式

放弃了原先 text-indent 的缩进方式,改用 position: relative 和 padding-left 来布局,这样能更精确地控制标记点和文字的位置,避免错位问题。

第二版微调:优化行间距

第一版改造后,整体效果已经很不错了,但我发现一个细节问题:每一行的行间距似乎有点大,目录显得有些松散。

这个问题主要来源于我为了增加链接点击区域而在 a 标签上设置的 padding 值。

调整方法很简单:

找到 .toc .toc-content ul a 选择器,将其 padding 的垂直值从 4px 或 2px 直接改为 0。

修改前: padding: 2px 0;

修改后: padding: 0 0;

同时,也移除了层级之间 ul 的 margin 值,让整个列表更加紧凑。

最终代码展示

  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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
// --- 变量定义 (假设这些变量已在别处定义) ---
// $toc-title-font-size: 1.1em;
// $toc-content-font-size: 0.9em;
// $single-link-color: #333;
// $single-link-hover-color: #007bff;
// $global-border-color: #eee;
// ... 以及对应的 dark 和 black 主题变量

// --- 通用 TOC 基础样式 ---
.toc {
  .toc-title {
    font-size: $toc-title-font-size;
    font-weight: bold;
    text-transform: uppercase;
  }

  .toc-content {
    font-size: $toc-content-font-size;

    // --- 移除 text-indent, 使用更现代的 Flexbox/Grid 或伪元素布局 ---
    ul {
      padding-left: 0; // 重置内边距, 在 a 标签上控制
      list-style: none;

      a {
        // --- 优化: 将 a 标签设为块级或行内块, 方便添加 padding 和 hover 背景 ---
        display: block;
        padding: 0 0; // --- 调整这里 (1): 减小链接的上下内边距来缩小行距 ---
        position: relative;
        padding-left: 1.35rem; // 为伪元素留出空间
        text-decoration: none; // 确保没有下划线
        transition: color 0.2s, background-color 0.2s; // 平滑过渡

        // --- 新增: 鼠标悬停效果 ---
        &:hover {
          color: $single-link-hover-color;
          background-color: rgba($global-border-color, 0.5); // 添加一个淡淡的背景色
          border-radius: 4px;

          [theme=dark] & {
            color: $single-link-hover-color-dark;
            background-color: rgba($global-border-color-dark, 0.2);
          }
          [theme=black] & {
            color: $single-link-hover-color-black;
            background-color: rgba($global-border-color-black, 0.2);
          }
        }
      }

      // --- 优化: 使用 > 选择器区分层级 ---
      // Level 1
      > li > a::before {
        content: "●"; // 使用实心圆点作为一级标记
        font-weight: bolder;
        position: absolute;
        left: 0;
        top: 50%;
        transform: translateY(-50%);
        color: $single-link-color;
        [theme=dark] & { color: $single-link-color-dark; }
        [theme=black] & { color: $single-link-color-black; }
      }

      ul {
        padding-left: 1rem; // 子列表的缩进
        // --- 调整这里 (2): 减小层级之间的垂直间距 ---
        // margin-top: 0.1rem;
        // margin-bottom: 0.1rem;

        // --- 新增: 视觉引导线 ---
        position: relative;
        &::before {
          content: '';
          position: absolute;
          left: 0.35rem; // 调整线的位置
          top: 0.5rem;
          bottom: 0.5rem;
          width: 1px;
          background-color: $global-border-color;
          [theme=dark] & { background-color: $global-border-color-dark; }
          [theme=black] & { background-color: $global-border-color-black; }
        }

        // Level 2
        > li > a::before {
          content: "○"; // 使用空心圆点
        }

        // Level 3 (如果需要)
        ul > li > a::before {
          content: "◌"; // 使用更小的空心圆点
        }
      }
    }
  }

  // --- ruby 样式 ---
  ruby {
    background: $code-background-color;

    rt {
      color: $global-font-secondary-color;
    }

    [theme=dark] & {
      background: $code-background-color-dark;

      rt {
        color: $global-font-secondary-color-dark;
      }
    }
    [theme=black] & {
      background: $code-background-color-black;

      rt {
        color: $global-font-secondary-color-black;
      }
    }
  }
}

// --- 浮动目录 ---
#toc-auto {
  display: block;
  position: absolute;
  width: $MAX_LENGTH;
  max-width: 0;
  // --- 优化: 增加内边距, 让内容有呼吸空间 ---
  padding: 0 1rem;
  border-left: 3px solid $global-border-color; // 边框稍微细一点
  @include overflow-wrap(break-word);
  box-sizing: border-box;
  top: 10rem;
  left: 0;
  visibility: hidden;

  // --- 新增: 半透明背景, 提升可读性 ---
  border-radius: 0 8px 8px 0; // 右侧添加圆角
  @include blur;

  [theme=dark] & {
    border-left-color: $global-border-color-dark;
  }
  [theme=black] & {
    border-left-color: $global-border-color-black;
  }

  [header-desktop=normal] & {
    top: 5rem;
  }

  .toc-title {
    margin: 1rem 0;
  }

  .toc-content {
    ul {
      // --- 优化: 为 active 状态的伪元素也应用高亮颜色 ---
      a.active {
        font-weight: bold;
        color: $single-link-hover-color; // 使用 hover 颜色以示强调
        [theme=dark] & { color: $single-link-hover-color-dark; }
        [theme=black] & { color: $single-link-hover-color-black; }

        &::before {
          color: $single-link-hover-color;
          [theme=dark] & { color: $single-link-hover-color-dark; }
          [theme=black] & { color: $single-link-hover-color-black; }
        }
      }

      .has-active > ul {
        display: block;
      }

      ul {
        display: none;
      }
    }

    &.always-active ul {
      display: block;
    }

    > nav > ul {
      margin: .625rem 0;
    }
  }
}

// --- 静态目录 (页面内) ---
#toc-static {
  display: none;
  margin: 1.5rem 0; // 增加上下边距
  border: 1px solid $global-border-color; // --- 优化: 使用边框代替背景色, 更简洁
  border-radius: 8px;
  overflow: hidden; // 配合圆角

  [theme=dark] & { border-color: $global-border-color-dark; }
  [theme=black] & { border-color: $global-border-color-black; }

  &[kept=true] {
    display: block;
  }

  .toc-title {
    display: flex;
    justify-content: space-between;
    line-height: 2em;
    padding: 0.5rem 1rem;
    background: $code-background-color;
    cursor: pointer; // --- 新增: 表明标题可点击

    [theme=dark] & { background: $code-background-color-dark; }
    [theme=black] & { background: $code-background-color-black; }
  }

  .toc-content {
    background-color: transparent; // --- 优化: 移除背景色, 使其与文章背景一致

    > nav > ul {
      margin: 0;
      padding: .8rem 1rem .8rem 1rem; // 统一内边距
    }
  }
}