替 blog 找設計詞彙:從 Vuetify 到 Magazine 風的一次設計診斷

我這個 blog 改版想做很久了。每次打開自己的站,都覺得「不對」,但說不出哪裡不對。看別人的 blog ──Josh ComeauRobin Rendle、那些做得好的個人站 ──就知道差在哪一個層級的東西,但翻不出名字。

這篇是替自己補課的紀錄。把這次從 Vuetify1 換到 UnoCSS2 + Magazine 風的過程,整理成一個診斷 + 三個詞彙。詞彙的目的不是裝逼,是讓下次再感覺「不對」的時候,能指著問題的地方說「就是這個」。

如果你也有「明明會寫 code、會選色票、會抄 Figma,但自己做出來的東西就是少一口氣」的感覺,這篇是寫給你的。


診斷:Tool vs Goal Mismatch

Vuetify 沒有做錯什麼,是我把它放錯地方了。

Vuetify 是一套以 Material Design3 為基礎的 Vue component library。它的設計目標非常清楚:讓你能快速做出功能密集的應用程式介面。Dashboard、admin、CRM、SaaS 工具 ── 一個畫面要塞 100 個 component 都還能活下去的場景。

要做到這件事,Material Design 在底層解決的是這些問題:

  • Affordance:使用者要一眼看出「哪裡可以點」。所以 button 有陰影、card 有 elevation、chip 有填色膠囊形狀。
  • 密度:資訊量要大、操作要密,所以間距 token 設得緊、字級壓縮在小範圍內。
  • 可發現性:功能要被找到,所以視覺權重平均分配 ── 沒有單一元素特別搶戲。

Blog 的目標剛好相反。Blog 要做的事只有一件:讓人停下來讀一段話

而要讓人停下來讀,需要的是:

  • 節奏:標題、段落、引言之間要有明顯的呼吸頓挫
  • 留白:眼睛要有地方休息,不能塞滿
  • 層級戲劇:一篇文章的 H1 必須壓制其他元素,讓讀者知道「故事從這裡開始」

把 Vuetify 套在 blog 上,就是用「能讓 100 個 component 活下去」的設計系統,去做一件「需要讓 1 段話呼吸」的事。整齊、乾淨、但平。沒有一個地方做錯,但整體就是「不對」。

這個錯位有名字 ── 我把它叫做 tool-vs-goal mismatch。工具本身沒問題,但工具要解的問題跟你要解的問題不是同一個。

第一個詞彙:tool-vs-goal mismatch。下次選 framework 之前,先問「這個工具預設要解的問題,跟我要解的問題一致嗎?」


第一招:Type Scale 的戲劇對比

editorial / magazine 風的張力來自哪裡?很多人第一直覺是「配色」。錯了。是字級。

Vuetify 的 typography token 是這樣設計的:text-h1text-body-1 之間,倍率大約落在 1.25–1.5x。這個倍率區間在 app UI 場景非常合理 ── 你不希望某個元素特別搶戲,因為畫面上每個 component 都要被看到。

但在 blog,這個倍率讓整頁字看起來「同等重要」。沒有戲。

Before:舊版首頁,Vuetify 字級節奏
Before:舊版首頁,Vuetify 字級節奏

新版的 type scale 直接拉到 4x 以上:

css
/* Magazine type scale */
--font-display:  clamp(2.25rem, 6vw, 4rem);  /* line-height: 1.05 */
--font-h-article: clamp(2rem, 4vw, 3rem);    /* line-height: 1.10 */
--font-h2:        2.125rem;                   /* line-height: 1.20 */
--font-h3:        1.625rem;                   /* line-height: 1.30 */
--font-body:      1.125rem;                   /* line-height: 1.75 */
--font-meta:      0.875rem;                   /* uppercase, tracking-wider */

兩個關鍵設定:

  1. clamp() 讓大字級隨視窗寬度伸縮,桌機展現戲劇感、手機自動收回不爆版
  2. 大字級 line-height 壓到 1.05–1.10、body 卻拉到 1.75 ── 反差越大越「印刷」

這套 scale 的精神可以從 Refactoring UI4 借一句話:「If you want to make something look beautiful, make it large or make it small ── never make it medium.」 Vuetify 的問題就是所有東西都 medium。

After:新版首頁,戲劇對比
After:新版首頁,戲劇對比

第二個詞彙:type scale drama。editorial 設計的張力來自字級的戲劇對比,不是配色。倍率拉不開,配再多顏色都救不回來。


第二招:Typographic Marker 取代色階

字級拉開之後,下一個問題:meta 資訊(tag、日期、reading time)要怎麼區分?

我的第一直覺是「加顏色」── 用一個 muted 灰、或一個低彩度的 accent 來標。但做 PostCard 的時候踩到一個有意思的決策。

當時 PostCard 的 tag 區塊原本是 Vuetify 的 v-chip:彩色填底膠囊、視覺權重很重。我想換成 plain text,加 # prefix 做身份提示。同時想把字色從 muted 拉回正常文字色 ── 直覺覺得這樣「更乾淨」。

結果發現 ── # 做完身份提示的工,色階就不該再加上去

Before:舊版 PostCard 的 v-chip tag
Before:舊版 PostCard 的 v-chip tag

理由是:editorial minimal 的視覺邏輯是 ── 用 typographic marker(符號、字距、italic、uppercase)做身份/分類的線索;色階則保留給真正的視覺層級(標題 vs meta vs body)。

