今天要解的問題
kiwi-walk 是我用 Hugo 架的部落格,Kiwi 的每日筆記是上面的每日連載。從鐵人賽開始,每篇標題都長這樣:Day31 [llm-wiki] 模型換了,設定還停在上個月:…。

標題會原封不動變成 <title>,也就是搜尋結果裡那一行。最前面的 Day31 [llm-wiki] 佔掉最顯眼的位置,可是沒有人會去搜「Day31」。我想把它拿掉,但又不想丟掉 Day 編號:在系列裡,Day 是這條連載的骨架。當時我的說法是:
想要標題去掉 dayx,但卻同時希望能在系列識別 dayx 的資訊,只不過只有在 series 會有。不確定會不會導致越來越複雜。
所以這次的條件是:標題乾淨、Day 只在系列的脈絡出現,而且不要多一套要手動維護的東西。
想法與取捨
專案名要不要一起拿掉
一開始我選了保留 [llm-wiki] 這類專案前綴,只拿掉 Day。後來想到它可以跟 Day 用同一招:每篇工程文的 front matter 本來就有 project: llm-wiki,文章頁也已經用它顯示「專案:LLM Wiki →」的連結。既然資料已經在那裡,標題上的方括號就只是重複,於是兩個前綴一起拿掉,專案名改成列表上的小標籤。
Day 存在哪:tags、日期推算,還是既有欄位
我問過「這個可以用標籤嗎?」。如果「標籤」指的是 Hugo 的 tags(每篇加 tags: [Day35]),答案是不要:每個 Day 都會生出一個 /tags/day35/ 頁,裡面只有一篇文章。這種頁面對搜尋引擎是內容稀薄的頁,跟這次要做的 SEO 反方向;tags 列表也會被 Day31、Day32… 塞滿。
第二個想法是從日期算。連載的規則本來就是 Day N = 發文日 − 2026-09-14,所以可以在系列的 _index.md 放一個起算日,由模板算出編號。這樣不用每篇手填。
實際去看文章才發現更簡單的路:每一篇(鐵人賽 Day1–30 和之後的每日筆記)front matter 都已經有 day: N。模板直接讀它就好,不需要新欄位,也不需要起算日。原本 /series/ 卡片上的「Day30+7」反而是用 regex 從最新一篇的標題撈數字,改完之後改讀 day,等於少了一個依賴標題寫法的地方。這也回答了「會不會越來越複雜」:資料本來就在,只是換個地方顯示。
鐵人賽的舊標題要不要一起改
鐵人賽 Day1–30 的標題沒有動。所以模板要同時面對兩種標題:新的沒有前綴,舊的還是 Day2 [art-tracking] …。處理方式是在加 Day 標籤的地方,一律把標題開頭的 DayN 去掉;專案標籤則在標題已經含 [專案名] 時不加。兩個系列在系列頁上看起來就一致。
Day 放在卡片右上角,專案名不要框
第一版的 Day 和專案名都是標題前的小框標籤。在系列頁看過之後,我想要換個位置:「想要放在文章卡片的右上角,可以不要用標籤,可以純文字,然後 day 固定,x 較大一點」。所以系列頁卡片的 Day 改成右上角的純文字,「Day」用小字、數字用大字,又在看過之後要求數字「要比標題文字小」,最後定在 20px,比 24px 的卡片標題小一號。它和左上角的分類徽章在同一行,標題下面一行可以用滿整個寬度。
專案名則是「不要用太明顯的邊框」,拿掉底色,變成標題前一段淡棕色的粗體字,讀起來像前綴而不是按鈕。
封面下的系列導覽沒有跟著改:那是一行一篇的清單,沒有卡片可以放右上角,所以 Day 還是標題前的小標籤。
分類標籤與 archives
標題的前綴拿掉之後,列表卡片上方的分類(ENGINEER、BOOKS)只是一行灰色小字,存在感變得更低。我的要求是「希望 books, engineer 的標籤更明顯,或者直接用 logo」。每個分類在設定裡本來就有自己的 kiwi logo 和底色(分類頁的 hero 用的就是它們),所以直接把那一行字換成徽章:logo 加分類名,底色用分類色。
archives 頁也跟著改成一致。中間繞了一段:我先試了讓文章靠左、月份放上面,看過之後覺得「不要靠左比較好看」,就退回原本「月份在左、文章在右」的兩欄,只加強月與月的區隔:粗一點的分隔線、月份前加一條直線,並讓月份標題在捲動時停在畫面上。

