Webアクセシビリティの完全ガイド:Lighthouse / axe による自動テスト、WCAG基準策定、キーボード操作・スクリーンリーダー対応まで

2023/11/21に公開

はじめに

Webアクセシビリティは、すべてのユーザーが快適にWebサイトを利用できるようにするために欠かせない要素です。特に、高齢者や視覚・聴覚障害を持つユーザーにとって、適切なアクセシビリティ対応が行われていないと、情報へのアクセスが制限されてしまいます。

本記事では、Lighthouseやaxeを活用したアクセシビリティテストの自動化、WCAGガイドラインに基づく評価基準の整備、スクリーンリーダー対応の改善、カラーコントラストの最適化、ARIAランドマークの導入など、Webアクセシビリティ向上のための具体的な方法を詳しく解説します。これらの取り組みを継続的に行うことで、より多くのユーザーにとって使いやすいWebサイトを実現しましょう。

アクセシビリティテストの自動化(Lighthouse / axe での監視設定)

アクセシビリティテストを自動化することで、継続的な品質保証が可能になります。特に、Lighthouse と axe-core は、開発フローに組み込みやすい代表的なツールです。

Lighthouse を使用したアクセシビリティテスト

Lighthouse は Google が提供するオープンソースの監査ツールで、以下の点を自動チェックできます。

  • アクセシビリティ(ウェブコンテンツのユーザビリティ)
  • パフォーマンス(ページ速度)
  • SEO(検索エンジン最適化)
  • ベストプラクティス(セキュリティやモバイル対応)
  • PWA(プログレッシブウェブアプリの適合性)

Lighthouse の実行方法

  • Chrome で対象ページを開く。
  • F12 または Cmd + Option + I(Mac)/ Ctrl + Shift + I(Windows) で DevTools を開く。
  • Lighthouse タブを選択し、"Accessibility" にチェックを入れる。
  • Analyze page load をクリックして分析。

axe-core を使用したアクセシビリティテスト

Deque Systems が開発した axe-core は、開発者向けのアクセシビリティテストツールで下記のような特徴があります。

  • ウェブページのアクセシビリティ違反を細かく検出
  • Jest や Cypress などのテストフレームワークと統合可能
  • ブラウザ拡張機能としても利用可能(axe DevTools)

1. Cypress + axe-core の組み合わせ

E2E テストツールの Cypress でも cypress-axe を使えばアクセシビリティテストを実行できます。

cypress-axe のインストール

npm install --save-dev cypress-axe

Cypress テストで axe-core を利用

describe("Accessibility check", () => {
  it("Should have no detectable accessibility violations", () => {
    cy.visit("https://example.com");
    cy.injectAxe();
    cy.checkA11y();
  });
});
  • cy.injectAxe(): axe-core をページに読み込む。
  • cy.checkA11y(): ページのアクセシビリティ違反を検出。

メリット
✅ ページ全体のアクセシビリティ違反を検出
✅ 実際のブラウザ環境で検証できる
✅ キーボード操作やフォーカスの移動などのテストも可能

2. Playwright + axe-core を使う

Cypress 以外にも、Playwright を使った E2E テストの中で axe-core を利用する方法があります。

Playwright + axe-core のインストール

npm install --save-dev @playwright/test @axe-core/playwright

Playwright テストで axe-core を利用

import { test, expect } from "@playwright/test";
import { injectAxe, checkA11y } from "@axe-core/playwright";

test("Page should be accessible", async ({ page }) => {
  await page.goto("https://example.com");
  await injectAxe(page);
  await checkA11y(page);
});

Lighthouseaxe-core の使い分け

ツール 用途・特徴
Lighthouse アクセシビリティスコアの算出とレポート生成が得意。全体の改善点を把握するのに向いている。
axe-core アクセシビリティ違反を詳細に検出し、Jest や Cypress での自動テストに適している。

アクセシビリティ評価基準の整備(WCAG ガイドラインに基づく基準策定)

Webアクセシビリティの評価基準は、WCAG(Web Content Accessibility Guidelines) に基づいて策定されます。WCAG は、すべてのユーザーがウェブコンテンツにアクセスできるようにするための国際的なガイドラインであり、主に 4つの原則 に基づいています。

