今天要解的問題

今天換回部落格本身。Day20 整理完 /projects/ 的卡片之後,Projects 在導覽列上還是一個獨立的項目,跟 Blogs 平起平坐。/blogs/ 那一排分類方塊裡,每個分類都有自己的奇異鳥插畫;Projects 不在其中,設定檔裡給它的是通用的吉祥物和一個灰綠色。

今天一開始丟給 Claude 的需求只有一句:

我想要把 project 整合到 blogs 區塊,讓她變成一個小方塊 但是我的小方塊,我希望變得更有趣,目前的 logo 與顏色有點無趣

我完全沒有畫面,也不知道什麼好,所以 /blogs/ 的方塊排版前後改了四版。

/blogs/ 三個階段:改版前的三欄卡片、中途的貼紙牆、現在的奇異鳥徽章

想法與取捨

Projects 要搬進 /blogs/ 嗎?不搬,只露臉

第一個問題是 Projects 怎麼「整合進」Blogs。最直接的做法是把 content/projects/ 搬到 content/blogs/projects/,它就自然變成 /blogs/ 的子分類,但 /projects/ 底下十個專案頁的網址都會跟著變,要補轉址。Claude 的計畫是目錄不動、網址不動,只讓 /blogs/ 的方塊多讀一個「外面的」section,我照這個做。導覽列的 Projects 則直接移除,減少肥大。

「有趣」是什麼:叫 Claude 隨手畫一隻新的 kiwi,不做圖示拼貼

Claude 給了三個方向:用現有十個專案的 icon 拼成一面小拼貼牆、新畫一隻「工程師 kiwi」、或兩個都要。它推薦拼貼牆,我選了新畫一隻 kiwi,跟其他分類的奇異鳥徽章同一套。

Claude 畫的是 SVG:戴黃色工地安全帽的 kiwi,背景是藍圖方格和齒輪,旁邊一支扳手;配色從灰綠換成藍圖藍。有點奇特,的確是 Claude 的 SVG 風格。

/projects/ 頁首換上新的工程師 kiwi 和藍色

第二版:貼紙牆

計畫寫好要動手時,我把它退回去了:

我想要重新設計 blogs/ 內的小方塊排版

Claude 做了三個 mockup 讓我比:便當盒(Engineer、Projects 佔大格)、貼紙牆(微旋轉、紙膠帶、logo 從卡片上緣冒出來)、橫向清單。方塊上要放什麼,我勾了「最新一篇標題」和「只要 logo+名稱」。最後選了貼紙牆。

貼紙牆做出來之後,一直都覺得不確定不舒適,所以又陸續改了幾次:

安靜版本好了,然後下面放我每個 domain 的描述好了,不要用新文章

希望一排最多三個,字可以放大

內框的顏色幫我調整,目前過多一樣

顏色過於相近的各 domain,所以有改成各自的顏色,雖然色系還是很相近,容易混淆,等之後再決定吧。這時也順便將順序定下來:books、art、ideas、projects、engineer、coffee、skateboard、others。

第三版:交給 Claude Design,敘述藏到 hover

但其實改完成果,我還是覺得怪怪的,非常的浮動,因此我丟給 claude 設計老大哥 Claude Design。另外在 Claude Design 開專案、從頭做的。問卷上我填的是「整體視覺想換新」「完全重新設計」「都不要,保持簡潔」,要三個變化版。

它跑了三輪:

  1. 目錄(一列一個分類)、徽章(拿掉卡片外框,大張奇異鳥當主角)、色塊(兩欄平面色塊)。我選了徽章
  2. 從徽章延伸出色暈、散步(兩排錯開高低)、色暈的手機版。我回「往 2a 走,感覺下面的敘述有點干擾」
  3. 兩版都以色暈為底:網格只留插圖和名稱,敘述放到各分類頁;或者滑鼠移到某隻鳥時,敘述才出現在標題下方。我選了後者

所以描述這件事繞了一圈:一開始方塊上有三行標語 → 換成最新文章 → 換回描述 → 最後從方塊上拿掉,只在 hover 時出現在標題「走去哪裡?」底下,網格上只剩鳥和名字。

