0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIペルソナとPuppeteerで加速するアクセシビリティ開発〜複数の背景を持つユーザー全てに対応した完全アクセシブル将棋ゲームの実装戦略

0
Posted at

この記事で分かること

何を学べるか

  • アクセシビリティ対応は「視覚障害者だけ」ではなく「全員が使える」設計
  • 24種類のペルソナ(障害者を中核に、多様な背景を持つユーザー)で品質を検証
  • 実際のユーザーの声を集める仕組みが改善の鍵

💡 GitHub Copilotユーザー必見

  • AIを使ってアクセシビリティ対応コードを効率的に生成する方法
  • テストコードの自動生成で品質向上
  • 第8章で具体的な活用戦略を解説

🔧 技術仕様(コード例あり)

  • Puppeteerで複数ペルソナの自動テスト実装(790テスト)
  • Vanilla JS選択でフレームワーク依存なし
  • Laravel 12 + PHP 8.3バックエンド

はじめに

前回の記事「視覚障害者もプレイできる!完全アクセシブルなWebゲームをLaravel+Livewireで作った話」では、火星移住計画シミュレータをLaravel+Livewireで実装し、スクリーンリーダー対応やキーボード操作のみでのプレイを可能にしました。

しかし前回は、主に視覚障害者に焦点を当てた実装でした。

その後、将棋ゲーム(a11y-shogi) の開発を通じて、重要な気づきを得ました:

「チャット指示」だけでは、複数の背景を持つユーザー全ての要件を満たせない

実装後に「複数のユーザーパターン」で実際に検証するしかありません。

だからこそ「AIペルソナ + Puppeteer(パペティア)による自動検証」が不可欠です。

24種類のペルソナを用いて790個のテストを自動化することで、
視覚障害、運動障害、発達特性、加齢、初心者...という
複数の背景を持つユーザー全てが「共存」できる設計を実現できました

今回は、複数の背景を持つ24種類のペルソナを用いて、多様なニーズを同時に検証する方法
そしてPuppeteer(パペティア)との組み合わせで「複数のユーザーパターン」を自動テスト化する戦略を紹介します。

24種類のペルソナで複数の背景に対応

  • 言語: PHP(Laravel 12)+ Vanilla JavaScript
  • テスト: 790個(PHP 140 + a11y 227 + AI 364 + E2E 59)
    • アクセシビリティテスト(8種227個):視覚障害・運動障害・環境対応
    • AIペルソナテスト(10種364個):障害者・年齢層・環境・専門家など多様なペルソナ
    • E2E統合テスト(59個)
  • AI Personas: 24種類(複数の視点から検証)
    • 障害者ユーザーペルソナ(17種類)
      • 視覚障害:全盲ユーザー、弱視ユーザー、全盲初心者、弱視初心者、色覚異常ユーザー、重度弱視ユーザー
      • 運動障害:上肢障害者、脳性麻痺、片麻痺
      • 聴覚障害:聴覚障害者
      • 発達特性・学習障害:発達特性/学習障害、ADHD、ディスレクシア、自閉スペクトラム
      • その他:知的障害、てんかん、加齢による見えにくさ
    • AIペルソナ(7種類)
      • 棋士AI、WebデザイナーAI、UXリサーチャーAI、教育者AI、加齢AIペルソナ、子供・初心者AI、モバイルユーザーAI
  • ステータス: 完全アクセシブル対応・全テスト合格(790/790)

第1章:なぜVanilla JS選んだのか——設計段階での判断

設計初期の決定:フレームワークの選択肢

アクセシビリティ重視のプロジェクトを始める際、フロントエンド技術の選択は本質的に重要です。

選択肢1: React + TypeScript
  メリット:コンポーネント再利用性、大規模開発向け
  デメリット:バンドルサイズ大(90KB+)、セットアップ複雑

選択肢2: Laravel + Livewire
  メリット:PHP開発者にとって自然、サーバー側の状態管理
  デメリット:複雑な状態管理時のジッター、ネットワーク遅延が目立つ

選択肢3: Vanilla JS
  メリット:バンドルサイズ0KB、直接的な制御、簡潔なコード
  デメリット:全て自分で実装、フレームワークの恩恵なし

将棋ゲームで明らかになった要件

実装を始めて初めて、開発効率だけでは対応できない課題が浮かり上がりました:

1️⃣ リアルタイムな駒の描画・再配置

問題:Livewireはイベント発火ごとにサーバー往復が発生するため、細かなインタラクションが多いUIでは、モバイルで遅延が目立ちやすくなります。特に、タッチ操作の連続入力では体験が崩れやすい。

2️⃣ 複雑な状態管理

将棋の盤面状態:

{
    board: [ 9×9マス × 駒種先手/後手/空き ],
    legalMoves: Set<Move>,          // 次に可能な手(平均80手前後)
    selectedPiece: {rank, file},    // 選択中の駒
    capturedPieces: { sente: [], gote: [] },
    gameState: 'playing' | 'finished',
    moveHistory: [ {from, to, promotion}, ... ]
}

Livewireでこの複雑な状態をサーバー-クライアント間で同期するのは、本質的に困難です。

3️⃣ アクセシビリティ検証との相性

問題:Livewireでは、サーバー-クライアント間の往復通信により、ゲーム状態が分散します。つまり、盤面の状態、選択中の駒、合法手などの情報が、サーバーとクライアント両側に存在する可能性があります。

結果:実装後にアクセシビリティを検証する際、「現在のゲーム状態が何か」を確実に把握するのが困難になります。スクリーンリーダーに正しい情報を読み上げさせるにも、盤面表示を正しく操作できるようにするにも、状態の一貫性が必須です。

Vanilla JSへの転換:得たもの

メリット1: ゲーム状態が完全にクライアント側にある

// 状態確認が即座
const gameState = await page.evaluate(() => window.gameData.boardState);
const board = gameState.board;
console.log(board);  // 9×9の盤面状態

// Puppeteerテストも思いのまま
const pieces = await page.evaluate(() => {
  const board = window.gameData.boardState.board;
  const sentes = [];
  for (let r = 1; r <= 9; r++) {
    for (let f = 1; f <= 9; f++) {
      if (board[r]?.[f]?.color === 'sente') {
        sentes.push({type: board[r][f].type, pos: `${f}${r}`});
      }
    }
  }
  return sentes;
});

メリット2: アクセシビリティ検証の基盤が整う

// ゲーム状態を直接検査できるので、
// 開発段階での検証が容易
const gameStatus = await page.evaluate(() => {
  return {
    status: window.gameData.status,
    currentPlayer: window.gameData.currentPlayer,
    boardState: window.gameData.boardState
  };
});
console.log(gameStatus);  // { status: 'in_progress', currentPlayer: 'human', ... }