WCAGの4つの原則

1. 知覚可能(Perceivable)

ユーザーがコンテンツを 視覚・聴覚・触覚などを通じて認識できるようにする。

    • 画像 に 代替テキスト(alt属性) を設定する。
    • 動画・音声 に 字幕や文字起こし を提供する。
    • コントラスト比 を十分に確保する(例:テキストと背景の色のコントラスト比 4.5:1 以上)。
    • スクリーンリーダー で情報が正しく読み上げられるようにマークアップする。

2. 操作可能(Operable)

すべてのユーザーがコンテンツを操作できるようにする。

    • キーボード操作 に対応させる(マウスなしでも利用可能)。
    • フォーカス管理 を適切に設定する(タブキーでのナビゲーションを明確に)。
    • 制限時間付きの操作 に対する猶予を設ける(自動ログアウトやポップアップに十分な時間を与える)。
    • 点滅や強いアニメーション を抑える(光過敏性発作を引き起こさないようにする)。

3. 理解可能(Understandable)

ユーザーが コンテンツを理解できる ようにする。

    • 簡潔でわかりやすい言葉を使用する。
    • フォームのエラーメッセージ を明確に伝える(例:「入力が必要です」ではなく「メールアドレスを正しく入力してください」)。
    • ナビゲーションの一貫性を保つ(ページごとに異なるレイアウトを避ける)。

4. 堅牢(Robust)

支援技術と互換性 があり、将来的にも使用できるようにする。

    • 適切なHTMLマークアップ(見出しタグ h1~h6 の適切な使用、ARIA属性の活用)。
    • 最新のWeb技術 に対応する(例:JavaScriptがなくても基本的な操作ができる)。
    • テストを複数の支援技術(スクリーンリーダー、音声入力ソフトなど)で実施する。

企業がアクセシビリティ評価基準を整備するメリット

  1. ユーザー体験(UX)の向上
    アクセシビリティを向上させることで、より多くの人が快適に利用可能。
  2. 法規制の遵守
    日本:「障害者差別解消法」に基づく合理的配慮の提供。
    EU:「European Accessibility Act(EAA)」に準拠。
    アメリカ:「ADA(Americans with Disabilities Act)」や「Section 508」などの基準。
  3. SEO(検索エンジン最適化)への影響
    適切なHTML構造やaltテキストの設定はSEOにもプラス。
  4. 企業の社会的責任(CSR)
    インクルーシブなデザインは企業のブランド価値向上に貢献。

企業内での評価基準の策定手順

1. WCAGの適用レベルを決定

WCAGには A、AA、AAA の3つの適用レベルがある。

  • A(最低基準):最低限のアクセシビリティ要件を満たす(例:画像のalt設定)。
  • AA(推奨基準):多くのユーザーが利用できる基準(例:色のコントラスト比の確保)。
  • AAA(高度基準):高水準のアクセシビリティを確保(例:手話翻訳の提供)。

2. 自社製品に適した評価基準を文書化

  • 企業の業務やターゲットユーザーに応じた基準を策定。
  • 評価基準ドキュメント を作成し、社内の関係者と共有。

例)評価基準の文書化

項目 説明 適用レベル
画像のalt属性 すべての画像に適切なaltを設定する A
キーボード操作 すべてのUI要素がキーボードで操作可能であること AA
色のコントラスト比 4.5:1以上(テキストと背景) AA
フォームのラベル 入力欄には明確なラベルを付与する AA
動画の字幕 すべての動画に字幕を提供する AAA

3. 自動テスト + 手動テストの組み合わせで定期的な評価を実施

  • 自動テスト
    • Axe、Lighthouse、WAVE などのツールを活用して定期的にスキャン。
  • 手動テスト
    • キーボードのみでの操作確認。
    • スクリーンリーダー(NVDA、VoiceOver、JAWS)での検証。
    • ユーザビリティテスト(実際のユーザーによる評価)。

4. 監査結果をドキュメント化し、改善サイクルを確立

  • 監査レポート を作成し、問題点と改善策を明確にする。
  • 改善計画を策定し、定期的にレビュー(例:四半期ごとのアクセシビリティレビュー)。

キーボード操作の確認(全ての機能がキーボードで操作可能か確認)

