跳到主要內容區
理解 Accessible components: Dialog 篇
開發概念

理解 Accessible components: Dialog 篇

一個無障礙的對話框應具備哪些行為?有哪些常見錯誤?實做套件怎麼選?

更新日期:2023/12/12

發布日期:2023/11/12

作者:Kelly CHI

在多數產品中,我們常會看到以對話框來呈現系統訊息、確認操作或是請求資料輸入,它通常是一個覆蓋住整個主要頁面的視窗,使得使用者只能與視窗中的內容互動,而被其所復蓋的頁面則是暫時無法操作。

對話框允許開發者刻意地打斷使用者的工作流程,藉此引起其注意並獲得回應。運用對話框時必須注意應謹慎控制其出現的頻率與時機,以避免打擾使用者的操作;其內容應簡潔明確,並提供清楚的退出方式。特別對螢幕閱讀器的使用者來說,如果對話框的焦點設定不當,他們可能難以感知或與之互動。我們本篇就來與大家介紹一個良好的無障礙對話框應具備哪些行為、有哪些常見錯誤與如何實做吧!

Dialog pattern

常見的對話框元件通常都會具有以下幾個元素:container 容器、title 標題、description 描述、一個或多個 buttons 按鈕以及 backdrop 背景
常見的對話框元件通常都會具有以下幾個元素:容器、標題、描述、一個或多個按鈕以及背景

依據 APG Dialog pattern,對話框元件需要遵循以下這些無障礙實踐:

重要的無障礙功能

  • 當對話框被開啟時,焦點應自動移動到對話框中的元素上。
  • 當對話框為開啟狀態時,焦點不應移動到對話框以外的元素上。
  • 初始焦點的設定和可訪問性描述可能會依據情況而有所不同:
  1. 以範例的第一個對話框 「Add Delivery Address」 為例,開啟對話框後的自動聚焦應設定在第一個輸入框,也就是「Address」輸入框上。
  2. 此對話框不需要新增 aria-describedby 標籤,因為沒有相對應的文字來描述該對話框 (可以和其他幾個對話框比較看看)。
  3. 當使用者關閉對話框時,焦點應自動返回到觸發該對話框的 「Add Delivery Address」 按鈕。

重要的鍵盤操作

  • Tab 鍵:將焦點移動到對話框中下一個可聚焦的元素上。
  • Shift + Tab 鍵:將焦點移動到對話框中上一個可聚焦的元素上。
  • Escape 鍵:關閉該對話框。

重要的 ARIA 屬性

  • role="dialog":若該對話框的行為符合 dialog pattern,role="dialog" 可讓輔助科技知道這是一個對話框元件。
  • aria-labelledby="IDREF":透過指向某個提供該對話框標題的元素來為對話框提供可訪問名稱。
  • aria-describedby="IDREF":透過指向某個提供該對話框主要內容的元素來為對話框提供可訪問描述。
  • aria-modal="true":告訴輔助科技對話框後方的背景目前「無法互動」

一個對話框元件重要的 aria 屬性有:role=’dialog’, aria-modal=’true’, aria-labelledby=’ID’, aria-describedby=’ID’,其中 ID 通常會指向該對話框的標題和主要描述
一個對話框元件重要的 aria 屬性有:role=”dialog’” aria-modal=”true”, aria-labelledby=”ID”, aria-describedby=”ID”

實作無障礙對話框時的常見錯誤

在讀過一些重要的實踐準則後,就可以理解在實作無障礙對話框元件時常見的錯誤:

  • ❌ 沒有透過「改變焦點」的方式來讓螢幕閱讀器使用者感知到對話框的開啟與關閉。
  • ❌ 沒辦法「使用 Escape 鍵」來關閉對話框。
  • ❌ 對話框的「關閉」按鈕缺乏可訪問名稱,例如只用單字「X」而非「關閉」。
  • ❌ 沒有將焦點限制在對話框內,導致鍵盤使用者在對話框開啟時仍然能透過鍵盤去操作後方背景中的可互動元素。
  • ❌ 當對話框關閉後,焦點並沒有返回到觸發該對話框的元素。

原生的 <dialog> 元素有符合規範嗎?

根據這篇文章中有提過的 ARIA 原則:優先使用原生元素,我們在製作對話框時,可以優先考慮使用已存在好一段時間的 HTML 原生 <dialog> 元素。