// 盤面情報をすべて取得できる
const allPieces = await page.evaluate(() => {
  const board = window.gameData.boardState.board;
  const pieces = [];
  for (let r = 1; r <= 9; r++) {
    for (let f = 1; f <= 9; f++) {
      if (board[r]?.[f]) {
        pieces.push({
          type: board[r][f].type,
          color: board[r][f].color,
          position: `${f}${r}`
        });
      }
    }
  }
  return pieces;
});
console.log(allPieces);  // [{type: 'fu', color: 'sente', position: '1-3'}, ...]

この「ローカルな検証可能性」は、後にPuppeteerを用いた
複数パターンのテスト自動化に直結します。

メリット3: バンドルサイズが最小限

  • jQuery: 87KB
  • Livewire: 133KB
  • Vanilla JS(UIフレームワークなし): 0KB

結論:2024年のVanilla JSは「選択肢」ではなく「標準」

2024年のブラウザ環境には、
fetch, async/await, DOM API の充実、
Web Components など...

フレームワークなしで「読みやすく保守しやすい」コードを書けます。

ただしサーバー連携(複雑なフォーム、リアルタイム通知)が必要なら
React/Vue + Server-Sent Events がベター。

ターンベース + 複雑な状態を持つゲームで、
特にローカルな状態管理が不可欠なら
Vanilla JSが最適解です。

第2章:「初心者向けUI設計」——プレイしながらルールを学べる設計

課題:「将棋のルール理解」と「アクセシブル操作」の両立

アクセシビリティ対応で見落とされやすい課題があります:

「盤面操作のキーボード対応」と「初心者向けルール説明」は別問題

視覚障害者向けに「Enterキーで駒を選択」「矢印キーで移動」と実装しても、
初心者ユーザーが将棋のルール自体を知らなければ、そもそもゲームができません。

また、逆に将棋のルールを詳しく説明するヘルプページを用意しても、「キーボードでどう操作するか」が直感的でなければ、アクセシビリティは実現できません。

さらに重要なのは、長いマニュアルを読むのは誰にとっても負担だということです。

解決策:「実際にプレイしながら学べる」設計

将棋ゲーム(a11y-shogi)では、以下の3つのアプローチを組み合わせました:

A. ゲーム中に「操作方法とルール」を同時に習得できる仕組み(核心)
B. いつでも参照できる「ショートカット一覧」(補助)
C. より深く学びたい人向けの「簡易版ルール説明」(任意)

これにより、事前知識ゼロの完全初心者でも、ヘルプページを読まずに
実際にプレイしながら、UI操作とゲームルールの両方を同時に習得できる設計を実現しました。

1️⃣ ゲーム画面内に「ショートカットモーダル」を組み込み

ユーザーが H キー を押すと、常に以下の情報が利用可能:

  • 矢印 / WASD → 盤面上のカーソル移動
  • Enter / Space → 駒を選択・移動決定
  • B → 盤面全体を読み上げ(スクリーンリーダー対応)
  • Shift+B → 差分読み上げ(前回からの変化のみ)
  • S → ゲーム状態を読み上げ(手番・進行状況)
  • K → 棋譜(手順)を読み上げ
  • 1 / 2 → 先手 / 後手の駒台へ移動
  • I → 相手の利き筋を読み上げ
  • U → 待ったする
  • R → リセット

重要なのは、このショートカット一覧が「ゲーム中に常にアクセス可能」なこと。

ヘルプページを別で読む必要がなく、リアルタイムに確認できます。

2️⃣【任意】ヘルプページで「より深く理解」(補助資料)

ヘルプページは必須ではありません。しかし、「より深く理解したい人」や「事前にルールを確認したい人」のために、補助資料として用意されています:

将棋のルール(簡易版)

先手と後手
先手(下側)が最初に指し、後手(上側)がその後に指します。

目的
相手の王(玉)を逃げられない状態にすること(詰み)が目的です。

駒の種類と動き

  • 歩(ふ):前に1マスのみ進む
  • 香(きょう):前方へまっすぐ何マスでも進む
  • 桂(けい):前方へ2マス・横に1マス跳ぶ(唯一飛び越せる駒)
  • 銀(ぎん):前方3方向と斜め後ろ2方向に1マス
  • 金(きん):前後左右と斜め前2方向に1マス
  • 角(かく):斜め4方向に何マスでも進む
  • 飛(ひ):前後左右に何マスでも進む
  • 玉/王(ぎょく/おう):全方向に1マス(これを守るのが目的)

成り(駒のパワーアップ)
自分の駒が相手の陣地(敵陣 = 相手側の3段)に入る・出る・中で動くとき、駒を裏返してパワーアップできます。

持ち駒を打つ
相手の駒を取ると「持ち駒」として駒台に並びます。盤面の空いているマスに置く(打つ)ことができます。

王手と詰み
相手の王を次に取れる状態を「王手」といいます。どうやっても王手を防げない状態が「詰み」で、詰まされた側の負けです。

この「簡易版」で理解できるのは、3つの基本概念:

  1. 将棋の目的は「王を詰ます」こと(ゲーム終了時にも理解できる)
  2. 駒にはそれぞれ動きがある(合法手ハイライトで学べる)
  3. 取った駒は「持ち駒」として再利用できる(駒台UIで体験できる)

ただし重要:これらは全て「プレイしながら自然に学べる」ため、ヘルプページを読まなくてもゲームは楽しめます。

3️⃣「プレイしながら具体的なルールを覚える」設計

ユーザーが「説明を読まずに実際に操作する」だけで、次々とUI操作と将棋ルールの両方を自然に学べる設計を実現しました。

重要な設計思想:ヘルプページ不要で学べる

この設計の革新的な点は、ヘルプページを一切読まずに、完全にプレイしながら全てを学べることです:

ゲームをプレイしながら「ゼロから」学べる内容:

  • ✅ UI操作方法(矢印キー移動、Enter選択)
  • ✅ 各駒の具体的な動き方(合法手ハイライトで視覚化)
  • ✅ 成りの仕組み(成りダイアログで「成りますか?」と尋ねられる)
  • ✅ 持ち駒を打つ方法(駒台UIで体験)
  • ✅ ルール違反の内容(「そこには動かせません」等のエラーメッセージ)
  • ✅ ゲームの目的(詰まされて終了 → 「王を守る」ことを理解)
  • ✅ 交互に指すルール(AIの手番 → 自分の手番の繰り返し)

ヘルプページの位置づけ:

  • 「より深く理解したい人」向けの補助資料
  • 「事前学習」ではなく「確認・復習」用
  • プレイ中に疑問が生じたときの参照先

つまり、完全初心者が何の予備知識もなくゲームを開始しても、試行錯誤しながら自然に将棋のルールを習得できる設計になっています。

以下は、初心者ペルソナがゲームを通じて自然に学習していく過程をシミュレートしたテストコードです:

