Featured image of post Hugo Stack 主题魔改及维护

Hugo Stack 主题魔改及维护

我是如何魔改 Hugo Stack 主题以及如何维护相关仓库的

前言

所有修改都采用 Hugo 的主题覆盖机制,不直接修改主题文件,确保主题可以随时更新而不影响自定义内容。


基础知识:理解 Hugo 主题覆盖机制

Hugo 的文件查找顺序

Hugo 在查找模板和资源文件时,会按以下顺序查找:

1. 项目根目录/layouts/          (最高优先级)
2. 项目根目录/themes/主题名/layouts/
3. 项目根目录/assets/           (最高优先级)
4. 项目根目录/themes/主题名/assets/

原理:只需在项目根目录创建与主题相同路径的文件,就可以覆盖主题的默认文件。

使用 Git 子模块管理主题

添加主题子模块:

git submodule add https://github.com/CaiJimmy/hugo-theme-stack themes/hugo-theme-stack

优势:

  • 主题和自定义修改完全分离
  • 可以随时更新主题:git submodule update --remote
  • 版本控制清晰

全局样式定制方法

创建自定义样式文件

步骤 1:在项目根目录创建 assets/scss/custom.scss 文件

// assets/scss/custom.scss
// 这个文件会被 Hugo 自动编译并添加到页面中

步骤 2:确保 Hugo 配置中启用了自定义样式

主题会自动查找并加载 assets/scss/custom.scss,无需额外配置。

使用 CSS 变量统一管理样式

方法:在 :root 中定义全局变量

:root {
  // 间距和尺寸
  --main-top-padding: 30px;
  --card-border-radius: 10px;
  --tag-border-radius: 8px;
  --section-separation: 40px;

  // 字体大小
  --article-font-size: 1.8rem;

  // 颜色定义
  --other-color: #e99312;                // 主题强调色
  --code-background-color: #f8f8f8;      // 代码背景色
  --code-text-color: var(--other-color); // 代码文字颜色
}

为什么使用 CSS 变量?

  • 统一管理样式,修改一处即可全局生效
  • 支持暗色模式切换
  • 便于维护和调试

实现暗色模式适配

方法:在 :root 中添加暗色模式条件

:root {
  // 浅色模式样式
  --code-background-color: #f8f8f8;
  --code-text-color: var(--other-color);

  // 暗色模式样式
  &[data-scheme="dark"] {
    --code-background-color: #ff6d1b17;
    --code-text-color: var(--other-color);
  }
}

修改文字选中效果

需求:让选中的文字有更明显的视觉反馈

::selection {
  color: #fff;              // 选中文字颜色
  background: #34495e;      // 选中背景色
}

提示:可以使用主题色或品牌色,增强品牌识别度。

优化链接和代码的换行

问题:在移动端,长链接和代码会导致页面横向滚动

解决方法

// 修复引用块和代码在窄屏幕显示问题
a {
  word-break: break-all;     // 允许在任意字符间断行
}

code {
  word-break: break-all;
}

注意break-all 可能会在不理想的位置断行,如果需要更智能的断行,可以使用 word-wrap: break-word

定制文章列表封面高度

需求:让封面图高度在不同屏幕尺寸下都有合适的显示

方法:使用响应式断点

.article-list article .article-image img {
  width: 100%;
  height: 200px !important;          // 移动端
  object-fit: cover;                 // 裁剪图片以填充区域

  @include respond(md) {             // 平板
    height: 250px !important;
  }

  @include respond(xl) {             // 桌面
    height: 285px !important;
  }
}

Stack 主题的响应式断点:

  • sm: < 768px
  • md: ≥ 768px
  • lg: ≥ 1024px
  • xl: ≥ 1280px

为文章内图片添加圆角和自适应

方法

.article-page .main-article .article-content {
  img {
    max-width: 96% !important;       // 略小于容器,留出边距
    height: auto !important;         // 自动高度保持比例
    border-radius: 8px;              // 圆角
  }
}

美化引用块样式

方法

.article-content {
  blockquote {
    border-radius: 10px;                  // 整体圆角
    margin-left: -8px;                    // 轻微左移
    max-width: 102%;                      // 略超出容器
    // 左侧彩色边框
    border-inline-start: var(--blockquote-border-size) solid var(--accent-color);
  }
}

美化全局滚动条

方法一:隐藏滚动条(如菜单)

.menu::-webkit-scrollbar {
  display: none;
}

方法二:定制滚动条样式

html {
  ::-webkit-scrollbar {
    width: 20px;                          // 滚动条宽度
  }

  ::-webkit-scrollbar-track {
    background-color: transparent;        // 轨道透明
  }

  ::-webkit-scrollbar-thumb {
    background-color: #d6dee1;           // 滑块颜色
    border-radius: 20px;                 // 圆角滑块
    border: 6px solid transparent;       // 内边距效果
    background-clip: content-box;        // 背景裁剪
  }

  ::-webkit-scrollbar-thumb:hover {
    background-color: #a8bbbf;          // 悬停时加深
  }
}

