今天要解的問題
每篇文章的 front matter 都有 tags,Hugo 會幫每個 tag 產生一頁 /tags/<tag>/,然後 /tags/ 會把全部 tag 和各自的篇數列出來。
但 tag 我沒有很認真,甚至會把作者、譯者、出版社、出版年月都當 tag,這導致有 159 個 tag,其中 115 個只有一篇。

這導致 /tags 大部分是右上角掛著 1 的 tag,完全是視覺干擾啊,反而將有多個 tag 的文章給沈沒在裡面了。
技術背景
tag 頁和 tag 總覽是兩回事
這個需求做得到,靠的是 Hugo 把兩件事分開:
- 每個 tag 的頁面
/tags/<tag>/:只要有文章用了這個 tag,Hugo 就會產生,跟總覽列不列它無關。 - 總覽
/tags/:taxonomy 的 terms 頁,用的是主題的themes/PaperMod/layouts/_default/terms.html,它對.Data.Terms.Alphabetical逐一range,每個 term 印出名稱和.Count(篇數)。
所以「不顯示」只需要改總覽的模板,tag 頁本身不用動。模板查找會先找站台自己的 layouts/<taxonomy>/terms.html,再退回主題的 _default/terms.html,所以放一個 layouts/tags/terms.html 就只會蓋掉 tags,/categories/ 和 /series/ 不受影響(/series/ 本來就有自己的 layouts/series/terms.html)。
另外,hugo.yml 早就把 /tags/** 設成 robotsNoIndex、也排除在 sitemap 外。tag 頁本來就不進搜尋索引,這次的調整只影響讀者在總覽上看到的清單,跟 SEO 無關。
想法與取捨
「少於 1」要怎麼解讀
照字面看,「少於 1 篇」就是 0 篇,但 Hugo 本來就不會替沒有文章的 tag 產生任何東西,照字面做等於什麼都沒改。所以寫規格時把它解讀成「只有 1 篇文章的 tag 不顯示」,寫明在規格裡,我審過才開始做。
為什麼預設是 2
門檻放在 2,是因為一個 tag 至少要有兩篇文章,點進去才看得到「彼此有關的幾篇」;只有一篇的 tag,tag 頁就只是那篇文章換個入口。
但 2 不是唯一合理的值,所以門檻沒有寫死在模板裡,而是放在 hugo.yml 的 params.tagsMinCount。之後覺得清單還是太長,改成 3 只要改一個數字。我順手試了一次:門檻 3 時總覽剩 24 個 tag。
單篇 tag 的頁面要留著
隱藏只發生在總覽。單篇 tag 的 /tags/<tag>/ 照樣產生,因為文章底下的 tag chip 還是會連過去,頁面拿掉就變成一堆 404。同樣的理由,這次不動任何文章的 tags front matter,也不隱藏文章頁底下的 tag:作者、出版社這種 tag 放在書評底下仍然有意義,只是不值得在總覽佔一格。
這個做法的代價是 layouts/tags/terms.html 是主題檔案的複本:PaperMod 之後改了自己的 terms.html,這份不會跟著變。
實作
改動只有兩個檔案。第一個是 layouts/tags/terms.html,從 PaperMod 的 terms.html 複製過來,只多了讀門檻和一個 if:
{{- $min := site.Params.tagsMinCount | default 2 }}
<ul class="terms-tags">
{{- $type := .Type }}
{{- range $key, $value := .Data.Terms.Alphabetical }}
{{- $name := .Name }}
{{- $count := .Count }}
{{- if ge $count $min }}
{{- with site.GetPage (printf "/%s/%s" $type $name) }}
<li>
<a href="{{ .Permalink }}">{{ .Name }} <sup><strong><sup>{{ $count }}</sup></strong></sup> </a>
</li>
{{- end }}
{{- end }}
{{- end }}
</ul>
| default 2 讓設定檔沒寫這個參數時一樣有合理的行為;ge $count $min 是「篇數大於等於門檻才列」。排序沿用主題的 .Data.Terms.Alphabetical,跟原本一樣按字母排,剩下的 tag 位置不會亂跳。
第二個是 hugo.yml 的 params 多兩行:
# /tags/ 總覽只列出篇數 ≥ 這個值的 tag(單篇 tag 頁照樣產生)
tagsMinCount: 2
要試別的門檻,不用改檔案。Hugo 會把 HUGO_PARAMS_ 開頭的環境變數蓋到 params 上,所以用環境變數就能跑一次:
HUGO_PARAMS_TAGSMINCOUNT=3 hugo --minify
改完重新 build,總覽從 159 個變成 44 個,剛好是 159 − 115,清單裡篇數 1 的 tag 是 0 個。總覽裡的 tag 數跟單篇 tag 頁都數過一次:
$ grep -o '<sup><strong><sup>' public/tags/index.html | wc -l
44
$ ls public/tags/amis/index.html
public/tags/amis/index.html
amis 是一個只有一篇文章的 tag,總覽裡已經沒有它,但從那篇咖啡課筆記底下點 amis,還是會開到它自己的頁面:

手機寬 375px 也看過,tag 少了只是行數變少,沒有橫向捲動。
踩到的坑
壓縮過的 HTML 只有一行
原本的驗證步驟寫的是用 grep -c 數總覽有幾個 tag。實際跑出來是 1。
hugo --minify 會把整頁壓成一行,而 grep -c 數的是「有幾行符合」,不是符合幾次。改成 grep -o 把每個符合的片段各印一行,再交給 wc -l 數,才得到 44。
帶斜線的 tag 會變成兩層目錄
確認「單篇 tag 頁還在」時,我用總覽列出的 tag 跟 public/tags/ 底下的目錄做差集,想挑一個沒被列出的 tag 當樣本,挑到的是 2023。結果 public/tags/2023/index.html 不存在,public/tags/2023/ 底下只有一個 04/ 目錄。
回頭找,是一篇書評的 tags 裡寫了出版年月 2023/04。Hugo 照著斜線把它拆成 /tags/2023/04/ 兩層路徑,改前的總覽上它顯示成 04,就排在最前面。2023 不是 tag,只是目錄。換成 amis 這種單純的 tag 當樣本,驗證就過了。這個 tag 只有一篇,改完之後也不會出現在總覽。
hugo server 沒有跟上
為了截改前的圖,我把 layouts/tags/ 暫時移開,開著的 hugo server 立刻變回 159 個;截完把目錄搬回來,它卻還是 159 個。新出現的 layout 目錄 hugo server 沒有接到。截圖是在搬走之前就先截好改後的版本,所以沒受影響,但如果是邊改邊看,就會以為改動沒生效。這種時候要停掉 server、清掉 public/ 和 resources/ 再重開,或直接看 hugo --minify 的輸出。
小結
/tags/ 總覽現在只列篇數 2 以上的 tag,從 159 個縮到 44 個,claude-code、hugo、llm-wiki 這些真的能串起好幾篇文章的 tag 終於看得到。單篇 tag 的頁面照樣存在,文章底下的 tag 一個都沒少,門檻要改只要動 hugo.yml 的一個數字。
這次能只改一個模板就做完,是因為 Hugo 本來就把「tag 頁」和「tag 總覽」分成兩件事:要藏的只是入口,不是內容。
