はじめに

こんにちは。and factory フロントエンドエンジニアの坂内です。

4月の個人テーマとして、Webアクセシビリティ(WCAG・WAI-ARIA)の基礎を調べ、自社プロダクトで計測まで試したので共有します。

「アクセシビリティは大事」と聞くものの、何から手をつければよいか分からないと感じるエンジニアは多いはずです。本記事はフロントエンドの実装視点で、まず押さえておきたい範囲に絞ってまとめました。


結論

先に持ち帰ってほしいポイントを3つに絞ります。

  • 実務の目標ラインはAAレベル準拠(JIS X 8341-3:2016もWCAG 2.0と同内容のため、AAが事実上のスタンダード)
  • 大原則は「No ARIA is better than Bad ARIA」で、<button> を使えばロール・キーボード操作・フォーカスがすべて自動で揃う
  • 自社プロダクト4ページの横断計測の平均は93点(自動検査の範囲)で、指摘はselectのラベル不足とコントラスト比不足が中心、修正コストも小さい

詳細を順番に説明します。


調査の背景

私が担当しているプロダクトは占いコンテンツのWebサービスです。複数ページで同じ <select> やドロップダウンが繰り返し登場するため、共通コンポーネント越しにUIを組み立てる構成です。

フロントエンドの実装では、<div><span> だけでUIを組む場面が増えました。Reactなどのライブラリでカスタムコンポーネントを作る場合、見た目はモーダルやドロップダウンに見えても、HTML上は意味を持たないただの箱になりがちです。

この状態では、スクリーンリーダーや支援技術を使うユーザーから見ると、機能の伝わらないコンポーネントが増えていきます。

現状把握として、ChromeのLighthouseでトップページを計測したところ、Accessibilityスコアは86点でした。検出された指摘は <select> のラベル不足やコントラスト比不足など、共通コンポーネントとデザイントークン由来の項目が中心です。共通部品の不備は複数ページに波及します。改修へ進む前段階として、まず基礎を押さえておきたいと判断しました。

以前からアクセシビリティは気になっていたテーマでもあり、チームでSEOやUI品質について話し合う機会が増えたことも後押しになりました。そこで4月の個人テーマとして、WCAG・WAI-ARIAの基礎調査と自社プロダクトの横断計測を据えました。次の章からWCAG・WAI-ARIAの順に整理し、最後に自社プロダクトの計測結果へつなげます。


WCAGとは

WCAG(Web Content Accessibility Guidelines)は、W3Cが策定したWebアクセシビリティの国際ガイドラインです。障害のある人や高齢者を含むあらゆるユーザーが、Webコンテンツを利用できるようにするための指針です。

4つの原則(POUR)

すべての達成基準は、次の4つの原則に分類できます。

原則内容
Perceivable(知覚可能)情報がユーザーに認識できる画像のalt、字幕
Operable(操作可能)UIが操作できるキーボード操作対応
Understandable(理解可能)内容や操作方法が理解できるエラー表示の分かりやすさ
Robust(堅牢)多様な環境で動く正しいHTML構造

3段階の適合レベル

レベル位置づけ具体例
A最低限。これがないと使えない人が出るキーボード操作可能、画像にalt属性
AA一般的に目指すべき標準コントラスト比4.5:1以上、200%拡大対応
AAA最高レベル。完全準拠は現実的に困難コントラスト比7:1以上、手話付き動画

日本ではJIS X 8341-3:2016がWCAG 2.0と同内容のため、公的機関でも参照されています。実務で目標とする標準ラインは AA準拠 です。

バージョンの違い

  • WCAG 2.0(2008年):4原則・12ガイドラインで構成される基礎
  • WCAG 2.1(2018年):モバイル・弱視・認知障害への対応を追加
  • WCAG 2.2(2023年):さらに9つの達成基準を追加(現時点の最新勧告)

いずれも後方互換性があるため、新しいバージョンに準拠すれば前のバージョンも満たす扱いです。

AAレベルで特に押さえる5つのポイント

達成基準は多数ありますが、フロントエンド実装で特に頻出する5つを取り上げます(WCAG AAの全達成基準を網羅するものではありません)。

  1. コントラスト比 4.5:1 以上(大きいテキストは3:1以上)
  2. キーボードのみで全機能が操作できる(フォーカス可視、トラップなし)
  3. フォームにラベルやエラー表示がある(labelやaria-labelの付与)
  4. 見出しが意味的に正しく構造化されている(h1〜h6を装飾目的で使わない)
  5. 画面幅320pxでも情報が失われない(リフロー対応、WCAG 2.1 1.4.10から追加)