代码块工具栏功能增强

需求分析

目标:为每个代码块添加工具栏,显示编程语言并提供一键复制功能。

效果预览

┌─────────────────────────────────┐
│ JAVASCRIPT           ✂ Copy     │ ← 工具栏
├─────────────────────────────────┤
│ console.log('Hello World')      │ ← 代码
└─────────────────────────────────┘

添加工具栏样式

步骤 1:覆盖主题的文章样式文件

在项目根目录创建 assets/scss/partials/layout/article.scss

步骤 2:添加工具栏样式

.article-content {
  .highlight {
    max-width: 102% !important;
    padding: 0px;
    position: relative;
    border-radius: 8px;
    margin-left: -8px !important;
    margin-right: -2px;
    box-shadow: var(--shadow-l1) !important;

    // 工具栏样式
    .toolbar {
      position: relative;
      width: 100%;
      height: 30px;
      background-color: var(--scrollbar-thumb);  // 使用主题变量
      z-index: 10;
      display: flex;
      justify-content: space-between;           // 两端对齐
      align-items: center;
      padding: 6px 16px;
      border-top-left-radius: 8px;
      border-top-right-radius: 8px;             // 只有顶部圆角

      // 语言标签按钮
      .languageTagButton {
        background: none;
        border: none;
        font-size: 12px;
        font-family: var(--code-font-family);
        color: var(--code-button-color);
        cursor: default;                         // 不可点击
      }

      // 复制按钮
      .copyCodeButton {
        background: none;
        border: none;
        color: var(--code-button-color);
        font-size: 12px;
        cursor: pointer;                         // 可点击
      }
    }

    // 代码区域样式
    pre {
      margin: initial;
      padding: 15px 25px;
      margin: 0;
      width: auto;
      border-radius: 0 0 8px 8px;                // 只有底部圆角
    }
  }
}

实现 JavaScript 功能

步骤 1:覆盖主题的 JavaScript 文件

在项目根目录创建 assets/ts/main.ts

步骤 2:复制主题原有的 main.ts 内容

首先从 themes/hugo-theme-stack/assets/ts/main.ts 复制所有代码。

步骤 3:在初始化函数中添加代码块增强功能

Stack.init() 函数中添加:

/**
 * 为代码块添加复制按钮和语言标签
 */
const highlights = document.querySelectorAll('.article-content div.highlight');
const copyText = '✂ Copy';
const copiedText = '✔ Copied';

highlights.forEach(highlight => {
  const codeBlock = highlight.querySelector('code[data-lang]');
  if (!codeBlock) return;

  // 获取并处理语言标签
  const rawLang = codeBlock.getAttribute('data-lang');
  const lang = (rawLang === "fallback" || !rawLang) ? "TEXT" : rawLang.toUpperCase();

  // 创建语言标签
  const langTag = createButton(lang, 'languageTagButton');

  // 创建复制按钮
  const copyButton = createButton(copyText, 'copyCodeButton');

  // 创建工具栏容器
  const toolbar = document.createElement('div');
  toolbar.classList.add('toolbar');
  toolbar.appendChild(langTag);
  toolbar.appendChild(copyButton);

  // 将工具栏插入到代码块前面
  highlight.insertBefore(toolbar, highlight.firstChild);

  // 绑定复制功能
  copyButton.addEventListener('click', () => {
    navigator.clipboard.writeText(codeBlock.textContent)
      .then(() => {
        copyButton.textContent = copiedText;

        // 1秒后恢复按钮文字
        setTimeout(() => {
          copyButton.textContent = copyText;
        }, 1000);
      })
      .catch(err => {
        alert(err);
        console.error('Copy failed:', err);
      });
  });
});

// 辅助函数:创建按钮
function createButton(text, className) {
  const button = document.createElement('button');
  button.innerHTML = text;
  button.classList.add(className);
  return button;
}

代码解析:

  1. 选择所有代码块querySelectorAll('.article-content div.highlight')
  2. 获取语言信息:从 data-lang 属性读取,Hugo 会自动添加
  3. 处理特殊情况:如果语言为 fallback 或空,显示为 TEXT
  4. 使用 Clipboard APInavigator.clipboard.writeText() 实现复制
  5. 用户反馈:复制成功后改变按钮文字 1 秒

兼容性:Clipboard API 需要 HTTPS 或 localhost 环境。


页添加欢迎横幅动画

创建动画效果

步骤 1:在 custom.scss 中定义动画

动画一:握手摇摆动画

