Webアクセシビリティ入門|WAI-ARIA・セマンティックHTMLの実践ガイド

kento_morota 24分で読めます

「うちのサイト、スクリーンリーダーでちゃんと読めるのだろうか」「障害者差別解消法の改正で、Webアクセシビリティ対応が求められているけど、何から手をつければいいかわからない」——こうした悩みを持つWeb開発者やサイト運営者は少なくありません。

2024年4月の障害者差別解消法改正により、民間企業にも合理的配慮の提供が義務化されました。Webサイトのアクセシビリティ対応は、もはや「やった方がいいこと」ではなく「やるべきこと」になっています。

本記事では、Webアクセシビリティの基本概念からWAI-ARIA属性の使い方、セマンティックHTMLの実装まで、実務で即活用できる形で解説します。

Webアクセシビリティとは?なぜ今重要なのか

Webアクセシビリティとは、障害の有無や利用環境にかかわらず、誰もがWebコンテンツを利用できる状態を指します。視覚障害のある方がスクリーンリーダーを使ってサイトを閲覧したり、手の不自由な方がキーボードだけで操作したりといった場面を想像してください。

アクセシビリティ対応が重要な理由は、大きく3つあります。

法的要件への対応
障害者差別解消法の改正により、合理的配慮の提供が民間企業にも義務化されました。Webサイトのアクセシビリティ未対応が「合理的配慮の不提供」に該当する可能性があります。また、海外展開を考える企業にとっては、ADA(Americans with Disabilities Act)やEAA(European Accessibility Act)への準拠も重要です。

ユーザー体験の向上
アクセシビリティの改善は、障害者だけでなくすべてのユーザーの体験を向上させます。たとえば、適切な見出し構造はスクリーンリーダー利用者にとって重要ですが、同時にSEO効果も高めます。キーボード操作への対応は、マウスが使えない状況の健常者にも役立ちます。

SEOへの好影響
検索エンジンはセマンティックなHTML構造を重視します。適切な見出し階層、alt属性、ARIA属性の利用は、検索エンジンのクローラーがコンテンツを正確に理解する助けとなり、検索順位の向上につながります。

WCAG 2.2の4原則を理解する

Webアクセシビリティの国際的なガイドラインであるWCAG(Web Content Accessibility Guidelines)2.2は、4つの原則に基づいています。

1. 知覚可能(Perceivable)
情報やUIコンポーネントが、ユーザーが知覚できる方法で提示されていること。画像にはalt属性でテキストの代替を、動画には字幕を提供します。

2. 操作可能(Operable)
UIコンポーネントやナビゲーションが操作可能であること。すべての機能がキーボードで利用でき、操作に十分な時間が確保されていることが求められます。

3. 理解可能(Understandable)
情報やUIの操作方法が理解できること。テキストが読みやすく、Webページの動作が予測可能であること、入力エラー時に修正方法が提示されることなどが含まれます。

4. 堅牢(Robust)
コンテンツがさまざまなユーザーエージェント(ブラウザ、支援技術など)で確実に解釈できること。標準に準拠したマークアップが重要です。

WCAG 2.2では、適合レベルがA・AA・AAAの3段階で定義されています。一般的には、レベルAA準拠を目標とすることが推奨されます。

セマンティックHTMLの基本と実装

アクセシビリティ対応の第一歩は、セマンティック(意味論的)なHTMLを正しく書くことです。HTMLタグにはそれぞれ固有の意味があり、適切なタグを使うことで支援技術がコンテンツの構造を正確に把握できます。

ランドマーク要素の適切な使用

HTML5のランドマーク要素は、ページの主要なセクションを明示します。スクリーンリーダーのユーザーは、これらのランドマークを使ってページ内をすばやく移動できます。

<!-- 良い例:セマンティックなランドマーク -->
<header>
  <nav aria-label="メインナビゲーション">
    <ul>
      <li><a href="/">ホーム</a></li>
      <li><a href="/about">会社概要</a></li>
      <li><a href="/contact">お問い合わせ</a></li>
    </ul>
  </nav>
</header>

<main>
  <article>
    <h1>記事タイトル</h1>
    <p>記事本文...</p>
  </article>
</main>

<aside aria-label="関連記事">
  <h2>関連記事</h2>
  <ul>...</ul>
</aside>

<footer>
  <p>&copy; 2026 会社名</p>
</footer>

主なランドマーク要素とその役割は以下の通りです。

<header>:ページやセクションのヘッダー。サイトロゴやナビゲーションを含みます。
<nav>:ナビゲーションリンクのセクション。複数ある場合はaria-labelで区別します。
<main>:ページのメインコンテンツ。ページに1つだけ配置します。
<article>:独立した完結するコンテンツ。ブログ記事やニュースなどに使います。
<aside>:メインコンテンツに関連するが独立した補足情報。
<footer>:ページやセクションのフッター。著作権情報や関連リンクを含みます。

