
文章同步發表在我的個人 Blog
發現一個缺口,補上去就結束了嗎?下一次有人漏掉同樣的東西,誰來擋?
Day 14 寫過:verify.sh 只驗證「寫成規則的事」,沒有寫成規則的東西,檢查不會知道少了。這幾天在手機上實際用,剛好找到兩個這種缺口:App 圖示是空的(Day 19)、Dark 模式的顏色沒有色碼(Day 19 補上)。今天把它們寫成檢查,下次漏掉,verify.sh 會直接擋下來。
先講結果:新增一項圖示掃描,加 4 個自我測試案例;新增 3 個顏色對比測試。故意把一個顏色改壞,確認測試真的會失敗。
Day 19 的問題是,AppIcon.appiconset 只有 Contents.json,每個位置都沒填 filename,建置不會出錯。掃描要檢查三件事:
filename
PNG 的寬高不需要額外的工具:檔案開頭 8 個位元組是固定的簽名,接著 IHDR 區塊,寬和高就在第 16 到 24 個位元組。腳本用 Python 的標準函式庫讀出來:
head = open(path, "rb").read(24)
if head[:8] != b"\x89PNG\r\n\x1a\n": # PNG signature
problems.append(f"{name} is not a PNG")
w, h = struct.unpack(">II", head[16:24]) # width, height from IHDR
宣告是 1024x1024,圖片卻是 512×512,掃描一樣會失敗。這個掃描接進 scripts/scans.sh,所以 verify.sh 每次都會跑。
Day 14 提過,掃描本身要先證明抓得到,不然寫錯了、永遠零命中也看不出來。scans-selftest.sh 的做法是現場建立假專案,違規的一定要失敗、合法的一定要通過。這次新增 4 個案例:
| 案例 | 預期 |
|---|---|
位置沒有 filename(只宣告位置、沒有圖) |
失敗 |
filename 有寫,但檔案不存在 |
失敗 |
| 圖片只有 512×512,宣告是 1024×1024 | 失敗 |
| 圖片齊全、尺寸正確 | 通過 |
第一個案例就是 Day 19 的原始狀況。測試要用到真的 PNG,這裡也不依賴外部工具:用 Python 的 struct 和 zlib 現場組出一張純色的 PNG。自我測試的案例從 46 個變成 50 個,全部符合預期。
Day 19 選 Dark 模式的燈號顏色時,我算了三種做法的對比值,結果寫在文章裡。但那只是一次性的計算,之後有人改了顏色,沒有東西會提醒。所以把公式寫成測試。
WCAG 2.1 的定義:每個色頻(0 到 1)先做 gamma 轉換,再加權成亮度,對比值是亮的那個加 0.05,除以暗的那個加 0.05:
func linear(_ c: Double) -> Double {
c <= 0.03928 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4)
}
let luminance = 0.2126 * linear(r) + 0.7152 * linear(g) + 0.0722 * linear(b)
let ratio = (lighter + 0.05) / (darker + 0.05) // AA text: at least 4.5
新增三個測試:
#767676 配白色剛好過 4.5,#777777 差一點不到。公式寫錯,這個測試會先失敗。測試通過只代表目前的顏色沒問題。要確認它真的會擋,我把 Dark 的紅色暫時改成一個很暗的 #8A3A33,再跑 Dark 燈號色的測試,結果失敗了。確認之後,馬上還原。
順便把 Light 模式的顏色也算了一遍,對畫面底色 #FBF7F2 的對比值:
| 顏色 | 對比值 |
|---|---|
| 綠 | 2.45 |
| 黃 | 1.74 |
| 紅 | 3.28 |
都不到 4.5,連非文字元件常見的 3:1 也有兩個不到。這幾個顏色的 Light 值是設計規範訂的,只有我能改,而且畫面上沒有拿它們當文字用,所以我沒有寫成測試(寫了,測試就會一直失敗)。這個結果只是記下來:如果之後要把這些顏色當文字,就會踩到。
| 項目 | 結果 |
|---|---|
| 圖示掃描 | 新增,接進 scans.sh |
| 掃描自我測試 | 50 個案例(原 46 個,新增 4 個) |
| 顏色對比測試 | 新增 3 個 |
| 故意改壞確認 | Dark 紅色改成 #8A3A33,測試失敗,已還原 |
verify.sh |
VERIFY: PASS(規則測試 93、App 測試 170) |
Day 21 中場回顧:回頭看這兩週 Claude Code 和 Codex 怎麼分工,以及投票頁的結果。
官方文件