アクセシビリティ後付けで失敗した話──3年の実装記から学んだこと
Webサイトのアクセシビリティ対応、実は後付けすると大変です。うちのチームが3年かけて経験した失敗と成功、スクリーンリーダー検証の現実を実務ベースで語ります。
「あ、アクセシビリティ対応してないんだ……」
プロジェクトが本番1年経った時点で、営業から連絡が来た。「大手企業さんがうちのサイト使えないって言ってるんですけど」。
スクリーンリーダーで読めていないページが大量にあった。画像に代替テキストがない、ボタンのラベルが不正、フォーカス順序が意味不明……。最悪な状態だった。
それがきっかけで、うちのチームは3年間アクセシビリティと格闘することになった。正直、最初は「ユーザーの5%未満だろ」くらいの認識だった。でも気づいたら、コード品質、テスト体制、チーム文化全てが変わってた。
アクセシビリティは最初は「コスト」だと思ってた
初期段階で僕たちが取り組んだのは、とにかくWCAG 2.1 AA対応だった。でも正直、最初の6ヶ月は地獄だった。
既存のHTMLを全部見直す。<button>に見えても実は<div onclick>だ。画像にはaltが必須だけど、なぜか3000個の画像全部に「image」とだけ書いてある。フォーム要素のラベルがない。キーボード操作ができない。
当時のメンバーの反応は「こんなの全部直すのに何ヶ月かかるんだよ……」だった。実際に試算したら、3ヶ月フルリソースで対応しても、カバーできるのは既存ページの60%。
その時の失敗
取り組み当初は、いくつか痛い判断をしてしまった:
- 全ページ一括対応を目指した(分割実装すべきだった)
- 静的チェック(Axe)のみに頼った(実ユーザーテストなし)
- デザイナーにアクセシビリティ知識がなかった
- CIに組み込まなかった(後で属人的になった)
こういった試行錯誤を通じて、ようやく正しいアプローチが見えてきた。
転機は「誰が使えないか」を実際に見たとき
4ヶ月目に、実際に視覚障害者のユーザーにテストしてもらう企画をやった。JAWS(スクリーンリーダー)を使ってサイトを実際に操作してもらう。
「え、これ読める……?」
ボタンのaria-labelがないから、「ボタン」としか聞こえない。フォーム送信ボタンをタブで探してるのに、見つけるのに2分かかる。画像は全部「image」と読み上げられる。
その日、チームの温度が一気に変わった。「これ、ユーザーの視点だとこうなってるんだ」という現実が、スライド資料の100回分より効いた。
以降、月1回のスクリーンリーダーテスト会が定例化した。これが本当に重要だったんですよね。
2年目:自動化とチーム体制の見直し
アクセシビリティ対応を持続可能にするには、自動化とルール化が不可欠だと気づいた。ここからが、ようやく「実装体制」が整った段階。
CIに組み込んだチェック
1年目は手動チェックばかりだったけど、これは絶対に続かない。以下の自動ツールをCIパイプラインに組み込んだ:
// package.json の例
{
"scripts": {
"test:a11y": "axe --exit-code 1 --follow-links",
"test:lighthouse": "lighthouse --only-categories=accessibility",
"test:pa11y": "pa11y-ci --config .pa11yci.json"
},
"husky": {
"hooks": {
"pre-push": "npm run test:a11y && npm run test:lighthouse"
}
}
}
Axe DevTools + lighthouse-ci + pa11y-ci の3つで、プルリクエスト段階で問題を検出する体制にした。地味だけど、この仕組みがないと本当に属人化する。
スクリーンリーダーテストの定期化
実ユーザーテストは月1回だけにして、その間は開発チームが WebAIM スクリーンリーダー検証リストで簡易チェック。毎週自動で実行される。
# 毎週水曜に自動実行
schedule:
- cron: "0 10 * * 3"
jobs:
a11y-test:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run test:a11y
- run: npm run test:lighthouse -- --output-path=./lighthouse.json
- uses: actions/upload-artifact@v3
with:
name: a11y-reports
path: ./lighthouse.json
3年目:WCAG 2.1 AAはゴールではなく基本
2年で WCAG 2.1 AA をほぼ達成した時点で、むしろ難しい問題が見えてきた。そこからが本当の勝負だったんだ。
カラーコントラスト設計の現実
WCAG要件は「テキストとの比率 4.5:1」。でも実際には……
/* これは技術的には OK だけど */
.subtitle {
color: #777;
background: #fff;
/* コントラスト比: 4.54:1 */
}
/* 実際に視認すると結構キツい */
/* 薄い灰色背景では、もっと濃い色が必要 */
WCAG AAは「最低限」に過ぎない。実務では AAA(7:1)を目指すか、より視認性の高い色設計が必要。実際に見比べると、その差は一目瞭然。
うちのチームは最終的に、全ページのカラーシステムを再設計した。デザイナーと開発者で「WCAG AA以上 + 実ユーザー検証」を基準にした。
キーボード操作フローの複雑性
// フォーカスシステム設計の失敗例
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
// ここでモーダルを開く
openModal();
// でも、フォーカスをモーダル内に移動するのを忘れた
}
};
// 正解版
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
openModal();
// 次のチックでモーダル内の最初の入力フォーカスに移動
setTimeout(() => {
const firstInput = modalRef.current?.querySelector('input');
firstInput?.focus();
}, 0);
}
};
Tabキーでの移動順序、フォーカストラップ(モーダル外に出ないようにする)、aria-liveでの動的更新通知。こういった細かい実装が、スクリーンリーダーユーザーの体験を大きく左右する。小さな工夫の積み重ねなんですよね。
チーム体制の工夫
役割分担を明確にした
最初は「誰かがアクセシビリティを見る」という曖昧な体制だった。3年目には以下のように整理した:
| 役割 | 責務 | 頻度 |
|---|---|---|
| フロントエンド開発者 | HTML/CSSのセマンティック実装、aria属性 | 毎PR |
| デザイナー | カラーコントラスト、フォントサイズ、レイアウト | デザイン段階 |
| QAエンジニア | 自動ツール実行、簡易スクリーンリーダーテスト | 毎スプリント |
| PM | スクリーンリーダーユーザーテスト実施 | 月1回 |
役割を明確にするだけで、「誰かやってくれてるだろ」という責任転嫁が減る。全員が自分のフェーズで気をつけるようになった。
学習投資
アクセシビリティはチーム全体のスキルである必要がある。毎月30分の勉強会を開催した。
最初の数ヶ月は外部講師を招いたけど、1年目終盤からはチームメンバーが自分たちで事例共有するようになった。「このコンポーネントはこうやって対応した」みたいな。
2026年時点での「アクセシビリティ対応」の現実
この3年で気づいたことがある。アクセシビリティ対応は、単なるコンプライアンスではなく、コード品質向上の手段だということ。
セマンティックHTMLの威力
<!-- 悪い例 -->
<div onclick="navigate()" role="button" tabindex="0" aria-label="ホーム">
Home
</div>
<!-- 良い例 -->
<a href="/">Home</a>
セマンティックなHTMLを書くことで、スクリーンリーダーの対応は自動的についてくる。同時に、ブラウザのネイティブ動作(キーボード対応など)も自動で働く。
これは結果的に、バグが少ない、テストしやすい、メンテナンスしやすいコードになる。正直、アクセシビリティを意識した開発って、別にアクセシビリティユーザーだけのものじゃなくて、全員にとって良いんだなって。
実装パターンの確立
3年運用して、チーム内で「アクセシビリティ対応パターン集」ができた。新入りが来ても、このテンプレートを使えば最初からアクセシビリティを組み込んだコードが書ける。
// フォーム入力のテンプレート
export const AccessibleInput = ({
label,
id,
error,
helperText,
...props
}) => (
<div className="form-group">
<label htmlFor={id}>{label}</label>
<input
id={id}
aria-describedby={error ? `${id}-error` : undefined}
aria-invalid={Boolean(error)}
{...props}
/>
{error && (
<p id={`${id}-error`} role="alert">
{error}
</p>
)}
{helperText && (
<p id={`${id}-helper`} className="helper-text">
{helperText}
</p>
)}
</div>
);
こんな感じで、「正しいパターン」を最初から提供することで、属人化を防げる。
正直なところ、まだ課題は残ってる
完璧なアクセシビリティは存在しない
正直、3年やってもWCAG 2.1 AAA完全対応には至ってない。リッチなUIコンポーネント(データテーブル、複雑なモーダル、動的レイアウト)は、アクセシビリティとの緊張関係が常にある。
完璧を目指すと、プロダクト開発そのものが止まっちゃう。だから現実的には「優先度をつけながら少しずつ」という感じ。
アクセシビリティはコスト
だから、プロダクトのステージによって優先度を分ける必要がある。初期段階はMVP的なアプローチ(基本のHTML/CSS対応)で、ユーザーが増えてから本格的に投資する、というのが現実的。
新規プロジェクトなら「最初から対応」で最小コストだけど、既存大規模サイトなら「段階的対応」を検討すべき。
ユーザーとのコミュニケーション
最後に気づいたのは、アクセシビリティ対応を伝えることの難しさ。外部的には「WCAG 2.1 AA対応」と書くけど、実際のユーザーが感じる「使いやすさ」はそれ以上に大事。
スクリーンリーダーユーザーの生の声を定期的に聞く環境があると、チーム全体の動機づけが全く違う。「この機能は本当に使えてますか?」って直接聞くと、統計データには出ない問題が見えてくる。
まとめ
3年間Webチームでアクセシビリティに取り組んで気づいたことを3つ:
-
アクセシビリティは後付けできない ──HTML設計、カラー設計の段階から組み込まないと、後から修正するのに何倍ものコストがかかる。次のプロジェクトからは、最初から組み込むべき。
-
自動ツール + 実ユーザーテストの両輪が必須 ──Axe や Lighthouse は大事だけど、スクリーンリーダーユーザーの生の反応を定期的に見ることで、チームの意識が圧倒的に変わる。机上の知識と現実のギャップは想像以上だったんだ。
-
チーム全体の理解が最重要 ──デザイナーがカラーコントラストを意識する、開発者がセマンティックHTMLを書く、QAが確認する。これが全て揃わないと続かない。1人のアクセシビリティの担当者がいても、チーム全体が関心なかったら意味ない。
アクセシビリティは、ユーザーの一部を排除しないための最低限の配慮だ。でも同時に、コード品質を高め、チーム全体のスキルを向上させる投資でもある。
次のプロジェクトに関わるエンジニアの皆さんへ:初期段階で「最初はアクセシビリティなしでいこう」という判断は、後々の技術負債になります。最初から組み込む労力と、後から修正する労力を比べたら、前者が絶対に正解です。僕たちも最初から対応してたら、3年間のうち1年半は別のことに使えてたんじゃないかな。