B-138調査結果: ゲームLayout共通化と品質要素追加
調査結果レポート
1. ゲーム一覧と現状
現在4ゲームが存在する:
- 漢字カナール (
/games/kanji-kanaru): 毎日漢字を当てるWordle系パズル - 四字キメル (
/games/yoji-kimeru): 毎日四字熟語を当てるWordle系パズル - ナカマワケ (
/games/nakamawake): 16語を4グループに分ける仲間分けパズル - イロドリ (
/games/irodori): 毎日5色を作る色彩感覚チャレンジ
2. 各ゲームのpage.tsx構造比較
4ゲームのpage.tsxは構造的にほぼ同一。差異は以下のみ:
共通パターン(全ゲーム同一)
// page.tsx の構造
<div className={styles.wrapper}>
<script type="application/ld+json" ... /> // JSON-LD
<Breadcrumb items={[ホーム, ゲーム, {ゲーム名}]} />
<TrustLevelBadge level={gameMeta.trustLevel} note={gameMeta.trustNote} />
<GameContainer /> // 各ゲーム固有のコンポーネント
// ← footer/関連導線は一部のみ
</div>
page.module.css
全4ゲームともほぼ同一の CSS:
.wrapper { max-width: 600px; margin: 0 auto; padding: 1rem 0.5rem; width: 100%; }
漢字カナールのみ .attribution クラスも定義(帰属表示用)。
ゲームごとの差異
- 漢字カナール:
<footer className={styles.attribution}>でKANJIDIC2クレジットとリンクを個別実装 - 四字キメル:
<p style={{...}}>インラインスタイルで四字熟語辞典リンクを個別実装 - ナカマワケ: footer/関連導線なし
- イロドリ: footer/関連導線なし
3. B-100で作成された品質要件の仕組み
作成済みドキュメント
docs/content-quality-requirements.md が存在し、4つの品質要素を定義:
- valueProposition - 40字以内の一行価値テキスト
- usageExample - 入力→出力の具体例 (
{ input, output, description? }) - FAQ - Q&A形式 2〜5問 (
Array<{ question, answer }>) - 関連導線 - 既存フィールドで対応済み
ゲームは「今サイクル対象外」として明記されており、GameLayout共通化が先決とされている。
FaqSection共通コンポーネント
src/components/common/FaqSection.tsx が実装済み:
faq: FaqEntry[] | undefinedを受け取るシンプルなprops設計- details/summaryタグによるアコーディオン形式
- CSS Modulesで管理 (
FaqSection.module.css)
ToolMeta / CheatsheetMeta への3フィールド追加
両型とも以下3フィールドがoptionalとして追加済み:
valueProposition?: string;
usageExample?: { input: string; output: string; description?: string };
faq?: Array<{ question: string; answer: string }>;
ToolLayout / CheatsheetLayout の更新
両Layoutとも品質要素の表示が実装済み:
valueProposition: headerセクション内に条件付き表示usageExample: コンテンツの前/後に条件付き表示FaqSection: FaqSection共通コンポーネントを使用
4. 既存のLayout共通化パターン
ToolLayout (src/tools/_components/ToolLayout.tsx)
<article>
<Breadcrumb />
<header>
<h1>{meta.name}</h1>
<TrustLevelBadge />
<p>{meta.description}</p>
{meta.valueProposition && <p>...</p>}
</header>
{meta.usageExample && <div>...</div>}
<section>{children}</section>
<p privacyNote />
<FaqSection faq={meta.faq} />
<section shareSection>
<ShareButtons />
</section>
<RelatedTools />
<RelatedBlogPosts />
</article>
CheatsheetLayout (src/cheatsheets/_components/CheatsheetLayout.tsx)
同様の構成。children後にusageExampleが来る点とTableOfContentsが前に来る点が異なる。
ツールとチートシートの共通化パターン
metaオブジェクトをpropsで受け取るchildrenでコンテンツを受け取る- CSS Modulesでスタイル管理
- 品質要素は条件付き表示(フィールドが存在する場合のみ)
5. GameMeta型の現状と必要な変更
現在の GameMeta (src/games/types.ts)
export interface GameMeta {
slug: string;
title: string;
shortDescription: string; // ~30chars
description: string; // ~60chars
icon: string;
accentColor: string;
difficulty: string;
keywords: string[];
statsKey: string;
ogpSubtitle: string;
sitemap: { changeFrequency: ...; priority: number };
trustLevel: TrustLevel;
trustNote?: string;
// ← valueProposition, usageExample, faq は未追加
}
追加が必要なフィールド
ToolMeta/CheatsheetMetaと同じパターンでoptionalとして追加する:
valueProposition?: string;
usageExample?: { input: string; output: string; description?: string };
faq?: Array<{ question: string; answer: string }>;
relatedGameSlugs?: string[]; // 関連ゲームへの導線
関連導線について
- ツールは
relatedSlugs: string[](必須フィールド) - チートシートは
relatedToolSlugs: string[]+relatedCheatsheetSlugs: string[](必須フィールド) - ゲームには関連導線フィールドが現状なし
- ResultModalのNextGameBannerで他ゲームへの導線が提供されているが、これはゲーム完了後のモーダル内のみ
- GameLayout作成時に関連ゲームや関連ブログ記事への導線を追加することが望ましい
6. 関連ブログ記事の現状
src/blog/content/ に以下のゲーム関連記事が存在:
2026-02-14-japanese-word-puzzle-games-guide.md2026-02-22-game-infrastructure-refactoring.md
ブログのfrontmatterには related_tool_slugs フィールドがあり、ゲームスラグ(kanji-kanaru等)もこのフィールドに入っている。cross-links.ts の getRelatedBlogPostsForTool(slug) 関数はtool slugで絞り込むが、ゲームslugもtool_slugsに混在しているため、既存の仕組みをそのまま流用できる。
7. 共通化方針の提案
作成すべきコンポーネント: GameLayout
場所: src/games/_components/GameLayout.tsx (新規作成)
<article className={styles.layout}>
<Breadcrumb items={[ホーム, ゲーム, {meta.title}]} />
<header>
<TrustLevelBadge level={meta.trustLevel} note={meta.trustNote} />
{meta.valueProposition && <p>...</p>}
</header>
{meta.usageExample && <div>...</div>} // ゲーム概要として表示
{children} // GameContainer(ゲーム本体)
{attribution} // ゲーム固有の帰属表示(props or slot)
<FaqSection faq={meta.faq} />
<section shareSection>
<ShareButtons url={`/games/${meta.slug}`} title={meta.title} />
</section>
<RelatedGames currentSlug={meta.slug} relatedSlugs={meta.relatedGameSlugs} />
<RelatedBlogPosts gameSlug={meta.slug} /> // cross-links.tsに関数追加が必要
</article>
各page.tsxの変更
現在の各page.tsxから Breadcrumb, TrustLevelBadge, <div className={styles.wrapper}> 等を削除し、GameLayoutに置き換える。
新規コンポーネントの必要性
RelatedGamesコンポーネント (新規作成) - RelatedToolsと同様のパターンcross-links.tsにgetRelatedBlogPostsForGame(gameSlug)を追加GameLayout.module.css(新規作成)
既存の attribution 対応
漢字カナールと四字キメルには個別の帰属/関連導線テキストがある。以下の対応が考えられる:
- GameLayout の
attributionprop (ReactNode) で受け取る - または各game/meta.tsに
attributionフィールドを追加する
8. 実装の優先度
GameMeta型に3フィールド追加(valueProposition, usageExample, faq)GameLayoutコンポーネント作成RelatedGamesコンポーネント作成getRelatedBlogPostsForGame関数をcross-links.tsに追加- 各ゲームpage.tsxを
GameLayout使用に置き換え - 各ゲームのmeta.ts (registry.ts) に品質要素フィールドを追加(4ゲーム分)
9. 注意事項・考慮点
- usageExampleのゲーム向け表示: ゲームの場合「使い方の例」よりも「こんなゲームです」「遊び方の概要」が適切。CheatsheetLayoutの「こんなときに使えます」に近い表現にする
- ゲームのmax-width: 全ゲーム共通で
max-width: 600pxを使用。GameLayoutでもこれを維持する - JSON-LDの扱い: 現在各page.tsxで
generateGameJsonLdを呼び出しているが、GameLayoutには含めない(page.tsxがServer Componentとして維持するため) - metadata: Next.jsのmetadata export は各page.tsxで維持する