最佳實務

Angular 的無障礙設計

網路的使用者形形色色,其中也包括視覺或行動方面有障礙的人。 市面上有各種輔助技術,能讓這些族群更容易與以網路為基礎的軟體應用程式互動。 此外,把應用程式設計得更容易使用,通常也能改善所有使用者的整體體驗。

若想深入了解設計無障礙應用程式的議題與技巧,請參閱 Google 在 web.dev 上的 Learn Accessibility 課程。

本頁說明設計 Angular 應用程式的最佳實務,讓所有使用者(包括仰賴輔助技術的人)都能順暢使用。

無障礙屬性

要打造無障礙的網路體驗,往往需要設定 Accessible Rich Internet Applications(ARIA)屬性,在原本缺少語意的地方補上語意。 你可以用屬性繫結範本語法,控制與無障礙相關的屬性值。

ARIA 屬性與內容

在 Angular 中繫結 ARIA 屬性時,可以像其他 HTML 屬性一樣直接使用。

<button [aria-label]="myActionLabel">…</button>

靜態的 ARIA 屬性就和一般 HTML 屬性一樣運作。

<button aria-label="Save document">…</button>

HELPFUL: 依照慣例,HTML 屬性使用小寫名稱(tabindex),屬性(property)則使用 camelCase 名稱(tabIndex)。

有些 ARIA 模式會公開接受結構化值的 DOM API 或指令輸入(例如 Element 參考的集合)。這類情況請使用標準的屬性繫結,讓底層的關聯保持同步。

@Component({
  template: `
    <h2 #dialogTitle>Attention</h2>
    <p #dialogDescription>Please review your answers before continuing.</p>

    <section role="dialog" [ariaLabelledByElements]="[dialogTitle, dialogDescription]">
      <ng-content />
    </section>
  `,
})
export class ReviewDialog {}

這裡的 [ariaLabelledByElements] 接受元素陣列,因此只要範本資料一變,屬性繫結就能讓元素參考隨時保持最新。

ARIA 屬性語法的摘要,請參閱繫結指南。

Angular UI 元件

由 Angular 團隊維護的 Angular Material 程式庫,是一套可重複使用的 UI 元件,目標是做到完全無障礙。 Component Development Kit(CDK) 包含 a11y 套件,提供支援各個無障礙領域的工具。 例如:

  • LiveAnnouncer 會利用 aria-live 區域,為螢幕閱讀器使用者播報訊息。 更多關於 aria-live 區域 的資訊,請參閱 W3C 文件。

  • cdkTrapFocus 指令會把 Tab 鍵的焦點限制在某個元素內。 你可以用它為模式對話框之類的元件打造無障礙體驗,這類元件必須限制焦點的移動範圍。

這些工具與其他工具的完整說明,請參閱 Angular CDK 無障礙總覽。

對於需要可重複使用 WAI-ARIA 互動模式的自訂樣式元件,Angular Aria 提供無頭(headless)指令,涵蓋手風琴、組合框、清單框、選單、頁籤與工具列等模式。 這些指令會處理鍵盤互動、ARIA 屬性、焦點管理與螢幕閱讀器支援,同時讓你自行提供應用程式的 HTML 結構與樣式。

強化原生元素

原生 HTML 元素本身就具備幾個對無障礙很重要的標準互動模式。 撰寫 Angular 元件時,只要做得到,就應該直接重複使用這些原生元素,而不是重新實作已經有良好支援的行為。

例如,與其為新型態的按鈕建立自訂元素,不如建立一個使用屬性選擇器、搭配原生 <button> 元素的元件。 這最常用在 <button> 與 <a> 上,但也能用在許多其他類型的元素。

你可以在 Angular Material 裡看到這種模式的範例: MatButton、MatTabNav 與 MatTable。

用容器包裝原生元素

有時候,要使用合適的原生元素,就需要一個容器元素。 例如,原生 <input> 元素不能有子元素,所以任何自訂文字輸入元件都需要用額外元素把 <input> 包起來。 若只是把 <input> 放進自訂元件的範本裡,元件的使用者就無法對 <input> 元素設定任意的屬性與特性。 這種情況下,應該建立一個容器元件,利用內容投影把原生控制項納入元件的 API。

你可以把 MatFormField 當作這種模式的範例。

案例研究:打造自訂進度列

下面的範例示範如何利用主機繫結(host binding)控制與無障礙相關的屬性,讓進度列變得無障礙。

  • 元件定義了一個支援無障礙的元素,同時用上標準 HTML 屬性 role 與 ARIA 屬性。 ARIA 屬性 aria-valuenow 會繫結到使用者的輸入。
  • 在範本中,aria-label 屬性確保螢幕閱讀器能存取這個控制項。

路由

導航後的焦點管理

在 UI 中追蹤並控制焦點,是無障礙設計的重要考量。 使用 Angular 路由時,你應該決定頁面導航後焦點要移到哪裡。

為了避免只仰賴視覺提示,你需要確保路由程式碼在頁面導航後會更新焦點。 你可以用 Router 服務的 NavigationEnd 事件,掌握更新焦點的時機。

下面的範例示範導航後如何在 DOM 中找到主要內容的標題並把焦點移到它上面。

router.events.pipe(filter((e) => e instanceof NavigationEnd)).subscribe(() => {
  const mainHeader = document.querySelector('#main-content-header');
  if (mainHeader) {
    mainHeader.focus();
  }
});

在真實應用程式中,接收焦點的元素取決於你應用程式的具體結構與版面配置。 取得焦點的元素,應該讓使用者能立刻進入剛被路由帶進畫面的主要內容。 你應該避免路由切換後焦點回到 body 元素的情況。

使用中連結的標示

套用在作用中 RouterLink 元素上的 CSS 類別(例如 RouterLinkActive),會提供視覺提示來標示作用中的連結。 可惜的是,視覺提示對盲人或視障使用者沒有幫助。 把 aria-current 屬性套用到元素上,能協助標示作用中的連結。 更多資訊請參閱 Mozilla Developer Network(MDN)的 aria-current)。

RouterLinkActive 指令提供 ariaCurrentWhenActive 輸入,當連結變成作用中時,會把 aria-current 設為指定的值。

下面的範例示範如何把 active-page 類別套用到作用中的連結,同時在連結作用中時把它的 aria-current 屬性設為 "page":

<nav>
  <a routerLink="home" routerLinkActive="active-page" ariaCurrentWhenActive="page"> Home </a>
  <a routerLink="about" routerLinkActive="active-page" ariaCurrentWhenActive="page"> About </a>
  <a routerLink="shop" routerLinkActive="active-page" ariaCurrentWhenActive="page"> Shop </a>
</nav>

延遲載入

使用 Angular 的 @defer 區塊延遲載入內容時,請考慮對使用輔助技術的使用者有何影響。 當延遲載入的元件完成載入時,螢幕閱讀器不一定會自動播報內容變化,使用者可能因此不知道有新內容出現。

為了確保延遲內容的變化能被正確播報,請把 @defer 區塊包在帶有適當 ARIA live 區域的元素裡。 詳細指引與範例,請參閱 defer 指南中的無障礙章節。

更多資訊

書籍

  • "A Web for Everyone: Designing Accessible User Experiences," Sarah Horton and Whitney Quesenbery
  • "Inclusive Design Patterns," Heydon Pickering