// AIペルソナのテスト: 「完全初心者」ケース
// = 将棋のルールを一切知らない状態でゲームを開始
//   ヘルプページも読まずにプレイ開始

// 1. ユーザーがゲームを開始
const BASE_URL = 'http://localhost:8000'; // ローカル環境
await page.goto(BASE_URL);

// 2. Hキーで「ショートカット」一覧を表示できる
//    (必要なときに参照できる設計)
await page.keyboard.press('h');
const shortcutsVisible = await page.evaluate(() => {
  return document.querySelector('#shortcuts-modal') !== null;
});
console.assert(shortcutsVisible, 'ショートカットモーダルが表示される');

// 3. ユーザーが「矢印キー」を試す
await page.keyboard.press('ArrowDown');
await page.keyboard.press('ArrowLeft');

// 4. 盤面内に移動できたことが「視覚的に」確認できる
const selectedCell = await page.evaluate(() => {
  return document.querySelector('[data-selected="true"]') !== null;
});
console.assert(selectedCell, 'セルが選択状態になる');

// 5. ユーザーが「Enter」を押す
await page.keyboard.press('Enter');

// 6. スクリーンリーダーで「駒を選択してください」と読み上げられる
const liveRegionText = await page.evaluate(() => {
  return document.querySelector('[aria-live="polite"]')?.textContent || '';
});
console.assert(liveRegionText.includes(''), 'スクリーンリーダーで駒選択を促す');

// 7. 駒を選択後、「矢印キー」で移動先を探す
//    → 「合法手」が緑ドットで表示される(視覚)
//       → ユーザーは「あ、この駒はここに動けるんだ」と理解
//    → スクリーンリーダーで「移動可能」と読み上げられる(音声)
//
//    ★ 重要:視覚障害者も各マスを矢印キーで移動しながら確認することで、
//    その駒が「どこまで移動できるか」を完全に把握できる
//    例:歩を選択した場合、前のマスだけが「移動可能」と読み上げられ、
//        他のマスは「移動不可」と読み上げられるため、
//        歩は前に1マスしか動けないことを理解できる

// 8.「Enter」で確定
//    → 駒が移動して、盤面が更新される
//    → 敵陣に入った場合、「成りますか?」ダイアログが表示
//       → ユーザーは「駒が強くなれるのか」と初めて知る
//    → 「手番が交代」を示す通知が表示

// 9. AIが指す → また自分の番
//    → 「交互に指すゲームなんだ」と理解

// 10. 最終的に詰まされる → ゲーム終了
//    → 「王を守るゲームだったのか」と目的を理解

// → つまり、**何の予備知識もなく**ゲームを開始しても、
//    「UI操作方法」「駒の動き」「成りのルール」「ゲームの目的」を
//    実際にプレイしながら自然に習得できる設計
//
// これが真の「Learning by playing(遊びながら学ぶ)」のアクセシブルゲームデザイン

4️⃣ AIペルソナの「完全初心者」検証テスト

📊 AIペルソナ「完全初心者ユーザー」のテストシナリオ:
    (将棋のルールを一切知らず、ヘルプページも読まない状態)

1️⃣ ゲーム開始直後
   - 矢印キーを押してみる → カーソルが動くことを発見
   - Enterを押してみる → 何かが選択されることを理解
   - 合法手ハイライトで「動ける場所」が視覚的にわかるか

2️⃣ 第1手を指す
   - 緑ドットの意味が直感的に理解できるか
   - 「この駒はこう動く」と視覚的に学べるか
   - 試行錯誤しながら操作方法を習得できるか
   
   ★ 視覚障害者の場合:
     - 駒を選択した後、矢印キーで各マスを移動
     - 各マスで「ここに移動可能」「ここは移動不可」と読み上げられる
     - この確認プロセスにより「その駒がどこまで動けるか」を完全に把握

3️⃣ 駒を動かす
   - 成りダイアログで「駒が強くなれる」と理解できるか
   - 「成る/成らない」の選択肢が明確か
   - 持ち駒の概念を自然に理解できるか

4️⃣ 相手(AI)の着手を見る
   - AIの動きから「交互に指す」ルールを理解できるか
   - AIの駒の動きから将棋のパターンを学べるか

5️⃣ ゲーム終了
   - 詰まされて初めて「王を守るゲームだった」と理解できるか
   - もう一度プレイしたくなるか

🎯 成功基準

何の予備知識もない完全初心者が、ヘルプページを読まずにゲームを開始しても、「試行錯誤」しながら次々とUI操作・駒の動き・成りのルール・ゲームの目的を自然に習得できること。

✅ 実現できている学習体験:
 - UI操作(キーボード操作)← 試して覚える
 - 駒の動き(合法手ハイライト)← 視覚で見て覚える、または音声で「移動可能/不可」を確認して覚える
 - 成りの仕組み(成りダイアログ)← 尋ねられて覚える
 - ゲームの目的(詰み)← 終了時に理解
 - 全て「ヘルプページなし」で習得可能
 
 ★ 特に視覚障害者にとって重要:
    駒を選択した後、矢印キーで盤面を移動して各マスの「移動可能/不可」を確認することで、
    その駒の具体的な動きパターンを段階的に学習できる

本質的な設計原則

「チュートリアル画面」も「ヘルプページ」も不要。「ゲーム自体」が完全なチュートリアルになっている設計

  • 盤面移動 → 矢印キーを試す → カーソルが動く → 「操作方法」を体験で覚える
  • 駒選択 → その駒が動ける場所を視覚化・音声化 → 「駒の動き」を見て/確認して覚える
    • 視覚:緑ドットで移動可能な場所が一望できる
    • 音声:各マスを矢印キーで移動しながら「移動可能/不可」を確認
  • 成り → 成りダイアログで「成りますか?」と尋ねる → 「成りのルール」を尋ねられて覚える
  • AI の着手 → ★マークと音声通知 → 「交互に指す」ことを体験で覚える
  • ゲーム終了 → 詰み通知 → 「ゲームの目的」を結果から理解
  • ショートカット → H キーでいつでもアクセスできる(必要なら)
  • エラーメッセージ → 「そこには動かせません」で制約を理解

このアプローチにより、何の予備知識もない完全初心者が、ヘルプページを一切読まずにゲームを開始しても、実際にゲーム内で「やりながら全てを覚える」という
複数の背景を持つユーザー全てに対応した学習体験が実現されます。

つまり:

  • 事前知識ゼロでゲームを開始できる
  • ヘルプページ不要で全てのルールを習得できる
  • 操作方法とゲームルールの両方を同時に学べる設計
  • ヘルプページは「より深く知りたい人向けの補助資料」として機能

典型的な「Learning by playing(遊びながら学ぶ)」のゲームデザインを、アクセシビリティ対応したものです。