實作
Day 標籤與專案標籤
兩個小 partial。Day 標籤只讀 day:
{{- with .Params.day }}<span class="series-day">Day{{ . }}</span>{{ end -}}
專案標籤讀 project,標題已經帶著 [專案名] 的舊文就不加:
{{- with .Params.project }}
{{- if not (strings.Contains $.Title (printf "[%s]" .)) }}<span class="project-tag">{{ . }}</span>{{ end }}
{{- end -}}
它刻意是 <span> 而不是連結:列表卡片整張被一個透明的 .entry-link 蓋住當作點擊區,裡面再放一個連結會疊在一起。要連到專案頁,文章頁原本的專案連結還在。
只在系列裡顯示 Day
封面下的系列導覽,每一列先放兩個標籤,再放去掉 DayN 前綴的標題:
<li>
{{- if eq .RelPermalink $page.RelPermalink }}
{{- partial "series-day.html" . }}{{ partial "project-tag.html" . }}
{{- $title := replaceRE `^Day\d+\s*` "" .Title }}
<span class="series-nav-current" aria-current="page">{{ $title }}</span>
{{- else }}
{{- partial "series-day.html" . }}{{ partial "project-tag.html" . }}
<a href="{{ .RelPermalink }}">{{ replaceRE `^Day\d+\s*` "" .Title }}</a>
{{- end }}
</li>
系列頁(/series/kiwi-daily/)共用一般的列表模板,所以在那個模板裡加一個判斷,只有系列的 term 頁才在卡片角落放 Day:
{{- $isSeries := and (eq .Kind "term") (eq .Data.Plural "series") }}
...
<article class="{{ $class }}">
{{- if $isSeries }}{{ with .Params.day }}
<div class="series-day-corner"><span class="series-day-corner-word">Day</span>{{ . }}</div>
{{- end }}{{ end }}
...
<h2>
{{- partial "project-tag.html" . }}
{{- if $isSeries }}{{ replaceRE `^Day\d+\s*` "" .Title }}{{ else }}{{ .Title }}{{ end }}
分類頁和首頁也會走到這段,但 $isSeries 是 false,只會出現專案名,不會出現 Day。卡片本身已經是 position: relative,角落的 Day 用絕對定位放上去:
.series-day-corner {
position: absolute;
top: var(--gap);
right: var(--gap);
color: var(--ink-soft-warm);
font-size: 20px; /* below the 24px card title */
font-weight: 700;
line-height: 1;
font-variant-numeric: tabular-nums;
}
.series-day-corner-word {
margin-right: 2px;
font-size: 12px;
font-weight: 500;
letter-spacing: 1px;
}

