畫布沒有 HTML:Figma 如何從零把無障礙做回來
一個 Figma 檔案不管幾百個圖層,畫布上只有一個 input。他們怎麼在底下長出一套 Mirror DOM,讓看不見螢幕的人也能逐層走過一份設計?
Figma 的畫布跟電玩遊戲是同一種東西——它把畫面渲染的工作從瀏覽器手上接過來,自己畫。這樣才撐得起無限縮放、多人即時協作這些重度功能。代價藏在看不見的地方:瀏覽器本來免費附贈的無障礙能力,整套消失了。
一個 Figma 檔案裡不管有幾百個圖層,畫布上只有一個 <input> 元素在接收焦點。對螢幕報讀器(screen reader)來說,這整張畫布看起來就是一個巨大、空白、沒有內容的方塊。
工程師 David Winslow 和產品經理 Elynn Lee 這篇文章,拆解 Figma 怎麼在畫布底下偷偷長出一套「Mirror DOM」,讓看不見螢幕的人也能用鍵盤逐層走過一個檔案、聽見每一次修改被念出來。
瀏覽器引擎裡有一個叫「無障礙樹」(accessibility tree)的資料結構,是從整份文件蒸餾出來、專門給輔助科技用的非視覺資訊。當一位報讀器使用者下「跳到下一個表單欄位」的指令,就是這棵樹告訴報讀器該往哪裡去。
瀏覽器怎麼長出這棵樹?靠 DOM、語意化的 HTML 標籤、ARIA 屬性,再加上一些額外運算出來的狀態。一般網頁裡每個元件都有自己的 DOM 元素——<button>、<p>、<img>。但 Figma 的畫布只有一個 <input>,於是瀏覽器替 Figma 建出來的那棵樹,幾乎是空的。
Figma 畫布真正渲染的資料結構叫 scenegraph——一棵由節點組成的樹。他們的做法是:在畫布背後、看得見畫面的人完全察覺不到的地方,渲染出一批「鏡像」DOM 元素,只映射 scenegraph 裡對輔助科技有意義的部分。
這套機制由四個系統合作完成:一棵快取所有圖層無障礙資訊的內部樹、一組把元素真的塞進 DOM 的 Mirror DOM 元件、一套讓畫布選取與系統焦點雙向同步的機制,以及一個負責把各種修改念出來的播報系統。下面這張官方流程圖,把「一個圖層」如何一路變成報讀器讀得到的內容講得很清楚。

Figma 從「瀏覽器最終需要知道什麼」反推回去,自己造了一棵內部無障礙樹。它替文件裡的每一個圖層,產生一段會被報讀器念出來的「accessible summary」。
這段摘要會隨情境改變。在 prototype 裡,多數編輯功能可以省略,只把最終觀看者需要的內容留下——文字欄位就念文字,有點擊互動的元件就給它一個 button 的角色。反過來,autolayout frame 在 prototype 裡被排除,但當使用者在編輯文件時就必須放進來。摘要做完後,系統由上往下走一遍整棵樹,把被省略的節點壓平。第一次載入文件時整棵樹會完整建好,之後則全程盯著每一次編輯,做外科式的局部更新,避開整棵重建的昂貴成本。
建好無障礙樹之後,就能生成 DOM。這件事交給一個會遞迴渲染自己的 React 元件:每個實例只訂閱某一個特定圖層在無障礙樹上的變化,圖層變了,React 幫忙把 DOM 的改動也壓到最小。
有一個細節會讓人意外——Figma 沒有用常見的「視覺上隱藏」手法來處理這些看不見的內容。原因是:一份 Figma 文件的空間佈局本身就承載著意義,就算不顯示畫面,這些元素的位置依然餵給輔助科技使用。螢幕放大鏡會平移畫面、把聚焦的元素留在視野內;報讀器會替弱視使用者畫出高對比外框;語音控制工具也會在畫面上標出提示。每個圖層都帶著一個 affine transform(仿射變換),記錄位移、縮放、旋轉,Figma 用它把這些元素透過 CSS 精準擺到對的位置,連旋轉和傾斜都還原。

焦點(focus)是系統層級的鍵盤屬性,標記使用者此刻正在互動的元素。對報讀器和純鍵盤使用者來說,按 Tab 應該讓焦點依視覺佈局在頁面上移動。
麻煩在於「選取」和「焦點」不一定是同一個東西——想像你從下拉選單挑了一項,再按 Tab 把焦點移到送出按鈕。在 Figma 畫布裡,Tab 會改變被選取的節點,卻不動系統的鍵盤焦點,於是整張畫布在報讀器眼中看起來就是一個巨大的導覽目標。Figma 因此在 Mirror DOM 裡做了雙向同步:點畫布時,正確的項目會替報讀器取得焦點;報讀器動作移動了焦點時,畫面上的選取高亮也跟著更新。連 VoiceOver 的 rotor 這種控制項,都能因此直接選到畫布裡的節點。
前面兩套系統把 Figma 檔案的內容攤開給輔助科技,但光這樣不夠用來編輯——編輯時,應用程式必須把使用者的動作回報給他。像 nudge(用方向鍵微調位置)這種對明眼人一目了然的手勢,也需要提供可以被念出來的文字。
做法只有一種:靠 ARIA 標準裡的 live region。Figma 替產品裡每一個 toast 提示都加上 live region 標記,並支援「隱形播報」——那些明眼人看了只會覺得吵的事件(例如每按一次方向鍵就跳一個 toast),對無障礙卻很重要。他們還做了自動「合併」(coalescing):同類事件在很短時間內連續發生就併成一則。連按五次方向鍵各移一像素,系統會送出一句「移動了 5 像素」,取代把「移動了 1 像素」念五遍。
↓ 移動了 1 像素
↓ 移動了 1 像素
↓ 移動了 1 像素
↓ 移動了 1 像素
Figma 的無障礙工程走了好幾年,靠著和無障礙顧問機構 Fable 的合作,持續收進真實輔助科技使用者的回饋。它從 2022 年的 Prototype 觀看器開始,2023 年做到 FigJam,2024、2025 兩年推出「Adapt content for screen readers」設定、第一版畫布鍵盤控制、加強對比模式,還有十五項以上的無障礙改進。
對正在學 Figma 的你,有幾個當下就能用的成果:色彩選擇器會告訴你一組配色是否符合無障礙準則;替可見圖層改名字,名字會傳給報讀器;而新的 check designs 功能能把你的設計對照 design system,一鍵標出低對比、建議符合 WCAG 2.0 AA 或 AAA 的顏色,讓你在交付給工程師之前先修掉。無障礙這件事永遠不會真正完成,每補上一塊,就替更多人移走一道參與創作的障礙。
② 解法是 Mirror DOM:在畫布底下鏡射出真的 DOM 元素給輔助科技用。
③ 內部無障礙樹替每個圖層寫「摘要」,隨情境變、只做局部更新。
④ 鏡射元素刻意不隱藏,因為空間位置本身就是意義(affine transform + CSS)。
⑤ 選取與焦點雙向同步;ARIA live region 把動作念出來,還會合併成「移動了 5 像素」。