Day 17 · W3 · 無障礙線 · 難度 ★★★☆☆
本系列由 AI 協作撰寫。 內容、技術判斷、程式碼由 light-design 數位顧問團隊與 Claude 共同產出,最終由作者驗證後 publish。完整協作模式與把關方式見 Day 01。
四天前我把鍵盤按過去,一站一站量焦點外框跟背後顏色的對比。33 個停留點,兩個的外框眼睛看不見。
今天我把同一個網站切成深色,再按一次。
淺色 33 個停留點,2 個不合格
深色 33 個停留點,32 個不合格
CSS 一行都沒改,連焦點外框的色碼都一模一樣。變的只有它背後那片顏色。
一句話主軸:配色切了,狀態樣式沒跟著切。
prefers-color-scheme 是一個媒體查詢,跟 max-width 同一個層級。使用者在系統或瀏覽器選了深色,這段就會生效:
@media (prefers-color-scheme: dark) {
:root { --bg: #15171A; --ink: #E4EEEC; }
}
所以深色模式不是「反色濾鏡」,是同一份 CSS 的另一個分支。設計系統通常只在這個分支裡重新定義色票,其他規則原封不動。
問題就出在原封不動的那些。對比是兩個顏色之間的關係,換掉其中一個,另一個沒動,數字就變了。
要量它必須真的開瀏覽器:
a11y-moda site https://light-design.com.tw --level AAA \
--render --dark-mode
--dark-mode 會跑兩趟,淺色一次深色一次,然後合併。只在深色出現的問題會標上 [深色模式] 前綴,才分得出哪些是原本就有的。
代價是時間翻倍。值不值得,看你的網站有沒有那個分支。
不裝工具也看得到。Chrome DevTools 的 Rendering 面板有一個 Emulate CSS prefers-color-scheme,切成 dark,然後用 Tab 一站一站走。
麻煩的地方不是切換,是記錄。一頁三十幾個停留點,走完要幾分鐘,而且你得同時記住哪一個看得見、哪一個看不見、以及每一個的外框是什麼顏色。六個頁面走下來,這件事就從「看一眼」變成「做一份表」。
我把焦點外框的實際色值印出來,兩種模式各一次。
淺色 深色
outline-color rgb(10, 71, 176) rgb(10, 71, 176)
頁面底色 rgb(252,253,254) rgb(21, 23, 26)
外框顏色一模一樣。 品牌藍在深色分支裡沒有被重新定義過。
拿 WCAG 的對比公式算一次:
品牌藍 vs 淺底 8.17 : 1 ✅
品牌藍 vs 深底 2.17 : 1 ❌ 門檻 3:1
同一個色碼,一個過得很寬鬆,一個不到門檻的三分之二。

外框的色碼沒變,變的是它畫在什麼顏色上面。
會拿外框跟頁面底色比而不是跟元件自己的填色比,是因為這個外框畫在元件外面。那件事 Day 13 講過,這裡不重複。
這像是換了整個房間的燈。牆壁、地板、家具的顏色都跟著配色方案換了,只有你貼在牆上那張黃色便利貼沒換 —— 白牆上一眼看到,深色牆上就融進去了。
焦點外框就是那張便利貼。
而且它只在鍵盤操作的時候才會出現。我自己驗收深色配色是拿滑鼠點的,從頭到尾沒有一個元件顯示過焦點外框,那圈東西根本沒進到我的視野裡。
它不是內容,是狀態。而狀態樣式很少被列進配色檢查清單,因為做清單的時候它不在畫面上。
深色那趟多報了 6 條,全部是同一個檢測碼。
CS3241300E 焦點指示器對比(WCAG 2.4.13)
淺色 深色
/ 2/33 32/33
/contact 1/22 22/22
/about-us 1/18 18/18
/pricing 1/28 28/28
/works 1/22 22/22
/blog 1/51 51/51
─────────────────────────────
合計 7/174 173/174

同一份 CSS,同一批停留點,只換了配色分支。
五個頁面在深色下是全滅。到這個地步已經不用數哪些不合格,要數的是還活著的那一個。
部落格那頁 51 個全倒,是六頁裡最多的。停留點多是因為文章列表每一則都是連結,而每一則用的是同一個外框。
同一個錯誤複製五十一次,不會變成五十一種錯誤。它還是一個。
首頁 33 個停留點裡,深色下有一個過關。我把外框顏色不是品牌藍的元件全部挑出來,只有兩個,其中一個是搜尋開關:
淺色 深色
其他所有元件 rgb(10, 71, 176) rgb(10, 71, 176)
搜尋開關(3px 外框) rgb( 32, 34, 37) rgb(245,247,250)
淺色下它是深灰,深色下換成近白。有人替這一顆按鈕想過深色。
一個網站裡有一兩個元件做對、其他沒有,通常不是判斷錯誤,是那幾個元件的樣式剛好被單獨改過 —— 可能是後來補的,也可能是從別的地方複製來的。
這種「局部正確」比全錯難發現。你隨手點到它的時候會覺得沒問題。
工具報的最低對比是 1.15,比我手算的 2.17 還低。追下去是搜尋框:
outline-color rgba(10, 71, 176, 0.25)
四分之一透明度的藍,疊在深色底上,實際呈現的顏色會非常接近底色本身,所以對比掉到 1.15。這個數字是對的:半透明的顏色要先跟背後的顏色合成,再拿合成後的結果去算對比。如果直接把 alpha 丟掉,算出來會是 2.17,反而看起來沒那麼糟。
工具在這裡做對了它該做的事。
淺色底下同一個外框是 1.54,本來就不合格。深色只是讓它更慘。
我原本以為深色那趟會多出一批文字對比 —— 那是這個維度最典型的問題型態。結果一條都沒有。 多出來的 6 條全是焦點外框。
Day 02 提過我們曾經被指出一組深色模式下的文字對比問題,數字是 2.24。那組後來修好了,今天在深色下重新量,那個區塊乾淨。
所以「切成深色之後整個網站都要重驗」不成立。實測是只有一條規則在動,但那條規則整站全滅。這兩件事聽起來差不多,做起來完全不同 —— 前者是重來一遍,後者是改一個色票。
設計系統的色票通常長這樣:
:root { --brand: #0A47B0; }
@media (prefers-color-scheme: dark) { --brand: #0A47B0; }
深色分支裡把 --brand 又寫了一次,值一樣。看起來像是「已經考慮過了」,實際上只是複製貼上。
真正該被定義的不是顏色本身,是這個顏色在哪些底色上要成立。--brand 當內文連結、當按鈕填色、當焦點外框,背後的顏色完全不同,能不能過門檻也完全不同。而 :focus 的樣式通常寫在元件檔案裡,離色票定義很遠,配色改版的時候沒有人會打開那個檔案 —— 色票檔改完、視覺確認過、深色分支上線,整條流程沒有任何一步會碰到它。
這條檢測碼的正式描述是「建立雙色焦點指示器以確保與所有組件有足夠的對比度」。第一次讀到「雙色」我以為是美術要求,其實它是在解一個幾何問題。
單色外框只有一個顏色,它必須同時跟外面的頁面底色、以及裡面的元件填色都拉開距離。兩種配色模式一乘,就是四個組合都要過。一個色碼很難同時滿足。
雙色的做法是讓外框自己帶對比:
button:focus-visible {
outline: 2px solid #FFF;
outline-offset: 0;
box-shadow: 0 0 0 4px #0A47B0; /* 外圈深、內圈淺 */
}
白色那圈貼著元件,藍色那圈落在頁面上。不管背後是淺是深,總有一圈跟它形成對比。這樣就不必為每個配色分支各調一次。
另一個更省的寫法是讓外框吃 currentColor。文字顏色本來就會隨配色切換,外框跟著走,兩邊自動成立。代價是外框顏色不再是品牌色。
:focus、:hover 寫在元件裡,配色改版時沒人會打開那個檔案。明天 Day 18:焦點的顏色看完了,接下來看它的順序。我的部落格上有三顆按鈕,Tab 走不到它們,因為我把它們從 Tab 順序裡拿掉了,而那樣鍵盤才算走得對。
iThome鐵人賽