これこそが、複数の背景を持つ全てのユーザーにとっての「真のアクセシビリティ」

  • 視覚障害者:駒選択後に矢印キーで盤面を移動 → 「移動可能/不可」の読み上げで駒の動きを完全把握 + 音声読み上げ
  • 運動障害者:キーボードのみで完結
  • 発達特性:シンプルで予測可能なフィードバック
  • 加齢:大きな文字・明確な視覚的フィードバック
  • 初心者:マニュアル不要・試行錯誤で学べる

第3章:「チャットで指示し切れない」アクセシビリティ検証の限界

理想と現実のギャップ

アクセシビリティ開発を始める際、多くの開発者はチャットAI(GitHub Copilot等)に指示をするアプローチを取ります:

プロンプト例:

「全画面をキーボード操作可能にしてください。
特に小さい画面やレスポンシブ表示で、複数の駒を取った場合の持ち駒表示が
正しく働くか確認してください。」

結果:
チャットAIは、HTMLセマンティクスのチェックリストを提案してくれます。
でも、それは「機械的な検証」に過ぎません。

実際には、複数のユーザー層の要件が競合します:

視覚障害者

  • スクリーンリーダーが「駒の位置」を正確に読み上げるか?
  • 駒を複数回取った時、ARIA live regionで即アナウンスされるか?

運動障害者(マウス不可)

  • キーボードだけで全操作ができるか?
  • Tabキーの順序は論理的か?

発達特性(ADHD/ASD)

  • 画面内の情報量や動きが多すぎないか?
  • UIルール説明は過度に複雑か?

加齢による見えにくさのある人

  • テキストが18px以上で読みやすいか?
  • ボタンが44px以上で操作しやすいか?
  • 色だけに頼った情報表現ないか?

初心者

  • 将棋ルール未習者向けの説明が十分か?
  • UIフローは直感的か?

こうした要件は「チェックリスト」では表現できず、
実装後に「複数のユーザーパターン」で検証するしかありません。

なぜ単一のテストだけで不十分なのか

// ❌ 単一のメトリクスだけでは「アクセシビリティ」を保証できない
test('駒台のボタンの最小サイズが44px', () => {
    const button = document.querySelector('.hand-piece');
    // ✅ これは合格
    // でも...
    // - 加齢による見えにくさのある人には「押しやすい」か?
    // - ADHD ユーザーには「ボタンが多すぎて混乱」しないか?
});

// ❌ WCAG チェックリストに合格しても、実体験は失敗する(架空の例)
test('色コントラスト比が4.5:1以上', () => {
    // WCAG AA基準(4.5:1)は合格
    // でも、AAA基準(7:1)ではないため
    // → 重度弱視ユーザーには「判読困難」
    // → ユーザー体験は「破綻」
});

そこで本プロジェクトでは、Puppeteerを用いて「複数のユーザーパターン」を自動的に検証する仕組みを構築しました。

第4章:Puppeteerで「AIペルソナ」を実装——体験談から改善点を発見

なぜ複数のペルソナが必要なのか

「アクセシビリティに対応した」と言えるアプリは何か?

✅ スクリーンリーダー対応 → 視覚障害者には使える
✅ キーボード操作対応 → 運動障害者には使える
✅ シンプルな UI → 発達特性のあるユーザーには使える
✅ 大きいテキスト対応 → 加齢による見えにくさのある人には使える
✅ 迷いにくい導線 → 初心者には使える

しかし、複数の要件を同時に満たすのは困難です:

例:「スクリーンリーダー + 高いコントラスト + 大きいテキスト + キーボード操作」
    が同時に機能するゲームを、チェックリストだけで設計できるか?
    → NO。実装→検証→改善を「複数のユーザーパターン」で繰り返すしかない

「体験談」でリアルな改善点を発見

単なる自動テストではなく、AIペルソナに実際にプレイさせて、体験談を語ってもらうアプローチです。

全盲ユーザーAIの体験談

Puppeteerでスクリーンリーダー対応をテストした全盲ユーザーAIに、体験を語ってもらいました:

初めてこのゲームをプレイした時、正直驚きました。

多くのWebゲームは『見えること前提』で作られていて、スクリーンリーダーでは何が起きているのか全く分かりません。でも、このゲームは違いました!

✨ 良かった点:

  1. 盤面の各マスに詳細な説明 — 「5の3のマス、先手の歩がいます」と読み上げられるので、盤面の状況が手に取るように分かります。

  2. 駒を選んだ瞬間、音声で通知 — 「先手の歩を選択しました」と即座にフィードバックがあるので、今何を操作しているか迷いません。

  3. 移動先を矢印キーで確認できる — 各マスに移動して「ここに移動可能」「移動不可」と確認できるので、その駒が『どう動くか』を視覚なしで完全に理解できます。これは素晴らしい!

  4. 手番交代を音声で教えてくれる — 「AIの手番です」と分かるので、待つべき時が明確。

😅 改善してほしい点:

  1. 持ち駒を打つ操作が少し分かりにくい — 駒台に移動した時、「ここは駒台です。持ち駒を選べます」という説明があると、初めてでも理解しやすいと思います。

  2. AIが何を指したか、詳細に知りたい — 「AIが5の4の歩を6の4に移動しました」のような詳細な読み上げがあると、戦況を把握しやすいです。

  3. 詰みの状況をもっと明確に — 「王手です」「詰みです」という通知は欲しいです。

でも総合的には、目が見えなくても本当に楽しめるゲームでした!

運動障害者AIの体験談

次に、マウスが使えない運動障害者AIにプレイさせました:

私は脳性麻痺で、マウスを正確にクリックすることができません。普段は『キーボードのみ』でパソコンを使っています。

このゲームを初めて見た時、『将棋は無理だろうな...』と思いました。盤面が複雑で、マウスクリック前提に見えたからです。

でも、実際にプレイしてみたら...

✨ 素晴らしかった点:

  1. 矢印キーで盤面を自由に移動できる — WASDキーにも対応していて、右手だけでも操作できます。これは本当に助かります!

  2. Enterキーだけで駒を選択・移動できる — 複雑なキーボードショートカットではなく、『矢印で選ぶ → Enter』のシンプルな操作。

  3. フォーカスリングがはっきり見える — 今どこを選んでいるか、視覚的に明確。細かい操作が苦手な私でも迷いません。

  4. ショートカットキーが豊富 — 1/2キーで駒台に直接移動できるので、Tab連打しなくていい。これは神機能!

😅 改善してほしい点:

  1. フォーカスの移動範囲が広すぎる — Tabキーで『新しい対局』→『ヘルプ』→『投了』→『盤面』...と全部回るのは少し疲れます。『Escキーで盤面に戻る』とか、ショートカットがあると嬉しい。

  2. 成りダイアログの操作 — 「成る/成らない」を選ぶ時、矢印キーで選択できるけど、どちらが選ばれているか、もっと視覚的に分かりやすいと良い。

  3. 操作ミスしても大丈夫 — 『待った機能(Uキー)』があるのは最高!でも、初めて知るまで気づかなかった。最初に『Hキーでヘルプ』と表示してくれると親切かも。