ここまでがWCAGの全体像です。次は、HTMLだけでは表現しきれない部分を補うWAI-ARIAを見ていきます。


WAI-ARIAとは

WAI-ARIA(Web Accessibility Initiative – Accessible Rich Internet Applications)はW3Cが策定した仕様です。HTMLだけでは表現しきれないUIの「役割・状態・プロパティ」を支援技術へ伝える機能を担います。

たとえば、見た目はモーダルでもHTML上はただの <div> というケースに対し、「これはダイアログだ」「いま開いている」といった情報を補えます。

3つの構成要素

graph LR
  A[WAI-ARIA] --> B["Role 役割"]
  A --> C["Property プロパティ"]
  A --> D["State 状態"]
  B --> B1["role=button<br>role=dialog"]
  C --> C1["aria-label<br>aria-labelledby"]
  D --> D1["aria-expanded<br>aria-hidden"]

以下、図と同じ内容をテキストで示します。

  • Role:要素の役割(button、dialog、navigationなど)
  • Property:静的な性質(aria-label、aria-labelledbyなど)
  • State:動的に変化する状態(aria-expanded、aria-selectedなど)

大原則:No ARIA is better than Bad ARIA

WAI-ARIAを学ぶ前に押さえておきたい大原則があります。それが「No ARIA is better than Bad ARIA」(雑なARIAを付けるくらいなら、何も付けない方がよい)です。

W3Cのドキュメント(Using ARIA)でも、最初のルールは「ネイティブHTML要素で実現できるならそれを使う」と明記されています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!-- BAD: divでボタンを再発明する
     - Enter/Space で発火しない(tabindex="0" だけでは onclick が動かない)
     - disabled 属性が効かない
     - フォーカスリングのデフォルトが付かない -->
<div role="button" tabindex="0" onclick="handleClick()">送信</div>

<!-- GOOD: ネイティブHTMLを使う -->
<button type="button">送信</button>
<script>
  document.querySelector('button').addEventListener('click', handleClick);
</script>

<button> を使うだけで、ロール・キーボード操作・フォーカスが自動で揃います。WAI-ARIAは、HTMLでどうしても表現できないときの補助として位置づけます。


よく使う属性3選

実装で頻出する3つの属性を、コード例とともに整理します。

role — これは何の要素か

要素の役割を支援技術へ伝える属性です。HTMLの意味論を補う用途で使います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!-- ナビゲーションが複数ある場合は名前で区別する -->
<nav aria-label="メインメニュー">...</nav>
<nav aria-label="フッター">...</nav>

<!-- ネイティブ <dialog> 要素を使う(Baseline 2023: 全主要ブラウザ対応済み)
     showModal() を呼ぶだけで focus trap / Escape / aria-modal 相当の挙動が得られる -->
<dialog id="confirm-dialog" aria-labelledby="dialog-title">
  <h2 id="dialog-title">確認</h2>
  <p>削除してもよろしいですか?</p>
  <button>キャンセル</button>
  <button>削除する</button>
</dialog>
<!-- dialog.showModal() で開く -->

<dialog> が使えない場合のみ role="dialog" を検討してください。ただしその場合、aria-modal="true"・focus trap・Escape処理・返却フォーカスの実装がすべて別途必要です。

<button><nav> など適切なHTMLタグを使う場合、role の付与は不要です。

aria-label — 名前を付ける

視覚的なラベルがない要素にアクセシブルな名前を与える属性です。アイコンのみのボタンや、コンテキスト依存の入力欄で使います。

1
2
3
4
5
6
7
8
<!-- アイコンボタン:見た目は ✕、読み上げは「閉じる」 -->
<button aria-label="閉じる">
  <svg aria-hidden="true">...</svg>
</button>

<!-- 検索ボックス:可視ラベルを置けない場合のみ aria-label を使う
     可視 <label> を置ける場合は <label> を優先する(クリック領域も広がる) -->
<input type="search" aria-label="サイト内検索">

aria-hidden — 支援技術から隠す

装飾目的の要素をアクセシビリティツリーから除外する属性です。スクリーンリーダーで読み上げさせたくない要素に使います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<!-- 装飾アイコン:隣にテキストがあるので読み上げ不要 -->
<button>
  <svg aria-hidden="true">...</svg>
  保存する
</button>

<!-- 意味を持つ SVG(ロゴ・ステータスアイコンなど):role="img" と <title> で名前を付ける -->
<svg role="img" aria-labelledby="logo-title">
  <title id="logo-title">and factory</title>
  ...
</svg>