キーボード操作の確認は、アクセシビリティ(A11Y)を向上させるために不可欠です。特に、マウスが使えないユーザーやスクリーンリーダーを利用するユーザーにとって、キーボードだけで全機能を利用できることが求められます。

キーボード操作の確認ポイント

1. Tab キーで全てのインタラクティブ要素を移動できるか

  • tab キーを押すと、ページ内のボタン、リンク、入力欄などの フォーカス可能な要素 に移動できる必要があります。
  • Shift + Tab で逆順に移動できるかも確認します。
  • tabindex="-1" の要素はフォーカスされないため、必要に応じて tabindex="0" を設定。

2. Enter / Space でボタンが押せるか

  • ボタン (<button>, <a href>, <input type="submit">) は Enter または Space でアクションが実行できるか確認します。
  • divspan をボタンの代わりに使っている場合、role="button" を付けるだけでは不十分です。keydown イベントで EnterSpace をハンドリングする必要あります。

3. Escape キーでモーダルを閉じられるか

  • モーダルウィンドウが開いているときに Escape を押したら閉じる仕様になっているか確認。
  • aria-hiddenaria-modal="true" の設定が適切かチェックします。

4. フォーカスインジケーター(アウトライン)が適切に表示されるか

  • outline: none を指定していないか確認(見た目だけでなく、視認性を確保することが重要)。
  • カスタムフォーカススタイルを適用する場合、:focus-visible を利用するとマウス操作時には非表示にできる。
button:focus-visible {
  outline: 2px solid #005fcc; /* アクセシブルなフォーカス色 */
  border-radius: 4px;
}

手動テストによる確認

tab キーを使用してフォーカスの移動を確認します。

自動テストによる確認

Cypress は E2E テストに便利なツールで、キーボード操作のテストも可能です。

describe('Keyboard Navigation Test', () => {
  it('should allow navigation via Tab key', () => {
    cy.visit('https://example.com');
    cy.get('button').first().focus().should('have.css', 'outline');
    cy.tab().should('have.focus'); // Tabキーで次の要素に移動
  });

  it('should close modal on Escape key press', () => {
    cy.get('#open-modal').click();
    cy.get('#modal').should('be.visible');
    cy.get('body').type('{esc}');
    cy.get('#modal').should('not.exist');
  });
});

Playwright は keyboard.press() を使って詳細なキーボード操作をテストできます。

import { test, expect } from '@playwright/test';

test('Keyboard navigation works correctly', async ({ page }) => {
  await page.goto('https://example.com');

  // Tabキーでボタンをフォーカス
  await page.keyboard.press('Tab');
  await expect(page.locator('button')).toBeFocused();

  // Enterキーでクリックしモーダルが表示される
  await page.keyboard.press('Enter');
  await expect(page.locator('#modal')).toBeVisible();

  // Escapeキーでモーダルを閉じる
  await page.keyboard.press('Escape');
  await expect(page.locator('#modal')).not.toBeVisible();
});

カラーコントラストの最適化(色盲や視力低下者向けに高コントラストを確保)

カラーコントラストの重要性

  • 視覚障害者や色覚異常(色盲)の方にも読みやすい・区別しやすいサイトを作るために必要です。文字色と背景色のコントラストが不十分だと、文字が判別しづらくなります。
  • 高齢者や視力の低下がある方にも有効で、しっかり文字情報を伝えられるようになり、アクセシビリティが向上します。
  • WCAG(Web Content Accessibility Guidelines)2.1では、文字の可読性と判別性を高めるため、特定のコントラスト比を遵守することを推奨しています。

こうした配慮は「見る人みんなが心地よく読みやすい」ウェブサイトを作る土台になります。あなたがその価値を理解し、丁寧に取り組むことで、多くのユーザーが助かり、その結果サービス全体の満足度も向上します。

推奨されるコントラスト比

  • 通常のテキスト: コントラスト比 4.5:1 以上
  • 大きなテキスト(18px 以上 or 14px 太字): コントラスト比 3:1 以上

「コントラスト比」とは、背景色と前景色(文字色)の明暗差を数値化したものです。この比率が高いほど、両色の違いがはっきりと認識できる、つまり読みやすいということになります。

