怎麼選一組分類色票
色彩語意說的是已定案的那一組:8 色、沿色相環排序、 把色覺障礙下最難分辨的一段拆到陣列兩端。
這一頁講的是怎麼走到那個結論。因為結論不能被複製—— 換品牌色、品牌方指定主色、或另一個產品線要一套自己的色票時, 你需要的是程序,不是別人的八個色碼。
問題
「挑八個看起來不一樣的顏色」聽起來像十分鐘的工作。實際上目測挑出來的色票, 會在四個地方同時出問題,而且全部要等到上線後才會被發現:
| 什麼時候露餡 | 症狀 |
|---|---|
| 有人用灰階印出來 | 兩個長條變成同一種灰 |
| 色覺障礙使用者看報表 | 兩條折線在他眼裡從頭到尾疊在一起 |
| 切到深色模式 | 有一色糊進背景、另一色亮到刺眼 |
| 第九個類別出現 | 沒有人知道該接哪個色,於是隨手加一個 |
前三項的共同點:做決定的人看不到問題。所以這件事不能靠目測,要靠驗證器。
做法:候選 → 驗證 → 排序 → 定案
1. 一次做多組候選,並排比較
至少做 5–7 組,每組給一個風格描述(「沉穩企業感」「高飽和儀表板感」「柔和親和」), 用同一份資料、同樣幾張圖、淺深兩色模式並列呈現。
候選的來源可以是既有色票、品牌情緒板、公開的色票庫。但要先接受一件事: 沒有一組能直接拿來用。 情緒板通常只有 4–6 色,而且是為「一張海報好看」挑的, 不是為「八個類別要能被分辨」挑的。它們提供的是方向(色相家族、飽和度層級),不是答案。
把候選組與它們的驗證結果留在文件或 story 裡, 包含被淘汰的那幾組。半年後有人問「為什麼不用那個藍」, 答案要找得到。
不要只做一組然後開會表決。單一候選沒有比較基準—— 任何一組看久了都會覺得可以,會議的結論只會是「就這樣吧」。
2. 四項驗證器:用程式跑,不要用眼睛
四項各自擋掉一種失敗,缺一項就會有一整類問題漏出去。
| 檢查 | 在問什麼 | 沒過的後果 |
|---|---|---|
| 亮度帶 | 相鄰的兩色是否落在同一個亮度區間 | 灰階列印、單色印表機、低視力使用者眼中無法區分 |
| 彩度 | 一組裡有沒有特別灰或特別螢光的離群色 | 離群色會被讀成「這一類比較重要」——分類色不該有階級 |
| 色覺障礙 ΔE 分離 | 模擬三種色覺障礙後,任兩色的最小色差 | 低於地板值=那兩色對約 8% 的男性使用者是同一個顏色 |
| 對比 | 每一色對實際頁面背景的對比是否 ≥ 3:1 | 未達標的色塊只能靠標籤辨義,不能單獨承載訊息 |
// 驗證器的輸出長這樣:每組候選、每個模式各一份
// { 亮度帶: pass, 彩度: pass, CVD分離: warn(ΔE 11.4 floor), 對比: pass }
//
// 三件事必須寫進報告,否則結果無法被重現:
// 1. 用哪個背景值驗的
// 2. ΔE 的地板值定在多少
// 3. 哪幾色只是「勉強過」(下次微調時第一個被動的就是它們)
for (const mode of ["light", "dark"]) {
report[mode] = validate(palette[mode], { background: SURFACE[mode].bg, deltaEFloor: 10 });
}
用真正的頁面背景值驗(卡片底色、表格底色), 不是白色與純黑。這與無障礙四原則第 3 條是同一條規則。
不要用色票研究時的比較底色驗完就收工。比較用的白底通常比實際卡片亮, 在它上面過關的色,放到真實介面上可能差 0.3 個對比比值。
驗證器的覆蓋範圍要逐字等於你宣稱的保證。 這一條看起來像廢話, 但它是本書自己踩過的坑——只驗了一個對象、卻以為驗完了一整類,見下方〈第二輪〉。
3. snap-to-passing:微調,不要重挑
某一色差一點沒過時,正確動作是保留色相、微調明度與彩度讓它剛好過線, 而不是換一個色相重來。
理由:色相承載的是品牌情緒(「這是那個藍」),明度與彩度承載的是可辨識性。 換色相會把前面的取材工作整個丟掉,而調明度不會。
實務上兩色相差太近(例如只差 11°,會被讀成同一種棕)時才需要真的動色相—— 這時要兩色各推向兩端,而不是只推一色,否則被推走的那一色會離它的取材來源太遠。
4. 排序是設計決定,不是登記順序
色票是陣列,而陣列的相鄰關係會直接變成圖表上的相鄰關係 (前兩個類別、堆疊圖裡連在一起的兩段、折線圖裡先畫的兩條)。
兩條規則:
- 沿色相環排序(藍→青→綠→金→橙→粉→紅→紫)。 讓相鄰序列的關係可預期,也讓「第 5 色大概是什麼顏色」變成可推測的事。
- 把驗證出來最不安全的那一對,切在陣列的頭尾兩端。
色相環是一個圈,總有一個接縫——把接縫切在最危險的地方,
因為
PALETTE[0]與PALETTE[7]永遠不會是相鄰序列。
PALETTE[0] 要穩定。它是單序列圖表沒指定顏色時的預設色,
換掉它等於把全站所有單序列圖表換色。
不要用「取材來源的命名順序」當陣列順序。那個順序是為了念起來好聽, 不是為了讓第 3 與第 4 條折線分得開。
5. 定案=只改 token 的值
換色票不該動到任何一行使用它的程式碼——這正是色彩語意那套命名的用意。
但有一個例外要事先講清楚:重新排序會打到寫死索引的畫面。
任何寫成 PALETTE[3] 的地方,在重排之後對應到的是不同色相。
這些畫面不會壞、不會報錯、測試也不會紅——它們只是默默換了顏色,
而且通常是那些「這一段固定用綠色」的堆疊圖。
重排色票時,把所有寫死索引的位置列出來逐一目視, 當成這次變更的驗收項目。
不要以為「反正只是換顏色」。使用者對圖表顏色的記憶比想像中強, 某一類昨天是綠的今天變成橘的,他第一個反應是「資料錯了」。
一條硬知識:背景改版之後,驗證結果全部作廢
四項檢查裡有三項(亮度帶、對比、色覺障礙分離的觀感)依賴背景值。 所以只要動過背景色——深色模式重做表面層次、卡片底色調亮一階—— 所有色票的驗證結果都必須重跑。
這條規則值得寫下來,是因為它非常容易被跳過:背景改版是「主題」的工作, 色票驗證是「圖表」的工作,兩件事通常不是同一次變更、甚至不是同一個人做的。
真實案例:驗證結果過期而沒有人發現
本規範的來源專案就漏了這一步。深色背景在重做表面抬升層次時換過值, 當時只對上線中的那一組色票重新驗證,另外六組候選的深色驗證結果留在文件裡沒有更新—— 它們至今仍是舊背景下的數字。 結果是:那六組看起來「有完整驗證資料、可以直接切換」,實際上一組都不能直接用。 比沒有數字更糟,因為沒有數字的人至少知道自己要去驗。
正確做法是把驗證所用的背景值記在結果旁邊,並在背景 token 變更的檢查清單裡 加一條「重跑色票驗證」。前者讓過期的結果一眼看得出來,後者讓它不會過期。
本書自己走過的四輪
上面那套程序不是推導出來的,是這本書的分類色票在 v0.3.0 → v0.6.0
之間被改了四次、每次都由驗證器逼出一條規則。四輪的紀錄都在
CHANGELOG,
這裡摘出程序上的收穫。
第一輪:淺深必須各驗一組
改版前 8 色裡有 6 色的淺深模式共用同一個 hex。 在淺色背景上通過的色,放到深色背景上對比與亮度帶全部要重算—— 共用值等於只驗了一半。第一輪的產出是把 8 色拆成淺深各一組獨立值。
對應到上面的 §2:「每個模式各一份報告」不是格式要求,是覆蓋要求。
第二輪:只驗一個對象,等於沒有驗那一類
分類色除了彼此要分得開,還不能撞到語意狀態色——否則使用者會把某一類讀成「警示」。
但當時的守衛只比對了 danger 一個狀態色,於是:
| 撞色對(深色) | ΔE00 | 門檻 |
|---|---|---|
chart-5 ↔ warning | 7.3 | 10 |
chart-1 ↔ info | 7.9 | 10 |
兩對都低於色票自己訂的「分不開」門檻,而 CI 全綠。 守衛存在、也在跑,只是它保護的範圍比它看起來保護的範圍小。
這一輪的產出是把比對對象改成全部六個狀態色, 以及上面 §2 最後那句話:覆蓋範圍要逐字等於你宣稱的保證。
第三輪:加緊一項約束時,要同時放寬美感約束
補上「不得靠近狀態色」之後,色票品質掉了:
| 加守衛前 | 只加守衛 | 同時放寬美感約束 | |
|---|---|---|---|
| 最差對 ΔE00(淺/深) | 12.2/11.8 | 10.1/9.4 | 13.1/11.3 |
中間那一欄的深色還出現兩對低於色覺障礙門檻 10。 原因不難理解:多一條約束就少一塊可行空間,最佳化器只能在更窄的地方找解。
修法不是放寬色覺障礙門檻——那條是無障礙門檻,不能為了美感讓步。 放寬的是美感約束:色相間距 30°→22°、明度帶上下各放寬 0.02。 結果是最差對比加守衛之前還好。
這條規則解釋了前兩次加碼防守為什麼是負收益
這本書在這之前有兩次「加碼防守」都讓品質下降,當時歸因於「約束太多本來就會變醜」。 第三輪才看清真正的原因:那兩次只加緊、沒有同時給最佳化器空間。 加一條硬約束的同時,要把某條軟約束讓開,否則就是在更小的空間裡找同樣好的解。
門檻的優先序因此被寫死成規則:無障礙門檻(對比、色覺障礙 ΔE00)不得為了美感放寬; 擠不下去時放寬的是美感約束(明度帶、彩度上下限、色相間距)。 這條有兩次實測支撐,不是偏好。
第四輪:數字整齊不等於感知整齊
同一輪還修了四個狀態淡底。原本的參數是「固定明度+固定彩度」—— 在數字上非常整齊,實測的染色量卻是 info 11.0/danger 12.6/warning 14.1/success 18.5, 差 1.69 倍(深色 2.13 倍),而且順序是反的:綠色的「已完成」比紅色的「無法確認」還搶眼。
根因是 sRGB 色域不是色相對稱的:在很亮的地方,綠與黃能撐住的彩度遠高於藍與紅, 於是 info 與 danger 被色域裁切、success 沒有。 參數改成固定染色量(讓「染了多少」相等,而不是讓「彩度數值」相等)之後是 1.04 倍。
對應到上面的 §1:驗證器要驗的是感知量,不是參數值。 參數整齊是實作的方便,感知整齊才是使用者拿到的東西。
取捨
代價一:跑完一輪要半天到一天。 做候選、跑驗證、微調、重驗、排序、目視。
值不值得,看你打算用這組色多久。色票是所有圖表的地基, 而地基的問題會以「每張圖都要人工挑色覆寫」的形式,每個月收一次利息。
代價二:通過驗證的色票,不一定是最好看的那一組。 高飽和的組別常常四項全過但看起來吵,柔和的組別好看卻在淺色模式對比全軍覆沒。
這時的判斷順序是:先過驗證,再談風格。 因為風格差一點的色票只是不夠漂亮,沒過驗證的色票是有一部分使用者讀不到資料。
反例
驗證器的結果連同 warn 一起留下,並註明 warn 的補償措施 (例如「這一色只能搭配標籤或明細表使用」)。
不要把 warn 當成 pass。warn 的意思是「這一色不能單獨承載訊息」, 它是一個使用限制,不是一個可以忽略的提醒。
八色不夠時,合併尾端類別成「其他」,並在別處提供明細。
不要「再加兩個顏色就好」。八色是驗證器能同時滿足四項檢查的實際上限, 第 9、10 色一定會與既有的某一色在色覺障礙模擬下撞在一起—— 而加色的人看不到那個撞色。