/series/ 卡片的連載天數從撈標題改成讀欄位:
{{- with $latest.Params.day }}
{{- $base := $s.Params.streakBase | default 0 }}
{{- $n := int . }}
<span class="series-accent-text">Day{{ $base }}+{{ sub $n $base }}</span>
{{- end }}
最後是內容本身:Day31–37 七篇的標題拿掉開頭的 DayNN 和 [專案名] ,網址不受影響,因為目錄名本來就不帶編號。
分類徽章
分類 partial 原本的 label 模式只輸出一行字,改成 logo 加名稱的徽章,顏色從設定讀進 CSS 變數:
{{- else if eq $mode "label" -}}
<span class="entry-category-badge"
style="--cat-accent: {{ $cat.accent }}; --cat-bg: {{ $cat.bg }}; --cat-bg-dark: {{ $cat.bgDark }};">
<span class="entry-category-badge-mark"><img src="{{ $logoURL }}" alt="" loading="lazy"/></span>
<span class="entry-category-badge-label">{{ $cat.displayName | default $key }}</span>
</span>
深色模式用 --cat-bg-dark。archives 一行一篇,徽章縮小一號(logo 18px)。
archives 的月份區隔
保留 PaperMod 的兩欄排版,只加樣式:
.archive-month { padding: 24px 0; }
.archive-month:not(:last-of-type) { border-bottom: 2px solid var(--border-warm); }
.archive-month-header {
margin: 10px 0;
padding-left: 12px;
border-left: 4px solid var(--ink-warm);
font-size: 20px;
}
@media (min-width: 769px) {
.archive-month-header { position: sticky; top: 16px; }
}
sticky 只在桌面版開:手機版 PaperMod 會把月份和文章改成上下排,月份標題跟著文章一起捲走比較自然。
踩到的坑
archives 的專案標籤被推到最左邊
第一次在 archives 加上專案標籤,畫面是標籤靠在最左邊、標題被推到右邊,中間空了一大段。原因是 archives 的標題元素是一個 flex 列,原本只有「標題文字」和「草稿標記」兩個子元素,用 space-between 撐開。新加的標籤變成第三個子元素,於是三個東西被平均攤開。修法是把標籤和標題包進同一個 <span>:
<span>{{ partial "project-tag.html" . }}{{ .Title | markdownify }}</span>
專案名和標題沒有置中
專案名拿掉底色之後,看起來比後面的標題字高一點。這種差距用眼睛調很難準,所以在瀏覽器裡量:用 canvas 的 measureText 拿到兩段文字實際筆畫的上下緣,再加上各自基線的位置,算出筆畫的中心。量出來專案名的中心比標題高約 1.5px。
原因是它還留著有底色時的設定:小框標籤用 vertical-align: 0.15em 往上抬,讓框看起來置中。框拿掉之後就不需要抬那麼多。把專案名自己的上移量改成 0.07em,再量一次,系列頁前幾張卡片的中心差都在 0.04px 以內:
.project-tag {
padding: 0;
margin-right: 8px;
vertical-align: 0.07em;
color: var(--ink-soft-warm);
font-weight: 600;
}
用 em 而不是 px,是因為專案名的字級是標題的 0.75 倍,在 archives 這類字比較小的列表也會等比例縮放。
dev server 給的是舊 CSS
改完月份的樣式,畫面完全沒變。在瀏覽器查 .archive-month-header 的計算後樣式:背景透明、padding 0,新規則一條都沒套上。另外跑一次 hugo 建置,查打包出來的 stylesheet,新規則確實在裡面,所以檔案沒寫錯,是開著的 hugo server 還在送舊的打包結果。刪掉 public/、resources/ 再重開 server 就正常了。
拿掉編號之後,搜尋找不到 Day35
收尾重讀一次改動時發現的。站內搜尋用 Fuse.js,索引欄位是 title、permalink、summary、content。編號原本只在標題裡,拿掉之後搜「Day35」就找不到那篇書評了。
修法是蓋掉 PaperMod 的搜尋索引模板,多存一個 day 欄位:
{{- $entry := dict "title" .Title "content" .Plain "permalink" .Permalink "summary" .Summary }}
{{- with .Params.day }}{{ $entry = merge $entry (dict "day" (printf "Day%v" .)) }}{{ end }}
{{- $.Scratch.Add "index" $entry -}}
再把 hugo.yml 的 fuseOpts.keys 加上 day:
keys: ["title", "day", "permalink", "summary", "content"]
改完在搜尋頁輸入 Day35,第一筆就是那篇書評。
同一輪也補了一件事:Day33 當初寫下的標題規則是「工程文維持 DayN [專案名] …」,現在不成立了。照這個部落格的慣例,不改舊文原句,而是在那條規則下面加一則日期註記,說明標題改成只放內容句。產生每日文章的寫作模板也一併改掉,之後的文章不會再把前綴加回來。
小結
每日連載的標題現在只剩內容句,<title> 和搜尋結果不再以 Day31 [llm-wiki] 開頭。Day 編號只在系列的地方出現:系列頁卡片的右上角、系列導覽和 /series/ 卡片;專案名在列表上是標題前的淡色字;分類變成帶 logo 的徽章;站內搜尋仍然找得到 Day35。
整件事沒有新增任何欄位,day 和 project 本來就在每篇的 front matter 裡。擔心「會不會越來越複雜」的時候,先看資料是不是早就在某個地方,往往比設計一套新的表示法省事。代價是鐵人賽的舊標題沒改,模板裡要多兩個針對舊格式的判斷。