雖然原生的 <dialog> 元素在 2014 年就已經被寫入 HTML 標準中,但要一直到 2022 年才得到廣泛的瀏覽器支援。**在 <dialog> 元素被普遍支援後,它已經成為開發者要實作對話框元件時的建議最佳實踐。**然而,它仍然有一些美中不足的地方,需要開發者額外留意:

  • 點擊對話框外的區域在某些瀏覽器中並不會關閉該對話框。
  • 對話框的預設樣式由各家瀏覽器自行決定,可能會彼此不一致。
  • <dialog> 元素所觸發的 JavaScript 事件有點讓人困惑 (同時有 cancel 和 close 事件,但是沒有 open 事件) 而且只會在特定的情況下被觸發。
  • <dialog> 開啟時,雖然使用 tab 鍵焦點無法移到後方背景中的可互動元素上,但當在對話框底部時,按下 tab 鍵會使得焦點跳到瀏覽器顯示頁面外的功能選單,不是純循環遊走對話框內元素。
  • 警示對話框 (role=”alertdialog”) 的設計模式並無法於 <dialog> 元素上實踐。

警示對話框 (role=”alertdialog”) 與一般的對話框 (role=”dialog”) 最大的差異在於,由於必須實現「警示」的作用,alertdialog 的出現通常會伴隨輔助科技的特殊行為 (例如發出提示音響),來引起使用者的注意,可能會例如在向使用者取得裝置位置的存取權、無法復原的刪除動作等情境中使用。

至於警示對話框 (role=”alertdialog”) 與一般的警示 (role=”alert”) 的差異則在於,警示對話框會透過控制焦點來要求使用者「採取一些動作」,例如點擊確認按鈕,但是一般的警示是不會轉移使用者目前的焦點的。因此,根據情境來正確選擇 ARIA 角色是無障礙設計中很重要的一件事。

對於從頭建立一個無障礙的 dialog 元件有興趣的讀者,可以進一步去閱讀這幾篇文章,來理解實作一個對話框元件需要注意的眉眉角角:Creating An Accessible Dialog From ScratchAccessible dialog tutorial

使用套件來製作無障礙對話框

由於像是上述所提到的 <dialog> 的一些限制,我們可能無法直接使用原生 <dialog> 標記,而會需要製作客製化的對話框元件 。在開發無障礙元件時,為了節省時間與資源,使用受到社群歡迎的套件就是一種普遍的做法。此外,比起使用原生的元素,元件庫可能提供了更多人性化的功能。現今有許多套件都標榜符合 ARIA Authoring Practices Guide (APG) 的設計與實作規範,不過不同的套件在實作細節上仍然會有些許的差異。

HeadlessUI

HeadlessUI 是一個開源的前端套件,旨在提供無樣式、可客製化的基本 UI 元件,它也標榜具有完整的無障礙體驗。該元件庫與 Tailwind CSS 可以無痛整合,也支援 React 與 Vue 兩種前端框架的版本。

(codesandbox example)

  • 使用鍵盤操作來打開對話框,焦點會自動轉移到對話框內的第一個可聚焦元素上,例如上方範例的 「Got it, thanks!」 按鈕。元件也提供了 initialFocus ref 來讓開發者決定初始焦點應落在哪個元素上。
  • 目前的版本並沒有支援警示對話框 (role=”alertdialog”) 的設計模式,沒辦法透過元件庫提供的 api 來改變該元素的 role 屬性,一律預設為 role=”dialog”。不過,根據這支官方的 PR,在未來的更新版本中會開放讓使用者可以設定該對話框為 role=“alertdialog”。

a11y-dialog

a11y-dialog 是一個輕量級的套件,顧名思義用來實現具有良好可訪問性的對話框,它遵循 APG,開發者也可以客製化元件的視覺外觀。它是以純 JavaScript 開發,因此可以與任何前端框架一起使用。

(codesandbox example)

  • 對話框開啟後的預設焦點是整個 dialog container 元素,而非對話框內部的元素,但若對話框內部有表單元素 (例如輸入框),且有設置 autofocus 屬性的話,即會將預設焦點設為該元素。
  • 該套件有支援警示對話框 (role=”alertdialog”) 的設計模式,預設的行為是:使用者無法透過 escape 鍵、或是點擊對話框外部來關閉警示對話框 (可參考下方 demo)。

(codesandbox example)

結語

在這篇文章中,我們探討了對話框元件在無障礙實作上需要關注的重要面向。透過研讀 ARIA 標準和指南,我們可以瞭解如何正確使用 ARIA 屬性以及適當的焦點管理、鍵盤操作來建立具有良好無障礙體驗的對話框。

此外,我們也談到了常見的實作錯誤、套件的案例以及原生 <dialog> 元素。看完本篇希望你能對如何製作出一個無障礙體驗的對話框有更深的認識,好的對話框設計,對無障礙體驗的提身會是非常有感的。下次當你遇到對話框時別忘了本篇所提到的技巧喔!

參考資料

本文作者

本文作者

Kelly CHI

法文系畢業的前端工程師,致力於打造具有美感和良好用戶體驗的介面,同時也是個愛看冷門電影的骨灰級影迷。