見出し階層の正しい設計

見出しタグ(h1〜h6)は、ページの論理構造を表現するために使います。見た目のサイズを変える目的で見出しタグを選んではいけません。

<!-- 悪い例:見出し階層が飛んでいる -->
<h1>ページタイトル</h1>
<h3>セクション1</h3>  <!-- h2を飛ばしている -->
<h5>サブセクション</h5>  <!-- h4を飛ばしている -->

<!-- 良い例:正しい見出し階層 -->
<h1>ページタイトル</h1>
<h2>セクション1</h2>
<h3>サブセクション1-1</h3>
<h3>サブセクション1-2</h3>
<h2>セクション2</h2>
<h3>サブセクション2-1</h3>

スクリーンリーダーには見出しリストを表示する機能があり、ユーザーは見出しだけを拾い読みしてページの全体像を把握します。そのため、見出しテキストは内容を的確に表すものにしましょう。

WAI-ARIAの基礎と正しい使い方

WAI-ARIA(Web Accessibility Initiative - Accessible Rich Internet Applications)は、HTMLの意味を補完し、動的なWebコンテンツやUIコンポーネントのアクセシビリティを向上させる仕様です。

ただし、重要な原則があります。ネイティブHTMLで実現できることにARIAを使わないということです。これは「ARIAの第一ルール」と呼ばれています。

ARIAのロール・ステート・プロパティ

ARIAには3種類の属性があります。

ロール(role):要素の役割を定義します。

<!-- カスタムタブUIにroleを付与 -->
<div role="tablist" aria-label="設定タブ">
  <button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">
    一般設定
  </button>
  <button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2">
    詳細設定
  </button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
  <p>一般設定の内容...</p>
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
  <p>詳細設定の内容...</p>
</div>

ステート(state):要素の現在の状態を示します。aria-expanded、aria-selected、aria-checkedなどがあります。

プロパティ(property):要素の特性を定義します。aria-label、aria-describedby、aria-requiredなどがあります。

aria-labelとaria-labelledbyの使い分け

要素にアクセシブルな名前を付与する2つの方法を理解しましょう。

<!-- aria-label:テキストを直接指定 -->
<button aria-label="メニューを閉じる">
  <svg>...</svg> <!-- アイコンのみのボタン -->
</button>

<!-- aria-labelledby:他の要素のテキストを参照 -->
<h2 id="search-heading">商品を検索</h2>
<form aria-labelledby="search-heading">
  <input type="search" aria-label="検索キーワード" />
  <button type="submit">検索</button>
</form>

<!-- aria-describedby:補足説明を関連付け -->
<label for="password">パスワード</label>
<input type="password" id="password" aria-describedby="password-hint" />
<p id="password-hint">8文字以上、英数字を含めてください</p>

使い分けのポイントは、ページ上に表示されるテキストを参照する場合はaria-labelledbyを、表示されないテキストを設定する場合はaria-labelを使います。

フォームのアクセシビリティ実装

フォームは、ユーザーがWebサイトとやり取りする重要な接点です。アクセシビリティの問題が最も発生しやすい箇所でもあります。

ラベルとフォームコントロールの関連付け

すべてのフォームコントロールには、明示的にラベルを関連付ける必要があります。

<!-- 良い例:明示的なラベル関連付け -->
<div>
  <label for="email">メールアドレス</label>
  <input type="email" id="email" name="email" required
         aria-describedby="email-error" />
  <p id="email-error" role="alert" class="error" hidden>
    有効なメールアドレスを入力してください
  </p>
</div>

<!-- ラジオボタンのグループ化 -->
<fieldset>
  <legend>お支払い方法</legend>
  <div>
    <input type="radio" id="credit" name="payment" value="credit" />
    <label for="credit">クレジットカード</label>
  </div>
  <div>
    <input type="radio" id="bank" name="payment" value="bank" />
    <label for="bank">銀行振込</label>
  </div>
</fieldset>

placeholderをラベルの代わりに使うのは避けましょう。placeholderは入力開始時に消えてしまい、ユーザーが何を入力すべきか分からなくなります。

エラーメッセージとバリデーション

フォームバリデーションでは、エラーの内容をすべてのユーザーに確実に伝える必要があります。

<!-- アクセシブルなバリデーション実装例 -->
<form novalidate>
  <div>
    <label for="username">ユーザー名 <span aria-hidden="true">*</span></label>
    <input type="text" id="username" name="username"
           required aria-required="true"
           aria-invalid="false"
           aria-describedby="username-hint username-error" />
    <p id="username-hint">半角英数字3〜20文字</p>
    <p id="username-error" role="alert" hidden></p>
  </div>
  <button type="submit">送信</button>