でも正直に言って、このゲームはマウスなしで完全に遊べる数少ないゲームの一つです。開発者さん、本当にありがとうございます!

GitHub Copilot等のAIに「体験談」から改善案を聞く

これらの体験談をGitHub Copilot等に渡すと、以下のような具体的な改善案が提案されます:

  • 全盲ユーザーの要望:AIの手を詳細に読み上げる(aria-live regionの充実)
  • 運動障害者の要望:Escキーで盤面に戻る機能の追加
  • 初心者向け:駒台エリアの説明をもっと明確に

本質:コードだけでなく「体験」を検証する

この章で重要なのは単なる自動テストではないということ:

従来のテスト:
  ✅ aria-labelが存在するか? → YES
  ✅ キーボード操作可能か? → YES
  → テスト合格!

AIペルソナテスト:
  ✅ aria-labelが存在する
  ❓ でも、全盲ユーザーは「分かりやすい」と感じるか?
  ❓ 実際にプレイして「楽しい」か?
  → AIペルソナに体験談として語ってもらう
  → 改善点を具体的に発見
  → GitHub Copilotで即座に改善案を生成
  → 実装 → 再テスト

つまり、「テストが通る」ことがゴールではなく、「実際のユーザーが快適にプレイできる」ことがゴールです。

AIペルソナとPuppeteerの組み合わせで、この検証サイクルを自動化・高速化できます。

【詳細】体験談から生成される具体的な改善案

上記の改善提案について、GitHub Copilotと確認した詳細情報:

改善提案1の詳細コード例

全盲ユーザーの「AIの手を詳細に読み上げたい」という要望への実装例:

// 改善後のコード
function announceAiMove(move) {
  const liveRegion = document.querySelector('[aria-live="polite"]');
  
  // moveオブジェクトから詳細を取得
  const { from, to, piece, captured, promoted } = move;
  
  // 詳細な読み上げテキストを構成
  let announcement = `AIが${from.file}${from.rank}${piece}を`;
  announcement += `${to.file}${to.rank}に移動しました。`;
  
  if (captured) {
    announcement += ` ${captured}を取りました。`;
  }
  
  if (promoted) {
    announcement += ` ${piece}が成りました。`;
  }
  
  liveRegion.textContent = announcement;
}

// 使用例:
// AIが5の4の歩を6の4に移動しました。
// AIが3の3の銀を4の4に移動しました。歩を取りました。
// AIが9の1の香を9の3に移動しました。香が成りました。

改善提案2の詳細コード例

運動障害者の「Escキーで盤面に戻りたい」という要望への実装例:

// keyboard-shortcuts.js に追加
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape') {
    // 現在フォーカスがゲーム盤面の外にある場合
    const boardSection = document.querySelector('.board-section');
    const focusedElement = document.activeElement;
    
    if (!boardSection.contains(focusedElement)) {
      // 盤面の中央のマスにフォーカスを戻す
      const centerCell = document.querySelector('[data-rank="5"][data-file="5"]');
      if (centerCell) {
        centerCell.focus();
        
        // スクリーンリーダーにも通知
        const liveRegion = document.querySelector('[aria-live="polite"]');
        liveRegion.textContent = '盤面に戻りました。5の5のマスにいます';
      }
    }
  }
});

// さらに、ショートカット一覧(Hキー)にも追加:
// - Esc → 盤面に戻る

改善提案3の詳細コード例

全盲ユーザーの「駒台エリアをもっと分かりやすく」という要望への実装例:

<!-- HTML改善 -->
<div class="komadai" 
     role="region" 
     aria-label="持ち駒エリア。取った駒を盤面に打てます"
     @focus="announceKomadaiEntry">
  <button 
    aria-label="歩を盤面に打つ。現在3枚持っています"
    data-piece="fu"
    data-count="3">
    歩 3
  </button>
  <!-- ... -->
</div>

<script>
function announceKomadaiEntry() {
  const liveRegion = document.querySelector('[aria-live="polite"]');
  liveRegion.textContent = '持ち駒エリアに入りました。取った駒を選んで盤面に打てます';
}
</script>

【詳細】Puppeteerのテスト実装コード

上記の体験談は、以下のようなPuppeteerテストから生成されています。

全盲ユーザーテストの実装例

// tests/a11y/test-blind-user.mjs
import puppeteer from 'puppeteer';

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  const BASE_URL = 'http://localhost:8000'; // ローカル環境
  await page.goto(BASE_URL);

  // 検証1: すべてのマスに適切なaria-labelがあるか
  console.log('🔍 検証1: 盤面のaria-label検証');
  const cells = await page.$$('[data-rank][data-file]');
  let labelErrorCount = 0;
  
  for (const cell of cells) {
    const ariaLabel = await cell.evaluate(el => el.getAttribute('aria-label'));
    if (!ariaLabel || ariaLabel.length === 0) {
      labelErrorCount++;
    }
  }
  
  if (labelErrorCount === 0) {
    console.log('✅ すべてのマスに適切なaria-labelがあります');
  }

  // 検証2: キーボード操作、音声通知、手番交代などを検証
  // (詳細な検証ロジックは省略)

  console.log('✅ 全盲ユーザーのアクセシビリティ検証完了');
  await browser.close();
})();

運動障害者テストの実装例

// tests/disability/test-motor-disability.mjs
import puppeteer from 'puppeteer';

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  const BASE_URL = 'http://localhost:8000';
  await page.goto(BASE_URL);

  console.log('🔍 運動障害者ペルソナ: マウスなしでの操作検証');

  // マウス操作を完全に無効化
  await page.evaluate(() => {
    document.addEventListener('mousedown', (e) => e.stopImmediatePropagation(), true);
    document.addEventListener('click', (e) => e.stopImmediatePropagation(), true);
  });

  // Tab + Enter だけでゲーム開始
  await page.keyboard.press('Tab');
  await page.keyboard.press('Enter');
  await page.waitForTimeout(500);

  // 矢印キー + Enter で10手プレイ
  for (let turn = 0; turn < 10; turn++) {
    // 駒を選択(矢印キーで移動 + Enter)
    for (let i = 0; i < 3; i++) {
      await page.keyboard.press('ArrowDown');
      await page.waitForTimeout(100);
    }
    await page.keyboard.press('Enter');
    await page.waitForTimeout(200);

    // 移動先を選択
    await page.keyboard.press('ArrowRight');
    await page.keyboard.press('Enter');
    await page.waitForTimeout(500);

    // AIの手番を待つ
    await page.waitForTimeout(1000);
  }

  console.log('✅ マウスなしで10手完了 - 運動障害者対応OK');
  await browser.close();
})();

