UI 拋光
拋光來自於大量微小細節的累積。這項技能是判斷哪些細節值得擁有,以及它們應採用何種數值的參考手冊。
在審查時,請放慢介面的速度。在 10% 速度下感覺不對勁的地方,就是全速運行時細微錯誤的所在。
請保持專案的元件庫、標記與密度,並與其動態語言保持一致,除非以下規則明確指定了特定的互動方式。
下列每個持續時間、曲線、縮放與模糊值都是具體數值,而非一個近似範圍。cubic-bezier(0.2, 0, 0, 1) 不是 cubic-bezier(0.4, 0, 0.2, 1),0.96 也不是 0.95。請使用文中所寫的數值。
文字換行、字型渲染、表格數字與文字間距屬於 better-typography。點擊區域、焦點、鍵盤支援、ARIA 與減少動態效果屬於 better-accessibility。分組、區段間距、斷點與空間 RTL 屬於 better-layout。
同心圓邊框半徑
外層半徑 = 內層半徑 + 內邊距 (padding)。巢狀元素半徑不匹配是讓介面感覺不對勁最常見的原因。半徑、陰影與輪廓的配方在 surfaces.md 中。
光學對齊優於幾何對齊
當幾何置中看起來不對勁時,請使用光學對齊。帶有圖示的按鈕、播放三角形以及不對稱圖示都需要手動微調。
陰影營造深度,邊框定義結構
如果邊框僅用於創造深度,偏好使用分層的透明 box-shadow 值。保留用於溝通結構或狀態的邊框:分隔線、分割線以及選中或焦點狀態。
可中斷的動畫
對互動狀態變化使用 CSS 過渡效果 (transition),因為它們可以在動畫中途被中斷。將關鍵幀動畫 (keyframes) 保留給只執行一次的階段性序列。
拆分與交錯進場動畫
對於不頻繁的階段性進場(其序列用於溝通階層關係),將內容拆分為語意區塊,並以約 100 毫秒的間隔交錯出現。為一個容器製作動畫,同樣的成本獲得的回報較少。高頻率互動則不要交錯。請參閱 enter-exit.md。
細微的離場動畫
使用一個小的固定 translateY 而非完整高度。離場動畫應比進場動畫更柔和。兩個方向都使用 ease-out。
情境式圖示動畫
使用 opacity、scale 和 blur 來製作圖示動畫,而不是切換可見性。請精確使用這些數值:縮放 0.25 到 1,透明度 0 到 1,模糊 4px 到 0px。
如果使用動畫函式庫(package.json 中有 motion 或 framer-motion),請匹配該套件的匯入路徑,或附近同時存在兩者時的匯入方式。使用 transition: { type: "spring", duration: 0.3, bounce: 0 }。反彈係數始終為 0。
如果沒有使用動畫函式庫,則將兩個圖示都保留在 DOM 中,其中一個使用絕對定位,並使用 cubic-bezier(0.2, 0, 0, 1) 進行交叉淡入淡出。這無需依賴外部套件即可實現進場與離場效果。兩種實作方式都在 icon-transitions.md 中。
圖片輪廓
為圖片添加一個低透明度的 1px 輪廓,以保持一致的深度層次。淺色模式使用純黑色 (oklch(0 0 0 / 0.1)),深色模式使用純白色 (oklch(1 0 0 / 0.1))。絕不使用近乎黑色的顏色(如 slate 或 zinc),也絕不使用帶色調的中性色。帶色調的輪廓會吸附下方的底色,在圖片邊緣看起來像污垢。
按壓縮放
點擊時應用的 scale(0.96) 能為按鈕提供觸覺回饋。始終使用 0.96;任何低於 0.95 的值都感覺過於誇張。添加一個 static prop 以在動畫可能造成干擾時將其關閉。請參閱 CSS、Tailwind 和 Motion 的配方。
頁面載入時跳過動畫
在 AnimatePresence 上使用 initial={false} 以在首次渲染時避免進場動畫。請確認這不會影響到有意的頁面進場動畫。
在主題切換時抑制過渡效果
主題切換會同時改變幾乎所有元素的顏色、背景、邊框與陰影。這些屬性上的每個過渡效果會同時觸發,導致切換過程變得模糊而非乾脆俐落。注入 *,*::before,*::after{transition:none !important},強制進行回流 (reflow),然後在下一幀將其移除。請參閱配方。
僅對會變化的屬性應用過渡效果
始終明確指定屬性名稱:transition-property: scale, opacity。Tailwind 的 transition-transform 涵蓋了 transform, translate, scale, rotate。
謹慎使用 will-change
僅對 transform、opacity 和 filter 使用,這些屬性可由 GPU 合成。絕不使用 will-change: all。在觀察到首幀卡頓時再添加,而非預先添加。請參閱 performance.md。
匹配圖示描邊與文字粗細
圖示旁邊的文字應與圖示的光學重量匹配:常規 (400) 文字旁使用 1.5px 描邊,半粗體 (600) 文字旁使用 2px 描邊。每個圖示集使用一種描邊粗細,每個表面使用一種圖示函式庫。尺寸調整與 RTL 翻轉在 icons.md 中。
一個 SVG,按狀態重新著色
圖示使用 currentColor,並從 CSS 顏色與透明度中獲取懸停、選中和禁用狀態,而非使用單獨的資源檔。輪廓是預設變體;填滿表示活躍狀態。
動畫節制
為高頻率互動提供即時回饋,或在透明度與顏色上使用不超過 150ms 的過渡效果。在那裡使用自定義動畫,每次觸發都會消耗注意力成本。
每個動畫狀態變化也需要一個靜態提示:顏色、圖示或標籤。動畫絕非唯一的回饋管道。
完成前
| 錯誤 | 修正 |
|---|---|
| 圖示看起來未置中 | 使用內邊距 (padding) 進行光學微調,或修正 SVG |
| 不和諧的階段性進場或離場 | 交錯不頻繁的進場;保持離場細微 |
| 主題切換導致整個頁面交叉淡入淡出 | 在切換時禁用過渡效果,強制回流,在下一幀恢復 |
在元素上使用 transition: all | 指定確切的屬性 |
| 首幀動畫卡頓 | 添加 will-change: transform(謹慎使用) |
| 細線圖示旁有粗體文字 | 將描邊寬度匹配到文字粗細 |
報告
嚴重性。 HIGH 會破壞互動、使動畫無法使用,或導致狀態變化僅在動畫運行時可見。MEDIUM 是表面、圖示或動畫中可見的不一致。LOW 是孤立細節的拋光。
驗證。 如果沒有瀏覽器:檢查元件定義的每個狀態,包括懸停、焦點、活躍、載入和空狀態,並從程式碼中讀取動畫持續時間和緩動函數。如果有瀏覽器:遍歷每個狀態,並在瀏覽器的「動畫」面板中以 10% 的速度重播動畫。將每個無法執行的檢查報告為 Not verified。
格式。 將發現的問題按其違反的原則分組,依嚴重性排序,每個根本原因一行,列出其出現的每個位置:
| 嚴重性 | 位置 | 修改前 | 修改後 | 原因 |
|---|
位置 是 path/to/file:line。原因 指出違反的原則以及對使用者的影響。
當任何 HIGH 嚴重性的問題仍存在時,以 Block 結束,否則以 Approve 結束,並將剩餘問題保留在表格中作為待辦事項。絕不對未檢查的範圍 Approve。如果沒有發現問題,請說明「未發現可操作的 UI 拋光相關問題」並報告驗證結果。