Code Connect、llms.txt、MCP、規則檔都補上之後,agent 產出的東西確實變像樣了——但「像樣」正是問題的開始。前天那篇的結論是 agent 只會照它能機器讀到的東西做,於是我們把輸入端補到極限;今天處理輸出端,而輸出端的難處在於:AI 產的 UI 最擅長的就是「看起來對」。有位作者測完多家工具產出的 React 元件後給了一句很準的話——同樣的像素,一個是門,一個是門的畫。截圖過得了審查,螢幕報讀器卻打不開它。所以驗收標準不能是「看起來還行」,得換成一組能被機器執行、而且會擋住合併的檢查。
📖 學
三種典型缺陷,以及它們為什麼是結構性的
第一種是語意結構錯但視覺可過。Smashing Magazine 六月那篇〈Why Accessibility Is An Operational Capability, Not A Feature〉引了一個很具體的案例:一個 AI 產出的側邊欄,二十九行程式碼裡有十個獨立的無障礙缺陷——沒有 landmark、沒有標題、沒有清單結構、用帶 click handler 的元素代替 button、沒有 aria-expanded、沒有鍵盤處理、圖示沒有標籤。攤開 accessibility tree,回來的是一片扁平無結構的文字。
這不是偶發,是三股力量的合力。GitHub 上多數 React 程式碼本來就是非語意的 div 濃湯,模型學的就是那個;人類評審用眼睛判斷輸出好不好,回饋迴路獎勵外觀不獎勵語意;而 <div onClick> 的 token 數就是比 <button aria-expanded="true"> 少,沒有明確約束時模型會走便宜那條路。三股力量同向,所以結論是「AI 產的 UI 預設不無障礙」——不是偶爾,是預設。
第二種是狀態不完整。happy path 做得漂漂亮亮,空狀態、載入中、錯誤、長文字截斷全部沒有。原因很直接:prompt 裡沒寫,訓練資料的範例也多半只示範主要路徑。這類缺陷特別陰險,因為它在 demo 時完全看不出來,要等真實資料進來才炸。
第三種是細節層的品質衰退。NN/g 用真實專案評測了一輪 AI 原型工具,做啟發式評估並和自家設計師的產出對比。結論是:prompt 夠詳細時,工具能產出乍看和人類設計師很接近的版本,但湊近看就會發現相關元素缺乏視覺層級與群組、顏色用過頭、對比不足、margin 不一致。他們把這狀態命名為 good from afar, but far from good。
這些不是軼事。WebAIM Million 2026 掃了全球前一百萬個首頁:平均每頁 56.1 個可偵測的無障礙錯誤,比 2025 年的 51 個增加 10.1%;95.9% 的首頁有可自動偵測到的 WCAG 2 失敗,比 2025 年的 94.8% 更差,逆轉了前六年逐年改善的趨勢。同時每頁平均元素數衝到 1437,一年內成長 22.5%。WebAIM 自己在結論裡把這歸因於對第三方框架的依賴以及「自動化或 AI 輔助的開發實踐(vibe coding)」。
可機器驗證的那一層
先把能自動化的部分榨乾,因為人的注意力是最貴的資源。四層,由低到高。
代幣使用率與硬編碼債是前天那組 grep 指標,直接拿來當關卡:CI 裡數寫死的 hex 與 px,超過門檻就紅燈。關鍵是設「相對門檻」——新增的硬編碼不得超過 N 個,比「總量必須為零」實際得多。
元件來源檢查用 ESLint 的 no-restricted-imports 做白名單,或用 no-restricted-syntax 攔截「在系統元件上直接塞 className、style」的覆寫行為。覆寫率高通常不是違規而是需求訊號:元件缺了某個 prop,或 Code Connect 的 snippet 沒把對映寫清楚。
TypeScript 型別是最被低估的關卡。把禁用組合寫進型別——用 discriminated union 讓 variant: 'ghost' 根本無法接受 elevation,讓非法狀態在型別層不可表示。槓桿高在時機:agent 產完碼會自己跑型別檢查,紅字當下就自己修好了,這道關卡連人都不用經過。文件寫十遍「ghost 不要加陰影」,不如一行型別。
jsx-a11y 這類 lint 快而便宜,該裝。但要清楚邊界:它是靜態 AST 檢查,看不穿 spread 運算子、無法判斷 runtime 才決定的變數值、無法驗證 aria-labelledby 指向的元素真的存在。最麻煩的是它抓不到組合層的錯:每個元素單獨看都合法,湊起來卻壞掉——這種 bug 過得了 lint 直接上線。官方文件自己就寫著要搭配 @axe-core/react 去測渲染後的 DOM。
無障礙自動化的天花板究竟在哪
這裡的數字要講清楚,因為業界流傳兩個看似矛盾的版本。長年被引用的說法是自動化只能涵蓋 20–30%;Deque 後來用超過兩千次稽核、一萬三千多頁、近三十萬個問題的匿名資料做研究,得出 axe 自動涵蓋 57%。
兩個數字不衝突,差在分母。三成那個算的是「WCAG 成功準則的條數」,57% 算的是「實際遇到的問題總量」。差這麼多是因為低對比、缺 alt、缺表單標籤這幾類出現頻率極高,而它們剛好都是機器抓得到的。WebAIM 的資料佐證了這點:六類錯誤(低對比 83.9%、缺替代文字 53.1%、缺表單標籤 51%、空連結 46.3%、空按鈕 30.6%、缺文件語言 13.5%)就佔了全部偵測到錯誤的 96%,而且這份名單七年沒變過。
所以正確的理解是:自動化掃掉「量大且機械」的那一半,剩下的是判斷題——alt 寫得好不好、焦點順序合不合理、錯誤訊息有沒有真的幫上忙。WebAIM 在方法論那段寫得很直白:沒有偵測到錯誤,不代表頁面是無障礙或合規的。
| 這層交給機器 | 這層只能人驗 |
|---|---|
| 對比度數值、缺 alt、缺 label、空按鈕空連結 | alt 文字寫得對不對、有沒有意義 |
| ARIA 語法合不合法、role 有沒有必填屬性 | 焦點順序符不符合視覺與心智順序 |
| 標題階層有沒有跳級、有沒有多個 h1 | 標題結構反不反映真實內容層級 |
| 能不能被 focus、有沒有 tabindex 陷阱 | 鍵盤走完一遍實際順不順、卡不卡 |
| 表單有沒有關聯到 label | 錯誤訊息說不說得清楚該怎麼修正 |
| 代幣使用率、硬編碼債、元件來源 | 這個介面是不是在解對的問題 |
流程上分四層擺:commit 前跑 ESLint;元件開發當下用 Storybook 的 a11y addon 看面板(底層就是 axe-core),CI 用 test runner 掃過每一個 story;關鍵動線用 Playwright 加 axe 在真實流程裡掃;最後是人工——每個 PR 至少拔掉滑鼠用 Tab 走一遍,每季找一種螢幕報讀器實測。GOV.UK 設計系統的做法值得抄:元件同時跑自動化測試與 JAWS、NVDA、VoiceOver、TalkBack 人工測試,而他們也講得很清楚,用了設計系統不會「魔法般地」讓服務變無障礙,它只是給你一個比較高的起點。
視覺回歸測試在 AI 時代的角色變了
前幾天談 VRT 時,前提是「非預期的視覺變化必須有人點頭」。AI 大量產出後,這個前提被兩件事扭曲。
第一件是量。一個 agent PR 可能同時動到幾十個元件,基準圖維護成本從邊際成本變成主要成本。TurboSnap 這類用 bundler 依賴圖裁剪的機制從「省錢功能」升格成必要條件——它只截受改動影響的 story,在兩百個元件的專案裡能把一次三元件 PR 的截圖量壓掉一個數量級。
第二件是對 flaky 的容忍度必須歸零。當 diff 數量翻倍,只要有一個測試偶爾無理由變紅,團隊會在兩週內養成「反正是雜訊,全部核可」的習慣,那一刻整套關卡就死了。量越大,雜訊殺傷力越強,不是越弱。
還有一個容易被忽略的界線:VRT 對「AI 修改既有元件」有效,對「AI 生成全新畫面」幾乎無效。新畫面沒有基準可比,第一次跑產生的基準就是它自己,等於自己認證自己。新畫面要靠的是狀態覆蓋與 a11y 掃描,VRT 得等它進入維護期才開始發揮作用。搞錯這點,會誤以為裝了 Chromatic 就守住了新功能。
狀態覆蓋:AI 最常漏的一類
這是投報率最高的檢查表:寫成 Storybook story 之後,一次買到三種覆蓋——VRT 有截圖對象、a11y 掃描有掃描對象、型別檢查有實例可檢。
| 狀態 | 該問的問題 |
|---|---|
| 空狀態 | 第一次使用時畫面說了什麼?有沒有指出下一步 |
| 載入中 | 骨架屏還是轉圈?有沒有 aria-busy 或 live region 通知報讀器 |
| 錯誤 | 訊息在哪出現?焦點會不會移到它?說不說得出怎麼修 |
| 長文字 | 標題 80 個字會怎樣?截斷後完整內容還讀得到嗎 |
| 極端資料 | 0 筆、1 筆、9999 筆、負數、超長數字各長什麼樣 |
| RTL | 鏡射後圖示、箭頭、進度條有沒有跟著翻 |
| 暗色模式 | 對比度在暗色下還過得了 AA 嗎?陰影還看得見嗎 |
| 鍵盤焦點 | 焦點指示器在每個狀態下都看得見嗎,包括暗色與 hover |
| 200% 縮放 | 放大兩倍後有沒有橫向捲軸、有沒有內容被切掉 |
把「每個元件的 story 數 ÷ 應有狀態數」當指標追蹤,比追「有沒有寫測試」有意義得多。而且這張表可以直接貼進 AGENTS.md,讓 agent 產元件時就一次把 story 補齊——這是把驗收前移到生成階段最省力的一招。
人該看什麼
自動化把機械層掃乾淨之後,人的時間要留給四件機器碰不到的事:資訊層級(使用者第一眼該看到什麼,實際上先看到什麼)、動線(流程能不能少一步)、文案(這段字在使用者最焦慮時讀起來像什麼),以及最上層那個問題——這個介面是不是在解對的問題。
NN/g 那份評測講到一個值得咀嚼的悖論:要讓 AI 產出精準結果,你得先把規格寫到很細、或附上高保真參考檔;而當你做完這些,大部分設計工作其實已經由你完成了。AI 降低了產出門檻,卻放大了「還可以」與「真的好」的差距。對驗收的意義是:人的價值往上游(定義問題、把規格寫到能被執行)和最上層(判斷這是不是對的解)移動。
具體到 code review,直接改變提問方式:不要問「這個顏色對不對」(機器會抓),改問三題——使用者在這個畫面要完成的事情是什麼,視覺權重有沒有指向它?這個流程有沒有哪一步其實可以刪掉?如果操作失敗了,使用者知道發生什麼事、也知道怎麼辦嗎?
界線與反方
關卡會被繞過,而且成本極低。// eslint-disable-next-line、git commit --no-verify、把 CI 裡失敗的檢查悄悄改成 warning——這三招在趕上線的那週一定會出現。所以除了通過率,更要量豁免數:eslint-disable 註解總數、被 skip 的測試數、被降級成 warning 的規則數。這些趨勢比通過率誠實太多,因為通過率可以靠關掉檢查來提升。
**指標一旦變成目標就會被優化。**代幣覆蓋率 100% 不代表設計好,只代表沒人寫死顏色;axe 零違規不代表介面能用,只代表機器抓得到的那半沒問題。Goodhart 定律在這裡特別容易發作,因為這些數字太好看、太容易做成儀表板。防禦方式是把每個自動化指標都綁一個人工訊號:零違規要配上「這季有沒有真的用螢幕報讀器走過一次」,覆蓋率要配上「使用者測試裡有沒有人卡住」。
**不要把設計審查變成純機械檢查表。**檢查表擅長抓「已知的錯」,抓不到「沒想到的可能性」。一個全綠的 PR 可能是個沒有任何錯誤、也沒有任何觀點的介面。設計討論若全部變成跑分,你會慢慢失去「這裡好像哪裡怪怪的」那種判斷力,而那正是 AI 最缺、也最難補的東西。
最後一個實務提醒:現在要對的標準仍然是 WCAG 2.2 AA。WCAG 3 在 2026 年三月更新了工作草案,改成約 174 條以結果為導向的要求、用彈性評分取代檢查表,但它仍是 Working Draft,候選推薦約落在 2027 年第四季,正式標準不會早於 2028 年。別為了還沒定案的標準重寫關卡,但值得留意它的方向——「評分取代通過/不通過」本身就在說:無障礙從來不是二元的。
🧠 記
- AI 產的 UI 三種典型缺陷:語意結構錯(像素對但 accessibility tree 是扁平文字)、狀態不完整(只有 happy path)、細節品質衰退(層級、群組、對比、間距,遠看好近看差)。
- 這是結構性而非偶發:訓練資料以非語意 div 為主、人類評審用眼睛給回饋、
<div onClick>比語意標籤省 token——三股力量同向。 - 自動化天花板的兩個數字不衝突:按 WCAG 準則條數算約三成,按問題總量算 Deque 測得 57%,因為高頻問題剛好都是機器抓得到的那幾類。
- 最被低估的關卡是 TypeScript 型別:把禁用組合寫成不可表示的狀態,agent 產碼當下就會自己修,連人都不用經過。
- VRT 對「AI 改既有元件」有效,對「AI 生成全新畫面」幾乎無效——新畫面的第一張基準是自己認證自己。
- 通過率可以靠關掉檢查來提升,豁免數(disable 註解、skip 測試、降級成 warning 的規則)才是誠實的指標;人的時間則留給資訊層級、動線、文案,以及「這個介面是不是在解對的問題」。
✍️ 實踐
挑一個最近由 AI 產出、還沒被仔細看過的元件,用 15 分鐘跑三道關卡。
- **語意檢查(4 分鐘)。**打開 DevTools 的 Accessibility 面板看它的 accessibility tree,問三件事:互動元素是
button/a還是帶 click handler 的div?有沒有 landmark 與標題?圖示按鈕有沒有可讀的名稱?把「扁平成一串文字」的地方圈出來。 - **狀態盤點(5 分鐘)。**對照上面那張狀態表逐項打勾,數出「已有狀態數 ÷ 應有狀態數」。缺的直接開成 Storybook story 空殼——只要名稱和 args,不用寫完,光是列出來就會逼出一堆沒想過的問題。
- **鍵盤走一遍(4 分鐘)。**手離開滑鼠,只用 Tab、Shift+Tab、Enter、Space、Esc 走完完整互動。記三件事:所有可操作的東西都到得了嗎?焦點看得見嗎?打開的東西關得掉、焦點回得去嗎?
- **記下基線(2 分鐘)。**把「狀態覆蓋比」與「axe 違規數」連同日期寫進你那張指標表,註明這是 AI 產出的元件。
自我檢查兩題:
- 如果只准保留一道自動化關卡,你會留哪一道?答案若不是「能在 agent 產碼當下就紅字」那種(型別、lint),代表你的關卡都擺得太後面,修復成本會比必要的高一個量級。
- 你剛才用眼睛看出來的問題裡,有幾個其實機器抓得到?那幾個就是下次該自動化的對象——人只該看機器看不到的東西。
🔗 延伸學習
- The WebAIM Million — 2026 report — 全球前百萬首頁的年度掃描,56.1 錯誤/頁、95.9% 有 WCAG 失敗、元素數一年增 22.5%,結論直指 AI 輔助開發。
- Automated Testing Study Identifies 57% of Digital Accessibility Issues — Deque — 57% 的來源與方法論,說明它和「三成」的舊說法為何不矛盾。
- Why Accessibility Is An Operational Capability, Not A Feature — Smashing Magazine — 含 AI 側邊欄「29 行 10 個缺陷」的案例與 CI 關卡擺法。
- Good from Afar, But Far from Good: AI Prototyping in Real Design Contexts — NN/G — 用真實專案做的啟發式評估,說清楚 AI 原型在細節層漏掉什麼。
💬 問 AI
我們團隊有相當比例的 UI 程式碼由 AI agent 產出(React + TypeScript + Storybook,
設計系統已有 design token 與 Code Connect),要建立一套產出後的驗收關卡。
請幫我:
1. 設計一套四層 CI 關卡(型別 → lint → 元件層自動化測試 → 動線層測試),
每層寫出:工具、擋什麼、擋不住什麼、執行時間,
並標出哪層該是 blocking、哪層只該產報告。
2. 給我一份「AI 產出元件的人工審查清單」,明確排除所有機器抓得到的項目,
只留下需要人類判斷的問題,每題附上判斷準則而不只是問句。
3. 針對狀態覆蓋(空/載入/錯誤/長文字/極端資料/RTL/暗色/200% 縮放),
給我可複製的 Storybook CSF3 story 模板,並說明每個 story 能讓 VRT 與 axe 掃到什麼。
4. 設計三個「反 Goodhart」的稽核指標,用來偵測關卡被繞過
(eslint-disable 數量、skip 測試、規則降級等),並說明健康門檻與異常時怎麼追。
請用繁體中文、台灣用語回答,設定檔與程式碼給最小可用版本即可。