🧑‍🍳

アクセシビリティ実装レシピ20 — Webサイトを美味しくする作り方

に公開

はじめに

オライリーから出版されている『Web Accessibility Cookbook』を読みました。
https://accessibility-cookbook.com/

この本は、ナビゲーション、フォーム、テーブル、ダイアログなどといった Webでよく使うコンポーネントをアクセシブルに実装するための「レシピ集」 です。
単に「こう作る」だけでなく「なぜそれが必要なのか」まで解説されており、問題 → 解決策 → 実装例 → 議論という流れで整理されているのが特徴です。

読んでいて「これはフロントエンド実装で押さえておきたい」と感じた項目を、20個に絞ってまとめました。

Chapter 1. Structuring Documents — 要約と実装ガイド

Webアクセシビリティはボタンやフォームといったコンポーネント単位の実装だけでなく、HTML文書全体の構造から始まります。
特に <head><body> の設計は、パフォーマンスやSEOだけでなく、スクリーンリーダー利用者や翻訳ツールに直接影響します。


1.1 ページの言語を定義する(lang 属性)

要点

  • ページの言語を <html lang="..."> で明示する
  • 部分的に異なる言語がある場合は <span lang="fr">Bonjour</span> のように付与
  • 言語を指定することで:
    • スクリーンリーダーが正しい発音に切り替える
    • 翻訳ツールやハイフネーション(自動改行)が適切に働く
    • 引用符や数字フォーマットも正しくなる

実装例