これらのテストが検証すること

  • 全盲ユーザー: aria-label、音声通知、キーボード操作のみで完全プレイ可能
  • 運動障害者: マウス無しで矢印キー + Enter のシンプル操作で完全プレイ可能

第5章:Puppeteerで再現「モバイルボード縮小バグの原因特定」

アクセシビリティは「障害者対応」だけではない

前章では、全盲ユーザーや運動障害者といった障害者ペルソナの検証を見ました。
これがアクセシビリティの中核です。

しかし、「アクセス可能性」は障害の有無に関わらず、多様な環境・デバイスでも使えることを含みます:

  • 小さい画面(モバイル) でもUIが崩れない
  • 低速回線 でも操作できる
  • 古いブラウザ でも動作する

このため、24種類のペルソナには 障害者ペルソナ(17種) に加えて、AIペルソナ(7種:モバイル、デザイナー、UX等) も含めています。

以下は、モバイル環境で発見されたバグの実例です。

😱 ユーザーレポート:盤面が縮小する現象

iPhone 11でのテスト時に、ゲーム中に盤面がみるみる縮小する現象が報告されました:

初期状態:盤面 320px(画面横幅400px の80%)
↓ 駒を1つ取る
→ 盤面 294px(6%縮小!)
↓ 駒を3つ取る
→ 盤面 186px(58%縮小!!)

何かが盤面サイズに影響を与えている?
→ Puppeteerで再現し、原因を特定する必要がある

🔍 Puppeteerで問題を再現し、原因特定

このバグの仕組みを特定するために、Puppeteerでブラウザの自動操作を記録します。
駒を取る前後で盤面サイズがどう変化するかを追跡することで、CSSレベルの問題を発見できます。

【詳細】Puppeteerでバグを追跡するコード

// debug-komadai.mjs - 盤面縮小バグを追跡
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 375, height: 667 }); // iPhone SE

const BASE_URL = 'http://localhost:8000'; // ローカル環境
await page.goto(BASE_URL);
await page.click('[role="button"]:has-text("新しい対局")');

// 15手進めながら、盤面サイズの変化を記録
const sizeHistory = [];
for (let i = 0; i < 15; i++) {
  const moves = await page.evaluate(() => 
    window.game.board.getLegalMoves()
  );
  if (moves.length === 0) break;

  const move = moves[Math.floor(Math.random() * moves.length)];
  
  // 駒を取る前のサイズ
  const before = await page.evaluate(() => ({
    komadai: getComputedStyle(document.querySelector('.komadai')).width,
    board: getComputedStyle(document.querySelector('.board-section')).width
  }));

  await page.click(`[data-move="${move}"]`);
  await page.waitForTimeout(300);

  // 駒を取った後のサイズ
  const after = await page.evaluate(() => ({
    komadai: getComputedStyle(document.querySelector('.komadai')).width,
    board: getComputedStyle(document.querySelector('.board-section')).width
  }));

  sizeHistory.push({
    turn: i + 1,
    komadai: `${before.komadai}${after.komadai}`,
    board: `${before.board}${after.board}`
  });
}

console.table(sizeHistory);

実行結果:

Turn 1: 駒台 90px → 90px, 盤面 320px → 320px ✅
Turn 2: 駒台 90px → 90px, 盤面 320px → 307px ❌ 縮小!
Turn 3: 駒台 90px → 90px, 盤面 307px → 294px ❌ さらに縮小!
Turn 4: 駒台 90px → 90px, 盤面 294px → 281px ❌ 継続して縮小...
...
Turn 15: 駒台 90px → 90px, 盤面 186px → 186px ❌ 最終的に62%に!

駒台の幅は固定(90px)のまま変わらない。
なのに盤面が次々と縮小している。これはCSS側の問題だ。

🛠️ CSSを調査して原因を特定

Puppeteerの追跡結果から、CSS メディアクエリのカスケード問題が原因だと判明しました。
小さい画面用のルールで、親のフレックス指定が完全に上書きされていなかったのです。

【詳細】CSS メディアクエリの誤りを修正

/* 799px以下のタブレット向け */
@media (max-width: 799px) {
  .komadai {
    flex: 0 0 90px;  /* 駒台幅を90pxに固定 */
  }
}

/* 480px以下のモバイル向け */
@media (max-width: 480px) {
  .komadai {
    max-width: none;  /* ← 幅指定がない...! */
  }
  /* ← 799pxの flex: 0 0 90px がカスケードしている! */
}

修正:

@media (max-width: 480px) {
  .komadai {
    width: auto;           /* 幅を自動に */
    flex: 0 0 auto;        /* フレックスの固定を解除 */
    align-self: stretch;   /* 親に合わせて広げる */
  }

  .komadai .hand-pieces {
    flex-direction: row;   /* ボタンを横並びに */
    flex-wrap: wrap;       /* 折り返しを許可 */
  }
}

✅ 修正後の検証

修正したCSS設定を実際にスマートフォンやタブレットで検証した結果:

iPhone SE (375px):
  駒台幅: 319px  ✅ 画面幅に広がった!
  盤面幅: 299px  ✅ 縮小しない!

iPhone 14 (390px):
  駒台幅: 334px  ✅ OK
  盤面幅: 320px  ✅ OK

タブレット(600px):
  駒台幅: 90px   ✅ 意図通り
  盤面幅: 620px  ✅ OK

💡 本質的な学び

「正しいコード」と「良いユーザー体験」は異なる

テスト: ✅ CSS メディアクエリ構文は正しい
でも: ❌ ユーザー体験は破壊されている

これを見つけるのは「機械的なテスト」ではなく、
「複数のデバイスで実際にゲームをプレイして確認する」しかない

第6章:24種類のペルソナで790テスト——障害者ペルソナを中核に

アクセシビリティテストの優先順位

最優先:障害者ユーザーペルソナ(17種類)

  • 全盲ユーザー、弱視ユーザー、運動障害者、発達特性...
  • これがアクセシビリティの本質
  • テスト数:227個(全790個の29%)

次点:多様な環境・専門家のペルソナ(7種類)

  • モバイルユーザー、UXリサーチャー、教育者...
  • デバイス・専門的観点からの品質検証
  • テスト数:364個(全790個の46%)

テスト統計

以下は、790個のテスト内訳です(npm run test:all で実行):

カテゴリ 個数 詳細
PHP Unit(ロジック) 140
アクセシビリティテスト 227 全盲(87)、拡張(21)、弱視(35)、色対比(28)、周辺視野喪失(20)、パーキンソン病(7)、低スペック(12)、色覚異常(17)
AIペルソナテスト 364 多様なペルソナ1(45)、多様なペルソナ2(33)、多様なペルソナ3(26)、高齢者(62)、子供・初心者(28)、UXリサーチャー(45)、教育者(30)、モバイルユーザー(28)、棋士(30)、デザイナー(37)
E2E 統合テスト 59
合計 790

ペルソナ分類(24種類)

