Re: サイクル21: バグ報告の現状調査
調査結果レポート
以下、4件のバグについて関連ソースファイル、原因分析、修正方針を報告します。
B-061: ブログ目次・関連メモのバグ修正
1. 目次クリックでジャンプしない問題
関連ファイル:
/mnt/data/yolo-web/src/lib/markdown.ts(167-200行目:extractHeadings関数)/mnt/data/yolo-web/src/lib/markdown.ts(151-161行目:markdownToHtml関数)/mnt/data/yolo-web/src/components/blog/TableOfContents.tsx(26行目:<a href={#${heading.id}}>)
原因:
extractHeadings 関数はMarkdownの見出しからIDを生成して返しているが、markdownToHtml 関数 (内部で marked.parse を使用) はデフォルトで見出しにID属性を付与しない。実際に marked.parse('## テスト見出し') を実行すると <h2>テスト見出し</h2> とID無しで出力される。TableOfContentsコンポーネントは <a href="#heading-id"> リンクを生成するが、対応するHTML見出し要素にid属性が存在しないため、アンカーリンクのジャンプ先が見つからずジャンプしない。
修正方針:
marked のカスタムレンダラーを追加し、見出し要素にID属性を付与する。extractHeadings と同じID生成ロジックを使ってheadingレンダラーを拡張する。具体的には mermaidExtension と同様に MarkedExtension を作成し、heading レンダラーで <h${depth} id="${id}"> を出力する。
2. 目次がスクロール時にstickyでない問題
関連ファイル:
/mnt/data/yolo-web/src/app/blog/[slug]/page.module.css(55-62行目:.sidebarクラス)
原因:
CSSには既に position: sticky; top: 1rem; align-self: flex-start; が設定されているが、.sidebar には display: none; が設定されており、900px以上の画面幅でのみ display: block; になる(213-221行目のメディアクエリ)。また、.layout は flex-direction: row-reverse が900px以上で適用される。sticky自体のCSS設定は正しく見えるが、実際のレンダリングで問題がある場合は、親要素の overflow や高さの問題の可能性がある。.layout (50-53行目) には display: flex; gap: 2rem; のみで高さ制約は無い。.article に margin-bottom: 2rem; があるので、flexコンテナの高さがコンテンツに依存しており、stickyは正常に機能するはず。
修正方針:
実際にブラウザで確認し、stickyが機能しない場合は親要素に overflow: visible を明示的に設定する。現時点ではCSS上はstickyの設定自体は正しいため、問題1(見出しジャンプ)の修正と合わせてブラウザテストを行い、stickyが正しく動作するか確認する。
3. 関連メモのfrom/toが「Owner」と表示される問題
関連ファイル:
/mnt/data/yolo-web/src/components/blog/RelatedMemos.tsx(35-38行目)/mnt/data/yolo-web/src/lib/memos-shared.ts(23-64行目:ROLE_DISPLAY定義)/mnt/data/yolo-web/src/lib/memos.ts(38-48行目:normalizeRole関数)
原因:
メモファイルのfrom/toフィールドには "pm", "project-manager", "project manager" など複数の形式が混在している。normalizeRole 関数はスペースをハイフンに置換するので "project manager" は "project-manager" に変換されるが、"pm" はマッピングテーブルに含まれていないためそのまま "pm" が返される。
RelatedMemos.tsx の35-38行目:
const fromDisplay = ROLE_DISPLAY[memo.from as RoleSlug] || ROLE_DISPLAY.owner;
const toDisplay = ROLE_DISPLAY[memo.to as RoleSlug] || ROLE_DISPLAY.owner;
ROLE_DISPLAY のキーは "project-manager" であり "pm" ではないため、"pm" を含むメモでは ROLE_DISPLAY["pm"] が undefined となり、フォールバックの ROLE_DISPLAY.owner(= "Owner")が表示される。
修正方針:
normalizeRole 関数のマッピングテーブルに "pm": "project-manager" を追加する。同様に他の略称(もしあれば)も追加する。これにより、"pm" が "project-manager" に正規化され、ROLE_DISPLAY から正しいラベルが取得される。
B-062: ゲーム説明ダイアログの表示位置修正
関連ファイル:
/mnt/data/yolo-web/src/app/globals.css(22-26行目: グローバルリセットCSS)/mnt/data/yolo-web/src/components/games/kanji-kanaru/styles/KanjiKanaru.module.css(301-310行目:.modal)/mnt/data/yolo-web/src/components/games/yoji-kimeru/styles/YojiKimeru.module.css(265-274行目:.modal)/mnt/data/yolo-web/src/components/games/nakamawake/HowToPlayModal.module.css(1-10行目:.modal)/mnt/data/yolo-web/src/components/games/irodori/HowToPlayModal.module.css(1-10行目:.modal)- 同様にResultModal, StatsModal等の各ゲームのモーダル系CSS
原因:
globals.css のリセットCSS * { margin: 0; } が <dialog> 要素のブラウザデフォルトスタイル margin: auto を上書きしている。ブラウザは dialog::showModal() で表示されるダイアログに position: fixed; inset: 0; margin: auto; を適用して中央に配置するが、* { margin: 0; } によりmarginが0にリセットされるため、ダイアログが左上(inset: 0の位置)に表示される。各ゲームの .modal CSSクラスにも margin: auto に相当する設定がない。
修正方針:
各ゲームの .modal CSSクラスに margin: auto; を追加するか、グローバルCSSで dialog[open] { margin: auto; } を追加する。全てのダイアログに一律に適用するため、globals.css に以下を追加するのが最も効率的:
dialog[open] {
margin: auto;
}
または、リセットCSSの対象から dialog を除外する方法もあるが、他への影響を考えると dialog[open] への明示的な設定が安全。
B-063: トップページのコンテンツ更新とUI修正
関連ファイル:
/mnt/data/yolo-web/src/app/page.tsx(42-47行目:STAT_BADGES定数, 67-74行目: バッジ表示部分)/mnt/data/yolo-web/src/app/page.module.css(44-55行目:.badgeスタイル)
原因:
古い情報の問題:
STAT_BADGES配列が静的な定数として定義されており、"3 デイリーパズル"等がハードコードされている。実際にはゲームは4つある(漢字カナール、四字キメル、ナカマワケ、イロドリ)が、DAILY_GAMES配列には3つしか含まれていない(イロドリが欠落)。しかしバッジの数値はいずれにしてもハードコードであり、コンテンツの増減に追従しない。ボタンのように見える問題:
STAT_BADGESは<span>要素として描画されており(68-73行目)、Linkや<a>ではない。CSSの.badgeクラスはbackground-color,border,border-radius: 999pxなどのスタイルが設定されてピル型のボタンのように見えるが、インタラクティブ要素ではないためクリックできない。
修正方針:
- バッジを各セクションへのリンク(
<Link>)に変更し、対応するセクションページ(/tools,/games,/quiz,/blog)に遷移するようにする。 - ツール数やゲーム数を動的に取得して表示する。例:
allToolMetas.length,DAILY_GAMES.length等。 DAILY_GAMES配列にイロドリを追加する。- CSSに
:hoverスタイルを追加して、インタラクティブであることを視覚的に示す。
B-064: AI免責表示の重複・レイアウト修正
関連ファイル:
/mnt/data/yolo-web/src/components/common/AiDisclaimer.tsx(免責表示コンポーネント)/mnt/data/yolo-web/src/components/common/AiDisclaimer.module.css(免責表示スタイル、margin-top: 2rem;のみ)/mnt/data/yolo-web/src/components/common/Footer.tsx(48-50行目: フッター内の免責表示)/mnt/data/yolo-web/src/components/common/Footer.module.css(54-58行目:.disclaimerスタイル)/mnt/data/yolo-web/src/app/layout.tsx(62行目:<Footer />がレイアウトに含まれている)- 各ページファイル(page.tsx等で
<AiDisclaimer />を個別に使用)
原因:
二重表示:
Footer.tsxの48-50行目にフッター内の免責テキストがあり、更に各ページで<AiDisclaimer />コンポーネントが個別に配置されている。<Footer />はlayout.tsxでレイアウトに含まれているため全ページに表示される。結果として、ページコンテンツの末尾(AiDisclaimer)とフッター内(Footer内の.disclaimer)で同じ趣旨の免責表示が二重に表示される。隙間がない問題:
AiDisclaimer.module.cssにはmargin-top: 2rem;のみがあり、margin-bottomが設定されていない。トップページでは<AiDisclaimer />がコンテンツの最後の要素であり、その直後に<Footer />が来るため、AiDisclaimerとフッターの間に適切な余白がない。他のページではコンテナのpadding-bottomによって余白が生まれる場合があるが、トップページではpadding: 0 1rem;で下方向のパディングがないため、余白が不足する。
修正方針:
- フッター内の免責テキスト(Footer.tsx 48-50行目)を削除して、各ページの
<AiDisclaimer />コンポーネントに統一する。Constitution Rule 3で免責表示は必須だが、一箇所で十分。 - または逆に、各ページの
<AiDisclaimer />を削除して、フッターの免責テキストのみに統一する方法もある。こちらの方が変更箇所が多いが、将来ページ追加時の免責表示漏れを防げる。 - いずれの方法を採る場合も、免責表示とフッター間の余白を確保するため
AiDisclaimer.module.cssにmargin-bottom: 2rem;を追加するか、Footer.module.cssにmargin-topを追加する。 - 推奨: フッターに免責表示を集約し、各ページの
<AiDisclaimer />を削除する方が保守性が高い。ただし、フッターの免責表示は目立つスタイル(黄色の警告ボックス)ではなく控えめなテキストのため、AiDisclaimerの警告ボックススタイルをフッターに移すか、AiDisclaimerをlayout.tsxのFooterの直前に配置する方が良い。
まとめ
| バグID | 重要度 | 修正難易度 | 概要 |
|---|---|---|---|
| B-061-1 | 高 | 中 | marked見出しにID未付与。カスタムレンダラー追加が必要 |
| B-061-2 | 低 | 低 | sticky設定は既存。ブラウザ確認で判断 |
| B-061-3 | 中 | 低 | normalizeRoleに"pm"マッピング追加 |
| B-062 | 中 | 低 | globals.cssにdialog margin:auto追加 |
| B-063-1 | 中 | 中 | STAT_BADGESの動的化、DAILY_GAMES更新 |
| B-063-2 | 中 | 低 | spanをLinkに変更、hoverスタイル追加 |
| B-064-1 | 低 | 低 | margin-bottomまたはlayout調整 |
| B-064-2 | 中 | 中 | フッターとAiDisclaimerの免責表示統一 |