</form>

<script>
const form = document.querySelector('form');
const usernameInput = document.getElementById('username');
const usernameError = document.getElementById('username-error');

form.addEventListener('submit', (e) => {
  const value = usernameInput.value.trim();
  if (!value) {
    e.preventDefault();
    usernameInput.setAttribute('aria-invalid', 'true');
    usernameError.textContent = 'ユーザー名を入力してください';
    usernameError.hidden = false;
    usernameInput.focus();
  }
});
</script>

ポイントは以下の通りです。

aria-invalidでエラー状態を示す。
role="alert"でエラーメッセージの変更をスクリーンリーダーに通知する(ライブリージョン)。
aria-describedbyでエラーメッセージを入力欄に関連付ける。
エラー発生時に該当の入力欄にフォーカスを移動する。

キーボード操作とフォーカス管理

キーボードだけですべての機能を操作できることは、アクセシビリティの基本要件です。マウスを使えないユーザーや、スクリーンリーダー利用者にとって不可欠です。

フォーカスの可視化とフォーカストラップ

ブラウザのデフォルトのフォーカスインジケーターを消すのは、アクセシビリティの大きな問題です。カスタマイズする場合は、十分に目立つスタイルにしましょう。

/* 悪い例:フォーカスインジケーターを消す */
*:focus {
  outline: none; /* 絶対にやめましょう */
}

/* 良い例:カスタムフォーカスインジケーター */
:focus-visible {
  outline: 3px solid #4A90D9;
  outline-offset: 2px;
}

/* マウスクリック時はフォーカスリングを非表示にし、
   キーボード操作時のみ表示する */
:focus:not(:focus-visible) {
  outline: none;
}

モーダルダイアログでは、フォーカストラップ(ダイアログ内にフォーカスを閉じ込める)の実装が重要です。

<dialog id="modal" aria-labelledby="modal-title">
  <h2 id="modal-title">確認</h2>
  <p>この操作を実行しますか?</p>
  <button id="confirm-btn">実行する</button>
  <button id="cancel-btn">キャンセル</button>
</dialog>

<script>
// HTML <dialog> 要素を使えば、
// フォーカストラップはブラウザが自動で処理してくれます
const modal = document.getElementById('modal');
const cancelBtn = document.getElementById('cancel-btn');

function openModal() {
  modal.showModal(); // フォーカスが自動でダイアログ内に移動
}

cancelBtn.addEventListener('click', () => {
  modal.close(); // フォーカスがトリガー要素に自動で戻る
});
</script>

HTML標準の<dialog>要素を使うと、フォーカストラップやEscキーでの閉じる処理がブラウザネイティブでサポートされるため、カスタム実装の手間が大幅に減ります。

画像・メディアのアクセシビリティ

画像やメディアコンテンツは、代替テキストや字幕がなければ、視覚・聴覚障害のあるユーザーにはアクセスできません。

alt属性の書き方のベストプラクティス

画像のalt属性は、画像の役割に応じて書き分けます。

<!-- 情報を伝える画像:内容を具体的に記述 -->
<img src="sales-chart.png"
     alt="2025年度の売上推移グラフ。1月の500万円から12月の1200万円まで右肩上がりで推移" />

<!-- 装飾目的の画像:空のalt属性 -->
<img src="decorative-line.png" alt="" />

<!-- リンク内の画像:リンク先を説明 -->
<a href="/products">
  <img src="products-banner.jpg" alt="製品一覧ページへ" />
</a>

<!-- 複雑な図表:詳細説明を別途提供 -->
<figure>
  <img src="architecture.png"
       alt="システムアーキテクチャ図"
       aria-describedby="arch-desc" />
  <figcaption id="arch-desc">
    ユーザーのリクエストはCDNを経由してロードバランサーに到達し、
    3台のアプリケーションサーバーに分散されます。
    データベースはプライマリとレプリカの構成です。
  </figcaption>
</figure>

alt属性の書き方のコツは、「この画像が表示されなかったら、どんなテキストが必要か」を考えることです。「画像」「写真」という接頭辞は不要です(スクリーンリーダーが自動的に「画像」と読み上げるため)。

動画・音声コンテンツの対応

動画には字幕やトランスクリプトを提供しましょう。

<video controls>
  <source src="tutorial.mp4" type="video/mp4" />
  <track kind="captions" src="tutorial-ja.vtt"
         srclang="ja" label="日本語字幕" default />
  <track kind="descriptions" src="tutorial-desc-ja.vtt"
         srclang="ja" label="音声ガイド" />
  <p>お使いのブラウザは動画再生に対応していません。
     <a href="tutorial.mp4">動画をダウンロード</a>してご覧ください。</p>