# 已經是一個 typographic marker。它告訴讀者「這是一個 tag」。如果我同時把字色拉到正常文字色,就等於在同一個 meta 區塊內又加上一層「色階對比」。結果是 meta 區塊內部開始打架、搶走標題的主角地位。

最後決定:# 加上去、色調維持 muted

After:新版 PostCard 的 #tag
After:新版 PostCard 的 #tag

這個原則可以推廣到整個 blog:

場景typographic marker不要再加
Tag# prefix色階對比
日期uppercase + tracking-wider強色
Reading time· separatorbold
TOC itemindent + small caps邊框、底色
Archive 年份大字級 + serif色塊

色階是稀缺資源。一個畫面能用的「強色階對比」其實只有 2–3 階:標題 vs body、body vs muted、accent(link/blockquote)。把這個額度浪費在 meta 區塊內部分強弱,就會發現要「凸顯標題」的時候已經沒額度了。

第三個詞彙:typographic marker。需要「告訴讀者這是什麼」用符號/字距/italic;需要「建立真正的層級對比」才動色階。不要在同一個區塊內同時用兩種


第三招:奶油紙底 + 一個 accent

最後一個是底色跟 accent 的決策。

舊版用 Vuetify 預設的純白底 #FFFFFF + Material 預設藍紫色系。新版改成:

css
:root {
  --color-bg:      #FAF8F3;  /* 奶油紙感 */
  --color-surface: #FFFFFF;
  --color-text:    #1A1A1A;
  --color-muted:   #6B6B6B;
  --color-accent:  #B23A2F;  /* 磚紅,只給 link / blockquote 左線 */
  --color-border:  #E6E1D6;
}

兩個決策值得拆開講。

為什麼不是純白

純白 #FFFFFF 在 content-first 場景太冷、太「web app」。它會讓字看起來像「螢幕上顯示的字」,而不是「印刷在紙上的字」。

奶油紙底 #FAF8F3 是一個非常輕微的暖色偏移 ── HSL 約 hsl(40, 33%, 97%),肉眼幾乎看不出黃,但字落上去的感覺完全不同。它把「螢幕」這件事退到背景,把「文字」推到前景。

這是純粹氛圍的選擇,沒有功能上的理由。但氛圍就是 editorial 設計的功能本身。

為什麼 accent 只用一個、只給兩個地方

磚紅 #B23A2F 是整個 blog 唯一的 accent。它只出現在兩個地方:

  1. 文章內的 inline link
  2. blockquote 的左側細線

理由是:accent 一旦變裝飾,就喪失了 signal 功能

如果磚紅同時出現在 button、tag、icon、navigation hover、sidebar 強調、reading time 圈圈、social media icon ── 它就變成「這個 blog 的點綴色」,而不是「這個位置有重要資訊」的訊號。讀者掃過去的時候,眼睛不再停留。

把 accent 用得越省,每一次出現的訊號強度就越強。看到磚紅 → 知道是 link、知道是引言。signal vs noise 的取捨。

Before:舊版文章內頁
Before:舊版文章內頁

After:新版文章內頁
After:新版文章內頁

字體:Fraunces 為什麼撐得住

順帶提一句 ── 這套色票需要對的字體才撐得起來。我選了 Fraunces5 當標題 serif,繁中 fallback 到 Noto Serif TC。

Fraunces 是 variable font(可變字軸),支援 optical size:字級越大、字形的細節(serif 末端、curve 曲率)會自動增加;字級越小、字形自動簡化。也就是說,display 級別跟 H3 級別用同一個字體檔,但長得不一樣

這跟 type scale drama 是一組的:字級拉得越大、字形也跟著「變得更值得被看」。

第四個詞彙:signal vs noise。底色定氣質,accent 越省越有力。一個畫面只給 accent 一個工作。


把詞彙交回給你

整理一下這次補課拿到的四個詞彙:

  1. Tool-vs-goal mismatch ── 選工具之前先確認工具預設要解的問題,跟你要解的問題一致
  2. Type scale drama ── editorial 的張力來自字級倍率,不是配色;倍率拉不開,配色救不回來
  3. Typographic marker ── 身份提示用符號/字距/italic;色階只留給真正的層級對比;不要同一區塊內疊兩種
  4. Signal vs noise ── accent 越省越有力;一個畫面只給 accent 一個工作

這四個詞彙不是設計法則,是幫助你下次「感覺不對」的時候能指出來的工具。能命名的問題就能討論、能討論的問題就能改。卡住的時候不是品味的問題 ── 是缺詞彙。

下兩篇會分別記:

  • 工程篇:為什麼這次改版要拆 Phase A / Phase B、不能一次做完
  • Bug war story:Nuxt 3.21 的 payloadExtraction 對中文 URL prerender 的隱性 500 ── 一個花了幾小時才抓到的坑

環境資訊

  • Nuxt 3.21
  • UnoCSS 0.x(取代 Vuetify 3.5)
  • @nuxtjs/color-mode 3.x
  • Fraunces / Noto Serif TC(標題 serif)
  • Inter / Noto Sans TC(內文)
  • JetBrains Mono(code)

Reference

  1. Vuetify 官方站,Material Design Vue component framework:https://vuetifyjs.com/
  2. UnoCSS 官方站,instant on-demand atomic CSS engine:https://unocss.dev/
  3. Material Design 3 設計目標與基礎概念:https://m3.material.io/foundations
  4. Refactoring UI(Adam Wathan、Steve Schoger)── 字級對比與視覺層級的常被引用來源:https://www.refactoringui.com/
  5. Fraunces ── 由 Undercase Type 設計的 variable serif,支援 optical size:https://fonts.google.com/specimen/Fraunces