定稿交回 Claude Code 實作時,有幾個地方跟設計稿不一樣,都是照我前面講過的話:順序用我指定的、不用設計稿的;插圖用 CDN 上的原圖,設計稿是從截圖裁的;頁首、頁尾和全站底色沒動,這次只改 /blogs/ 中間那塊。另外設計稿桌機是一排 4 個,跟我前面講的「一排最多三個」衝突,Claude 照設計稿做、在回報裡提出來,我沒有改回 3 個。

最後再加一條:

希望 hover 移到某隻鳥後,裡面的 logo 會放大

滑鼠移到 Coffee:色暈裡的 logo 放大,標題下方換成「點一杯手沖」

實作

這次改動的主體在 layouts/_default/category-tiles.html、assets/css/extended/custom.css 和 hugo.yml,外加各分類 _index.md 的 weight 和一張新的 SVG。

讓 /blogs/ 讀到外面的 section

/blogs/ 的方塊原本是 range .Sections,只會列出 content/blogs/ 底下的子目錄。Projects 在 content/projects/,不在其中,所以在 /blogs/ 的 front matter 加一個清單:

# content/blogs/_index.md
layout: category-tiles
extraTiles: ["/projects"]

模板把兩個來源合成一個 slice,再一起照 weight 排:

{{- $tiles := slice }}
{{- range .Sections }}{{ $tiles = $tiles | append . }}{{ end }}
{{- range .Params.extraTiles }}{{ with site.GetPage . }}{{ $tiles = $tiles | append . }}{{ end }}{{ end }}

<div class="kiwi-hub-grid">
  {{- range sort $tiles "Weight" }}
  {{- $key := path.Base .RelPermalink }}
  {{- $cat := index site.Params.categories $key | default dict }}
  {{- $name := $cat.displayName | default .Title }}
  {{- $tagline := $cat.tagline | default .Description }}
  {{- /* ... logo URL ... */}}
  <a class="kiwi-hub-tile" href="{{ .RelPermalink }}" data-desc="{{ $tagline }}"
     {{- with $cat.halo }} style="--halo: {{ . }}"{{ end }}>
    <span class="kiwi-hub-halo">
      {{- with $logoURL }}<img src="{{ . }}" alt="" loading="lazy">{{ end -}}
    </span>
    <span class="kiwi-hub-name">{{ $name }}</span>
  </a>
  {{- end }}
</div>

path.Base 對 /projects/ 會得到 projects,所以它跟其他分類一樣去 params.categories.projects 拿名稱、logo 和顏色,模板不用為它開特例。順序要讓 Projects 插在 Ideas 和 Engineer 中間,所以不能用 .Sections 原本的排序,改成整個 slice sort 一次;content/projects/_index.md 加 weight: 4,七個分類的 weight 重編成 1 到 8。

