前端無障礙開發(fā)指南
作者 | 孫郁儼
The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect," said Tim Berners-Lee, W3C Director and inventor of the World Wide Web.
30年前,Tim Berners-Lee 在歐洲核子研究中心創(chuàng)建了第一個(gè) Web 網(wǎng)頁,宣告了萬維網(wǎng)的誕生。自此,萬維網(wǎng)就承載著開放平等的愿景。
Accessibility——無障礙設(shè)計(jì)&信息無障礙(也簡稱為 A11y),雖然常常會(huì)被解釋為”為殘障人士服務(wù)“,但其無障礙設(shè)計(jì)的核心在于為所有人提供同等的體驗(yàn)。我們每個(gè)人都有可能在某些時(shí)刻成為失能者,這稱為場景性殘疾(situational disability & temporary disability),比如受傷骨折后,暫時(shí)失去了部分活動(dòng)能力。又比如被強(qiáng)光照射時(shí),看不清楚事物,在嘈雜地鐵中的聽力產(chǎn)生障礙等等。
根據(jù) W3C 組織的定義,Web accessibility 意味著每個(gè)人都可以感知、理解并與 Web 交互,甚至為 Web 做出貢獻(xiàn)。中國工信部也指出,信息無障礙是指通過信息化手段彌補(bǔ)身體機(jī)能、所處環(huán)境等存在的差異,使任何人(無論是健全人還是殘疾人,無論是年輕人還是老年人)都能平等、方便、安全地獲取、交互、使用信息。
但在萬物互聯(lián)的當(dāng)下,盡管我們的衣食住行早已與網(wǎng)絡(luò)世界息息相關(guān),互聯(lián)網(wǎng)并未成一個(gè)平等的,人人都可以訪問的世界。根據(jù)2022 年 The WebAIM Million 統(tǒng)計(jì)報(bào)告,在對(duì) 100萬 個(gè)網(wǎng)站首頁進(jìn)行無障礙分析后,得到的結(jié)果卻差強(qiáng)人意:
- 在 100 萬個(gè)首頁中,一共檢測到 50,829,406 項(xiàng)非重復(fù)的無障礙錯(cuò)誤,平均每個(gè)首頁有50.8個(gè)錯(cuò)誤。
- 96.8% 的首頁檢測到了 WCAG 2 錯(cuò)誤,未能達(dá)到 WCAG 2的標(biāo)準(zhǔn),盡管現(xiàn)在最新的標(biāo)準(zhǔn)是WCAG 2.1
- 在殘障用戶的頁面訪問流程中,每交互 19 個(gè)首頁元素,就可能遇到一個(gè)無障礙錯(cuò)誤
圖源:2022 年 The WebAIM Million 報(bào)告
在這些頁面無障礙錯(cuò)誤中,96.5%的錯(cuò)誤歸屬于以下五類:
- 頁面顏色對(duì)比度不達(dá)標(biāo),影響視力障礙用戶的訪問體驗(yàn)。
- 未定義<img />標(biāo)簽的alt屬性,影響輔助技術(shù)(Assistive technologies, ATs) 如屏幕閱讀器等設(shè)備獲取圖片信息。
- 空鏈接和空按鈕,指不包含不包含實(shí)際的文本的<a>標(biāo)簽或 <button>標(biāo)簽。這些標(biāo)簽只包含一個(gè)圖像或一個(gè)文本的圖像,會(huì)導(dǎo)致使用 ATs 設(shè)備的用戶無法感知可交互元素的實(shí)際用途。
- 表單元素<input />標(biāo)簽沒有對(duì)應(yīng)的<label>標(biāo)簽。</label><label>標(biāo)簽對(duì)于 ATs 設(shè)備至關(guān)重要。沒有它,用戶將無法感知他們?cè)谂c哪個(gè) <input /> 標(biāo)簽交互。
- <html>標(biāo)簽沒有設(shè)置lang屬性。不同的語言類型在屏幕閱讀器中的發(fā)音是不同的,比如six單詞在法語和英文兩種類型的屏幕閱讀器中的發(fā)音就非常的不同。在</html><html>上定義lang屬性,會(huì)告知 ATs 設(shè)備當(dāng)前頁面所使用的語言。
作為前端開發(fā)者,我們要如何把關(guān)頁面的無障礙功能呢?
在前端開發(fā)的視角中,每一個(gè) Web 應(yīng)用都可以拆解為 HTML、CSS 和 JavaScript。HTML 會(huì)經(jīng)過 HTML Parser 將 HTML 結(jié)構(gòu)轉(zhuǎn)換成 DOM Tree;CSS 會(huì)經(jīng)過 CSS Parser 將 CSS 轉(zhuǎn)換成 CSSOM Tree。最終,瀏覽器根據(jù) DOM Tree 和 CSSOM Tree 構(gòu)建出最終的 Render Tree。
對(duì)于無障礙 Web 應(yīng)用,除了包含 DOM 和 CSSOM 之外,將包含 AOM (Accessibility Tree,可訪問性樹)。AOM 可訪問性樹和 DOM 樹平行存在。簡單來說,可訪問性樹是 DOM 樹的一個(gè)子集。每個(gè)需要暴露給 ATs 輔助技術(shù)的 DOM 元素都對(duì)應(yīng)一個(gè)在可訪問樹中存在的無障礙對(duì)象。定義 AOM 實(shí)現(xiàn)的標(biāo)準(zhǔn)是 WAI-ARIA(Web Accessibility Initiative – Accessible Rich Internet Application),即可訪問的互聯(lián)網(wǎng)富應(yīng)用標(biāo)準(zhǔn),致力于解決應(yīng)用的可訪問性問題,它與HTML5 標(biāo)準(zhǔn)同屬于 W3C 組織。Web 應(yīng)用的 AOM 也并非遙不可及,打開 Chrome 瀏覽器的 Devtools,我們即可查看頁面的 AOM 結(jié)構(gòu)。
在了解了無障礙的基本概念后,我們分別從 HTML、開發(fā)框架以及 CSS等角度,一起來看看無障礙頁面的實(shí)現(xiàn)方式吧。
編寫 HTML 時(shí)需要考慮的 Web Accessibility
就像瀏覽器引擎依賴 HTML 結(jié)構(gòu)以構(gòu)建頁面 UI 骨架,ATs 設(shè)備也依賴 HTML 結(jié)構(gòu)來構(gòu)建頁面的 AOM 可訪問性樹。所以語義化的 HTML 對(duì)于實(shí)現(xiàn) Web 應(yīng)用無障礙至關(guān)重要,因?yàn)樵?HTML 標(biāo)簽中包含了構(gòu)建 AOM 的必要元數(shù)據(jù)。
參考上圖,ATs 設(shè)備完全可以正確地渲染滑動(dòng)輸入框,即便我們沒有在HTML 標(biāo)簽上添加 WAI-ARIA 屬性。但我們?cè)陂_發(fā)時(shí)往往會(huì)忽略 HTML 元素的實(shí)際語意,而更多采用無語意的 <div> 和 <span> 標(biāo)簽 (<div> 和 <span> 之外的近 104 個(gè) HTML 標(biāo)簽都具有語義信息)。因?yàn)檫@兩個(gè)標(biāo)簽沒有默認(rèn)樣式,足夠簡單,就像白紙一樣可以隨意畫上 CSS 樣式。但這樣的標(biāo)簽,對(duì)于 ATs 設(shè)備來說,就是災(zāi)難。
以上圖為例,對(duì)于 <button> FOO </button> 標(biāo)簽,讀屏軟件將讀出 “Button, foo",告知用戶當(dāng)前元素是按鈕,包含文字 foo。但對(duì)于 <div class="button"> FOO </div> 標(biāo)簽,讀屏軟件只能讀出 “foo”,并不能提示當(dāng)前元素是一個(gè)可交互的按鈕。雖然我們也可以通過設(shè)置 WAI-ARIA 屬性為 HTML 標(biāo)簽增添無障礙語意,比如 <div class="button" role="button"> FOO </div>,但這樣會(huì)平添許多額外的工作,也增加了出錯(cuò)的機(jī)率:根據(jù) The WebAIM Million 統(tǒng)計(jì)報(bào)告,包含 ARIA 的頁面比不使用 ARIA 的頁面,檢測出無障礙性錯(cuò)誤的可能高 70%。
通過 HTML 提升頁面可訪問性
規(guī)則 1:結(jié)構(gòu)和樣式分離
在社區(qū)中一直都有人在提倡 CSS裸奔日(CSS Naked Day),編寫 HTML 時(shí)不要基于 UI 視覺效果(CSS 樣式),而是基于 UI 的頁面結(jié)構(gòu),可以確保 HTML 的語義完善,增強(qiáng)頁面可訪問性。
相關(guān)瀏覽器插件:HeadingsMap - Chrome Web Store
規(guī)則 2:只在必要時(shí)使用 ARIA
WAI-ARIA 的全稱是 Accessible Rich Internet Applications,簡稱 ARIA,是 W3C規(guī)范之一。ARIA 允許 Web 開發(fā)者創(chuàng)建只有 ATs 技術(shù)(比如屏幕閱讀器)可以看到的內(nèi)容(屬性),用以實(shí)現(xiàn) HTML 無法達(dá)成的無障礙功能,比如:
- 增強(qiáng)交互式控件的可訪問性,比如下拉菜單、彈窗,滑塊等
- 為頁面結(jié)構(gòu)定義有用的地標(biāo)
- 定義動(dòng)態(tài)更新的“活動(dòng)區(qū)域”
- 改善鍵盤可訪問性和交互性
ARIA 表現(xiàn)為 HTML 的屬性,確定了元素的 ARIA 角色、狀態(tài)和屬性。這些信息幫助 ATs 技術(shù)更好地理解 Web 頁面,確保用戶與頁面元素的交互。一般情況下,ARIA 不會(huì)影響 Web 頁面的渲染,也不會(huì)影響鼠標(biāo)或鍵盤用戶的行為,只有使用輔助技術(shù)的用戶才能感知到 ARIA。開發(fā)人員隨意使用 ARIA 所導(dǎo)致的問題,對(duì)于頁面無障礙功能往往是致命的,而且難以察覺。
但 ARIA 永遠(yuǎn)無法替代語義化 HTML 標(biāo)簽,NO ARIA is better than bad ARIA。請(qǐng)優(yōu)先考慮語義最貼近的 HTML 標(biāo)簽,只在必要時(shí)使用 ARIA。
相關(guān)瀏覽器插件:
- Visual ARIA - Chrome Web Store
- Landmark Navigation via Keyboard or Pop-up - Chrome Web Store
規(guī)則 3:提升表單結(jié)構(gòu)的包容性
(1) 采用 <fieldset> 為表單項(xiàng)分類
當(dāng)表單分為不同板塊時(shí),我們可能會(huì)使用 <div> 元素實(shí)現(xiàn)表單項(xiàng)的樣式板塊劃分,但這樣的劃分并不利于無障礙設(shè)備獲得表單項(xiàng)信息,可以使用<fileset>進(jìn)行替換。
(2) 正確使用 label,為 <input /> 標(biāo)簽設(shè)置對(duì)應(yīng)的 label
在實(shí)現(xiàn)表單時(shí),我們往往會(huì)通過 placeholder 來提示當(dāng)前表單項(xiàng)的填寫內(nèi)容。這樣的設(shè)計(jì)會(huì)導(dǎo)致當(dāng) input 得到焦點(diǎn)時(shí),placeholder 自動(dòng)消失,造成用戶無法感知當(dāng)前表單項(xiàng)的內(nèi)容。我們可以使用 label 充當(dāng) placeholder,這樣的交互方式也稱為 Float Label Pattern
(3) 盡可能使用原生的表單元素
在制作表單組件時(shí),我們往往會(huì)出于實(shí)現(xiàn) UI 樣式的要求,采用 <div> 替代原生的表單元素。盡管這些表單組件在視覺和功能上滿足了 UI 要求,但它們并未實(shí)現(xiàn)原生表單元素的無障礙功能。
(4) 為表單元素設(shè)置原生的校驗(yàn)屬性
required、minlength、pattern 等表單的原生校驗(yàn)屬性,不但可以滿足正常的表單校驗(yàn)需求,也具有更好的無障礙支持
規(guī)則 4:注意頁面的焦點(diǎn)管理,允許用戶僅通過鍵盤完成交互
很多行動(dòng)不便的用戶依賴鍵盤操作,靠 Tab 鍵和方向鍵等瀏覽網(wǎng)。因此我們?cè)跇?gòu)建 Web 應(yīng)用的時(shí)候要注意:
- 確保頁面所有內(nèi)容都可以通過鍵盤訪問
- 盡可能地提供鍵盤快捷鍵交互
- 避免設(shè)計(jì)只在鼠標(biāo) hover 時(shí)才會(huì)被激活的元素
一些 HTML 的原生標(biāo)簽具備可聚焦屬性,也被稱為可聚焦元素。這些原生 HTML 元素,天然存在于頁面 Tab 鍵順序內(nèi),內(nèi)置了鍵盤事件處理,可以通過 Tab 鍵聚焦,并且獲得焦點(diǎn)時(shí)有可見的焦點(diǎn)指示器(往往是顯眼的藍(lán)色框框)。但對(duì)于無法聚焦的元素,我們可以設(shè)置元素的 tabindexlace 屬性,使元素可聚焦。
如果想給當(dāng)前元素生成快捷鍵的話,可以給元素設(shè)置 accesskey 屬性。但使用 accesskey也需注意以下問題:
- accesskey 值可能與系統(tǒng)或?yàn)g覽器快捷鍵或輔助技術(shù)功能相沖突
- 當(dāng)考慮頁面國際化時(shí),某些 accesskey 值可能不會(huì)出現(xiàn)在一些鍵盤上
- 依賴于數(shù)值的 accesskey 可能會(huì)讓具有認(rèn)知障礙的用戶感到困惑,因?yàn)閿?shù)值和觸發(fā)的功能并沒有邏輯聯(lián)系
- 如果沒有告知用戶快捷鍵的存在,那么可以會(huì)造成用戶誤觸
相關(guān)瀏覽器插件:
- taba11y - Chrome Web Store
- NerdeFocus - Chrome Web Store
規(guī)則 5:定義文檔的語言類型
在 <html> 標(biāo)簽元素上設(shè)置正確的 lang 屬性。如果你的頁面沒有顯式設(shè)置當(dāng)前頁面所使用的語言,那么讀屏軟件將無法選擇匹配的語音配置文件和字符集,讀屏軟件讀出的頁面內(nèi)容是亂碼。所以,為了確保頁面的內(nèi)容正確,請(qǐng)務(wù)必為 </html><html> 元素指定有效的BCP 47語言。
規(guī)則 6:為 <img /> 添加 alt 屬性,明確鏈接和按鈕的信息
往往一張表情包圖片就可以抵千言萬語,但對(duì)于讀屏軟件來說,讀取 <img /> 標(biāo)簽的有效信息,只能靠 alt 屬性。所以不要忘記為 <img /> 標(biāo)簽添加描述性的 alt 屬性。如果圖片只是為了裝飾效果,那么可以考慮將 <img /> 標(biāo)簽 替換為 CSS 背景圖。
與 <img />標(biāo)簽類似,讀屏軟件對(duì)于 <a> 和 <button> 標(biāo)簽的信息獲取,依賴于標(biāo)簽包裹的文本。使用”閱讀更多“,甚至圖片作為這類標(biāo)簽的包裹內(nèi)容,并不能為用戶提供足夠的信息。如果不方便添加文本信息,也可以利用 aria-label 增強(qiáng)元素的語義信息:
<a href="post.php?post=632" aria-label="More on Using Meaningful Link Text">More...</a>
使用前端框架需要考慮的 Web Accessibility
根據(jù) 2022 年 The WebAIM Million 統(tǒng)計(jì)報(bào)告,使用 JavaScript 框架的頁面比不使用框架的頁面存在更多的無障礙錯(cuò)誤,其中 React 開發(fā)的頁面平均存在 50.8 個(gè)錯(cuò)誤,Vue 開發(fā)的頁面存在 63.4 個(gè)錯(cuò)誤。雖然統(tǒng)計(jì)結(jié)果不能說明框架導(dǎo)致了這些錯(cuò)誤,但在使用框架進(jìn)行 Web 開發(fā)時(shí),常常會(huì)忽略使用 HTML 原生標(biāo)簽,或者引入無障礙功能支持性不佳的組件庫,導(dǎo)致框架開發(fā)的 Web 應(yīng)用可訪問性普遍較差。
提升前端框架的無障礙支持性
規(guī)則 1:使用語義化 HTML 標(biāo)簽,完善 HTML 標(biāo)簽的屬性
規(guī)則 2:在設(shè)計(jì)組件時(shí)考慮整體的 HTML 結(jié)構(gòu)
維護(hù)層級(jí)明晰的 HTML 結(jié)構(gòu),對(duì)于 Web 應(yīng)用的無障礙功能十分重要。因?yàn)?ATs 軟件,特別是讀屏軟件,不止是由上至下地展現(xiàn)頁面信息,更會(huì)基于頁面不同級(jí)別的標(biāo)題或者文檔地標(biāo)元素進(jìn)行頁面導(dǎo)航。在將頁面拆分成不同組件后,保持 HTML 文檔結(jié)構(gòu)層級(jí)會(huì)更加復(fù)雜。比如當(dāng)一個(gè)組件包含 <h2> 標(biāo)簽時(shí),可能在一些位置該組件會(huì)破壞原有 HTML 文檔結(jié)構(gòu)。
規(guī)則 3:避免使用無意義的 HTML 標(biāo)簽
在使用 React、Vue 等框架時(shí),我們往往需要將組件包裹在一個(gè)根元素中:
但這樣的處理在編譯后,會(huì)在造成元素結(jié)構(gòu)的混亂:
<div> 標(biāo)簽混在 <tr> 標(biāo)簽中,會(huì)導(dǎo)致讀屏軟件無法正確解析 table ,造成用戶無法訪問表格內(nèi)容。此時(shí),我們應(yīng)該使用 React 的 <fragment> 標(biāo)簽(Vue 中可以使用 vue-fragment),確保根元素不存在于最終的 DOM 結(jié)構(gòu)內(nèi):