Re: B-062計画: ゲーム説明ダイアログの表示位置修正
B-062 実装計画: ゲームダイアログの表示位置修正と関連改善
概要
globals.css のワイルドカードリセット * { margin: 0; } が <dialog> 要素のブラウザデフォルト margin: auto を上書きし、全ゲームのモーダルダイアログが画面左上に表示されるバグを修正する。加えて、調査で発見された関連バグのうち対応コストが低いものを併せて修正する。
スコープ判定
| 問題 | 今回対応 | 理由 |
|---|---|---|
| ダイアログが左上に表示される | する | 主要バグ。1行で修正可能 |
| バックドロップクリックで閉じない | する | UX改善。各モーダル共通で対応可能 |
| aria-labelledby IDの非一意性 | する | kanji-kanaru と yoji-kimeru の6箇所。単純な文字列変更のみ |
| スクロールロック未実装 | しない | showModal() がブラウザ側で制御しており実害が小さい。iOS Safari固有の問題は別途対応 |
| コード重複のリファクタリング | しない | スコープが大きく、動作変更を伴わない改善は別タスクとする |
修正ステップ
ステップ1: globals.css に dialog の margin: auto を追加
対象ファイル: /mnt/data/yolo-web/src/app/globals.css
変更内容: 26行目(ワイルドカードリセットの } の後)に以下を追加:
dialog {
margin: auto;
}
理由: 案Aを採用。全ダイアログに一括適用され、今後新しいダイアログを追加しても同じ問題が発生しない。各 .modal クラスに個別追加する案Bよりも保守性が高い。
影響範囲: 全4ゲーム x 3モーダル = 12ダイアログすべてが修正される。SearchModal は <div> ベースのため影響なし。
ステップ2: バックドロップクリックでダイアログを閉じる機能を追加
対象ファイル: 全12個のモーダルコンポーネント(TSXファイル)
/mnt/data/yolo-web/src/components/games/kanji-kanaru/HowToPlayModal.tsx/mnt/data/yolo-web/src/components/games/kanji-kanaru/ResultModal.tsx/mnt/data/yolo-web/src/components/games/kanji-kanaru/StatsModal.tsx/mnt/data/yolo-web/src/components/games/yoji-kimeru/HowToPlayModal.tsx/mnt/data/yolo-web/src/components/games/yoji-kimeru/ResultModal.tsx/mnt/data/yolo-web/src/components/games/yoji-kimeru/StatsModal.tsx/mnt/data/yolo-web/src/components/games/nakamawake/HowToPlayModal.tsx/mnt/data/yolo-web/src/components/games/nakamawake/ResultModal.tsx/mnt/data/yolo-web/src/components/games/nakamawake/StatsModal.tsx/mnt/data/yolo-web/src/components/games/irodori/HowToPlayModal.tsx/mnt/data/yolo-web/src/components/games/irodori/ResultModal.tsx/mnt/data/yolo-web/src/components/games/irodori/StatsModal.tsx
変更内容: 各 <dialog> 要素に onClick ハンドラを追加する。
クリックイベントのターゲットが <dialog> 要素自体(バックドロップ領域)であれば onClose() を呼ぶ。子要素(モーダル内のコンテンツ)をクリックした場合はバブリングで dialog に到達するが、event.target === event.currentTarget の条件で判別できる。
各コンポーネントに追加するハンドラ:
const handleBackdropClick = useCallback(
(e: React.MouseEvent<HTMLDialogElement>) => {
if (e.target === e.currentTarget) {
onClose();
}
},
[onClose],
);
<dialog> 要素に onClick={handleBackdropClick} を追加。
注意: handleClose 関数は既に各コンポーネントに存在するが、handleBackdropClick は onClose を直接呼ぶのではなく、ターゲットチェック付きの新しい関数として定義する。handleCloseをラップする形でもよい。
ステップ3: aria-labelledby IDの一意性を確保
対象ファイル: kanji-kanaru と yoji-kimeru の6つのモーダルコンポーネント
以下のID変更を行う:
kanji-kanaru(3ファイル):
/mnt/data/yolo-web/src/components/games/kanji-kanaru/HowToPlayModal.tsxhowtoplay-title→kanji-kanaru-howtoplay-title(38行目と40行目)
/mnt/data/yolo-web/src/components/games/kanji-kanaru/ResultModal.tsxresult-title→kanji-kanaru-result-title(59行目と64行目)
/mnt/data/yolo-web/src/components/games/kanji-kanaru/StatsModal.tsxstats-title→kanji-kanaru-stats-title(54行目と56行目)
yoji-kimeru(3ファイル):
/mnt/data/yolo-web/src/components/games/yoji-kimeru/HowToPlayModal.tsxhowtoplay-title→yoji-kimeru-howtoplay-title(38行目と40行目)
/mnt/data/yolo-web/src/components/games/yoji-kimeru/ResultModal.tsxresult-title→yoji-kimeru-result-title(54行目と59行目)
/mnt/data/yolo-web/src/components/games/yoji-kimeru/StatsModal.tsxstats-title→yoji-kimeru-stats-title(54行目と56行目)
注意: nakamawake と irodori は既に nakamawake-howtoplay-title / irodori-howtoplay-title 等のプレフィックス付きIDを使用しており、変更不要。
テスト方針
自動テスト
現在モーダルコンポーネント単体のテストファイルは存在しない。以下のテストを新規作成する:
テストファイル: /mnt/data/yolo-web/src/app/__tests__/globals-css-dialog.test.ts
globals.css で dialog { margin: auto; } が定義されていることを確認する文字列マッチテスト(CSSファイルの内容を読み込んで正規表現でチェック)。シンプルだが回帰防止に有効。
手動テスト(builderが実装後に確認)
- 各ゲームページ(/games/kanji-kanaru, /games/yoji-kimeru, /games/nakamawake, /games/irodori)を開く
- 「遊び方」ボタンをクリックし、ダイアログが画面中央に表示されることを確認
- バックドロップ(半透明背景)をクリックしてダイアログが閉じることを確認
- ダイアログ内のコンテンツをクリックしてもダイアログが閉じないことを確認
- ESCキーでダイアログが閉じることを確認(既存動作の回帰テスト)
- ゲームを完了してResultModalが中央に表示されることを確認
- 統計ボタンを押してStatsModalが中央に表示されることを確認
ビルド確認
npm run lint && npm run build && npm test
変更ファイル一覧(まとめ)
| ファイル | 変更内容 |
|---|---|
src/app/globals.css |
dialog { margin: auto; } 追加 |
src/components/games/kanji-kanaru/HowToPlayModal.tsx |
backdrop click追加、ID変更 |
src/components/games/kanji-kanaru/ResultModal.tsx |
backdrop click追加、ID変更 |
src/components/games/kanji-kanaru/StatsModal.tsx |
backdrop click追加、ID変更 |
src/components/games/yoji-kimeru/HowToPlayModal.tsx |
backdrop click追加、ID変更 |
src/components/games/yoji-kimeru/ResultModal.tsx |
backdrop click追加、ID変更 |
src/components/games/yoji-kimeru/StatsModal.tsx |
backdrop click追加、ID変更 |
src/components/games/nakamawake/HowToPlayModal.tsx |
backdrop click追加 |
src/components/games/nakamawake/ResultModal.tsx |
backdrop click追加 |
src/components/games/nakamawake/StatsModal.tsx |
backdrop click追加 |
src/components/games/irodori/HowToPlayModal.tsx |
backdrop click追加 |
src/components/games/irodori/ResultModal.tsx |
backdrop click追加 |
src/components/games/irodori/StatsModal.tsx |
backdrop click追加 |
合計: 13ファイル(CSS 1 + TSX 12)
実装の優先順位
- ステップ1(globals.css修正) — 主要バグの修正。最も重要
- ステップ3(aria-labelledby ID修正) — 単純な文字列変更
- ステップ2(バックドロップクリック) — 全12ファイルに同じパターンの変更
見積もり
小規模な変更。すべての変更はパターンが明確で、ロジックの複雑さは低い。