# Cronicas de los Reinos - Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Build a digital book-style website that preserves MMORPG lore as narrative chapters, with a bookshelf UI and e-reader experience. **Architecture:** Next.js App Router with static generation. Content stored as MDX files per game/chapter. Three page types: bookshelf (home), book cover (game index), chapter reader. All pages statically generated at build time. **Tech Stack:** Next.js 14+, TypeScript, Tailwind CSS, MDX (via next-mdx-remote or @next/mdx), Google Fonts (Playfair Display for headings, Lora for body text). --- ### Task 1: Project Scaffolding **Files:** - Create: `package.json`, `tsconfig.json`, `tailwind.config.ts`, `next.config.ts`, `src/app/layout.tsx`, `src/app/page.tsx`, `src/app/globals.css` **Step 1: Initialize Next.js project** Run: ```bash npx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm ``` Expected: Project scaffolded with all config files. **Step 2: Install MDX dependencies** Run: ```bash npm install next-mdx-remote gray-matter ``` Expected: Dependencies added to package.json. **Step 3: Add Google Fonts to layout** Modify `src/app/layout.tsx`: ```tsx import type { Metadata } from "next"; import { Playfair_Display, Lora } from "next/font/google"; import "./globals.css"; const playfair = Playfair_Display({ subsets: ["latin"], variable: "--font-playfair", }); const lora = Lora({ subsets: ["latin"], variable: "--font-lora", }); export const metadata: Metadata = { title: "Cronicas de los Reinos", description: "Las historias completas de los MMORPGs que marcaron una era", }; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( {children} ); } ``` **Step 4: Configure Tailwind with custom fonts** Modify `tailwind.config.ts`: ```ts import type { Config } from "tailwindcss"; const config: Config = { content: [ "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", "./src/app/**/*.{js,ts,jsx,tsx,mdx}", "./src/components/**/*.{js,ts,jsx,tsx,mdx}", ], theme: { extend: { fontFamily: { playfair: ["var(--font-playfair)", "serif"], lora: ["var(--font-lora)", "serif"], }, colors: { parchment: "#f5f0e8", "parchment-dark": "#e8dfd3", "wood-dark": "#2c1810", "wood-medium": "#4a2c1a", "wood-light": "#6b3d2e", }, }, }, plugins: [], }; export default config; ``` **Step 5: Set base global styles** Replace `src/app/globals.css`: ```css @tailwind base; @tailwind components; @tailwind utilities; @layer base { ::selection { background-color: rgba(180, 140, 80, 0.3); } } ``` **Step 6: Verify dev server starts** Run: `npm run dev` Expected: Server starts at localhost:3000, shows default page. **Step 7: Commit** ```bash git add -A git commit -m "feat: scaffold Next.js project with Tailwind, MDX deps, and custom fonts" ``` --- ### Task 2: Content System (MDX + meta.json) **Files:** - Create: `content/wow/meta.json`, `content/ragnarok/meta.json`, `content/maplestory/meta.json`, `content/tales-of-pirates/meta.json` - Create: `src/lib/content.ts` - Create: `src/types/index.ts` **Step 1: Create type definitions** Create `src/types/index.ts`: ```ts export interface GameMeta { slug: string; title: string; subtitle: string; description: string; color: string; colorDark: string; accentColor: string; coverImage?: string; } export interface Chapter { slug: string; number: number; title: string; content: string; } export interface GameWithChapters extends GameMeta { chapters: Omit[]; } ``` **Step 2: Create meta.json files for each game** Create `content/wow/meta.json`: ```json { "title": "World of Warcraft", "subtitle": "Cronicas de Azeroth", "description": "Desde la creacion del universo por los Titanes hasta las guerras que definieron el destino de Azeroth, esta es la historia epica del mundo mas grande jamas creado en un MMORPG.", "color": "#1a3a5c", "colorDark": "#0f2440", "accentColor": "#d4a843" } ``` Create `content/ragnarok/meta.json`: ```json { "title": "Ragnarok Online", "subtitle": "La Saga de Midgard", "description": "En un mundo donde dioses y mortales coexisten, la tierra de Midgard se enfrenta a amenazas que podrian desatar el Ragnarok, el fin de todas las cosas.", "color": "#8b2500", "colorDark": "#5c1a00", "accentColor": "#ff8c42" } ``` Create `content/maplestory/meta.json`: ```json { "title": "MapleStory", "subtitle": "El Mundo del Arce", "description": "Bajo la sombra del Arbol del Mundo, heroes de todas las eras se alzan contra la oscuridad del Mago Negro en un mundo vibrante lleno de aventura y misterio.", "color": "#2d5a1e", "colorDark": "#1a3a12", "accentColor": "#ff9b2e" } ``` Create `content/tales-of-pirates/meta.json`: ```json { "title": "Tales of Pirates", "subtitle": "Leyendas del Mar", "description": "En un mundo de islas infinitas y mares inexplorados, piratas, aventureros y cazadores de tesoros escriben su legenda en las aguas del destino.", "color": "#1a3050", "colorDark": "#0f1e35", "accentColor": "#d4a843" } ``` **Step 3: Create content library** Create `src/lib/content.ts`: ```ts import fs from "fs"; import path from "path"; import matter from "gray-matter"; import { GameMeta, Chapter, GameWithChapters } from "@/types"; const contentDir = path.join(process.cwd(), "content"); export function getAllGames(): GameMeta[] { const gameDirs = fs.readdirSync(contentDir).filter((dir) => { const fullPath = path.join(contentDir, dir); return fs.statSync(fullPath).isDirectory(); }); return gameDirs.map((slug) => { const metaPath = path.join(contentDir, slug, "meta.json"); const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8")); return { slug, ...meta }; }); } export function getGame(slug: string): GameWithChapters { const metaPath = path.join(contentDir, slug, "meta.json"); const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8")); const gameDir = path.join(contentDir, slug); const chapterFiles = fs .readdirSync(gameDir) .filter((f) => f.endsWith(".mdx")) .sort(); const chapters = chapterFiles.map((file, index) => { const filePath = path.join(gameDir, file); const raw = fs.readFileSync(filePath, "utf-8"); const { data } = matter(raw); const chapterSlug = file.replace(".mdx", ""); return { slug: chapterSlug, number: index + 1, title: (data.title as string) || chapterSlug, }; }); return { slug, ...meta, chapters }; } export function getChapter(gameSlug: string, chapterSlug: string): Chapter { const gameDir = path.join(contentDir, gameSlug); const chapterFiles = fs .readdirSync(gameDir) .filter((f) => f.endsWith(".mdx")) .sort(); const chapterIndex = chapterFiles.findIndex( (f) => f.replace(".mdx", "") === chapterSlug ); const filePath = path.join(gameDir, `${chapterSlug}.mdx`); const raw = fs.readFileSync(filePath, "utf-8"); const { data, content } = matter(raw); return { slug: chapterSlug, number: chapterIndex + 1, title: (data.title as string) || chapterSlug, content, }; } export function getAdjacentChapters( gameSlug: string, chapterSlug: string ): { prev: string | null; next: string | null } { const gameDir = path.join(contentDir, gameSlug); const chapterFiles = fs .readdirSync(gameDir) .filter((f) => f.endsWith(".mdx")) .sort(); const currentIndex = chapterFiles.findIndex( (f) => f.replace(".mdx", "") === chapterSlug ); return { prev: currentIndex > 0 ? chapterFiles[currentIndex - 1].replace(".mdx", "") : null, next: currentIndex < chapterFiles.length - 1 ? chapterFiles[currentIndex + 1].replace(".mdx", "") : null, }; } ``` **Step 4: Verify types compile** Run: `npx tsc --noEmit` Expected: No type errors. **Step 5: Commit** ```bash git add -A git commit -m "feat: add content system with MDX support, types, and meta files for 4 games" ``` --- ### Task 3: Bookshelf Page (Home) **Files:** - Create: `src/components/BookShelf.tsx` - Create: `src/components/BookSpine.tsx` - Modify: `src/app/page.tsx` **Step 1: Create BookSpine component** Create `src/components/BookSpine.tsx`: ```tsx import Link from "next/link"; import { GameMeta } from "@/types"; export default function BookSpine({ game }: { game: GameMeta }) { return (
{/* Book spine edge */}
{/* Book cover content */}

