Stack 主题用了半年,这些地方我都改过
Stack 这个主题,默认配置出来的样子已经挺好了。我用它就是因为开箱即用,不用折腾。 但用久了总会想改点东西。有些是审美上的,有些是功能上的,有些纯粹是强迫症。 改的时候我一直坚持一个原则,能不碰主题源码就不碰。因为主题是 submodule,你一改,下次更新就冲突。 所以下面的改动,全部通过 Hugo 的覆盖机制实现。要么是配置,要么是在 layouts 下面放同名的文件。 先搞清楚覆盖是怎么工作的 Hugo 查找模板的顺序是,项目根目录的 layouts 优先,然后是主题的 layouts。 所以你想改主题里的 layouts/partials/footer.html,不需要去 themes/hugo-theme-stack/layouts/partials/ 改,只需要在项目的 layouts/partials/ 下放一个同名文件,Hugo 会自动用你的。 这个机制叫 template lookup order,官方文档里有完整的顺序表。 配置也一样。主题的 exampleSite/config/_default/ 是默认值,你在自己的 config/_default/ 里写同名文件,就是覆盖。 首页的副标题 Stack 的首页左侧有个大标题和一段描述,来自 config/_default/params.toml: 1 2 3 4 [sidebar] emoji = "���" subtitle = "记录一些折腾的过程" avatar = "img/avatar.png" emoji 会显示在标题旁边。subtitle 是标题下面那行小字。 avatar 的路径是相对于 assets 目录的。所以你要把头像放在 assets/img/avatar.png,不是 static。 这一点容易搞混。Stack 用的是 Hugo Pipes 处理图片,assets 下的东西才能被处理。 社交图标 1 2 3 4 5 6 7 8 9 10 11 [[params.social]] name = "github" icon = "brand-github" weight = 10 url = "https://github.com/yourname" [[params.social]] name = "rss" icon = "rss" weight = 20 url = "/index.xml" icon 用的是 Tabler Icons 的名字。完整列表在 tabler-icons.io 上,找的时候去掉前面的 brand- 或者 outline- 前缀看。 weight 决定顺序,小的在前。 侧边栏的菜单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [[menu.main]] identifier = "home" name = "首页" url = "/" weight = 10 [[menu.main]] identifier = "archives" name = "归档" url = "/archives/" weight = 20 [[menu.main]] identifier = "search" name = "搜索" url = "/search/" weight = 30 注意 url 结尾的斜杠。Hugo 的 baseURL 带斜杠,页面 URL 也带,不加斜杠会导致相对路径解析出错。 归档页和搜索页需要在 content/page/ 下面建对应的目录,不然点了 404。 代码块的样式 Stack 的代码高亮用的是 Chroma。默认的配色是 monokai,我觉得在亮色背景下太刺眼。 改成 github: 1 2 3 4 5 6 7 8 [markup.highlight] noClasses = false codeFences = true guessSyntax = false lineNos = false lineNumbersInTable = false tabWidth = 4 style = "github" noClasses = false 这个设置很关键。它让 Chroma 输出 class 而不是内联样式,这样你才能用自己的 css 覆盖配色。 guessSyntax = false 是我改的。默认是 true,它会猜代码块的语言,猜错了反而难看。显式写语言更可靠。 暗色模式下的配色,Stack 有单独的处理。在 assets/scss/custom.scss 里: 1 2 3 4 5 6 7 [data-scheme="dark"] { .highlight { .chroma { background-color: #1e1e1e; } } } 代码复制按钮 Stack 本身没带这个功能,但实现起来不难。 先加一个 partial,layouts/partials/article/components/copy-button.html: 1 2 3 4 5 6 <button class="copy-code-button" aria-label="复制代码"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <rect x="9" y="9" width="13" height="13" rx="2"></rect> <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path> </svg> </button> 然后在 assets/js/custom.js 里加事件: 1 2 3 4 5 6 7 8 9 10 11 12 document.querySelectorAll('pre > code').forEach((codeBlock) => { const button = document.createElement('button'); button.className = 'copy-code-button'; button.textContent = '复制'; button.addEventListener('click', async () => { await navigator.clipboard.writeText(codeBlock.innerText); button.textContent = '已复制'; setTimeout(() => { button.textContent = '复制'; }, 1500); }); codeBlock.parentElement.style.position = 'relative'; codeBlock.parentElement.appendChild(button); }); navigator.clipboard 需要 https,http 下是 undefined。本地开发用 localhost 是可以的,因为浏览器把 localhost 当安全上下文。 Stack 会自动加载 assets/js/custom.js,不用额外配置。这个约定挺方便的。 目录的样式 Stack 的 TOC 在文章右侧,宽屏下才显示。我改了一下它的位置和宽度。 在 assets/scss/custom.scss 里: 1 2 3 4 5 6 7 8 @media (min-width: 1280px) { .article-toc { position: sticky; top: 2rem; max-height: calc(100vh - 4rem); overflow-y: auto; } } position: sticky 让目录在滚动的时候跟着走,但滚到底部就停住。这个体验比固定定位好,不会挡住页脚。 页脚的备案号 国内的站需要放备案号,链接到工信部的网站。 layouts/partials/footer/footer.html: 1 2 3 4 5 6 7 8 9 10 11 12 13 <footer class="footer"> <div class="container"> <div class="footer-content"> {{- with .Site.Params.footer.since }} <span>© {{ . }} - {{ now.Year }}</span> {{- end }} <span>{{ .Site.Params.footer.copyright }}</span> <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener"> 京ICP备xxxxxxxx号 </a> </div> </div> </footer> rel="noopener" 一定要加。外链带 target="_blank" 而不加 noopener,被链接的页面可以通过 window.opener 操作你的页面,这是个经典的安全问题。 文章卡片的摘要长度 Stack 默认取文章的前若干字符做摘要。这个长度可以在 front matter 里用 description 控制,也可以在配置里全局设: 1 2 [params.article] summary = "auto" auto 是自动截取。如果你想让每篇文章用自己的 description,设成 "description"。 我觉得用 description 更好,因为自动截取经常截在奇怪的地方,句子断在半截。 暗色模式的默认值 Stack 默认跟随系统。但我想让它默认是暗色。 assets/js/custom.js: 1 2 3 4 5 6 7 document.addEventListener('DOMContentLoaded', () => { const saved = localStorage.getItem('prefers-color-scheme'); if (!saved) { document.documentElement.dataset.scheme = 'dark'; localStorage.setItem('prefers-color-scheme', 'dark'); } }); Stack 自己用 localStorage 存用户的选择,键名是 prefers-color-scheme。我们先检查有没有存过,没存过就设成 dark。 这个改动要小心,因为它会在页面渲染之后才执行,可能有一瞬间的闪烁。要彻底解决得在 <head> 里内联一段脚本,但那就要改 head.html 了。 归档页的分组 Stack 的归档页默认按年份分组,这个我觉得挺好的,没改。 但我加了一个显示每年文章数的功能。 layouts/partials/page/archive.html 里,找到渲染年份的地方,改成: 1 2 3 4 <h2 class="archive-year"> {{ .Key }} <span class="archive-count">{{ len .Pages }}</span> </h2> .Pages 是 Hugo 的分组对象里的字段,包含这一组的所有页面。len 拿到数量。 搜索 Stack 内置了搜索,用的是 fuse.js 加一个生成的 index.json。 需要在配置里开: 1 2 3 [params] [params.search] enable = true 然后建一个 content/page/search/index.md,fro
以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。

登录后,加入这场讨论。
登录参与