ここで重要な注意点があります。aria-hidden="true"フォーカス可能な要素(button、a、inputなど)に付けてはいけません。キーボード操作で「見えない要素」にフォーカスが当たり、ユーザーを混乱させます。要素を視覚的に隠しつつ支援技術からも除外したい場合は、tabindex="-1" でフォーカス対象から外したうえで aria-hidden="true" を組み合わせます。

基礎をひと通り押さえたところで、最後に自社プロダクトを計測してみます。


実プロダクトのLighthouse計測結果

ここまでの基礎知識を踏まえ、弊社が運営する「星ひとみの占いサイト」をChromeのLighthouseで計測しました。代表的な4ページを横断的に確認しました。

計測環境

  • OS:macOS 15
  • ブラウザ:Google Chrome(2026年4月計測時点の最新版)
  • ツール:Lighthouse(Chrome DevTools内蔵版)
  • form factor:Mobile(デフォルト)
  • スロットリング:Simulated Slow 4G, 4× CPU Slowdown(デフォルト)
  • 計測回数:各ページ1回
  • 状態:未ログイン(シークレットウィンドウ)
  • 観点:Accessibilityカテゴリのみ

スコアサマリー

ページスコア主な指摘
トップ86 / 100selectにラベルなし、コントラスト比不足
占い紹介92 / 100selectにラベルなし
占い詳細95 / 100コントラスト比不足
監修者100 / 100自動検出の指摘なし

Lighthouseのアクセシビリティ計測結果(スコア86点)。selectのラベル不足とコントラスト比不足の2件が指摘されている

スコアの平均は93点でした。今回の最大の発見は、ページごとに点数と指摘内容が大きく異なるという点です。

ただしLighthouse Accessibilityは自動検出可能なルールの範囲のみを対象としており、スコア100でもWCAG AA準拠の証明にはなりません。Lighthouse自身も結果画面で手動監査の必要性を明示しています。本記事の数字は「自動検査で検出された範囲」として読んでください。

検出された問題と修正方針

① selectにラベルがない

<select><label> / aria-label / aria-labelledby のいずれもついていない、という指摘です。スクリーンリーダーで読み上げると「何のための選択肢か」が伝わりません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<!-- Before -->
<select>
  <option value="1990">1990</option>
  <option value="1991">1991</option>
</select>

<!-- After: label を関連付ける -->
<label for="birth-year">生まれ年</label>
<select id="birth-year">
  <option value="1990">1990</option>
</select>

<!-- After: aria-label で補う -->
<select aria-label="生まれ年">
  <option value="1990">1990</option>
</select>

② 背景色と文字色のコントラスト比不足

WCAG AAの基準(通常テキスト4.5:1、大きい文字3:1)を満たしていないという指摘です。弱視や色覚特性のあるユーザーにとって読みづらくなります。

修正にはサイト全体のデザイントークン(CSS変数や設計システム上の色)の見直しが効きます。チェック用のツールには次の2つが使いやすいです。

  • Chrome DevToolsのカラーピッカー(コントラスト比をその場で確認できる)
  • WebAIM Contrast Checker(任意の2色を入力して判定できる)

4ページ比較してわかったこと

ページ単位の点数だけ見ると、サイト全体の状態を見誤ります。今回横断で計測したことで、次の3点が分かりました。

  • selectのラベル問題はselectを使うページで共通して発生し、共通コンポーネントを直せば複数ページで一気に改善可能
  • コントラスト比の問題はトップと占い詳細で共通発生し、サイト全体のデザイントークン起因の可能性が高くデザイナーとの連携が前提
  • 監修者ページは満点で、シンプルなコンテンツ中心のページは特別な対応をしなくても高スコアになりやすい

つまり、サイト全体の平均点だけを見て「うちは80点台だ」と判断すると本質を見失います。代表的な数ページを横断で計測し、共通コンポーネントとデザイントークンに分けて改善計画を立てる方が実務的です。


まとめ

4月のテーマとして、WCAGとWAI-ARIAの基礎を調べ、自社プロダクトで計測しました。学びは大きく次の3点です。

  • AAレベルが実務の目標ライン。コントラスト・キーボード・ラベル・見出し構造・リフローの5点を押さえる
  • WAI-ARIAより前にネイティブHTMLを使う。「No ARIA is better than Bad ARIA」を念頭に置く
  • Lighthouseは敷居が低く、共通コンポーネントとデザイントークンに分けた改善計画の起点として活用できる

自動ツールで検出できるのは一部の問題に限られます。引き続きアクセシビリティ改善を進め、得られた気づきは別記事で共有します。

参考リンク