Web アクセシビリティ (WCAG 2.2): Angular の完全ガイド
Web アクセシビリティはオプションではなく、最新のすべてのプロジェクトにとって基本的な要件です。 世界保健機関によると、10億人以上の人が何らかの形でウイルスに感染しながら暮らしている 障害の。アクセシビリティを無視するということは、重要な部分を省略することを意味します 多くの場合、欧州アクセシビリティ法などのますます厳しくなる規制に違反しています。
このガイドでは、 Web コンテンツ アクセシビリティ ガイドライン (WCAG) 2.2、 4 つの基本原則、コンプライアンスのレベル、そして何よりもその実装方法 プロジェクトにおける具体的なアクセシビリティ 角度のある a11y CDK を使用して、 ARIA 属性、フォーカス管理、自動テスト ツール。
この記事で学べること
- WCAG の 4 つの原則と準拠レベル (A、AA、AAA)
- Angular アプリケーションにアクセスできるようにする方法と実際の例
- フォーカス管理、キーボード ナビゲーション、ARIA 属性
- Angular CDK a11y: FocusTrap、LiveAnnouncer、および FocusMonitor
- コントラストカラー、読みやすいタイポグラフィー、アクセスしやすいフォーム
- テストツール: axe-core、Lighthouse、スクリーンリーダー
- WCAG 2.2 によって導入されたイノベーション
アクセシビリティが重要な理由
アクセシビリティは開発の二次的な側面として扱われることが多く、対処する必要があります。 プロジェクトが完了したら。このアプローチは費用がかかり、非効率的です。アクセシビリティの統合 開発サイクルの開始直後から、よりシンプルで、より安価で、生産性が高くなります。 すべてのユーザーにとってより良い結果が得られます。
法的動機
ヨーロッパでは、 指令 (EU) 2019/882 (欧州アクセシビリティ法) では、次のことが求められています。 デジタル製品とサービスは 2025 年 6 月までにアクセス可能になります。イタリアでは、 疲れた法律 (L. 4/2004) 行政に義務付け、2022 年からは売上高のある民間企業にも義務付ける 5億ユーロを超える。米国では、ADA (アメリカ障害者法) 裁判所は、ウェブサイトにも適用されると解釈しています。侵害に対する訴訟 アクセシビリティは常に増加しています。
倫理的およびビジネス上の動機
アクセシビリティのメリット
- 含まれるもの: 誰もがコンテンツにアクセスできるようにすることは倫理的な義務です
- SEO: セマンティック HTML、代替テキスト、正しい構造によりランキングが向上します
- 使いやすさ: アクセシブルなサイトは、接続が遅いユーザーやデバイスが限られているユーザーを含め、誰にとっても使いやすくなります。
- 市場: 障害を持つ 10 億人を超える潜在的なユーザーにリーチする
- コードの品質: アクセシブルなコードを書くと、よりクリーンでテストしやすく、保守しやすいコードが得られます。
- 評判: 包括性への配慮を示すことでブランドが強化される
WCAG の 4 つの原則
WCAG は、次の頭字語で知られる 4 つの原則に基づいています。 注ぐ: 知覚可能、操作可能、理解可能 そして堅牢。各原則には具体的なガイドラインが含まれており、それぞれに次のようなものがあります。 測定可能な成功基準。
1. 知覚可能
情報とインターフェイスのコンポーネントは、次のような方法で提示する必要があります ユーザーは認識することができます。すべてが視覚的である必要はありません。コンテンツには視覚的でなければなりません。 代替テキスト、ビデオの字幕、適切な色のコントラスト。
- 代替テキスト: 装飾的でない各画像には属性が必要です
alt説明的な - 字幕: オーディオとビデオのコンテンツには文字起こしと字幕が必要です
- 対比: 最小比率は通常のテキストの場合は 4.5:1、大きなテキストの場合は 3:1 (レベル AA)
- スケーリング: コンテンツは最大 200% ズームしても読める必要があります
- 向き: サイトは垂直方向と水平方向の両方で機能する必要があります
2. 操作可能
インターフェイス コンポーネントとナビゲーションが使用可能である必要があります。あらゆる機能 マウスで利用できるものは、キーボードからもアクセスできる必要があります。
- キーボード: すべての機能はキーボードからアクセスでき、アクティブ化できる必要があります
- 十分な時間: ユーザーは読んで対話するのに十分な時間を確保する必要があります
- ナビゲーション: タイトル、ランドマーク、重要なリンクを含む明確な構造
- 目に見える焦点: フォーカスインジケーターは常にはっきりと見える必要があります
- 最小目標: クリック可能な領域は少なくとも 24x24 ピクセルである必要があります (WCAG 2.2)
3. 理解できる
インターフェースの情報と操作は理解できるものでなければなりません。 テキストは読みやすく、ナビゲーションが予測可能で、エラーが明確に処理される必要があります。
- 舌: ドキュメントの言語とテキスト内の言語の変更を宣言します。
- 予測可能性: ナビゲーションと動作は一貫している必要があります
- フォームエラー: エラーを特定し、説明し、修正を提案する
- ヘルプ: 複雑なフィールドに対する指示と提案を提供する
4.ロブスト(ロブスト)
コンテンツは、さまざまなユーザー エージェントによって確実に解釈可能である必要があります。 スクリーン リーダーなどの支援テクノロジーも含まれます。
- 有効なHTML: 正しく整形式のマークアップ
- 空気: ARIA のロールとプロパティの正しい使用
- 互換性: さまざまな支援テクノロジーを使用してテストする
- コンポーネントのステータス: 状態 (展開/縮小、選択など) をプログラムで伝達します。
コンプライアンスレベル
各 WCAG 基準は、次の 3 つのレベルのいずれかに分類されます。
| レベル | 説明 | 客観的 |
|---|---|---|
| A | 基本的な必須要件 | 最も深刻な障壁を取り除く |
| AA | ほとんどのサイトで推奨される標準 | 障害を持つほとんどのユーザーに対するアクセシビリティ |
| AAA | 最大レベルのアクセシビリティ | 最適なアクセシビリティ。多くの場合、すべてのコンテンツで達成できるわけではありません |
どのレベルを選択しますか?
レベル AA ほとんどの Web プロジェクトで推奨されるターゲットです。 これは、欧州の法律およびほとんどの国際規制で要求されるレベルです。 AAA は理想的な目標ですが、多様なコンテンツを含むサイトにとって 100% 達成できる目標はめったにありません。
Angular でのアクセシビリティの実装
Angular は、アクセス可能なアプリケーションを構築するための優れたツールを提供します。組み合わせ セマンティック HTML、バインディングおよびフォームを介して管理される ARIA 属性の CDK a11y WCAG への準拠が必要なプロジェクトにとって、Angular は確実な選択肢となります。
基礎としてのセマンティック HTML
アクセシビリティの最初のルールは、目的に応じて適切な HTML 要素を使用することです。
あ <button> すでにキーボードからアクセス可能であり、正しく通信されています
スクリーンリーダーに。あ <div> クリックハンドラーの場合はそうではありません。
<!-- SBAGLIATO: div come bottone -->
<div class="btn" (click)="save()">Salva</div>
<!-- CORRETTO: button nativo -->
<button type="button" (click)="save()">Salva</button>
<!-- SBAGLIATO: span come link -->
<span class="link" (click)="navigate()">Vai alla pagina</span>
<!-- CORRETTO: anchor nativo -->
<a routerLink="/pagina">Vai alla pagina</a>
<!-- Usare landmark semantici -->
<header>...</header>
<nav aria-label="Navigazione principale">...</nav>
<main>...</main>
<aside aria-label="Contenuti correlati">...</aside>
<footer>...</footer>
Angular の ARIA 属性
ARIA (Accessible Rich Internet Applications) はカスタム コンポーネント間のギャップを埋める そして支援技術。 Angular では、ARIA 属性へのバインディングは直接的かつ反応的です。
// Componente Angular con ARIA dinamico
@Component({
selector: 'app-accordion',
template: `
<div class="accordion">
<button
[attr.aria-expanded]="isOpen"
[attr.aria-controls]="'panel-' + id"
(click)="toggle()"
(keydown.enter)="toggle()"
(keydown.space)="toggle(); $event.preventDefault()">
{{ title }}
</button>
<div
[id]="'panel-' + id"
role="region"
[attr.aria-labelledby]="'header-' + id"
[hidden]="!isOpen">
<ng-content></ng-content>
</div>
</div>
`
})
export class AccordionComponent {
@Input() id = '';
@Input() title = '';
isOpen = false;
toggle(): void {
this.isOpen = !this.isOpen;
}
}
ARIAの最初のルール
正しいセマンティクスを持つネイティブ HTML 要素を使用できる場合は、そうしてください。追加しないでください
ARIA ロールを、すでに持っている要素に追加します。例えば、 <button> 彼にはそれが必要ない
の role="button".
集中管理
Angular のようなシングル ページ アプリケーション (SPA) では、フォーカス管理が重要です。 ルート間を移動するときは、フォーカスを論理的に移動して、 スクリーン リーダー ユーザーが道を見つけることができます。
import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';
import { filter } from 'rxjs/operators';
@Component({
selector: 'app-root',
template: `
<a class="skip-link" href="#main-content">
Vai al contenuto principale
</a>
<app-navbar></app-navbar>
<main id="main-content" tabindex="-1">
<router-outlet></router-outlet>
</main>
<app-footer></app-footer>
`
})
export class AppComponent implements OnInit {
constructor(private router: Router) {}
ngOnInit(): void {
// Sposta il focus al contenuto principale dopo ogni navigazione
this.router.events.pipe(
filter(event => event instanceof NavigationEnd)
).subscribe(() => {
const main = document.getElementById('main-content');
if (main) {
main.focus();
}
});
}
}
Angular CDK a11y モジュール
フォーム @angular/cdk/a11y アクセシビリティのための専用ツールを提供します。
3つの主なコンポーネントは次のとおりです フォーカストラップ, ライブアナウンサー
e フォーカスモニター.
フォーカストラップ
ダイアログとモーダルに不可欠な要素内にフォーカスをトラップします。 ユーザーが Tab キーを押すと、フォーカスはコンポーネント内に残ります。 最終的には背景にある隠し要素にたどり着きます。
import { Component } from '@angular/core';
import { A11yModule } from '@angular/cdk/a11y';
@Component({
selector: 'app-modal',
standalone: true,
imports: [A11yModule],
template: `
<div class="modal-overlay" (click)="close()">
<div
class="modal-content"
cdkTrapFocus
cdkTrapFocusAutoCapture
role="dialog"
aria-modal="true"
[attr.aria-labelledby]="'modal-title'"
(click)="$event.stopPropagation()"
(keydown.escape)="close()">
<h2 id="modal-title">Conferma Eliminazione</h2>
<p>Sei sicuro di voler eliminare questo elemento?</p>
<div class="modal-actions">
<button (click)="close()">Annulla</button>
<button (click)="confirm()">Elimina</button>
</div>
</div>
</div>
`
})
export class ModalComponent {
close(): void { /* ... */ }
confirm(): void { /* ... */ }
}
ライブアナウンサー
ライブ ARIA リージョンを介してスクリーン リーダーに動的なメッセージをアナウンスできます。 通知、ステータス更新、検索結果に不可欠です。
import { Component, inject } from '@angular/core';
import { LiveAnnouncer } from '@angular/cdk/a11y';
@Component({
selector: 'app-search',
template: `
<input
type="search"
aria-label="Cerca articoli"
(input)="onSearch($event)" />
<p aria-live="polite" class="sr-only">
{{ resultCount }} risultati trovati
</p>
<ul role="list">
<li *ngFor="let item of results">
{{ item.title }}
</li>
</ul>
`
})
export class SearchComponent {
private announcer = inject(LiveAnnouncer);
results: any[] = [];
resultCount = 0;
onSearch(event: Event): void {
const query = (event.target as HTMLInputElement).value;
// ... logica di ricerca ...
this.announcer.announce(
`${this.resultCount} risultati trovati per "${query}"`,
'polite'
);
}
}
フォーカスモニター
モニターのフォーカス モード (マウス、キーボード、タッチ、プログラム) を有効にし、 フォーカスの原点に応じて異なるスタイルを適用します。
import { Component, ElementRef, OnDestroy, OnInit, inject } from '@angular/core';
import { FocusMonitor, FocusOrigin } from '@angular/cdk/a11y';
@Component({
selector: 'app-custom-button',
template: `
<button
#myButton
[class.keyboard-focus]="focusOrigin === 'keyboard'"
[class.mouse-focus]="focusOrigin === 'mouse'">
<ng-content></ng-content>
</button>
`,
styles: [`
.keyboard-focus {
outline: 3px solid #58a6ff;
outline-offset: 2px;
}
.mouse-focus {
outline: none;
}
`]
})
export class CustomButtonComponent implements OnInit, OnDestroy {
private focusMonitor = inject(FocusMonitor);
private el = inject(ElementRef);
focusOrigin: FocusOrigin | null = null;
ngOnInit(): void {
this.focusMonitor.monitor(this.el, true)
.subscribe(origin => this.focusOrigin = origin);
}
ngOnDestroy(): void {
this.focusMonitor.stopMonitoring(this.el);
}
}
コントラストカラーとタイポグラフィー
コントラストは、最も頻繁に違反される要素の 1 つです。 WCAG 2.2 には次のものが必要です。
| 要素 | AAレベル | AAAレベル |
|---|---|---|
| プレーンテキスト (<18px) | 4.5:1 | 7:1 |
| 大きなテキスト (太字 18 ピクセル以上、または 24 ピクセル以上) | 3:1 | 4.5:1 |
| UIコンポーネントとグラフィックス | 3:1 | - |
/* Sistema di colori accessibile con custom properties */
:root {
/* Testo su sfondo scuro (#0d1117) */
--text-primary: #e6edf3; /* Rapporto: 13.5:1 - OK */
--text-secondary: #8d96a0; /* Rapporto: 4.7:1 - OK per AA */
--text-link: #58a6ff; /* Rapporto: 5.2:1 - OK */
/* Focus indicator ad alto contrasto */
--focus-ring: #58a6ff;
--focus-ring-offset: 2px;
}
/* Focus visibile per navigazione da tastiera */
*:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: var(--focus-ring-offset);
}
/* Rispettare le preferenze utente */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
@media (prefers-contrast: high) {
:root {
--text-secondary: #c9d1d9;
--border: #6e7681;
}
}
アクセシブルなフォーム
フォームはアクセシビリティにとって最も重要なポイントの 1 つです。すべてのフィールドに必ず必要なものは、 関連するラベル、エラーは明確に伝えられ、検証される必要があります それは理解できるはずです。
<!-- Form accessibile in Angular -->
<form [formGroup]="contactForm" (ngSubmit)="onSubmit()">
<!-- Campo con label associata e descrizione -->
<div class="form-group">
<label for="email">Email *</label>
<input
id="email"
type="email"
formControlName="email"
[attr.aria-describedby]="'email-help email-error'"
[attr.aria-invalid]="contactForm.get('email')?.invalid
&& contactForm.get('email')?.touched"
autocomplete="email" />
<span id="email-help" class="help-text">
Inserisci un indirizzo email valido
</span>
<span
id="email-error"
role="alert"
class="error-text"
*ngIf="contactForm.get('email')?.invalid
&& contactForm.get('email')?.touched">
L'indirizzo email non è valido
</span>
</div>
<!-- Gruppo di radio button -->
<fieldset>
<legend>Tipo di richiesta</legend>
<label>
<input type="radio" formControlName="type" value="info" />
Informazioni
</label>
<label>
<input type="radio" formControlName="type" value="support" />
Supporto
</label>
</fieldset>
<button type="submit" [disabled]="contactForm.invalid">
Invia Richiesta
</button>
</form>
アクセシブルなフォームのチェックリスト
- 各入力には、
<label>経由で関連付けられていますfor/id - エラーの使用
role="alert"すぐに発表される aria-describedbyフィールドにヘルプ テキストとエラー メッセージを添付するaria-invalidエラーステータスを支援技術に報告するautocomplete自動入力のデータ型を提案します- 関連フィールドグループの使用
<fieldset>e<legend> - 送信ボタンはアクションを明確に示します
キーボードナビゲーション
キーボードによるナビゲーションは不可欠です。これはドロップダウン メニューの例です メニューの ARIA パターンに従って、マウスなしで完全にナビゲートできます。
@Component({
selector: 'app-dropdown-menu',
template: `
<div class="dropdown" (keydown)="onKeydown($event)">
<button
[attr.aria-expanded]="isOpen"
aria-haspopup="true"
(click)="toggleMenu()">
Menu <span aria-hidden="true">▼</span>
</button>
<ul
*ngIf="isOpen"
role="menu"
class="dropdown-list">
<li
*ngFor="let item of items; let i = index"
role="menuitem"
[tabindex]="i === activeIndex ? 0 : -1"
[class.active]="i === activeIndex"
(click)="selectItem(item)"
(keydown.enter)="selectItem(item)">
{{ item.label }}
</li>
</ul>
</div>
`
})
export class DropdownMenuComponent {
items = [/* ... */];
isOpen = false;
activeIndex = 0;
toggleMenu(): void {
this.isOpen = !this.isOpen;
if (this.isOpen) this.activeIndex = 0;
}
onKeydown(event: KeyboardEvent): void {
switch (event.key) {
case 'ArrowDown':
event.preventDefault();
this.activeIndex = Math.min(
this.activeIndex + 1, this.items.length - 1
);
break;
case 'ArrowUp':
event.preventDefault();
this.activeIndex = Math.max(this.activeIndex - 1, 0);
break;
case 'Escape':
this.isOpen = false;
break;
case 'Home':
this.activeIndex = 0;
break;
case 'End':
this.activeIndex = this.items.length - 1;
break;
}
}
selectItem(item: any): void {
// gestione selezione
this.isOpen = false;
}
}
テストツール
アクセシビリティ テストは開発サイクルに統合する必要があります。 最終チェックには降格されない。ここでは最も効果的なツールを紹介します。
Angular を使用した斧コア
斧の芯 は、最も人気のあるアクセシビリティ テスト ライブラリです。 Angular 単体テストに簡単に統合できます。
// test di accessibilità con axe-core e Jasmine
import axe from 'axe-core';
describe('AppComponent Accessibility', () => {
let fixture: ComponentFixture<AppComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent]
}).compileComponents();
fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
});
it('non dovrebbe avere violazioni di accessibilità', async () => {
const results = await axe.run(fixture.nativeElement);
expect(results.violations.length).toBe(0);
});
});
Lighthouse と Chrome DevTools
Google Lighthouse には、アクセシビリティ監査が組み込まれており、 多くの WCAG 基準が自動的に適用されます。 Chrome DevTools の「Lighthouse」タブから実行します。 または CLI 経由で CI/CD パイプラインに統合します。
# Eseguire audit Lighthouse da riga di comando
npx lighthouse http://localhost:4200 \
--only-categories=accessibility \
--output=json \
--output-path=./accessibility-report.json
# Integrare nella pipeline CI
npx lighthouse-ci autorun --config=lighthouserc.json
スクリーンリーダーのテスト
実際のスクリーン リーダーを使用したテストに代わる自動化ツールはありません。 最もよく使用されるのは次のとおりです。
| スクリーンリーダー | プラットフォーム | 料金 |
|---|---|---|
| NVDA | Windows | 無料 |
| ジョーズ | Windows | コマーシャル |
| ナレーション | macOS / iOS | 統合された |
| トークバック | アンドロイド | 統合された |
| シャチ | Linux | 無料 |
WCAG 2.2 の新機能
2023 年 10 月にリリースされた WCAG 2.2 では、9 つの新しい成功基準が導入されました。 Angular 開発者に最も関連のあるものは次のとおりです。
2.5.7 ドラッグ動作(AA)
ドラッグ アンド ドロップが必要な機能には代替手段が必要です そのようなジェスチャーは必要ありません。たとえば、ドラッグ アンド ドロップによる並べ替えでは、 「上に移動」/「下に移動」ボタンも提供します。
2.5.8 ターゲット サイズの最小値 (AA)
クリック可能な領域は少なくとも 24×24ピクセル、または持っています 隣接するターゲットからの十分な間隔。この基準は特に タッチデバイスや運動障害のあるユーザーにとって重要です。
3.2.6 一貫したヘルプ (A)
ヘルプ メカニズム (チャット、電話、FAQ) が複数のページに存在する場合、 すべてのページで同じ相対位置にある必要があります。ナビゲーションの一貫性。
3.3.7 冗長エントリ(A)
複数段階のプロセスでユーザーがすでに入力した情報は、必ずしも入力する必要はありません。 再度リクエストします。自動入力を使用するか、以前のデータから選択できるようにします。
3.3.8 アクセシブル認証 (AA)
認証には認知テスト (パズルベースの CAPTCHA など) が必要であってはなりません。 パスワードのコピー&ペーストなどの代替方法が利用可能である必要がある またはデバイス認証。
実践的なアクセシビリティ チェックリスト
各 Angular コンポーネントのチェックリスト
- セマンティック HTML: 正しいネイティブ要素を使用する
- 論理的なタブの順序: Tab を使用してナビゲーション順序を確認します。
- 可視フォーカス: フォーカス インジケーターは常に表示されますか?
- ラベル: 各対話型コントロールにはアクセス可能な名前が付いていますか?
- コントラスト: 最小比率 テキストの場合は 4.5:1、UI コンポーネントの場合は 3:1
- 画像: コンテンツの代替テキスト、装飾の空の代替テキスト
- レスポンシブ: 200% ズームでも機能しますか?
- キーボード: すべての機能にマウスなしでアクセスできますか?
- スクリーン リーダー: コンテンツは賢明な方法で発表されていますか?
- 動き:アニメーション尊重
prefers-reduced-motion? - 形式: 明確なエラー、関連するラベル、わかりやすい検証
- 言語: 属性
langHTMLでは正しい
結論
Web アクセシビリティは追加機能ではなく、よく作られたソフトウェアの本質的な品質です。 WCAG 2.2 は明確なロードマップを提供し、Angular とその a11y CDK は それを効率的に実装するために必要なツール。
最善のアプローチは、自動テストなどのアクセシビリティを最初から組み込むことです。 CI パイプライン内の axe-core を使用し、スクリーン リーダーによる手動チェック、および定数 カラーコントラストモニタリング。あらゆる改善がウェブを場に変える より包括的であると同時に、すべてのユーザーのエクスペリエンスが向上します。
重要なポイント
- 注ぐ: 知覚可能、使用可能、理解可能、堅牢が 4 つの柱
- AAレベル: 法令順守と優れたユーザーエクスペリエンスの標準目標です
- まずセマンティック HTML: ネイティブ要素を使用すると、AIR の必要性が減ります
- CDK a11y: FocusTrap、LiveAnnouncer、および FocusMonitor により、Angular での作業が容易になります
- 継続的なテスト: CI の axe-core、監査用の Lighthouse、手動検証用のスクリーン リーダー
- WCAG 2.2: ターゲット サイズ最小 24x24px、アクセス可能な認証、一貫したヘルプ
- すぐに統合します。 後付けのアクセシビリティにはコストがかかり、機能も低下します







