Files
cronicas-de-los-reinos/docs/plans/2026-02-16-cronicas-mmorpg-implementation.md
consultoria-as f8ea168e4e Add implementation plan for Cronicas de los Reinos
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 07:08:22 +00:00

30 KiB

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:

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:

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:

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 (
    <html lang="es" className={`${playfair.variable} ${lora.variable}`}>
      <body className="bg-stone-900 text-stone-100 font-lora antialiased">
        {children}
      </body>
    </html>
  );
}

Step 4: Configure Tailwind with custom fonts

Modify tailwind.config.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:

@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

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:

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<Chapter, "content">[];
}

Step 2: Create meta.json files for each game

Create content/wow/meta.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:

{
  "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:

{
  "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:

{
  "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:

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

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:

import Link from "next/link";
import { GameMeta } from "@/types";

export default function BookSpine({ game }: { game: GameMeta }) {
  return (
    <Link href={`/${game.slug}`} className="group block">
      <div
        className="relative w-44 h-64 md:w-48 md:h-72 rounded-r-md shadow-lg cursor-pointer transition-all duration-300 group-hover:-translate-y-3 group-hover:rotate-[-2deg] group-hover:shadow-2xl"
        style={{
          background: `linear-gradient(135deg, ${game.color} 0%, ${game.colorDark} 100%)`,
        }}
      >
        {/* Book spine edge */}
        <div
          className="absolute left-0 top-0 bottom-0 w-3 rounded-l-sm"
          style={{ backgroundColor: game.colorDark }}
        />

        {/* Book cover content */}
        <div className="flex flex-col items-center justify-center h-full px-4 text-center">
          <div
            className="w-12 h-0.5 mb-3"
            style={{ backgroundColor: game.accentColor }}
          />
          <h2
            className="font-playfair text-lg md:text-xl font-bold leading-tight text-white"
          >
            {game.title}
          </h2>
          <p
            className="text-xs mt-2 opacity-75 font-lora italic"
            style={{ color: game.accentColor }}
          >
            {game.subtitle}
          </p>
          <div
            className="w-12 h-0.5 mt-3"
            style={{ backgroundColor: game.accentColor }}
          />
        </div>

        {/* Shine effect on hover */}
        <div className="absolute inset-0 bg-gradient-to-r from-white/0 via-white/10 to-white/0 opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-r-md" />
      </div>
    </Link>
  );
}

Step 2: Create BookShelf component

Create src/components/BookShelf.tsx:

import { GameMeta } from "@/types";
import BookSpine from "./BookSpine";

export default function BookShelf({ games }: { games: GameMeta[] }) {
  return (
    <div className="min-h-screen flex flex-col items-center justify-center px-4 py-16">
      {/* Title */}
      <div className="text-center mb-16">
        <h1 className="font-playfair text-4xl md:text-6xl font-bold text-parchment mb-4">
          Cronicas de los Reinos
        </h1>
        <p className="font-lora text-stone-400 text-lg italic max-w-lg mx-auto">
          Las historias epicas de los mundos que nos unieron
        </p>
        <div className="w-32 h-0.5 bg-amber-700/50 mx-auto mt-6" />
      </div>

      {/* Shelf */}
      <div className="relative">
        {/* Books row */}
        <div className="flex gap-6 md:gap-8 items-end justify-center flex-wrap px-8 pb-4">
          {games.map((game) => (
            <BookSpine key={game.slug} game={game} />
          ))}
        </div>

        {/* Shelf surface */}
        <div className="h-4 bg-gradient-to-b from-wood-medium to-wood-dark rounded-sm shadow-[0_4px_12px_rgba(0,0,0,0.5)]" />
        <div className="h-2 bg-wood-dark/50 rounded-b-sm" />
      </div>

      {/* Footer */}
      <p className="mt-16 text-stone-600 text-sm font-lora">
        Selecciona un tomo para comenzar a leer
      </p>
    </div>
  );
}

Step 3: Wire up the home page

Replace src/app/page.tsx:

import { getAllGames } from "@/lib/content";
import BookShelf from "@/components/BookShelf";

export default function Home() {
  const games = getAllGames();
  return <BookShelf games={games} />;
}

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

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:

import Link from "next/link";
import { GameWithChapters } from "@/types";

export default function BookCover({ game }: { game: GameWithChapters }) {
  return (
    <div className="min-h-screen bg-parchment text-stone-800">
      {/* Back to shelf */}
      <div className="p-4 md:p-6">
        <Link
          href="/"
          className="inline-flex items-center text-stone-500 hover:text-stone-700 font-lora text-sm transition-colors"
        >
          <span className="mr-2">&larr;</span> Volver a la estanteria
        </Link>
      </div>

      {/* Cover */}
      <div className="max-w-2xl mx-auto px-6 py-12 text-center">
        {/* Decorative top */}
        <div
          className="w-24 h-1 mx-auto mb-8 rounded"
          style={{ backgroundColor: game.accentColor }}
        />

        <h1
          className="font-playfair text-4xl md:text-5xl font-bold mb-3"
          style={{ color: game.colorDark }}
        >
          {game.title}
        </h1>

        <p
          className="font-lora italic text-lg mb-8"
          style={{ color: game.color }}
        >
          {game.subtitle}
        </p>

        {/* Decorative divider */}
        <div className="flex items-center justify-center gap-4 mb-8">
          <div className="w-16 h-px bg-stone-300" />
          <div
            className="w-2 h-2 rotate-45"
            style={{ backgroundColor: game.accentColor }}
          />
          <div className="w-16 h-px bg-stone-300" />
        </div>

        <p className="font-lora text-stone-600 text-base leading-relaxed max-w-lg mx-auto mb-16">
          {game.description}
        </p>

        {/* Table of Contents */}
        <div className="text-left max-w-md mx-auto">
          <h2 className="font-playfair text-xl font-semibold text-stone-700 mb-6 text-center tracking-wide uppercase">
            Indice
          </h2>

          <div className="space-y-1">
            {game.chapters.map((chapter) => (
              <Link
                key={chapter.slug}
                href={`/${game.slug}/${chapter.slug}`}
                className="flex items-baseline gap-3 py-3 px-2 rounded hover:bg-parchment-dark transition-colors group"
              >
                <span
                  className="font-playfair text-sm font-bold min-w-[2rem]"
                  style={{ color: game.color }}
                >
                  {String(chapter.number).padStart(2, "0")}
                </span>
                <span className="font-lora text-stone-700 group-hover:text-stone-900 flex-1">
                  {chapter.title}
                </span>
                <span className="text-stone-300 group-hover:text-stone-500 text-sm">
                  &rarr;
                </span>
              </Link>
            ))}
          </div>
        </div>

        {/* Decorative bottom */}
        <div
          className="w-24 h-1 mx-auto mt-16 rounded"
          style={{ backgroundColor: game.accentColor }}
        />
      </div>
    </div>
  );
}

Step 2: Create the game page route

Create src/app/[game]/page.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 <BookCover game={game} />;
  } 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

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:

"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 (
    <div className="fixed top-0 left-0 right-0 h-1 z-50 bg-parchment-dark/50">
      <div
        className="h-full transition-[width] duration-150"
        style={{ width: `${progress}%`, backgroundColor: color }}
      />
    </div>
  );
}