</video>

自動再生される音声は、ユーザーが停止できるようにする必要があります。autoplay属性を使う場合はmuted属性も一緒に指定しましょう。

アクセシビリティテストの実践方法

実装したアクセシビリティ対応が正しく機能しているか、継続的にテストすることが重要です。

自動テストツールの活用

まず、自動テストツールで検出可能な問題を洗い出しましょう。

axe DevTools:ブラウザ拡張機能として利用できる定番のアクセシビリティテストツールです。ページを開いてワンクリックでWCAG違反を検出できます。

Lighthouse:Chrome DevToolsに組み込まれており、アクセシビリティスコアと改善ポイントを表示します。

eslint-plugin-jsx-a11y:React開発では、ESLintプラグインでコーディング時にアクセシビリティの問題を検出できます。

// CI/CDパイプラインでのaxe-coreを使った自動テスト
import { AxeBuilder } from '@axe-core/playwright';
import { test, expect } from '@playwright/test';

test('トップページのアクセシビリティ', async ({ page }) => {
  await page.goto('/');
  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa'])
    .analyze();
  expect(results.violations).toEqual([]);
});

test('お問い合わせフォームのアクセシビリティ', async ({ page }) => {
  await page.goto('/contact');
  const results = await new AxeBuilder({ page })
    .include('#contact-form')
    .analyze();
  expect(results.violations).toEqual([]);
});

手動テストとスクリーンリーダー検証

自動テストで検出できるのは、アクセシビリティの問題の約30%に過ぎません。以下の手動テストも実施しましょう。

キーボード操作テスト
Tabキーだけでページ内のすべてのインタラクティブ要素に到達できるか確認します。フォーカス順序が論理的か、フォーカスが見えなくならないかもチェックします。

スクリーンリーダーテスト
主要なスクリーンリーダーで実際にページを読み上げてみましょう。Windowsでは無料のNVDA、macOSではVoiceOverが利用できます。見出しナビゲーションやランドマークジャンプが正しく機能するか確認します。

ズームテスト
ブラウザのズーム機能で200%まで拡大して、コンテンツが切れたり重なったりしないか確認します。テキストサイズの変更にも対応できるよう、フォントサイズにはremやem単位を使いましょう。

色のコントラストチェック
WCAG 2.2では、通常のテキストで4.5:1以上、大きなテキストで3:1以上のコントラスト比が求められます。Chrome DevToolsのカラーピッカーでコントラスト比を確認できます。

アクセシビリティ改善のロードマップ

既存サイトのアクセシビリティを一度に完璧にするのは現実的ではありません。優先度をつけて段階的に改善していきましょう。

フェーズ1:基本対応(1〜2週間)

・すべての画像にalt属性を追加する
・見出し階層を正しく修正する
・フォームのラベルを関連付ける
・lang属性をhtml要素に設定する
・フォーカスインジケーターが見えることを確認する

フェーズ2:インタラクション改善(2〜4週間)

・キーボード操作の対応
・カスタムUIコンポーネントにARIA属性を追加
・エラーメッセージのアクセシビリティ改善
・色のコントラスト比を修正

フェーズ3:継続的改善(継続)

・CI/CDにアクセシビリティテストを組み込む
・スクリーンリーダーでの定期テスト
・新規ページ・機能のアクセシビリティレビュープロセスの確立
・チームメンバーへのアクセシビリティ研修

アクセシビリティ対応は、一度やって終わりではなく継続的な取り組みです。開発チーム全体がアクセシビリティの意識を持ち、日常の開発プロセスに組み込むことが重要です。

まとめ

Webアクセシビリティは、セマンティックHTMLの正しい使用から始まります。WAI-ARIAはHTMLだけでは表現しきれない情報を補完する強力なツールですが、まずはネイティブHTMLの機能を最大限活用しましょう。

本記事で解説した内容を振り返ります。

・WCAG 2.2の4原則(知覚可能・操作可能・理解可能・堅牢)を理解する
・セマンティックHTMLのランドマーク要素と見出し階層を正しく使う
・WAI-ARIAはネイティブHTMLで不足する場合の補完として使う
・フォームのラベル関連付けとエラーハンドリングを適切に実装する
・キーボード操作とフォーカス管理を確実にする
・自動テストと手動テストを組み合わせて継続的に検証する

まずはaxe DevToolsやLighthouseで現状のサイトをスキャンし、検出された問題から優先度の高いものを改善していきましょう。小さな改善の積み重ねが、すべてのユーザーにとって使いやすいWebサイトへとつながります。

#アクセシビリティ#WAI-ARIA#HTML
共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

プライバシーポリシーに基づき管理します

起業準備に役立つ情報、もっとありますよ。

まずは話だけ聞いてもらう