第一次為新專案建立配色與主題
專案目標
你剛接手一個內部客服後台,品牌端只給了一句「我們的品牌色是青色 (teal),整體要穩重」。你要把這句話變成可套用、可換膚、可通過 WCAG AA 對比度檢查的 CSS 變數,而且不能讓 AI 自己腦補十個顏色。
本範例假設你已完成 CLI 安裝並初始化,能在 AI 助理裡呼叫 palette-picker 與 theme-exporter 兩個 skill。
npm install -g ui-ux-pro-max-cli
uipro init --ai universal
1. 從產品情境出發,不要從色票
先把需求寫成一段結構化的輸入。palette-picker 不接受「藍色科技感」這種形容詞,它需要角色與限制。
產品情境:客服後台,使用者一整天盯著看
品牌色:teal #0d9488
主要操作:儲存、送出
狀態種類:成功、警告、錯誤、資訊
主題:亮色 + 深色
內容密度:高(表格、表單、明細)
不可妥協:正文文字對比度 ≥ 4.5:1
把「一整天盯著看」寫進去很重要。它會讓飽和度被壓低,避免高彩度背景長時間造成眼睛疲勞。
2. 用 palette-picker 展開語意角色
把上面這段輸入交給 palette-picker,它輸出的是語意角色,而不是十六支 hex 色。
:root {
/* raw 色票,元件不直接使用 */
--teal-500: 13 148 136;
--teal-600: 13 148 120;
--slate-900: 15 23 42;
--slate-700: 51 65 85;
/* 語意角色,元件只認這層 */
--surface-page: 248 250 252;
--surface-card: 255 255 255;
--text-primary: var(--slate-900);
--text-secondary: var(--slate-700);
--action-primary: var(--teal-600);
--action-primary-hover: var(--teal-500);
--border-subtle: 226 232 240;
--state-success: 22 163 74;
--state-warning: 217 119 6;
--state-error: 220 38 38;
}
注意 --action-primary 用 600 而不是 500。在白底上,teal-600 對比度更接近 4.5:1,而 500 用在表格次要文字上會踩到 AA 下限。
3. 檢查對比度,而不是目測
每個文字色和背景色都要算。把結果寫進 design notes,之後審查才看得出哪個角色沒過。
配對 對比度 結論
text-primary / surface-card 16.8:1 AAA
text-secondary / surface-card 7.4:1 AAA
action-primary / surface-card 4.6:1 AA(一般文字)
action-primary / surface-page 4.2:1 不過,改用於大字或圖示
state-warning / surface-card 4.0:1 不過,限大字或圖示
action-primary 在 page 背景上不過 4.5:1,所以按鈕的背景要用 card 色,不要直接放在 page 上。這就是「語意角色」的價值:它逼你在邊界處做選擇。
4. 為深色主題重新挑值,不要反轉
深色不是把每個變數反相。palette-picker 會另外輸出一組深色角色,通常需要降低飽和度並提高文字明度。
[data-theme='dark'] {
--surface-page: 15 23 42;
--surface-card: 30 41 59;
--text-primary: 241 245 249;
--text-secondary: 203 213 225;
--action-primary: 45 212 191;
--action-primary-hover: 94 234 212;
--border-subtle: 51 65 85;
--state-success: 74 222 128;
--state-warning: 251 191 36;
--state-error: 248 113 113;
}
深色模式下狀態色通常要提高明度,因為暗背景會吃掉彩度。重新算一次對比度,不要沿用亮色的結論。
5. 用 theme-exporter 交付 CSS 變數
角色通過驗證後,把整份 token 交給 theme-exporter。它會把 raw 與語意層分開輸出,並提醒哪些元件直接讀了 raw 色票(這是違規)。
// theme-exporter 產生的 entry,元件只 import 這份
export const tokens = {
light: {
surfacePage: '248 250 252',
surfaceCard: '255 255 255',
textPrimary: '15 23 42',
actionPrimary: '13 148 120',
},
dark: {
surfacePage: '15 23 42',
surfaceCard: '30 41 59',
textPrimary: '241 245 249',
actionPrimary: '45 212 191',
},
} as const;
交付前確認三件事:元件沒有 hard-code --teal-500、Tailwind 的 theme.extend.colors 是合併而不是覆蓋、首次載入沒有亮暗閃爍。
驗收
- 亮色與深色主題都通過 AA(正文 4.5:1,大字 3:1)。
- 元件只讀語意角色,在專案內搜尋不到 raw hex。
- 按鈕在 page 背景上仍可讀(沒有踩到 4.2:1 那條)。
- 切換主題不會出現首屏閃爍。
關鍵學習點
- 從產品情境輸入,而不是色形容詞,
palette-picker才能選出可解釋的角色。 - 元件只認語意 token,raw 色票只給 theme-exporter 用,這是日後換膚不爆炸的前提。
- 對比度要逐對計算並記錄,深色主題必須重算,不能沿用亮色結論。
- 交付前檢查元件有沒有偷接 raw 色,Tailwind 設定是合併不是覆蓋。