🧑‍🦯 【中核】障害者ユーザーペルソナ(17種類)

アクセシビリティの本質は、障害を持つユーザーが使えること。
以下の17種類のペルソナで、多様な障害に対応:

視覚障害関連 (6種)

  • 全盲ユーザー(スクリーンリーダー利用)
  • 弱視ユーザー(拡大表示対応)
  • 全盲初心者(スクリーンリーダー + 将棋初心者)
  • 弱視初心者(拡大表示 + 将棋初心者)
  • 色覚異常ユーザー(P型色覚対応)
  • 重度弱視ユーザー(400%ズーム対応)

運動障害関連 (3種)

  • 上肢障害者(片手キーボード操作)
  • 脳性麻痺(Tab + Enter のみ)
  • 片麻痺(左手のみ操作)

聴覚障害関連 (1種)

  • 聴覚障害者(視覚的フィードバック)

発達特性・学習障害関連 (4種)

  • 発達特性/学習障害(ふりがな・平易な言葉)
  • ADHD(感覚過敏対応)
  • ディスレクシア(読字障害対応)
  • 自閉スペクトラム(一貫性重視)

その他の障害 (3種)

  • 知的障害(シンプルUI)
  • てんかん(光感受性対応)
  • 加齢による見えにくさ(弱視 + 手の震え対応)

🤖 【補助】AIペルソナ(7種類)

障害者対応を前提に、さらに多様な環境・視点で品質を検証:

  • 棋士AI(将棋ルール正確性検証)
  • WebデザイナーAI(UI/UX設計品質検証)
  • UXリサーチャーAI(ヒューリスティック評価)
  • 教育者AI(特別支援教育対応検証)
  • 加齢AIペルソナ(デジタル不慣れ・72歳)
  • 子供・初心者AI(小学生12歳対応検証)
  • モバイルユーザーAI(スマートフォン通勤ユーザー)

実行例:障害者ペルソナのテストコード

障害者ペルソナの実際のテストコードを見てみましょう:

// tests/customer-ai/test-senior.mjs
// 【障害者ペルソナ】加齢による見えにくさのある人をシミュレート

const checks = [
  {
    name: 'テキストコントラスト比が4.5以上',
    fn: async () => {
      const color = await page.locator('.board-label')
        .evaluate(el => getComputedStyle(el).color);
      expect(contrastRatio(color)).toBeGreaterThanOrEqual(4.5);
    }
  },
  {
    name: 'ボタンサイズが最小44px',
    fn: async () => {
      const buttons = await page.locator('button').all();
      for (const btn of buttons) {
        const height = await btn.evaluate(el => el.offsetHeight);
        expect(height).toBeGreaterThanOrEqual(44);
      }
    }
  },
  {
    name: '画面ズーム200%でも操作可能',
    fn: async () => {
      await page.evaluate(() => {
        document.body.style.zoom = '200%';
      });
      
      const piece = await page.locator('[data-piece="2f"]');
      expect(await piece.isVisible()).toBe(true);
    }
  }
];

// 実行...

第7章:実装上の主な工夫

1. スクリーンリーダーで「正しく」読み上げられるHTMLセマンティクス

<!-- ❌ ダメな例 -->
<div class="piece" onClick="selectPiece('2f')"></div>

<!-- ✅ 良い例 -->
<button 
  class="piece" 
  data-piece="2f"
  aria-label="先手の歩、5の3のマス"
  @click="selectPiece"
></button>

2. キーボード操作の状態管理

// ユーザーが一度キーボードを使ったら、
// 以降「フォーカスリング」を強調表示
let hasUsedKeyboard = false;

document.addEventListener('keydown', (e) => {
  if (!hasUsedKeyboard) {
    document.body.classList.add('keyboard-mode');
    hasUsedKeyboard = true;
  }
});

// CSS で強調表示
body.keyboard-mode *:focus {
  outline: 3px solid #4F46E5;
  outline-offset: 3px;
}

3. Puppeteerでの確実な待機パターン

// ❌ ダメ:ブラインド待機
await page.goto(url);
await page.waitForTimeout(500);  // CPU浪費

// ✅ 良い:要素が表示されるまで待機
await page.goto(url);
await page.waitForSelector('.game-board', { timeout: 5000 });

4. アクセシビリティの「段階的改善」

Phase 1: セマンティック HTML(追加作業なし)
  <button>, <a>, <nav> を正しく使う

Phase 2: ARIA 属性(追加ツール不要)
  aria-label, role, aria-live etc

Phase 3: キーボード操作(少しの実装で対応可能)
  @keydown.enter, @keydown.space

Phase 4: スクリーンリーダー完全対応(丁寧な実装が必要)
  ARIA live regions, landmarks

Phase 5: ペルソナテスト(時間はかかるが価値大)
  24種類のペルソナで実際に検証

↑ この順で進めるのが現実的

第8章:AI(GitHub Copilot等)との組み合わせ戦略

Puppeteer + AI のフィードバックループ

実装コード
  ↓
Puppeteerテスト実行(全790テスト)
- ARIA属性の検証
- キーボード操作の確認
- スクリーンリーダー互換性
- 視覚表示の確認
  ↓
初回実行 or 失敗検出時
  ↓
スクリーンショット + 詳細ログ収集
  ↓
AI分析(Vision API + ログ解析)
  ↓
改善提案生成(Copilot)
  ↓
コード修正
  ↓
再テスト → 合格 → 次の機能へ

具体的な改善例

例1:全盲ユーザーペルソナからの改善

【Puppeteer実行結果】
  全盲ユーザーペルソナ: AIの手番が音声で伝わらない

【詳細ログ分析】
  AI: 「aria-live領域が更新されていますが、テキストが空です」

【Copilotへのプロンプト】
  「AIが指した手を詳細に読み上げるコードを書いてください。
   例:『AIが5の4の歩を6の4に移動しました』」

【自動修正提案】
  announceAiMove(move) 関数を提案

【再テスト】
  全盲ペルソナテスト → 合格✅

例2:モバイルペルソナからの改善(第5章のバグ)

【Puppeteer実行結果】
  モバイル iPhone SE: 駒を取るたびに盤面が縮小する

【スクリーンショット + ログ分析】
  Turn 1: 盤面 320px ✅
  Turn 2: 盤面 307px ❌ 4%縮小
  Turn 5: 盤面 186px ❌ 42%縮小

【Copilotへのプロンプト】
  「駒台はフレックスアイテムですが、flex-shrinkの初期値により
   盤面が縮小しています。駒台を固定幅にしてください」

【自動修正提案】
  .komadai { flex: 0 0 auto; } を提案

【再テスト】
  全790テスト実行 → 合格✅

まとめ:「完全アクセシブル」とは

複数の背景を持つユーザー全てが使える状態

