核心原則

對齊基準上的每個未知數都要解出來、整組才有解。 這跟線性方程組一樣 — 任何一個變數靠估算、整條基準線就不準。每個參與對齊的元素都需要「來源明確的數字」(寫死或量測),不能依賴「應該差不多吧」的視覺直覺。


為什麼缺一個值整個壞

商業邏輯

對齊不是「視覺感」、是「相對位置的數學關係」。filter 的 padding-top 要等於右側「H1 + input + gap」的總和;任何一個值不準、padding 就錯、視覺上看起來就是沒對齊。

人眼可以分辨 1px 的差異 — 估算「大概 60px」實際上 56 或 64 都可能、視覺一眼看出。

解線性方程組需要所有變數

步驟動作
1列出對齊基準上的所有元素
2對每個元素標註「值的來源」:寫死 / 量測 / 未知
3任何「未知」都要先解決(決定寫死或量測)才能寫對齊規則

跳過第 2 步直接寫對齊規則 = 拿一組有未知數的方程組嘗試代入解 — 不會對。


這次任務的實際情境

觀察

要把 filter sidebar 的內容上緣對齊到右側 results 上緣。filter 用 padding-top 把內容下推。

第一次嘗試:估 padding-top: 152px(H1 64 + input 68 + gap 20)。

實際渲染:filter 上緣比 results 上緣高了 ~10px。

判讀

152px 的計算用了估算的 H1 height(64px)。實際 H1 受 theme 的 margin-block-end 影響、總高度可能 ~70px。差了 ~6px。

進一步檢查:--pagefind-ui-scale: 0.8 時 input 高度 = 64 × 0.8 = 51.2px、不是 68px。又差 ~17px。

差距加總超過視覺可接受範圍。

執行

把所有變數轉為「來源明確」:

元素解決方式
H1寫死 height + line-height + margin: 0,強制等於 token
Pagefind input--pagefind-ui-scale: 1.0,加 border 共 68px、強制等於 token
Scope UI(高度受字型換行影響)用 ResizeObserver 量測寫回 CSS 變數
Gap(drawer margin-top)從 pagefind CSS 取得固定值 20px

padding-topcalc() 加總所有變數、永遠跟著走。


內在屬性比較:值的「來源」分類

來源適用情境維護負擔
Design token(CSS 變數寫死)設計可決定的固定值低 — 改一處全部跟上
組件提供的 hook(如 pagefind scale)透過組件 API 鎖定渲染參數低 — 跟組件升級走
Runtime 量測(ResizeObserver)內容動態決定的值中 — JS 程式要寫對
估算 / magic number不適用 — 永遠錯不該存在

不要把「估算 / magic number」當作來源。每個 magic number 都是未來 debug 的潛在炸彈。


把對齊看成方程組的步驟

1基準線 P 的位置 = sum(每個前置元素的 height + margin + padding + gap)

例:filter 的 padding-top = H1.height + input.height + drawer.margin-top

把每個變數列出、確認來源、用 CSS calc() + 變數寫成 single source。

1.search-filter-slot {
2  padding-top: calc(
3    var(--search-title-h)        /* 寫死 64px */
4    + var(--search-form-h)       /* 鎖 scale=1.0、寫死 68px */
5    + var(--search-scope-h)      /* ResizeObserver 量測 */
6    + 8px                        /* 固定 padding */
7    + var(--search-gap)          /* pagefind drawer margin 20px */
8  );
9}

設計取捨:對齊基準上每個值的來源

四種做法、各自機會成本不同。預設選擇取決於值的可預測性 — 設計可決定 → A、組件提供 hook → B、內容動態 → C、估算永遠不是答案。

A:Design token(CSS 變數寫死)

  • 機制--search-title-h: 64px 寫成設計系統 token
  • 選 A 的理由:build time 確定、純 CSS、改 token 全部跟上
  • 適合:設計可決定的固定值(spacing、typography scale、icon size)
  • 代價:值無法跟 runtime 內容變動 — 字型大幅變化時可能不適配

B:組件提供的 hook(如 pagefind scale)

  • 機制.search-shell { --pagefind-ui-scale: 1.0 }、透過組件 API 鎖定渲染參數
  • 跟 A 的取捨:B 把組件納入自家設計系統、A 自己決定值;B 在「組件渲染參數可調」時最乾淨
  • B 比 A 好的情境:值由組件決定但組件提供 hook 可控(例如 vendor library 的 size variant)

C:Runtime 量測(ResizeObserver 寫回 CSS 變數)

  • 機制:JS 量元素實際渲染尺寸、寫回 CSS 變數、其他元素引用
  • 跟 A/B 的取捨:C 自動跟著實際走、A/B 假設條件穩定;C 多 JS 一層
  • C 比 A 好的情境:值受字型 / 換行 / 內容動態影響、無法 build time 預測

D:估算 / magic number

  • 機制:執行者依感覺給數字、不寫變數、不量測
  • 成本特別高的原因:未來 debug 的潛在炸彈、估錯時錯誤被視覺接受不被發現、跨情境(字型 / scale / theme)必壞
  • D 是反模式:任何「沒來源」的值都是 unsolved 變數、會在邊界情境爆掉

判讀徵兆

訊號可能的根因第一個該檢查的事
視覺對齊「看起來差幾 px」某個元素的高度估算不準量測該元素真實 height、跟假設值比對
換 viewport 對齊壞掉某個值依賴 viewport 但沒處理找出該值、改用響應式變數
換字型 / 縮放後對齊壞掉某個值受字型影響但寫死了改用 ResizeObserver 量測
改某個 token 要去多處跟改沒用 CSS 變數把 magic number 提成變數、calc 串起來

核心原則:對齊問題的根因永遠是「某個變數沒解出來」。先找出那個變數、確定來源、再寫對齊規則。

「估值補方程式」是便利(用感覺寫死)、「找變數真實來源」是對齊(量測或從 token 算)— 同 #67 寫作便利度跟意圖對齊反相關。對齊問題的 silent 失敗常常是估值的後果。