.shake {
  display: inline-block;
  animation: shake 1s;
  animation-delay: 2s;              // 延迟2秒开始
}

@keyframes shake {
  0% {
    transform: rotate(0);
  }
  25% {
    transform: rotate(45deg) scale(1.2);
  }
  50% {
    transform: rotate(0) scale(1.2);
  }
  75% {
    transform: rotate(45deg) scale(1.2);
  }
  100% {
    transform: rotate(0);
  }
}

动画二:文字跳跃动画

// 基础跳跃动画
@keyframes jump {
  0% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-20px);    // 向上跳20px
  }
  100% {
    transform: translateY(0);
  }
}

// 创建9个不同延迟的类
.jump-text1 {
  display: inline-block;
  animation: jump .5s 1;
  animation-delay: 0s;
}

.jump-text2 {
  display: inline-block;
  animation: jump .5s 1;
  animation-delay: .1s;             // 递增延迟
}

// ... 依此类推到 .jump-text9 (延迟0.9s)

动画三:欢迎卡片样式

.welcome {
  color: var(--card-text-color-main);
  background: var(--card-background);
  box-shadow: var(--shadow-l2);
  border-radius: var(--card-border-radius);
  display: inline-block;
}

修改首页模板

步骤 1:覆盖首页模板

在项目根目录创建 layouts/index.html

步骤 2:复制主题原有代码

themes/hugo-theme-stack/layouts/index.html 复制所有代码。

步骤 3:在文章列表前添加欢迎横幅

{{ define "main" }}
    {{ $pages := where .Site.RegularPages "Type" "in" .Site.Params.mainSections }}
    {{ $notHidden := where .Site.RegularPages "Params.hidden" "!=" true }}
    {{ $filtered := ($pages | intersect $notHidden) }}
    {{ $pag := .Paginate ($filtered) }}

    <!-- 首页欢迎横幅 -->
    <div class="welcome">
      <p style="font-size: 2rem; text-align: center; font-weight: bold">
        <span class="shake">👋</span>
        <span class="jump-text1">Welcome</span>
        <span class="jump-text2">&nbsp;To&nbsp;</span>
        <span class="jump-text3" style="color:#e99312">K</span>
        <span class="jump-text4" style="color:#e99312">e</span>
        <span class="jump-text5" style="color:#e99312">y</span>
        <span class="jump-text6" style="color:#e99312">e</span>
        <span class="jump-text7" style="color:#e99312">'</span>
        <span class="jump-text8" style="color:#e99312">s&nbsp;</span>
        <span class="jump-text9" style="color:#e99312">Blog</span>
      </p>
    </div>
    <!-- 首页欢迎横幅 -->

    <section class="article-list">
        {{ range $index, $element := $pag.Pages }}
            {{ partial "article-list/default" . }}
        {{ end }}
    </section>

    {{- partial "pagination.html" . -}}
    {{- partial "footer/footer" . -}}
{{ end }}

Waline 评论系统优化

// Waline 主题颜色变量
:root {
  --waline-theme-color: var(--accent-color) !important;
  --waline-active-color: var(--other-color) !important;
  --waline-color: var(--card-text-color-secondary) !important;
}

// 移除评论面板外边距,实现无背景效果
.wl-panel {
  margin: 0px !important;
}

响应式布局优化

调整页面三栏布局宽度

需求:在不同屏幕尺寸下优化左侧边栏、主内容区、右侧边栏的宽度比例。

方法:在 custom.scss 中覆盖布局样式

.container {
  margin-left: auto;
  margin-right: auto;

  .left-sidebar {
    order: -3;
    max-width: var(--left-sidebar-max-width);
  }

  .right-sidebar {
    order: -1;
    max-width: var(--right-sidebar-max-width);

    @include respond(lg) {
      display: flex;
    }
  }

  // 扩展布局(三栏)
  &.extended {
    @include respond(md) {
      max-width: 1024px;
      --left-sidebar-max-width: 25%;
      --right-sidebar-max-width: 22% !important;
    }

    @include respond(lg) {
      max-width: 1280px;
      --left-sidebar-max-width: 20%;
      --right-sidebar-max-width: 30%;
    }

    @include respond(xl) {
      max-width: 1453px;           // 自定义最大宽度
      --left-sidebar-max-width: 15%;
      --right-sidebar-max-width: 25%;
    }
  }

  // 紧凑布局(两栏)
  &.compact {
    @include respond(md) {
      --left-sidebar-max-width: 25%;
      max-width: 768px;
    }

    @include respond(lg) {
      max-width: 1024px;
      --left-sidebar-max-width: 20%;
    }

    @include respond(xl) {
      max-width: 1280px;
    }
  }
}

实现归档页面双栏布局