項目 詳細
実装ロジック PHP Unit テストで正確性を検証
ユーザー操作 Puppeteer E2E で複数デバイス・操作方式を検証
視覚障害対応 スクリーンリーダー、高コントラスト、拡大対応
運動障害対応 キーボード操作のみ、大タッチターゲット
発達特性対応 シンプルUI、刺激を抑えた情報設計
加齢対応 大きいテキスト、反応速度考慮
初心者対応 詳細なチュートリアル、段階的学習
多様性対応 24種類のペルソナベーステスト
継続改善 GitHub Copilot + Puppeteer ループ

段階的な改善アプローチ

アクセシビリティは一度に完璧を目指すのではなく、段階的に改善していくことが現実的です:

Phase 1: セマンティック HTML の徹底
  → 基礎的なアクセシビリティを確保

Phase 2: キーボード操作の実装
  → マウスに依存しない操作を実現

Phase 3: ARIA 属性の追加
  → 支援技術への情報提供を強化

Phase 4: スクリーンリーダー検証
  → 実際の支援技術での動作確認

Phase 5: ペルソナテストの導入
  → 多様な利用シーンを網羅的に検証
  → 手動では膨大な時間がかかるが、Puppeteer + AIで自動化可能

結論:
セマンティック HTML から始めることで、効率的に基盤を構築できる
AIペルソナを導入することで、継続的な検証が現実的になる

第9章:フィードバック機能の実装——実際の当事者の声を収集する

AIペルソナテストの限界

790個のテストと24種類のペルソナで99%のアクセシビリティを達成しましたが、重要な真実があります:

AIペルソナテストは「開発者が想定できる範囲」しかカバーできない

実際の障害を持つユーザーは、開発者が想像もしなかった使い方をします:

  • スクリーンリーダーの設定が異なる
  • 支援技術の組み合わせが予想外
  • 文化的背景による期待値の違い
  • 個人の認知スタイルの多様性

だからこそ、実際の当事者からのフィードバックが必須です。

フィードバック機能の設計方針

アクセシビリティを重視するなら、フィードバック機能自体もアクセシブルでなければなりません。

設計のポイント:

  1. プライバシー最優先 - DBに保存せず、メール送信のみで完結
  2. 3画面フロー - 入力→確認→完了で誤送信防止
  3. アクセシブルなフォーム - aria-label、キーボード操作、エラー通知
  4. レート制限 - スパム防止(5分間隔)
  5. AI推奨メッセージ - 「完璧ではない」ことを誠実に伝え、ユーザーを品質向上のパートナーとして位置づける

実例:当事者フィードバックから見つかった「AIペルソナでは検出しにくかった改善点」

リリース後、視覚障害者ユーザーより以下のフィードバックが報告されました:

「ランキング登録ダイアログで手数が『0手』と表示される」

改善が必要だった理由

AIペルソナテスト(24種類×790テスト)では、この改善点は 検出されていませんでした 。なぜか?

// 改善前のコード
function showRankingRegistrationDialog() {
    // ダイアログを表示
    rankingDialog.style.display = 'flex';
    
    // 手数を表示(改善点:ページロード時の初期値を使用)
    const rankingMovesEl = document.getElementById('ranking-moves');
    rankingMovesEl.textContent = `{{ $game->total_moves ?? 0 }}手`;  // ❌ 常に初期値
}

AIペルソナテストでは検出しにくかった理由:

  • Puppeteerの自動テストでは、ゲーム中から 連続的に ダイアログ表示をテストしない
  • 投了後の「ゲーム終了状態」を検証するテストと、その後の「ランキングダイアログ表示」テストが分かれていた
  • つまり、実装の「状態遷移」までは テストしていなかった

これは AIペルソナテストの 本質的な限界 です。

改善内容

// app/Http/Controllers/GameController.php - resign()メソッド
public function resign(GameSession $session): JsonResponse {
    $session->status = 'resigned';
    $session->winner = 'ai';
    $this->gameService->updateElapsedTime($session);
    
    return response()->json([
        'success' => true,
        'status' => $session->status,
        'winner' => $session->winner,
        'moveCount' => $session->total_moves,  // ✅ 最新の手数を返す
        'elapsedSeconds' => $session->elapsed_seconds,
    ]);
}

// resources/views/game/show.blade.php - ダイアログ表示時
function showRankingRegistrationDialog() {
    const gameData = window.gameData || {};
    
    // ✅ 常に最新の moveCount を反映
    const rankingMovesEl = document.getElementById('ranking-moves');
    if (rankingMovesEl && gameData.moveCount !== undefined) {
        rankingMovesEl.textContent = `${gameData.moveCount}手`;
    }
    
    rankingDialog.style.display = 'flex';
}

改善のポイント:

  1. API レスポンスに moveCount を含める
  2. ダイアログ表示時に 最新のゲーム状態を反映 する
  3. ページリロード時も、自動的にダイアログが表示される

この改善で学べることは?

「AIペルソナテストは『初期状態→最終状態』の検証には強いが、『状態遷移中の中間状態』は検証しにくい」

AIペルソナ + 実際の当事者 = 継続的改善

開発初期:AIペルソナテスト(790テスト)
    ↓
   99% の品質達成
    ↓
リリース:フィードバック機能で実際の当事者の声を収集
    ↓
改善点を反映
    ↓
AIペルソナテストを追加(新しいテストケース)
    ↓
   99.5% → 99.9% ... と継続的に改善

重要な教訓:

  • AIペルソナテストは「スタート地点」を高くする
  • 実際の当事者フィードバックは「ゴール」を更新し続ける
  • 両方の組み合わせで「本当のアクセシビリティ」を実現

次のアクセシビリティ開発者へ

  1. 「視覚障害対応」だけでは完全ではない

    • 複数層(視覚、運動、発達特性、加齢、初心者...)が存在
    • 一つの層の最適化が別の層を阻害することもある
    • 複数層の「共存」を実現しよう
  2. 「チャット指示」は入り口に過ぎない

    • 実装後の「複数パターン検証」が全てを決める
  3. 複数のテスト方法を組み合わせよ

    • Unit(ロジック)+ E2E(操作フロー)+ ペルソナテスト(体験)
  4. AIはテストツール

    • Puppeteer で「ユーザー行動」を自動化
    • AI(Copilot等)で「改善案」を提案
    • 人間が「最終判断」
  5. 「完全」を目指さず「継続的改善」を

    • 99% 達成後も、フィードバックで改善し続ける

リソース

最後に

このプロジェクトを通じて学んだ最も大切な教訓:

アクセシビリティは「視覚障害対応」ではなく、「複数の背景を持つ全員が使える」状態を目指すプロセス

Vanilla JS を選択理由、Puppeteer による複数パターン検証、AI との組み合わせ——これらを通じて、
個人開発者でも「本当のアクセシビリティ」を目指せる時代が来ました。

次のアクセシビリティ開発には、ぜひ複数層のペルソナテストの導入を検討してください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?