html
<!DOCTYPE html>
<html lang="en">
  <body>
    <p>
      『ねじまき鳥クロニクル』
      (<span lang="en">The Wind-Up Bird Chronicle</span></p>
  </body>
</html>

フロントエンドTips

  • Vue/Reactの場合:i18nライブラリ(vue-i18n, react-intl)で lang を切り替えるときは <html> への反映も忘れない
  • デザイン上の工夫::lang() 疑似クラスで言語ごとにフォントやレイアウトを調整できる
css
:lang(ja) {
  font-family: "Noto Sans JP", sans-serif;
}

1.2 ページタイトルを記述する(<title>

要点

  • <title> は必ずユニークで簡潔に
  • SPAではルーティングごとに動的に変更する
  • 状況に応じて情報を追加(例:エラー件数、検索結果数、現在のステップなど)

フロントエンドTips

  • ユーザー第一:マーケティング用ワードを詰め込みすぎない。
  • SPAの場合、ページ遷移時に <title> が更新されないと、スクリーンリーダー利用者が遷移に気づけません。
  • フレームワークによっては自動で更新される場合もあるので仕様を確認する。

1.3 ビューポートを設定する(<meta name="viewport">

要点

  • ユーザーが自由にズームできるようにする。制限をかけないこと
  • width=device-width, initial-scale=1 が基本
  • maximum-scale=1user-scalable=no はアンチパターン

実装例

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

フロントエンドTips

  • 禁止されがちなズームは低視力ユーザーにとって必須
  • ズーム無効化の正当な理由は「地図のジェスチャーが干渉する場合」などごく例外

1.5 ランドマークを使った文書構造

要点

  • <header>, <main>, <footer> をベースにランドマークを定義
  • <nav>, <aside>, <search>(or <form role="search">) も利用して構造を明示
  • ARIAロールよりネイティブ要素を優先

実装例(シンプルな文書骨格)

html
<body>
  <header>
    <nav>
      ...
    </nav>
    <search>
      <form action="./search/">
        <label for="movie">動画を検索</label>
        <input type="search" id="movie" name="q" />
        <button type="submit">検索</button>
      </form>
    </search>
  </header>

  <main>
    <h1>Products</h1>
    ...
  </main>

  <footer>
    &copy; 2025 My Store
  </footer>
</body>

フロントエンドTips

  • SPAで複数 <main> がある場合 → 非表示のものに hidden を付ける
  • 開発時にチェック:NVDA/VoiceOver/JAWSのショートカットでランドマークに飛べるか試す
  • ブラウザ拡張(Landmark Navigation via Keyboard or Pop-upなど)でランドマーク一覧を確認すると便利

Chapter 2. Structuring Pages — 要約と実装ガイド

Chapter 1 では文書全体の構造(言語・タイトル・ビューポート・ランドマーク)を扱いました。
Chapter 2 では ページ単位の構造化 に焦点を当て、ナビゲーションやフォーム、見出し構造などを正しく設計する方法を解説しています。
特に大規模なページやコンテンツが多いページでは、正しいランドマークや見出しが 利用者の効率的な移動や理解 を大きく助けます。


2.5 適切な見出しアウトラインを作る

要点

  • <h2><h6> を階層的に使い、レベルを飛ばさない
  • デザイン都合でhタグを乱発するのはNG → CSSでサイズ調整
    • ❌大きい文字=見出し

実装例

html
<h1>Johanna’s Toy Store</h1>
<h2>Toys</h2>
  <h3>Toys for babies</h3>
  <h3>Toys for kids</h3>
<h2>Books</h2>
<h2>Contact</h2>

フロントエンドTips

  • SPAではページ遷移時に <h1> が変わるようにする(固定タイトルはNG)
  • NVDA/JAWS/VoiceOverで H キーや rotor を使い、見出しジャンプできるか確認

2.6 コンテンツを意味のある順序で提示する

要点

  • DOM順序と視覚的順序を一致させることが重要。
  • 「見出し → 画像 → テキスト」のように、意味やストーリーの流れに沿って並べる。
  • CSS の order, flex-direction, grid で視覚的な順序を入れ替えるのは慎重に。
    • 順序が崩れると、キーボード操作やスクリーンリーダーで読み上げる際に文脈が破綻する。

実装例(NGパターン)
視覚「画像→見出し→テキスト」に見えるが、読み上げは「見出し→テキスト→画像」になってしまう)

html
<section class="card">
  <h3>When CSS Isn’t Enough</h3>
  <p>by Stephanie Eckles</p>
  <img src="teaser.png" alt="UI例">
</section>

<style>
.card { display: flex; flex-direction: column; gap: .5rem; }
/* デザインで画像を最初に見せるために order を使用 → 読み上げ順やタブ順は変わらない */
.card img { order: -1; } /* 視覚だけ先頭へ */
</style>

フロントエンドTips

  • デザインで「画像を先に見せたい」と求められても、DOM順序は意味の流れを優先。スタイルで見た目だけ調整する方が安全。
  • DOM順序=タブキーでの移動順序になるため、インタラクティブ要素の並び替えは特に注意が必要。
  • HTMLだけで意味が通るかを確認する。

Chapter 3. Linking Content — 要約と実装ガイド

リンクはWebの根幹的な要素であり、ユーザーが「どこへ移動できるのか」「どのようなアクションが発生するのか」を理解できるように実装することが重要です。本章では、リンクの基本要件から、スタイル、ダウンロードやメールリンク、画像リンク、クライアントサイドレンダリング時の課題解決までを解説します。


3.1 適切な要素を使う(リンク vs ボタン)

要点

  • 「どこかへ移動」するなら <a href="...">

  • 「アクション(送信・JS実行)」なら <button>

  • リンクは以下を満たす必要がある:

    • href を持つ <a> 要素(暗黙的に link role を持つ)
    • ユニークで具体的なテキスト
      • ❌”もっと見る”, “click here” はNG
    • フォーカス可能・Enterキーで起動できる
    • 状態(未訪問/訪問/ホバー/フォーカス/アクティブ)を伝える

実装例

<!-- 正しいリンク -->
<a href="/blog">Blog</a>

<!-- 電話番号 -->
<a href="tel:+17078277019">(707) 827-7019</a>

<!-- メール -->
<a href="mailto:support@example.com">support@example.com</a>

<!-- ページ内移動 -->
<a href="#content">Skip to content</a>

フロントエンドTips

  • SPAで「擬似リンク (<a href="#" onclick=...>)」を作るのはアンチパターン。必ず <button> を使う。
  • aria-current="page" を活用すると、現在ページを明示できる。

3.3 ダウンロードリンクの実装

要点

  • リンクテキストに アクション・内容・形式・サイズ を含める。

    • Download sustainability report 2022 (PDF, 29MB)
    • Download だけ
  • download 属性で名前を指定可能(同一オリジン限定)。

    • CDN配布ファイルや外部リンクの場合はdownloadが無視される(保存されるかブラウザで開かれるかなどは、利用者環境によって異なる)。
  • アイコン併用は可だがテキストを必ず残す。

実装例

html
<a href="/report.pdf" download="report-2022.pdf">
  Download sustainability report 2022 (PDF, 29MB)
</a>

フロントエンドTips

  • PDFは非アクセシブルなことが多いため、可能ならHTML版も提供する。
  • ファイルサイズを明記すると、モバイルユーザーや低速回線利用者に配慮できる。
  • 拡張子(形式)を明記すると、利用者がアプリ互換性を事前に判断できる。

Chapter 4. Performing Actions — 要約と実装ガイド

この章は <button> 要素 に特化しています。
ボタンはWeb上で最も一般的に使われるUI要素の1つですが、正しく実装されていないケースが非常に多いです。
本章では「正しい要素の選択」「ラベル付け」「スタイルのリセット」「状態管理」「非推奨な無効化パターン」について解説します。


4.2 ボタンに明確なラベルを付ける

要点

  • ボタンには必ず アクセシブルネーム が必要。

  • アイコンボタンの場合 →

    • alt または <title> を利用
    • aria-label.visually-hidden で補完可能
  • 優先度(Adrian Roselliの推奨順

    1. ネイティブHTMLテキスト
    2. aria-labelledby
    3. 視覚的に非表示のテキスト(Tailwind での.sr-onlyなど)
    4. aria-label

実装例

html
<!-- テキストあり -->
<button>Download</button>

<!-- アイコン+alt -->
<button><img src="download.svg" alt="Download"></button>

<!-- SVG+title -->
<button>
  <svg role="img" aria-labelledby="title">
    <title id="title">Download</title>
    <path d="..."/>
  </svg>
</button>

フロントエンドTips

  • 空ボタン(中身なし・altなし)は自動テストでよく検出されるエラー。
  • 多言語対応時の翻訳漏れに注意。

4.5 ボタンを無効化しない

要点

  • disabled ボタンは以下の問題を引き起こしやすい:

    • フォーカス不可 → スクリーンリーダーユーザーは存在を認識できない
    • コントラスト低下 → 見えにくい
    • クリックしても無反応 → なぜ無効なのかフィードバックがなくユーザーが混乱する
  • 常時の disabled は避けるのが基本。ボタンを押した後にバリデーションでエラーを返す方がユーザーは理解しやすい。

実装例

html
<!-- ✅ 入力エラーは有効ボタンのまま知らせる -->
<button type="submit">Submit</button>
<p class="error" id="err-email">Email is required</p>
<input type="email" aria-describedby="err-email">

<!-- ❌ 常時 disabled にして入力できるまで押せない -->
<button type="submit" disabled>Submit</button>

フロントエンドTips

  • SPAフォームでは「未入力=disabled」ではなく、送信後にエラーリスト表示+フォーカス移動がベストプラクティス。
  • エラーメッセージは aria-describedby で入力欄と紐付けるとより分かりやすい。

Chapter 5. Styling Content — 要約と実装ガイド

この章では CSSによるスタイリングがアクセシビリティに与える影響 を整理しています。
色・動き・単位・非表示の扱いなど、見た目のデザインが セマンティクスや操作性そのものを壊してしまう ケースが多いため、注意が必要です。
また、OSやブラウザが提供するユーザー設定(ダークモード・ハイコントラスト・ reduced motion など)を尊重することが inclusive design の基本とされています。


5.1 色の扱い(Work with Color)

要点

  • 情報伝達に 色だけを使うのはNG。必ずテキストやアイコンと併用。
  • コントラスト比(WCAG 2.2 基準)
    • 通常テキスト:4.5:1
    • 大きなテキスト:3:1
  • ブランドロゴや装飾は対象外だが、実際には誰でも読める配色が望ましい。
  • 現行のコントラスト式には限界があり、将来は APCA への移行が検討中。

実装例

html
<label for="email">Your email address</label>
<input type="email" id="email" aria-invalid="true" value="kubidus21@" aria-describedby="error">

<div id="error" class="error" style="color: #D52A2A">
  <svg width="16" fill="currentColor" viewBox="0 0 640 640"><path d="..."/></svg>
  Please enter a valid e-mail address
</div>

フロントエンドTips

  • 開発中はコントラストチェッカーなどを使うと便利。
  • リンクは必ず下線つきに(色覚異常や白黒表示でも判別可能に)。

5.2 ユーザー設定を尊重する(Respect User Preferences)

要点

  • OS/ブラウザにはアクセシビリティ関連の設定がある。

  • CSSメディアクエリで検出し、体験を調整できる:

    • prefers-color-scheme(ダークモード)
    • prefers-contrast(高コントラスト)
    • forced-colors(Windows高コントラストモード)
    • inverted-colors(色の反転)
    • prefers-reduced-transparency(透過を減らす)
    • prefers-reduced-motion(動きを減らす)

実装例

css
/* ダークモード */
@media (prefers-color-scheme: dark) {
  body { background: #111; color: #eee; }
}

/* 高コントラスト */
@media (prefers-contrast: more) {
  * { border: 2px solid currentColor; }
}

/* Windows高コントラスト */
@media (forced-colors: active) {
  button { forced-color-adjust: none; }
}

/* 透過を減らす */
@media (prefers-reduced-transparency: reduce) {
  dialog { backdrop-filter: none; background: #fff; }
}

フロントエンドTips

  • JSでも matchMedia('(prefers-reduced-motion)') で動的に制御可能。
  • Progressive Enhancement(Aaron Gustafson, link)の考え方を参考にする。

5.4 セマンティクスと操作性を壊さない(Preserve Semantic Information and Operability)

要点

  • 一部CSSは 要素の意味や操作性を破壊 するので注意。
  • NG例:
    • <button><a>display: contents を 適用 → フォーカス不可
    • display: none / visibility: hidden → アクセシビリティツリーから消える
    • pointer-events: none → フォーカスできても操作不可

フロントエンドTips

  • 隠す場合は .sr-only(Tailwind),.visually-hidden(Bootstrap)などを使う

5.5 動きとアニメーション(Add Motion and Animation)

要点

  • 大きな動きやパララックスは めまい・吐き気など身体的悪影響 を引き起こす。

  • prefers-reduced-motion を尊重して、非必須の動きを抑制する。

  • アニメーションの役割は:

    • 認知負荷の軽減(動きで変化を可視化)
    • 意思決定の補助
    • 空間的なマップの形成
    • 状態変化の明示

実装例

css
/* デフォルトはフェード、動きを許可している場合のみスライド */
.banner {
  animation: fade 1s ease;
}
@media (prefers-reduced-motion: no-preference) {
  .banner { animation: slide 1s ease; }
}
html
<!-- motion設定でGIFを切替 -->
<picture>
  <source srcset="static.jpg" media="(prefers-reduced-motion: reduce)" />
  <img src="animated.gif" alt="Thumbs up boy">
</picture>

フロントエンドTips

  • アニメーションは 明確な目的(状態変化、関係性の理解)に限定する。
  • 自動再生アニメ・動画は必ず停止/一時停止オプションを提供。

Val Head Designing With Reduced Motion For Motion Sensitivities(モーションに敏感なユーザーのための“動きを抑えた”デザイン)

https://developer.mozilla.org/ja/docs/Web/CSS/@media/prefers-reduced-motion


Chapter 6. Managing Focus — 要約と実装ガイド

この章は「キーボード操作によるアクセシビリティ」を中心に扱っています。
Webページはもともとネイティブ要素(<a><button>)のおかげでキーボード操作に対応していますが、CSSやJavaScriptのカスタマイズによってその仕組みを壊してしまうケースが多いです。
フォーカスを見える化し、適切に移動させ、DOM順序を崩さず、必要に応じてスキップリンクを提供する ——これが本章のポイントです。


6.1 フォーカススタイルを提供する(Provide Focus Styles)

要点

  • フォーカスインジケータは マウスカーソルと同じくらい重要。消してはいけない。
  • :focus-visible を使えば、キーボード操作時のみスタイルを出せる。
  • フォーカスリングは WCAG 2.2 — 2.4.7 Focus Visible の要件。
  • アウトラインや背景色・下線など複数手段を組み合わせると強調効果が高い。

実装例

css
/* 基本的なフォーカスリング */
:focus-visible {
  outline: 3px solid #000;
  outline-offset: 3px;
}

/* マウス操作時だけ影を出す */
button:focus:not(:focus-visible) {
  outline: none;
  box-shadow: 0 0 5px rgba(0,0,0,0.6);
}

/* 固定ヘッダーがある場合にフォーカス時に要素が隠れないようにする */
html { scroll-padding-top: 72px; } /* ヘッダー高さに合わせる */

フロントエンドTips

  • デザイン性より「見えること」を最優先に。
  • fixed/stickyヘッダーがある場合はフォーカスしたときに要素が隠れないようscroll-padding-top(または要素側にscroll-margin-top)を設定。
  • Adrian Roselli: Dialog Focus in Screen Readers

https://adrianroselli.com/2019/10/dialog-focus-in-screen-readers.html


6.4 フォーカスを閉じ込める(Keep Focus Contained)

要点

  • モーダル表示中は モーダル以外の要素をフォーカスできないようにする 必要がある。
  • 従来はフォーカストラップ(TabをループさせるJS)を実装していた。
  • 今は inert 属性や <dialog> でより安全に実現可能。

実装例

html
<div class="page-wrapper">
  <button class="open">Open Modal</button>
</div>

<dialog>
  <button>Close</button>
</dialog>

フロントエンドTips

  • inert 属性をサポートしているブラウザならJSトラップ不要。
  • テストはスクリーンリーダーやキーボード操作で必ず実施。

https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/inert


6.6 要素をスキップできるようにする(Allow Users to Skip Elements)

要点

  • 多数のリンクやメニューを毎回Tab移動するのは身体的負担になる。
  • 「スキップリンク」を設置し、主要コンテンツへジャンプできるようにする。
  • WCAG 2.2 — 2.4.1 Bypass Blocks に準拠。

実装例

html
<body>
  <a href="#content" class="skip-link">Skip to content</a>
  <main id="content">
    <h1>Main Content</h1>
  </main>
</body>

フロントエンドTips


Chapter 7. Navigating Sites — 要約と実装ガイド

(ピックアップする項目がないため省略🙇‍♂️)


Chapter 8. Toggling Content Visibility — 要約と実装ガイド

(ピックアップする項目がないため省略🙇‍♂️)


Chapter 9. Constructing Forms — 要約と実装ガイド

フォームはウェブのインタラクションに欠かせない要素ですが、設計を誤るとユーザー体験を大きく損ねます。


9.1 フォームを作成する

要点

  • ネイティブ要素を優先<input>, <select>, <textarea>, <button> はデフォルトで多くのアクセシビリティ機能を持つ
  • 正しい要素を使用:メールは type="email"、電話番号は type="tel"、長文は <textarea>
  • 必ずラベルを付ける
  • 利用者に変化を伝える(ステップ進行や入力による変化など)

実装例

html
<form>
  <div>
    <label for="username">Username</label>
    <input type="text" id="username" autocomplete="username">
  </div>

  <div>
    <label for="email">E-Mail</label>
    <input type="email" id="email" autocomplete="email">
  </div>

  <fieldset>
    <legend>T-Shirt size</legend>
    <label><input type="radio" name="shirt" value="s"> Small</label>
    <label><input type="radio" name="shirt" value="m"> Medium</label>
    <label><input type="radio" name="shirt" value="l"> Large</label>
  </fieldset>

  <button>Sign up</button>
</form>

フロントエンドTips

  • toggleスイッチは注意:状態か操作かが曖昧。チェックボックスの方が明確 by Joel Holmberg

https://axesslab.com/toggles-suck/


9.4 エラーフィールドを強調する

要点

  • エラーは 明示的・近接して表示
  • aria-invalid="true"aria-describedby で結び付ける
  • 複数エラーはページ冒頭にサマリを配置し、各エラーへのリンクを付与

実装例

html
<label for="email">Your email</label>
<input type="email" id="email" aria-invalid="true" aria-describedby="email-error">
<div id="email-error" class="error">Please enter a valid email address</div>

フロントエンドTips

  • 色だけに頼らない → アイコンやテキストも必須
  • エラーメッセージは具体的に
    • ❌「正しく入力してください」
    • ✅「電話番号は半角数字で入力してください」
  • タイトルにエラー件数を入れる → ページ遷移時に気づきやすい

Chapter 10. Filtering Data — 要約と実装ガイド

(省略)


Chapter 11. Presenting Tabular Data — 要約と実装ガイド

テーブルは長年「レイアウト目的」で乱用された歴史があるため、開発者はアクセシビリティを損なうのではないかと慎重になりがちです。しかし、本来の目的 ― 多次元データの整理と比較 ― に使えば、テーブルはアクセシビリティを向上させる強力なツールです。


11.2 テーブルの構造化

要点

  • テーブルにラベルを付与

    • <caption> が基本
    • <figure><figcaption> で囲む方法も可(図やグラフのような扱いをしたいとき)
    • 直前の見出し+aria-labelledby でラベル化も可能
  • 列・行ヘッダは <th> を使う(scope="row"/"col" で明示)

実装例

html
<table>
  <caption id="scores_caption">グループA 合計スコア</caption>
  <thead>
    <tr>
      <th scope="col">名前</th>
      <th scope="col">Q1</th>
      <th scope="col">Q2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
     <th scope="row">Michael</th>
     <td>47</td>
     <td>28</td>
    </tr>
  </tbody>
</table>

フロントエンドTips

  • thead / tbody / tfoot はアクセシビリティ的必須ではないが、コード可読性とスタイル制御で有効
  • rowspan / colspan はスクリーンリーダーでのサポートが弱いため極力避ける

Chapter 12. Creating Custom Elements — 要約と実装ガイド

(省略)

Chapter 13. Debugging Barriers — 要約と実装ガイド

(省略)

こちらの章はデバッグの仕方などが紹介されていました。少し毛色が異なるため、別記事として紹介します。

まとめ

アクセシビリティ関連の仕様やブラウザ実装は常にアップデートされています。
ここで紹介した内容は、2025年9月時点の情報をもとにしていますが、間違いや古い情報を含んでいる可能性もあります。

もし誤りや改善点にお気づきの方がいれば、ぜひご指摘いただけるとありがたいです🙇‍♂️

参考文献

WCAG 2.2 — W3C WAI

2.4.1 Bypass Blocks
https://www.w3.org/WAI/WCAG22/Understanding/bypass-blocks.html

2.4.7 Focus Visible
https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html

2.4.11 Focus Appearance (Minimum)
https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance-minimum.html

2.4.12 Focus Not Obscured (Minimum)
https://www.w3.org/WAI/WCAG22/Understanding/focus-not-obscured-minimum.html

1.3.2 Meaningful Sequence
https://www.w3.org/WAI/WCAG22/Understanding/meaningful-sequence.html

ARIA関連

ARIA Authoring Practices Guide — W3C WAI
https://www.w3.org/WAI/ARIA/apg/

HTML仕様

HTML Living Standard — WHATWG
https://jp.htmlspecs.com/

MDN Web Docs

Using the viewport meta tag
https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag

prefers-reduced-motion
https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion

aria-current
https://developer.mozilla.org/ja/docs/Web/Accessibility/ARIA/Attributes/aria-current

Discussion