需求:在大屏幕上,归档页面使用网格布局显示为两栏。

方法

@media (min-width: 1024px) {
  .article-list--compact {
    display: grid;
    grid-template-columns: 1fr 1fr;        // 两等宽列
    background: none;
    box-shadow: none;
    gap: 1rem;                              // 列间距

    article {
      background: var(--card-background);
      border: none;
      box-shadow: var(--shadow-l2);
      margin-bottom: 8px;
      border-radius: 16px;
    }
  }
}

实现链接页面三栏布局

需求:友情链接页面在大屏幕上显示为三栏。

方法

@media (min-width: 1024px) {
  .article-list--compact.links {           // 注意 .links 类名
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;    // 三等宽列
    background: none;
    box-shadow: none;
    gap: 1rem;

    article {
      background: var(--card-background);
      border: none;
      box-shadow: var(--shadow-l2);
      margin-bottom: 8px;
      border-radius: var(--card-border-radius);

      &:nth-child(odd) {
        margin-right: 8px;
      }
    }
  }
}

使用方法:

在友情链接页面的 Markdown 前言中添加:

---
title: "友情链接"
links: true                # 启用 links 布局
layout: "archives"         # 使用归档布局
---

优化移动端间距

方法

.main-container {
  min-height: 100vh;
  align-items: flex-start;
  padding: 0 15px;                      // 移动端较小边距
  gap: var(--section-separation);
  padding-top: var(--main-top-padding);

  @include respond(md) {
    padding: 0 37px;                   // 平板和桌面较大边距
  }
}

维护和更新

更新主题

步骤 1:进入主题子模块目录

cd themes/hugo-theme-stack

步骤 2:拉取最新代码

git pull origin master

步骤 3:返回项目根目录并提交更新

cd ../..
git add themes/hugo-theme-stack
git commit -m "Update hugo-theme-stack theme"

注意事项:

  • 更新前先备份自定义文件
  • 查看主题的 CHANGELOG,了解重大变更
  • 更新后测试网站功能是否正常

常见问题排查

问题 1:自定义样式不生效

# 清除 Hugo 缓存
hugo mod clean

# 重新构建
hugo server --disableFastRender

问题 2:图标不显示

检查:

  • 文件路径是否正确(assets/icons/图标名.svg
  • SVG 文件是否有效
  • 配置中的图标名是否与文件名匹配(不含 .svg 后缀)

问题 3:代码块复制功能失效

检查:

  • 是否使用 HTTPS 或 localhost(Clipboard API 要求)
  • 浏览器控制台是否有 JavaScript 错误
  • main.ts 是否正确编译

问题 4:评论系统不加载

检查:

  • Waline 服务端是否正常运行
  • serverURL 配置是否正确
  • 浏览器控制台是否有网络错误
  • CDN 地址是否可访问

性能优化建议

1. 图片优化

# 使用 WebP 格式
# 使用图片压缩工具(TinyPNG、ImageOptim)
# 合理设置图片尺寸

2. CDN 配置

hugo.yaml 中配置 CDN:

params:
  cdn:
    # 使用七牛云、又拍云等 CDN 加速静态资源

3. 启用缓存

caches:
  getjson:
    maxAge: 24h
  getcsv:
    maxAge: 24h
  images:
    maxAge: 720h

版本控制最佳实践

提交信息规范:

# 功能添加
git commit -m "feat: add welcome banner animation"

# 样式修改
git commit -m "style: adjust article image border radius"

# 修复问题
git commit -m "fix: code copy button not working on mobile"

# 文档更新
git commit -m "docs: update theme modification guide"

分支管理:

# 开发新功能使用分支
git checkout -b feature/new-layout

# 测试通过后合并
git checkout main
git merge feature/new-layout

附录:快速参考

常用 CSS 变量

--card-background              // 卡片背景色
--card-text-color-main         // 主要文字颜色
--card-text-color-secondary    // 次要文字颜色
--card-text-color-tertiary     // 三级文字颜色
--accent-color                 // 主题强调色
--accent-color-darker          // 深色强调色
--card-border-radius           // 卡片圆角
--shadow-l1, --shadow-l2       // 阴影效果
--card-padding                 // 卡片内边距

响应式混入

@include respond(sm) { /* < 768px */ }
@include respond(md) { /* ≥ 768px */ }
@include respond(lg) { /* ≥ 1024px */ }
@include respond(xl) { /* ≥ 1280px */ }

常用 Hugo 模板函数

{{ .Title }}                    // 标题
{{ .Content }}                  // 内容
{{ .Params.custom }}            // 自定义参数
{{ with .Site.Params.foo }}     // 条件判断
{{ range .Pages }}              // 循环页面
{{ partial "name" . }}          // 引入局部模板

祝你打造出独特的个人博客!