例)

  • 黒文字(#000000) と 白背景(#FFFFFF) のコントラスト比は 21:1 と最高レベル
  • グレー文字(#767676) と 白背景(#FFFFFF) だとコントラスト比が低めになるため、場合によっては基準を満たせないこともありえます

テストツール

  1. Lighthouse

    • Google Chromeの開発者ツールに搭載されている監査ツール。
    • 「Accessibility」項目の中でカラーコントラストに関する指摘があれば表示されます。
  2. axe DevTools

    • Deque Systemsが提供しているブラウザ拡張機能。
    • ページをスキャンして、自動でアクセシビリティの問題点をリストアップ。コントラスト違反も見つけやすいです。
  3. Contrast Checker

    • 「WebAIM’s Contrast Checker」などのオンラインツール。
    • 手動で色のコードを入力すると、コントラスト比と合否をチェックできます。
    • デザイナーとやり取りする際、具体的な数値で「この色はコントラスト比4.2:1なので、あともう少し明るくしましょう」などと提案しやすくなります。

スクリーンリーダー対応の改善(alt 属性の適切な設定)

特に画像 (<img> タグ) に適切な alt 属性を設定することは、スクリーンリーダー利用者にとって不可欠です。以下のルールを守ることで、アクセシビリティを向上させられます。

alt 属性の基本ルール

1. 装飾目的の画像(視覚的な飾りのみ)

  • alt=""(空にする)
  • role="presentation" を併用すると、スクリーンリーダーが完全に無視
  • 例: 背景装飾としてのアイコン
    <img src="decorative-icon.png" alt="" role="presentation">
    

2. 意味のある画像(情報提供を目的とする画像)

  • 画像の内容を簡潔に説明する alt を設定
  • role="presentation" を併用すると、スクリーンリーダーが完全に無視
  • 例: 商品画像
    <img src="product.jpg" alt="赤いスニーカー">
    

3, リンク内の画像

  • alt にはリンク先の内容を説明するテキストを入れる
  • 例: 企業ロゴがリンクになっている場合
    <a href="https://example.com">
      <img src="logo.png" alt="Example社のホームページへ">
    </a>
    

4. テキストを含む画像

  • 画像内のテキストを alt にそのまま書く
  • 例: バナー画像
    <img src="sale-banner.jpg" alt="全商品20%オフ!今週末まで">
    

5. 図やグラフ

  • alt には要約を記載し、詳細な説明は figcaption や longdesc を活用
  • 例: グラフ
    <figure>
      <img src="sales-chart.png" alt="売上の推移を示す棒グラフ">
      <figcaption>2024年1月から3月の売上推移</figcaption>
    </figure>
    

ARIA 属性を活用したアクセシビリティ強化

alt 属性だけでは対応しきれないケースでは、ARIA (aria-*) 属性を活用することで、スクリーンリーダーが正しく情報を伝えられるようにできます。

aria-label / aria-labelledby

  • aria-label: 要素に直接ラベルを付ける

    <button aria-label="メニューを開く"></button>
    
  • aria-labelledby: 他の要素を参照してラベルを付ける

    <h2 id="section-title">製品一覧</h2>
    <section aria-labelledby="section-title">
      <p>最新の製品ラインナップをご紹介します。</p>
    </section>
    

aria-live(ライブリージョン)

aria-live(ライブリージョン)は、スクリーンリーダーに対して「このエリアの内容が変わったら、ユーザーに通知して」と指示するための属性です。
Webページ上のコンテンツが動的に変わる場合、視覚的には変化がわかっても、スクリーンリーダーには通知されません。
そのため、aria-live を適用することで、スクリーンリーダーに変更を伝えることができます。

<p id="status" aria-live="polite">カートに商品がありません。</p>
<button onclick="document.getElementById('status').textContent = '商品がカートに追加されました!'">
  カートに追加
</button>
  1. ページを開くと、スクリーンリーダーは カートに商品がありません と読み上げる。
  2. ボタンを押すと <p> の内容が 商品がカートに追加されました! に変わる。
  3. aria-live="polite" によって、スクリーンリーダーは適切なタイミングで 商品がカートに追加されました! を読み上げる。

スクリーンリーダーテスト方法

スクリーンリーダーを使ったテスト

  • Windows: NVDA
  • Mac: VoiceOver(Command + F5 で起動)
  • iPhone / iPad: VoiceOver(設定 > アクセシビリティ > VoiceOver

テストのポイント

  • alt が適切に読み上げられるか
  • aria-label / aria-labelledby の動作確認
  • 重要な情報が aria-live で適切に伝達されるか

自動テストツール

axe-core を使うと、アクセシビリティの問題を効率的に検出できる。

  1. Chrome拡張機能「axe DevTools」をインストール
  2. DevTools(F12)を開き、「axe DevTools」を実行
  3. alt のない画像や aria-* 属性の誤りを検出

https://chromewebstore.google.com/detail/axe-devtools-web-accessib/lhdoppojpmngadmnindnejefpokejbdd?pli=1

ARIA ランドマークの導入

ARIA(Accessible Rich Internet Applications)ランドマークは、Webページの主要なセクションをスクリーンリーダーや支援技術が認識しやすくするための重要な役割を果たします。適切に設定することで、視覚障害のあるユーザーがページの構造をすばやく把握し、必要な情報へ効率的にアクセスできるようになります。

ランドマークの種類と適用方法

HTML5 では、すでに多くの要素が暗黙的に ARIA の役割を持っていますが、場合によっては明示的に role 属性を指定することで、スクリーンリーダーの挙動を強化できます。

要素 デフォルトの役割 推奨される role 説明
<header> banner (条件付き) role="banner" サイト全体またはセクションのヘッダーを示す。
<nav> navigation role="navigation" ナビゲーションメニューを示す。
<main> main role="main" ページの主要なコンテンツ領域。
<aside> complementary role="complementary" 補助的な情報(サイドバーなど)を提供。
<footer> contentinfo (条件付き) role="contentinfo" フッター(著作権情報など)を示す。

ポイント

  • <header><footer> は、ページ全体のヘッダー・フッターとして使われる場合のみ role="banner"role="contentinfo" の適用が推奨されます。(セクション内で使う場合は適用しない)
  • <main> は、ページ内に 1 つだけ 存在するよう設定します。
  • <nav> を複数設置する場合は、aria-label を使ってどのナビゲーションかわかるようにするのが望ましいです。

コード例

以下のコードでは、適切に ARIA ランドマークを設定し、スクリーンリーダーが認識しやすい構造になっています。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ARIAランドマークの詳細な例</title>
</head>
<body>

  <!-- ヘッダー -->
  <header role="banner">
    <h1>サイトタイトル</h1>
  </header>

  <!-- グローバルナビゲーション -->
  <nav role="navigation" aria-label="グローバルナビゲーション">
    <ul>
      <li><a href="#">ホーム</a></li>
      <li><a href="#">製品情報</a></li>
      <li><a href="#">サポート</a></li>
    </ul>
  </nav>

  <div style="display: flex;">
    <!-- メインコンテンツ -->
    <main role="main">
      <h2>主要コンテンツ</h2>
      <p>このセクションには、ページの主要な情報が含まれます。</p>
    </main>

    <!-- 関連情報 -->
    <aside role="complementary" aria-labelledby="related-info">
      <h3 id="related-info">関連情報</h3>
      <p>補助的な情報を提供します。</p>
    </aside>
  </div>

  <!-- サブナビゲーション -->
  <nav role="navigation" aria-label="サイドナビゲーション">
    <ul>
      <li><a href="#">利用ガイド</a></li>
      <li><a href="#">FAQ</a></li>
    </ul>
  </nav>

  <!-- フッター -->
  <footer role="contentinfo">
    <p>&copy; 2025 会社名</p>
  </footer>

</body>
</html>

ポイント

  • aria-labelledby="related-info" を使用して、aside 内の補助情報が何に関連しているのかを明確にする。
  • aria-label="グローバルナビゲーション"aria-label="サイドナビゲーション" を使い、ナビゲーションの役割を区別する。
  • div でレイアウトを調整し、mainaside を横並びにする。

フォームのアクセシビリティ改善

フォームのラベルと入力フィールドを適切に関連付けることで、スクリーンリーダーを使用する視覚障害者やキーボード操作を行うユーザーにとって、より使いやすいフォームを設計できます。これにより、アクセシビリティの向上だけでなく、ユーザーエクスペリエンスの改善にもつながります。

label 要素を明示的に関連付ける

label 要素を for 属性を使って input 要素と関連付けることで、スクリーンリーダーがフォームの各入力フィールドを適切に読み上げるようになります。

<label for="username">ユーザー名</label>
<input type="text" id="username" name="username">

ポイント

  • labelfor 属性の値を、対応する inputid と一致させる。
  • labelinput の隣に配置すると、視覚的にもわかりやすくなる。

メリット

  • スクリーンリーダーが「ユーザー名、入力フィールド」と適切に読み上げる。
  • label をクリックすると、関連する input にフォーカスが移動し、クリックしやすくなる。

aria-labelledby を活用する

aria-labelledby を使うことで、1つのラベルを複数の入力フィールドに関連付けたり、視覚的にlabel を配置しづらい場合に代替として利用したりできます。

<div>
  <span id="email-label">メールアドレス</span>
  <input type="email" id="email" aria-labelledby="email-label">
</div>

ポイント

  • aria-labelledby の値に、関連付ける要素の id を指定する。
  • label 要素が使えない場面(例えば、ボタンや複雑なフォームレイアウト)で有効。

メリット

  • label なしでもスクリーンリーダーが適切に読み上げる。
  • 1つのラベルを複数のフィールドに関連付けることが可能。

aria-describedby を活用する

補足説明を提供する場合に aria-describedby を使用すると、入力フィールドの補足説明をスクリーンリーダーに伝えることができます。

<input type="text" id="phone" aria-describedby="phone-desc">
<span id="phone-desc">ハイフンなしで入力してください。</span>

ポイント

  • aria-describedby の値に、補足説明の id を指定する。
  • 使い方のヒントやエラーメッセージの説明を追加できる。

メリット

  • ユーザーがフィールドにフォーカスしたときに、スクリーンリーダーが補足説明を読み上げる。
  • エラーメッセージの伝達にも有効。

モーダルウィンドウの操作確認

モーダルウィンドウを適切に管理することで、キーボード操作の快適性とアクセシビリティを向上させることができます。本記事では、フォーカス制御の重要性と適切な実装方法について詳しく解説します。

モーダル内でフォーカスを適切に管理する

  • 最初のフォーカスの移動
    モーダルを開いた際に、適切なフォーカス要素(close ボタンや input フィールドなど)にフォーカスを移動させる。
  • 背景コンテンツへのフォーカス防止
    モーダルが開いている間、tab キーを押しても背景の要素にフォーカスが移動しないように制御する。
  • Esc キーによるモーダルの閉鎖
    ユーザーが Esc キーを押した際にモーダルを閉じられるようにする。

コード例(React + TypeScript 版モーダル)

components/Modal.tsx
import { useEffect, useRef } from "react";

interface ModalProps {
  isOpen: boolean;
  onClose: () => void;
}

const Modal: React.FC<ModalProps> = ({ isOpen, onClose }) => {
  const modalRef = useRef<HTMLDivElement>(null);
  const closeButtonRef = useRef<HTMLButtonElement>(null);

  useEffect(() => {
    if (isOpen) {
      // モーダルを開いたら最初のフォーカスを設定
      setTimeout(() => {
        closeButtonRef.current?.focus();
      }, 0);

      const handleKeyDown = (event: KeyboardEvent) => {
        if (event.key === "Escape") {
          onClose();
        } else if (event.key === "Tab") {
          // フォーカスをループさせる
          const focusableElements = modalRef.current?.querySelectorAll(
            'a[href], button, textarea, input, select'
          ) as NodeListOf<HTMLElement>;

          if (!focusableElements) return;

          const firstElement = focusableElements[0];
          const lastElement = focusableElements[focusableElements.length - 1];

          if (event.shiftKey) {
            if (document.activeElement === firstElement) {
              event.preventDefault();
              lastElement.focus();
            }
          } else {
            if (document.activeElement === lastElement) {
              event.preventDefault();
              firstElement.focus();
            }
          }
        }
      };

      document.addEventListener("keydown", handleKeyDown);
      return () => document.removeEventListener("keydown", handleKeyDown);
    }
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return (
    <div
      ref={modalRef}
      role="dialog"
      aria-hidden={!isOpen}
      aria-labelledby="modal-title"
      className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50"
    >
      <div className="bg-white p-6 rounded-lg shadow-lg w-96">
        <h2 id="modal-title" className="text-lg font-bold mb-4">
          モーダルタイトル
        </h2>
        <p className="mb-4">モーダルの内容をここに記述します。</p>
        <button
          ref={closeButtonRef}
          onClick={onClose}
          className="bg-red-500 text-white px-4 py-2 rounded"
        >
          閉じる
        </button>
      </div>
    </div>
  );
};

export default Modal;

ポイント

  • モーダルの開閉を isOpen で管理
    • isOpentrue のときのみ、モーダルを表示。
    • isOpenfalse の場合は null を返して非表示に。
  • フォーカス管理
    • モーダルが開かれたら、最初のフォーカスを 閉じる ボタンにセット。
    • Tab キーでフォーカスをループさせる (firstElementlastElement)。
  • Esc キーで閉じる
    • keydown イベントを監視し、Escape キーが押されたら onClose() を実行。
  • アクセシビリティ対応
    • <div>role="dialog" を設定し、aria-hidden でスクリーンリーダー対応。
    • aria-labelledby="modal-title" により、タイトルを読み上げられるように設定。

使用例 (App.tsx)

モーダルを開閉するボタンを App.tsx に配置し、useState を利用して状態管理します。

App.tsx
import { useState } from "react";
import Modal from "./Modal";

const App = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);

  return (
    <div className="flex flex-col items-center justify-center h-screen">
      <button
        onClick={() => setIsModalOpen(true)}
        className="bg-blue-500 text-white px-4 py-2 rounded"
      >
        モーダルを開く
      </button>

      <Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)} />
    </div>
  );
};

export default App;

このコードを適用すると、以下のような動作を確認できます。

  1. 「モーダルを開く」ボタンをクリックすると、モーダルが表示される。
  2. Tab キーを押すと、モーダル内の要素だけをフォーカス移動できる。
  3. Esc キーを押すと、モーダルが閉じる。
  4. モーダルが閉じると、元の「モーダルを開く」ボタンにフォーカスが戻る。

レスポンシブデザインの確認

レスポンシブデザインとは、異なるデバイスや画面サイズに応じて適切にレイアウトを調整する設計手法です。特にアクセシビリティ(利用しやすさ)を考慮することで、すべてのユーザーが快適に操作できるWebサイトを構築できます。ここでは、レスポンシブデザインの基本となる メディアクエリ と ビューポートメタタグ について詳しく解説します。

メディアクエリの活用

メディアクエリ(Media Queries)は、CSSの機能の一つで、画面サイズやデバイスの特性に応じて異なるスタイルを適用 できます。

@media (max-width: 600px) {
  nav {
    display: block;
  }
}

このメディアクエリは、画面の幅が600px以下の場合 に適用されます。

  • @media (max-width: 600px) { ... }
    • 画面の幅(width)が 600px以下のときに適用
    • スマートフォンや小型デバイスを想定
  • nav { display: block; }
    • 通常 navdisplay: flexdisplay: inline-block で横並びになっていることが多い
    • 小さい画面では display: block; に変更することで 縦方向に表示 できる
    • これにより、メニューが詰まるのを防ぎ、ユーザーが操作しやすくなる
/* 大きな画面(デスクトップ向け) */
@media (min-width: 1024px) {
  body {
    font-size: 18px;
  }
}

/* 中程度の画面(タブレット向け) */
@media (max-width: 1023px) and (min-width: 601px) {
  body {
    font-size: 16px;
  }
}

/* 小さな画面(スマートフォン向け) */
@media (max-width: 600px) {
  body {
    font-size: 14px;
  }
}

画面サイズごとに異なるデザインを適用することも可能です。

ビューポートメタタグの設定

<meta name="viewport" content="width=device-width, initial-scale=1.0">

この <meta> タグは、ページの表示領域(ビューポート)を適切に設定 するためのものです。

各属性の意味:

  • name="viewport"

    • ビューポートの設定を行うための指定
  • content="width=device-width"

    • デバイスの幅に合わせてページの幅を自動調整 する
    • これがないと、PC向けレイアウトがそのまま縮小されてしまい、文字が極端に小さくなったり、横スクロールが発生したりする
  • initial-scale=1.0

    • 初期のズーム倍率を1.0(100%)に設定 する
    • ユーザーが拡大縮小しない限り、ブラウザが勝手にズームしないようにする
  • ビューポートメタタグを設定しない場合の問題:

    • モバイルで閲覧すると、PC向けのデザインがそのまま表示されてしまう
    • 文字が小さくなり、拡大しないと読めないことがある
    • ユーザーが意図しないスクロールやズームをしないと操作できないことがある

アクセシビリティレポートの定期的な生成

アクセシビリティ(A11y)レポートを定期的に生成することで、Webアプリケーションが常にユーザーにとって使いやすい状態を維持できるようになります。
このプロセスでは、自動テストツールを活用して、アクセシビリティの問題を早期に発見し、改善に役立てます。

axe-core を使用したアクセシビリティテスト

axe-core は、Deque Systems によって開発された、業界標準のアクセシビリティテストライブラリです。
React コンポーネントのテストに @testing-library/reactjest-axe を使用すると、アクセシビリティ違反を検出できます。

jest-axe のインストール

npm install --save-dev jest-axe @testing-library/react

React コンポーネントのアクセシビリティテスト

import React from "react";
import { render } from "@testing-library/react";
import { axe, toHaveNoViolations } from "jest-axe";
import MyComponent from "../MyComponent";

expect.extend(toHaveNoViolations);

test("MyComponent is accessible", async () => {
  const { container } = render(<MyComponent />);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

ポイント

  • axe(container): DOM 内のアクセシビリティ違反をチェック。
  • toHaveNoViolations(): アクセシビリティ違反がないことを確認。

メリット

  • コンポーネント単位でアクセシビリティテストを実施できる。
  • CI/CD パイプラインに組み込んで、自動的にアクセシビリティをチェックできる。

Lighthouse を CI/CD に組み込む方法

lighthouserc.json をプロジェクトのルートに配置します。

{
  "ci": {
    "collect": {
      "url": ["https://example.com"],
      "numberOfRuns": 3
    },
    "assert": {
      "assertions": {
        "categories:accessibility": ["error", { "minScore": 0.9 }]
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

ポイント

  • numberOfRuns: 同じテストを複数回実行し、安定した結果を得るための設定。
  • assertions: アクセシビリティスコアが 0.9 未満 の場合にエラーを出す。
  • upload: 結果を一時的なストレージにアップロード。

次にGitHub Actions を使って CI/CD で Lighthouse を実行します。

name: Lighthouse Accessibility Check

on:
  schedule:
    - cron: '0 0 * * 1' # 毎週月曜日に実行

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - name: リポジトリのチェックアウト
        uses: actions/checkout@v4

      - name: Lighthouse の実行
        run: |
          npx lighthouse --config-path=lighthouserc.json

さいごに

Webアクセシビリティの向上は、単なる法令遵守やチェックリストを満たすことではなく、すべてのユーザーにとって快適で公平な体験を提供することが目的です。本記事で紹介したLighthouseやaxeによる自動テストの活用、キーボード操作やスクリーンリーダー対応の改善、ARIAランドマークの導入などを実践することで、アクセシビリティの高いWebサイトを構築できます。

しかし、アクセシビリティ対応は一度設定すれば終わりではなく、継続的な改善が求められます。定期的なレポート生成やユーザーテストを通じて、実際の利用者のフィードバックを取り入れながら改善を続けることが重要です。

すべてのユーザーが快適に利用できるWebを目指して、今日からアクセシビリティ向上に取り組んでいきましょう。

記事に関するお問い合わせ📝

記事の内容に関するご質問、ご意見などは、下記よりお気軽にお問い合わせください。
ご質問フォームへ

技術支援などお仕事に関するお問い合わせ📄

技術支援やお仕事のご依頼に関するお問い合わせは、下記よりお気軽にお問い合わせください。
お問い合わせフォームへ

関連する技術ブログ