--halo 只在有設定時才輸出:Hugo 的 html/template 會把 var(--x) 這種預設值轉義成 ZgotmplZ,所以預設色寫在 CSS 的 var(--halo, #DCCFBC) 裡。

分類設定多一個 halo

每個分類在 hugo.yml 本來就有 accent、bg、tagline、logo,給分類頁頂端的橫幅用。徽章背後那圈色暈是設計稿的另一套色票,所以新增一個 halo,不去動橫幅用的 bg:

    # Not under /blogs/, but shown as a tile there via `extraTiles` in
    # content/blogs/_index.md. Blueprint blue + hard-hat yellow logo.
    projects:
      displayName: Projects
      halo: "#BFD6E2"
      accent: "#2F6F8F"
      bg: "#D3E6EE"
      accentDark: "#6FB3D2"
      bgDark: "#1A2E38"
      tagline: "Side projects — 邊做邊記錄。"
      logo: /images/logos/logo-projects.svg

新 logo 放在 static/,路徑不是 http 開頭,模板會先試 resources.Get,找不到再 absURL,分類頁橫幅和文章上的分類標籤本來就是這樣處理 logo,三處都能直接用。

hover 換描述

描述藏在每個 <a> 的 data-desc 裡,標題下的 <p> 記著預設副標。滑鼠移入或 Tab 到某隻鳥時先淡出,120ms 後換字再淡入:

<script>
  (function () {
    var desc = document.querySelector('.kiwi-hub-desc');
    if (!desc) return;
    var fallback = desc.getAttribute('data-default');
    var timer;
    function show(text) {
      clearTimeout(timer);
      desc.style.opacity = 0;
      timer = setTimeout(function () {
        desc.textContent = text || fallback;
        desc.style.opacity = 1;
      }, 120);
    }
    document.querySelectorAll('.kiwi-hub-tile').forEach(function (tile) {
      var text = tile.getAttribute('data-desc');
      tile.addEventListener('mouseenter', function () { show(text); });
      tile.addEventListener('focus', function () { show(text); });
      tile.addEventListener('mouseleave', function () { show(null); });
      tile.addEventListener('blur', function () { show(null); });
    });
  })();
</script>

clearTimeout 讓滑鼠快速掃過一排鳥時,只有最後停下的那隻會換上去。沒有 JS 時就一直顯示預設副標;手機沒有 hover,看到的也是預設副標,這是設計稿就講好的。副標的 CSS 給了 min-height: 48px,兩行的描述換進來時下面的網格不會跳。

logo 在色暈裡放大

.kiwi-hub-halo img {
    width: 108px;
    height: 108px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
    transition: transform 0.25s ease;
}

/* The badge grows into its halo: 108px → ~131px of the 144px circle. */
.kiwi-hub-tile:hover .kiwi-hub-halo img,
.kiwi-hub-tile:focus-visible .kiwi-hub-halo img {
    transform: scale(1.21);
}

@media (prefers-reduced-motion: reduce) {
    .kiwi-hub-desc,
    .kiwi-hub-halo,
    .kiwi-hub-halo img {
        transition: none;
    }
}

色暈是 144px,logo 108px,放大 1.21 倍差不多填滿、只留一圈細邊;色暈本身照設計稿放大 1.06 倍。系統開了「減少動態效果」時只拿掉 transition,logo 還是會變大,只是沒有過程。

徽章的網格

/* 800px is the design's content width (880px minus its 40px side
   padding); at 880px auto-fill fits a fifth column and the 8 badges
   split 5 + 3. */
.kiwi-hub-grid {
    width: 100%;
    max-width: 800px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 44px 24px;
}

auto-fill 加 minmax(150px, 1fr) 讓欄數跟著寬度走:800px 內放得下 4 欄,430px 的手機是 2 欄,320px 剩 1 欄。那個 800px 是怎麼來的,在下面的坑裡。

手機寬 430px:兩欄徽章

踩到的坑

worktree 沒拉主題,Hugo 照樣 build 出一整站的空白頁

貼紙牆第一次開預覽,/blogs/ 是一片白。Hugo 的輸出沒有錯誤,只有一個 warning:

WARN  found no layout file for "json" for kind "home": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.

                   | ZH-TW
-------------------+--------
  Pages            |   280

用 curl 量各頁的大小,/、/blogs/、/projects/、/blogs/coffee/ 全部都是 114 bytes,內容只有 Hugo 注入的 livereload script。一整站都空,就不是我改的模板壞了。git submodule status 一看,themes/PaperMod 前面是 -:這個 Claude Code 開的 worktree 沒有初始化 submodule,主題目錄是空的。

git submodule update --init themes/PaperMod 拉回 repo 鎖定的那個 commit 之後,同一份內容 build 出 436 頁,畫面就正常了。Hugo 找不到主題時不會失敗,只會默默少產生一堆頁面,所以 280 和 436 這兩個數字是唯一的線索。

hover 不會晃,是 Windows 關了動畫效果

貼紙牆的設計是滑鼠移上去時,貼紙擺正、logo 晃一下。我回報:

滑鼠移上去不會晃動

在預覽頁查 matchMedia('(prefers-reduced-motion: reduce)').matches,回傳 true:我的 Windows 關掉了「動畫效果」,瀏覽器就會告訴網頁「使用者希望減少動態」。當時的 CSS 在這個條件下把 hover 效果全關了,所以我什麼都看不到。另一個原因是那個「晃動」其實只是 transform: rotate(-8deg) 轉過去停住,本來就不是晃。

Claude 先改成真的左右搖擺的 @keyframes,並且拿掉減少動態的例外;我看完之後說「安靜版本好了」,又加了回來。結果就是:我自己的電腦上永遠看不到那個晃動,其他沒關動畫的人才看得到。現在的徽章版也是一樣,減少動態時 logo 照樣放大,只是沒有放大的過程。

設計稿在 claude.ai 上,Claude Code 讀不到

Claude Design 給的交接說明是「用 claude_design MCP 匯入這個專案」,但這個 session 沒接上那個 MCP。Claude 先試了兩條路:用我的 Chrome(擴充功能沒連線)、用 Claude Code 內建的瀏覽器開專案網址(停在 claude.ai 登入頁,它不能替我登入)。我在內建瀏覽器裡自己登入之後,頁面打得開了,但設計畫布是另一個網域的 iframe,從外層頁面讀不到裡面的內容。

最後是從專案的檔案清單找到 Blog Categories Final.dc.html(4601 bytes),透過專案的檔案 API 拿到 base64 編碼的原始碼再解開。設計稿是 Claude Design 自己的模板格式:版面用 inline style 寫,八個分類、色暈色票和描述寫在一段 renderVals() 裡;同專案的 support.js 只是預覽用的 runtime,Hugo 用不到。Claude 照這份原始碼把間距、字級、色票和 hover 行為搬進 Hugo 模板和 CSS。

寫這篇時才發現:寬螢幕上排成 5+3

為了這篇文章截圖,我用 900px 寬的視窗截 /blogs/,徽章排成第一排 5 個、第二排 3 個。實作那時是在比較窄的預覽視窗看的,剛好是 4 欄,就沒發現。

原因是設計稿的內容區是 max-width: 880px 再加左右各 40px 的 padding,實際排徽章的寬度是 800px;我照抄了 880px,卻沒有那層 padding。800px 能放的欄數是 (800 + 24) ÷ (150 + 24) ≈ 4.7,取 4;880px 是 (880 + 24) ÷ 174 ≈ 5.2,就多塞了一欄。

修法是網格本身限寬 800px、置中。改完在 1280、900、430、320px 量過,分別是 4、4、2、1 欄,頁面寬度都等於視窗寬度,沒有出現左右捲動。

字體跟全站不一樣

文章寫到一半,我發現 /blogs/ 的字體跟整個網站不一樣:「走去哪裡?」和八個分類名稱是設計稿指定的 Noto Serif TC 襯線字,全站其他地方是 PaperMod 的系統無襯線字。設計稿只畫了 /blogs/ 這一頁,在它自己的畫面裡襯線字是統一的;搬進部落格之後,只有這一頁的標題長得不一樣。

Claude 問我要往哪邊統一:/blogs/ 改回全站字體,或是全站標題都換成襯線字。我選前者。拿掉兩處 font-family: 'Noto Serif TC', serif; 和模板裡的 Google Fonts 載入,字級和粗細照設計稿留著。這篇的截圖都是改回之後重截的。

小結

  • Projects 收進 /blogs/:目錄和網址都不動,用 extraTiles 讓它出現在 /blogs/;導覽列拿掉 Projects
  • 工程師 kiwi:戴安全帽的 SVG 新 logo,Projects 的配色換成藍圖藍,/projects/ 頁首一起換
  • /blogs/ 總覽:「走去哪裡?」加兩排奇異鳥徽章,桌機 4 欄、手機 2 欄;描述只在 hover 或 Tab 時出現在標題下方,logo 在色暈裡放大
  • 順序:books、art、ideas、projects、engineer、coffee、skateboard、others,由各分類的 weight 決定
  • /blogs/others/:共用同一個版型,標題是「其他角落」

這次 Claude Design 的設計只用在 /blogs/ 中間這一塊,頁首、頁尾和全站底色還是原本的。


本文同步發表於 kiwi-walk.com:https://kiwi-walk.com/blogs/engineer/ironman-2026-day25-blogs-kiwi-hub/