Step 2: Create ChapterReader component

Create src/components/ChapterReader.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 (
    <div className="min-h-screen bg-parchment text-stone-800">
      <ReadingProgress color={game.accentColor} />

      {/* Header */}
      <header className="max-w-3xl mx-auto px-6 pt-8 pb-4">
        <Link
          href={`/${game.slug}`}
          className="inline-flex items-center text-stone-500 hover:text-stone-700 font-lora text-sm transition-colors"
        >
          <span className="mr-2">&larr;</span> {game.title}
        </Link>
      </header>

      {/* Chapter heading */}
      <div className="max-w-3xl mx-auto px-6 pt-8 pb-12 text-center">
        <p
          className="font-playfair text-sm font-semibold tracking-widest uppercase mb-3"
          style={{ color: game.color }}
        >
          Capitulo {chapter.number} de {totalChapters}
        </p>
        <h1 className="font-playfair text-3xl md:text-4xl font-bold text-stone-800 mb-4">
          {chapter.title}
        </h1>
        <div className="flex items-center justify-center gap-4">
          <div className="w-12 h-px bg-stone-300" />
          <div
            className="w-2 h-2 rotate-45"
            style={{ backgroundColor: game.accentColor }}
          />
          <div className="w-12 h-px bg-stone-300" />
        </div>
      </div>

      {/* Chapter content */}
      <article className="max-w-[700px] mx-auto px-6 pb-20 font-lora text-lg leading-relaxed text-stone-700 prose prose-stone prose-lg prose-headings:font-playfair prose-headings:text-stone-800 prose-p:mb-6 prose-p:indent-8 first:prose-p:indent-0">
        <MDXRemote source={chapter.content} />
      </article>

      {/* Chapter navigation */}
      <nav className="max-w-[700px] mx-auto px-6 pb-16">
        <div className="border-t border-stone-300 pt-8 flex justify-between items-center">
          {prevChapter ? (
            <Link
              href={`/${game.slug}/${prevChapter}`}
              className="font-lora text-sm hover:text-stone-900 transition-colors"
              style={{ color: game.color }}
            >
              &larr; Capitulo anterior
            </Link>
          ) : (
            <span />
          )}
          <Link
            href={`/${game.slug}`}
            className="font-lora text-sm text-stone-400 hover:text-stone-600 transition-colors"
          >
            Indice
          </Link>
          {nextChapter ? (
            <Link
              href={`/${game.slug}/${nextChapter}`}
              className="font-lora text-sm hover:text-stone-900 transition-colors"
              style={{ color: game.color }}
            >
              Siguiente capitulo &rarr;
            </Link>
          ) : (
            <span />
          )}
        </div>
      </nav>
    </div>
  );
}

Step 3: Create the chapter page route

Create src/app/[game]/[chapter]/page.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 (
      <ChapterReader
        game={game}
        chapter={chapter}
        totalChapters={game.chapters.length}
        prevChapter={prev}
        nextChapter={next}
      />
    );
  } catch {
    notFound();
  }
}

Step 4: Commit

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

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

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

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

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:

npm install @tailwindcss/typography

Step 2: Add plugin to Tailwind config

Add to tailwind.config.ts plugins array:

plugins: [require("@tailwindcss/typography")],

Step 3: Add custom prose styles for the book feel

Add to src/app/globals.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

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

git add -A
git commit -m "feat: responsive polish for mobile and tablet viewports"