{game.title}

{game.subtitle}

{/* Shine effect on hover */}
); } ``` **Step 2: Create BookShelf component** Create `src/components/BookShelf.tsx`: ```tsx import { GameMeta } from "@/types"; import BookSpine from "./BookSpine"; export default function BookShelf({ games }: { games: GameMeta[] }) { return (
{/* Title */}

Cronicas de los Reinos

Las historias epicas de los mundos que nos unieron

{/* Shelf */}
{/* Books row */}
{games.map((game) => ( ))}
{/* Shelf surface */}
{/* Footer */}

Selecciona un tomo para comenzar a leer

); } ``` **Step 3: Wire up the home page** Replace `src/app/page.tsx`: ```tsx import { getAllGames } from "@/lib/content"; import BookShelf from "@/components/BookShelf"; export default function Home() { const games = getAllGames(); return ; } ``` **Step 4: Verify it renders** Run: `npm run dev` Navigate to http://localhost:3000 Expected: See the bookshelf with 4 book spines, hovering tilts them. **Step 5: Commit** ```bash git add -A git commit -m "feat: add bookshelf home page with animated book spine components" ``` --- ### Task 4: Game Cover Page (Book Index) **Files:** - Create: `src/app/[game]/page.tsx` - Create: `src/components/BookCover.tsx` **Step 1: Create BookCover component** Create `src/components/BookCover.tsx`: ```tsx import Link from "next/link"; import { GameWithChapters } from "@/types"; export default function BookCover({ game }: { game: GameWithChapters }) { return (
{/* Back to shelf */}
Volver a la estanteria
{/* Cover */}
{/* Decorative top */}

{game.title}

{game.subtitle}

{/* Decorative divider */}

{game.description}

{/* Table of Contents */}

Indice

{game.chapters.map((chapter) => ( {String(chapter.number).padStart(2, "0")} {chapter.title} ))}
{/* Decorative bottom */}
); } ``` **Step 2: Create the game page route** Create `src/app/[game]/page.tsx`: ```tsx import { notFound } from "next/navigation"; import { getAllGames, getGame } from "@/lib/content"; import BookCover from "@/components/BookCover"; interface PageProps { params: Promise<{ game: string }>; } export async function generateStaticParams() { const games = getAllGames(); return games.map((game) => ({ game: game.slug })); } export async function generateMetadata({ params }: PageProps) { const { game: gameSlug } = await params; try { const game = getGame(gameSlug); return { title: `${game.title} | Cronicas de los Reinos`, description: game.description, }; } catch { return { title: "No encontrado" }; } } export default async function GamePage({ params }: PageProps) { const { game: gameSlug } = await params; try { const game = getGame(gameSlug); return ; } catch { notFound(); } } ``` **Step 3: Verify route works** Run: `npm run dev` Navigate to http://localhost:3000/wow Expected: See WoW book cover with title, description, and empty chapter index (no MDX files yet). **Step 4: Commit** ```bash git add -A git commit -m "feat: add game cover page with table of contents and dynamic routing" ``` --- ### Task 5: Chapter Reader Page **Files:** - Create: `src/app/[game]/[chapter]/page.tsx` - Create: `src/components/ChapterReader.tsx` - Create: `src/components/ReadingProgress.tsx` **Step 1: Create ReadingProgress component** Create `src/components/ReadingProgress.tsx`: ```tsx "use client"; import { useEffect, useState } from "react"; export default function ReadingProgress({ color }: { color: string }) { const [progress, setProgress] = useState(0); useEffect(() => { function handleScroll() { const scrollTop = window.scrollY; const docHeight = document.documentElement.scrollHeight - window.innerHeight; setProgress(docHeight > 0 ? (scrollTop / docHeight) * 100 : 0); } window.addEventListener("scroll", handleScroll, { passive: true }); return () => window.removeEventListener("scroll", handleScroll); }, []); return (
); } ``` **Step 2: Create ChapterReader component** Create `src/components/ChapterReader.tsx`: ```tsx import Link from "next/link"; import { MDXRemote } from "next-mdx-remote/rsc"; import { GameMeta, Chapter } from "@/types"; import ReadingProgress from "./ReadingProgress"; interface Props { game: GameMeta; chapter: Chapter; totalChapters: number; prevChapter: string | null; nextChapter: string | null; } export default function ChapterReader({ game, chapter, totalChapters, prevChapter, nextChapter, }: Props) { return (
{/* Header */}
{game.title}
{/* Chapter heading */}

Capitulo {chapter.number} de {totalChapters}

{chapter.title}

{/* Chapter content */}
{/* Chapter navigation */}
); } ``` **Step 3: Create the chapter page route** Create `src/app/[game]/[chapter]/page.tsx`: ```tsx import { notFound } from "next/navigation"; import { getAllGames, getGame, getChapter, getAdjacentChapters } from "@/lib/content"; import ChapterReader from "@/components/ChapterReader"; interface PageProps { params: Promise<{ game: string; chapter: string }>; } export async function generateStaticParams() { const games = getAllGames(); const paths: { game: string; chapter: string }[] = []; for (const gameMeta of games) { const game = getGame(gameMeta.slug); for (const chapter of game.chapters) { paths.push({ game: gameMeta.slug, chapter: chapter.slug }); } } return paths; } export async function generateMetadata({ params }: PageProps) { const { game: gameSlug, chapter: chapterSlug } = await params; try { const game = getGame(gameSlug); const chapter = getChapter(gameSlug, chapterSlug); return { title: `${chapter.title} - ${game.title} | Cronicas de los Reinos`, description: `Capitulo ${chapter.number} de ${game.title}`, }; } catch { return { title: "No encontrado" }; } } export default async function ChapterPage({ params }: PageProps) { const { game: gameSlug, chapter: chapterSlug } = await params; try { const game = getGame(gameSlug); const chapter = getChapter(gameSlug, chapterSlug); const { prev, next } = getAdjacentChapters(gameSlug, chapterSlug); return ( ); } catch { notFound(); } } ``` **Step 4: Commit** ```bash git add -A git commit -m "feat: add chapter reader with reading progress bar and navigation" ``` --- ### Task 6: Content - World of Warcraft (3 chapters) **Files:** - Create: `content/wow/01-origenes.mdx` - Create: `content/wow/02-primera-guerra.mdx` - Create: `content/wow/03-arthas-y-la-plaga.mdx` **Step 1: Write WoW Chapter 1 - Origenes** Create `content/wow/01-origenes.mdx` with frontmatter `title: "Los Origenes de Azeroth"` followed by ~600-800 words of narrative lore covering: the Titans discovering Azeroth, the ordering of the world, the Old Gods imprisoned beneath the surface, the creation of the Dragon Aspects, and the Well of Eternity. **Step 2: Write WoW Chapter 2 - Primera Guerra** Create `content/wow/02-primera-guerra.mdx` with frontmatter `title: "La Primera Guerra"` followed by ~600-800 words covering: the Orcs on Draenor, Medivh's corruption, the opening of the Dark Portal, the Orcish Horde invasion of Azeroth, the fall of Stormwind. **Step 3: Write WoW Chapter 3 - Arthas y la Plaga** Create `content/wow/03-arthas-y-la-plaga.mdx` with frontmatter `title: "Arthas y la Plaga"` followed by ~600-800 words covering: Prince Arthas Menethil, the plague of undeath at Stratholme, the journey to Northrend, Frostmourne, Arthas becoming the Lich King. **Step 4: Verify chapters render** Run: `npm run dev` Navigate to http://localhost:3000/wow then click Chapter 1. Expected: Full narrative text renders in e-reader style. **Step 5: Commit** ```bash git add -A git commit -m "feat: add World of Warcraft lore chapters (origins, first war, arthas)" ``` --- ### Task 7: Content - Ragnarok Online (3 chapters) **Files:** - Create: `content/ragnarok/01-creacion-de-midgard.mdx` - Create: `content/ragnarok/02-guerra-de-los-dioses.mdx` - Create: `content/ragnarok/03-heroes-de-rune-midgard.mdx` **Step 1: Write Ragnarok Chapter 1 - Creacion de Midgard** Create `content/ragnarok/01-creacion-de-midgard.mdx` with frontmatter `title: "La Creacion de Midgard"` followed by ~600-800 words covering: Odin and the gods, the creation of the world of Midgard, the Norse mythology roots, the Yggdrasil tree, the races of the world. **Step 2: Write Ragnarok Chapter 2 - Guerra de los Dioses** Create `content/ragnarok/02-guerra-de-los-dioses.mdx` with frontmatter `title: "La Guerra de los Dioses"` followed by ~600-800 words covering: the conflict between gods, the thousand-year war, Satan Morocc, the seal of the demons, the aftermath. **Step 3: Write Ragnarok Chapter 3 - Heroes de Rune-Midgard** Create `content/ragnarok/03-heroes-de-rune-midgard.mdx` with frontmatter `title: "Los Heroes de Rune-Midgard"` followed by ~600-800 words covering: the adventurers' guilds, the classes, the quest to prevent Ragnarok, the monsters and dungeons. **Step 4: Commit** ```bash git add -A git commit -m "feat: add Ragnarok Online lore chapters (midgard, gods war, heroes)" ``` --- ### Task 8: Content - MapleStory (3 chapters) **Files:** - Create: `content/maplestory/01-mundo-del-arce.mdx` - Create: `content/maplestory/02-heroes-legendarios.mdx` - Create: `content/maplestory/03-el-mago-negro.mdx` **Step 1: Write MapleStory Chapter 1 - Mundo del Arce** Create `content/maplestory/01-mundo-del-arce.mdx` with frontmatter `title: "El Mundo del Arce"` followed by ~600-800 words covering: Maple World geography (Victoria Island, Ossyria, etc.), the Maple Tree, the peaceful beginnings, the different peoples. **Step 2: Write MapleStory Chapter 2 - Heroes Legendarios** Create `content/maplestory/02-heroes-legendarios.mdx` with frontmatter `title: "Los Heroes Legendarios"` followed by ~600-800 words covering: the six heroes (Aran, Mercedes, Phantom, Luminous, Evan, Shade), their battle against the Black Mage, their sacrifice and freezing in time. **Step 3: Write MapleStory Chapter 3 - El Mago Negro** Create `content/maplestory/03-el-mago-negro.mdx` with frontmatter `title: "El Mago Negro"` followed by ~600-800 words covering: the origin of the Black Mage, his quest for ultimate power, the corruption, the Commanders of the Black Mage, the return. **Step 4: Commit** ```bash git add -A git commit -m "feat: add MapleStory lore chapters (maple world, heroes, black mage)" ``` --- ### Task 9: Content - Tales of Pirates (3 chapters) **Files:** - Create: `content/tales-of-pirates/01-el-mar-infinito.mdx` - Create: `content/tales-of-pirates/02-islas-del-tesoro.mdx` - Create: `content/tales-of-pirates/03-era-de-los-piratas.mdx` **Step 1: Write Tales of Pirates Chapter 1 - El Mar Infinito** Create `content/tales-of-pirates/01-el-mar-infinito.mdx` with frontmatter `title: "El Mar Infinito"` followed by ~600-800 words covering: the vast ocean world, the mysterious origins, the first sailors, the discovery of scattered islands. **Step 2: Write Tales of Pirates Chapter 2 - Islas del Tesoro** Create `content/tales-of-pirates/02-islas-del-tesoro.mdx` with frontmatter `title: "Las Islas del Tesoro"` followed by ~600-800 words covering: Argent City, the major islands, the treasures hidden across the seas, the legends of great pirates. **Step 3: Write Tales of Pirates Chapter 3 - Era de los Piratas** Create `content/tales-of-pirates/03-era-de-los-piratas.mdx` with frontmatter `title: "La Era de los Piratas"` followed by ~600-800 words covering: the rise of pirate crews, the class system, naval battles, the endless quest for glory. **Step 4: Commit** ```bash git add -A git commit -m "feat: add Tales of Pirates lore chapters (sea, islands, pirate era)" ``` --- ### Task 10: Tailwind Typography Plugin + Final Polish **Files:** - Modify: `package.json` (add @tailwindcss/typography) - Modify: `tailwind.config.ts` (add typography plugin) - Modify: `src/app/globals.css` (prose customizations) **Step 1: Install typography plugin** Run: ```bash npm install @tailwindcss/typography ``` **Step 2: Add plugin to Tailwind config** Add to `tailwind.config.ts` plugins array: ```ts plugins: [require("@tailwindcss/typography")], ``` **Step 3: Add custom prose styles for the book feel** Add to `src/app/globals.css`: ```css @layer components { .prose p:first-of-type::first-letter { font-family: var(--font-playfair), serif; font-size: 3.5em; float: left; line-height: 0.8; margin-right: 0.1em; margin-top: 0.1em; color: #44403c; } } ``` **Step 4: Full build test** Run: `npm run build` Expected: Build succeeds with all static pages generated. **Step 5: Run production preview** Run: `npm run start` Expected: All pages load correctly. **Step 6: Commit** ```bash git add -A git commit -m "feat: add typography plugin and drop cap styling for book feel" ``` --- ### Task 11: Responsive + Mobile Polish **Files:** - Modify: `src/components/BookShelf.tsx` (responsive grid) - Modify: `src/components/BookSpine.tsx` (mobile sizing) - Modify: `src/components/ChapterReader.tsx` (mobile padding) **Step 1: Test on mobile viewport** Open dev tools, test at 375px width. Note any layout issues. **Step 2: Fix any responsive issues** Adjust components as needed: ensure books wrap on small screens, reading text has adequate padding on mobile, navigation is touch-friendly. **Step 3: Final build verification** Run: `npm run build && npm run start` Expected: Clean build, all pages render correctly. **Step 4: Commit** ```bash git add -A git commit -m "feat: responsive polish for mobile and tablet viewports" ```