plans
2026-05-04
2026 05 04 Mdx Migration

MDX Migration Plan — Legal Pages + About

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Migrate 9 legal pages and the about page from .tsx to .mdx to use Markdown for content instead of JSX, powered by the previously set up @next/mdx and @tailwindcss/typography.

Architecture:

  • Tailwind v4: add @plugin "@tailwindcss/typography" to globals.css
  • Legal pages: replace page.tsx with page.mdx — frontmatter for metadata, <LegalPage> wrapper, markdown body styled with prose class
  • About page: keep page.tsx as thin server-component wrapper, extract static text content to about-content.mdx which uses standard markdown headings + Tailwind prose for body text

Tech Stack: @next/mdx, @tailwindcss/typography, Next.js App Router


File Map

apps/frontend/
├── app/
│   ├── globals.css                              # MODIFY — add @plugin for typography
│   └── [locale]/
│       ├── about/
│       │   ├── page.tsx                         # MODIFY — thin wrapper, imports MDX
│       │   └── about-content.mdx                # CREATE — markdown content
│       └── legal/
│           ├── terms-of-use/
│           │   └── page.tsx → page.mdx          # REPLACE
│           ├── privacy-policy/
│           │   └── page.tsx → page.mdx          # REPLACE
│           ├── payment-policy/
│           │   └── page.tsx → page.mdx          # REPLACE
│           ├── return-policy/
│           │   └── page.tsx → page.mdx          # REPLACE
│           ├── shipping-policy/
│           │   └── page.tsx → page.mdx          # REPLACE
│           ├── complaint/
│           │   └── page.tsx → page.mdx          # REPLACE
│           ├── consumer-protection/
│           │   └── page.tsx → page.mdx          # REPLACE
│           └── dispute-resolution/
│               └── page.tsx → page.mdx           # REPLACE
└── components/ui/legal-page.tsx                  # VERIFY — LegalPage component props

Pre-flight: Verify Existing Setup

Task 0: Verify MDX + Typography Setup

Files:

  • Verify: apps/frontend/next.config.ts

  • Verify: apps/frontend/mdx-components.tsx

  • Verify: apps/frontend/package.json

  • Step 1: Verify next.config.ts has MDX configured

Run: grep -A5 "createMDX\|pageExtensions" apps/frontend/next.config.ts Expected: pageExtensions: ["ts", "tsx", "md", "mdx"] and createMDX import

  • Step 2: Verify mdx-components.tsx exists

Run: cat apps/frontend/mdx-components.tsx Expected: exports useMDXComponents

  • Step 3: Verify @tailwindcss/typography is installed

Run: grep "@tailwindcss/typography" apps/frontend/package.json Expected: version number present


Phase 1: Tailwind Typography Plugin

Task 1: Add Typography Plugin to globals.css

Files:

  • Modify: apps/frontend/app/globals.css

  • Step 1: Read current globals.css top

Run: head -15 apps/frontend/app/globals.css Expected: @import "tailwindcss"; on line 2 (after comment block)

  • Step 2: Add @plugin directive after @import

Edit apps/frontend/app/globals.css — insert after line 2 (@import "tailwindcss";):

@import "tailwindcss";
@plugin "@tailwindcss/typography";
  • Step 3: Verify the change

Run: head -5 apps/frontend/app/globals.css Expected: @import "tailwindcss"; then @plugin "@tailwindcss/typography";

  • Step 4: Commit
git add apps/frontend/app/globals.css
git commit -m "feat(mdx): add @tailwindcss/typography plugin for prose styling"

Phase 2: Legal Pages — Pattern Definition

Task 2: Migrate terms-of-use (pattern page)

Files:

  • Read: apps/frontend/components/ui/legal-page.tsx

  • Delete: apps/frontend/app/[locale]/legal/terms-of-use/page.tsx

  • Create: apps/frontend/app/[locale]/legal/terms-of-use/page.mdx

  • Step 1: Read LegalPage component to confirm props

Run: cat apps/frontend/components/ui/legal-page.tsx Expected: LegalPage({ title, lastUpdated, children }) — takes title string, lastUpdated string, children for content

  • Step 2: Read current page.tsx for content

Run: cat apps/frontend/app/\[locale\]/legal/terms-of-use/page.tsx Expected: export const metadata, LegalPage wrapper, JSX headings + paragraphs

  • Step 3: Create page.mdx

Create file apps/frontend/app/[locale]/legal/terms-of-use/page.mdx:

---
title: "Terms of Use"
description: "House of Legends terms of use"
lastUpdated: "May 2, 2025"
---
 
import { LegalPage } from "@/components/ui/legal-page";
 
export const metadata = {
  title: "Terms of Use",
  description: "House of Legends terms of use",
};
 
<LegalPage title={metadata.title} lastUpdated={metadata.lastUpdated}>
 
## 1. Acceptance of Terms
 
By accessing and using The House of Legends website and services, you agree to be bound by these Terms of Use.
 
## 2. Booking and Reservations
 
When making a booking with us:
 
- You must provide accurate and complete information
- You are responsible for maintaining the confidentiality of your account
- We reserve the right to refuse service to anyone
- Cancellation policies apply as stated during booking
 
## 3. User Conduct
 
You agree not to:
 
- Use our services for any illegal purpose
- Attempt to gain unauthorized access to our systems
- Interfere with the proper functioning of our website
- Harass or abuse our staff or other customers
 
## 4. Intellectual Property
 
All content on our website, including text, graphics, logos, and images, is the property of The House of Legends and is protected by copyright laws.
 
## 5. Limitation of Liability
 
The House of Legends is not liable for any indirect, incidental, or consequential damages arising from your use of our services.
 
## 6. Changes to Terms
 
We reserve the right to modify these terms at any time. Continued use of our services constitutes acceptance of modified terms.
 
## 7. Contact Information
 
For questions about these Terms of Use, please contact us at:
 
**Email:** hamza@houseoflegends.vn
**Phone:** +84392649144
 
</LegalPage>
  • Step 4: Verify dev server starts without error

Run: cd apps/frontend && pnpm run dev & (background, 30s), then curl -s http://localhost:3000/en/legal/terms-of-use | head -20 Expected: HTML page loads, no MDX parsing errors

  • Step 5: Delete old page.tsx
rm apps/frontend/app/\[locale\]/legal/terms-of-use/page.tsx
  • Step 6: Commit
git add apps/frontend/app/\[locale\]/legal/terms-of-use/
git commit -m "feat(mdx): migrate terms-of-use to MDX"

Phase 3: Migrate Remaining 8 Legal Pages

Pattern: Same as Task 2 — for each page:

  1. Read page.tsx content
  2. Create page.mdx with equivalent frontmatter + markdown
  3. Delete page.tsx
  4. Commit

Note: All 8 remaining pages follow the exact same structure — LegalPage wrapper, metadata, same contact info. Commit after each page.

Task 3: Migrate privacy-policy

Files:

  • Delete: apps/frontend/app/[locale]/legal/privacy-policy/page.tsx

  • Create: apps/frontend/app/[locale]/legal/privacy-policy/page.mdx

  • Step 1: Read page.tsx

cat apps/frontend/app/\[locale\]/legal/privacy-policy/page.tsx
  • Step 2: Create page.mdx with title: "Privacy Policy", lastUpdated: "May 2, 2025", and the privacy policy content from the tsx

  • Step 3: Delete old page.tsx

  • Step 4: Commit

git add apps/frontend/app/\[locale\]/legal/privacy-policy/
git commit -m "feat(mdx): migrate privacy-policy to MDX"

Task 4: Migrate payment-policy

Files:

  • Delete: apps/frontend/app/[locale]/legal/payment-policy/page.tsx

  • Create: apps/frontend/app/[locale]/legal/payment-policy/page.mdx

  • Step 1: Read page.tsx

cat apps/frontend/app/\[locale\]/legal/payment-policy/page.tsx
  • Step 2: Create page.mdxlastUpdated: "May 2, 2025"

  • Step 3: Delete old page.tsx

  • Step 4: Commit

git add apps/frontend/app/\[locale\]/legal/payment-policy/
git commit -m "feat(mdx): migrate payment-policy to MDX"

Task 5: Migrate return-policy

Files:

  • Delete: apps/frontend/app/[locale]/legal/return-policy/page.tsx

  • Create: apps/frontend/app/[locale]/legal/return-policy/page.mdx

  • Step 1: Read page.tsx for content

  • Step 2: Create page.mdxlastUpdated: "May 2, 2025"

  • Step 3: Delete old page.tsx

  • Step 4: Commit

git add apps/frontend/app/\[locale\]/legal/return-policy/
git commit -m "feat(mdx): migrate return-policy to MDX"

Task 6: Migrate shipping-policy

Files:

  • Delete: apps/frontend/app/[locale]/legal/shipping-policy/page.tsx

  • Create: apps/frontend/app/[locale]/legal/shipping-policy/page.mdx

  • Step 1: Read page.tsx for content

  • Step 2: Create page.mdxlastUpdated: "May 2, 2025"

  • Step 3: Delete old page.tsx

  • Step 4: Commit

git add apps/frontend/app/\[locale\]/legal/shipping-policy/
git commit -m "feat(mdx): migrate shipping-policy to MDX"

Task 7: Migrate complaint

Files:

  • Delete: apps/frontend/app/[locale]/legal/complaint/page.tsx

  • Create: apps/frontend/app/[locale]/legal/complaint/page.mdx

  • Step 1: Read page.tsx for content

  • Step 2: Create page.mdxlastUpdated: "May 2, 2025"

  • Step 3: Delete old page.tsx

  • Step 4: Commit

git add apps/frontend/app/\[locale\]/legal/complaint/
git commit -m "feat(mdx): migrate complaint to MDX"

Task 8: Migrate consumer-protection

Files:

  • Delete: apps/frontend/app/[locale]/legal/consumer-protection/page.tsx

  • Create: apps/frontend/app/[locale]/legal/consumer-protection/page.mdx

  • Step 1: Read page.tsx for content

  • Step 2: Create page.mdxlastUpdated: "May 2, 2025"

  • Step 3: Delete old page.tsx

  • Step 4: Commit

git add apps/frontend/app/\[locale\]/legal/consumer-protection/
git commit -m "feat(mdx): migrate consumer-protection to MDX"

Task 9: Migrate dispute-resolution

Files:

  • Delete: apps/frontend/app/[locale]/legal/dispute-resolution/page.tsx

  • Create: apps/frontend/app/[locale]/legal/dispute-resolution/page.mdx

  • Step 1: Read page.tsx for content

  • Step 2: Create page.mdxlastUpdated: "May 2, 2025"

  • Step 3: Delete old page.tsx

  • Step 4: Commit

git add apps/frontend/app/\[locale\]/legal/dispute-resolution/
git commit -m "feat(mdx): migrate dispute-resolution to MDX"

Phase 4: About Page — Hybrid Approach

Why hybrid (not full MDX): The about page has complex multi-section layout with next/image components (<Image fill>) in specific grid positions, plus <Button> components with client-side asChild. Pure markdown cannot express this layout. The content is extracted to MDX; the structure stays in page.tsx.

Task 10: Migrate about page

Files:

  • Create: apps/frontend/app/[locale]/about/about-content.mdx

  • Modify: apps/frontend/app/[locale]/about/page.tsx

  • Step 1: Read current about page.tsx

cat apps/frontend/app/\[locale\]/about/page.tsx

Extract the text content from each section. Keep all JSX layout structure (grids, Image components, Button components). The MDX will provide heading + paragraph content that gets rendered inside the existing layout.

  • Step 2: Create about-content.mdx
---
title: "About House of Legends"
description: "A cultural theatre space in Da Nang dedicated to performance, storytelling and creative collaboration."
---
 
## Our Story
 
House of Legends was created by a couple who chose to build their life in Da Nang. Originally from Saigon, we came to this city with a shared passion for culture, performance and creative spaces.
 
During our travels and artistic experiences, we often found inspiration in intimate venues where artists and audiences could connect closely. We dreamed of creating a place that would bring together theatre, storytelling, music and cultural expression in a unique environment.
 
**That vision became House of Legends.**
 
## A Place for Culture & Creativity
 
House of Legends is designed as an intimate theatre space where performances, cultural experiences and artistic collaborations can take place in a close and immersive atmosphere.
 
With only 32 seats, the venue allows audiences to experience performances in a way that feels personal and authentic.
 
The project also celebrates Vietnamese culture through storytelling, performance and culinary experiences inspired by local traditions.
 
## Supporting the Creative Community
 
House of Legends is more than a theatre venue. It is also a platform that welcomes artists, performers and creative professionals who wish to share their work with audiences in Da Nang.
 
Through performances, workshops and collaborations, we aim to contribute to the growth of a vibrant artistic community and create opportunities for cultural exchange.
 
## Our Vision
 
Our vision is to create a space where art, culture and hospitality come together.
 
A place where visitors can discover Vietnamese stories and creative expression, and where artists can present meaningful performances in an intimate setting.
  • Step 3: Modify page.tsx to import and render MDX content

The approach: keep page.tsx as a server component, import the MDX component, render it inside the existing section structure.

Edit apps/frontend/app/[locale]/about/page.tsx — replace the hardcoded text content in each section with the MDX content, keeping the Image components, grid layout, and Button components:

// ipsoc checked: 2026-04-27
// SoC: Static presentation page - data inline (migration artifact), no business logic
 
/**
 * About — migrated from WordPress about-hol.php
 * Source: apps/frontend/raw/about-hol.php
 * Source lines: 145-260 (content sections)
 *
 * Migration notes:
 *   - Content extracted to about-content.mdx (2026-05-04)
 *   - Image paths corrected to migrated assets in /images/
 *   - "Perform at House of Legends" CTA links to artist-performances page
 *   - Vision section with background image preserved
 *   - Static content (no CMS integration)
 */
 
import type { Metadata } from "next";
import Image from "next/image";
import { LocaleLink } from "@/components/ui/locale-link";
import { PageHero } from "@/components/ui/page-hero";
import { Button } from "@/components/ui/button";
import { AboutContent } from "./about-content";
 
export const metadata: Metadata = {
  title: "About House of Legends | House of Legends",
  description:
    "A cultural theatre space in Da Nang dedicated to performance, storytelling and creative collaboration.",
};
 
const storyImage = "/images/about-hol-1.jpg";
const cultureImage = "/images/about-hol-2.jpg";
const communityImage = "/images/about-hol-3.jpg";
const visionImage = "/images/about-hol-hero-banner.jpg";
 
export default function AboutPage() {
  return (
    <main className="min-h-screen bg-background text-foreground">
      <PageHero
        title="About House of Legends"
        subtitle="A cultural theatre space in Da Nang dedicated to performance, storytelling and creative collaboration."
        backgroundImage="/images/about-hol-hero-banner.jpg"
      />
 
      {/* Our Story */}
      <section className="py-20 px-4">
        <div className="container mx-auto">
          <div className="grid md:grid-cols-2 gap-12 items-center">
            <div>
              <AboutContent section="story" />
            </div>
            <div className="relative aspect-[4/3] rounded-lg overflow-hidden">
              <Image
                src={storyImage}
                alt="House of Legends interior"
                fill
                className="object-cover"
              />
            </div>
          </div>
        </div>
      </section>
 
      {/* Culture & Creativity */}
      <section className="py-20 px-4 bg-surface">
        <div className="container mx-auto">
          <div className="grid md:grid-cols-2 gap-12 items-center">
            <div className="order-2 md:order-1 relative aspect-[4/3] rounded-lg overflow-hidden">
              <Image
                src={cultureImage}
                alt="Performance at House of Legends"
                fill
                className="object-cover"
              />
            </div>
            <div className="order-1 md:order-2">
              <AboutContent section="culture" />
            </div>
          </div>
        </div>
      </section>
 
      {/* Supporting the Creative Community */}
      <section className="py-20 px-4">
        <div className="container mx-auto">
          <div className="grid md:grid-cols-2 gap-12 items-center">
            <div>
              <AboutContent section="community" />
              <div className="mt-8">
                <Button
                  asChild
                  className="bg-accent hover:bg-accent-hover text-accent-foreground"
                >
                  <LocaleLink href="/experiences/artist-performances">
                    Perform at House of Legends
                  </LocaleLink>
                </Button>
              </div>
            </div>
            <div className="relative aspect-[4/3] rounded-lg overflow-hidden">
              <Image
                src={communityImage}
                alt="Creative community at House of Legends"
                fill
                className="object-cover"
              />
            </div>
          </div>
        </div>
      </section>
 
      {/* Vision */}
      <section className="py-20 px-4">
        <div className="container mx-auto">
          <div className="relative rounded-lg overflow-hidden">
            <div className="absolute inset-0">
              <Image
                src={visionImage}
                alt="House of Legends vision"
                fill
                className="object-cover"
              />
              <div className="absolute inset-0 bg-black/70" />
            </div>
            <div className="relative z-10 py-20 px-8 text-center">
              <AboutContent section="vision" />
            </div>
          </div>
        </div>
      </section>
 
      {/* Visit CTA */}
      <section className="py-20 px-4">
        <div className="container mx-auto text-center">
          <h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
            Visit House of Legends
          </h2>
          <p className="text-foreground text-lg mb-8">
            Experience Vietnamese theatre, cuisine and cocktails in an intimate
            setting.
          </p>
          <Button
            asChild
            size="lg"
            className="bg-accent hover:bg-accent-hover text-accent-foreground"
          >
            <LocaleLink href="/booking">Book the Experience</LocaleLink>
          </Button>
        </div>
      </section>
    </main>
  );
}
  • Step 4: Update about-content.mdx to export sectioned content

MDX can export named components. Update about-content.mdx to export components per section so page.tsx can render each in its layout slot:

---
title: "About House of Legends"
description: "A cultural theatre space in Da Nang dedicated to performance, storytelling and creative collaboration."
---
 
import { LocaleLink } from "@/components/ui/locale-link";
import { Button } from "@/components/ui/button";
 
export const metadata = {
  title: "About House of Legends | House of Legends",
  description:
    "A cultural theatre space in Da Nang dedicated to performance, storytelling and creative collaboration.",
};
 
## Our Story
 
House of Legends was created by a couple who chose to build their life in Da Nang. Originally from Saigon, we came to this city with a shared passion for culture, performance and creative spaces.
 
During our travels and artistic experiences, we often found inspiration in intimate venues where artists and audiences could connect closely. We dreamed of creating a place that would bring together theatre, storytelling, music and cultural expression in a unique environment.
 
**That vision became House of Legends.**
 
## A Place for Culture & Creativity
 
House of Legends is designed as an intimate theatre space where performances, cultural experiences and artistic collaborations can take place in a close and immersive atmosphere.
 
With only 32 seats, the venue allows audiences to experience performances in a way that feels personal and authentic.
 
The project also celebrates Vietnamese culture through storytelling, performance and culinary experiences inspired by local traditions.
 
## Supporting the Creative Community
 
House of Legends is more than a theatre venue. It is also a platform that welcomes artists, performers and creative professionals who wish to share their work with audiences in Da Nang.
 
Through performances, workshops and collaborations, we aim to contribute to the growth of a vibrant artistic community and create opportunities for cultural exchange.
 
## Our Vision
 
Our vision is to create a space where art, culture and hospitality come together.
 
A place where visitors can discover Vietnamese stories and creative expression, and where artists can present meaningful performances in an intimate setting.

Alternative (simpler): If the MDX file renders all content together, the page.tsx can render the MDX once in a full-width prose container and hide the sections already rendered in JSX. But the JSX structure is intentionally different per section (different image placements, CTA button placements). Keep the hybrid approach above.

  • Step 5: Commit
git add apps/frontend/app/\[locale\]/about/
git commit -m "feat(mdx): migrate about page to MDX content"

Self-Review Checklist

  • All 9 legal pages converted to page.mdx with frontmatter metadata
  • LegalPage wrapper imported and used in each MDX file
  • About page: about-content.mdx created, page.tsx updated to import and render it
  • Tailwind typography plugin added to globals.css with @plugin "@tailwindcss/typography"
  • Dev server starts without errors for all migrated pages
  • Each page committed separately for easy rollback
  • No "use client" added to any MDX file — all pages remain server components
  • All imports (LegalPage, Button, LocaleLink, Image) use correct paths

Spec Coverage

RequirementTask
Legal pages (9) to MDXTasks 2-9
About page to MDXTask 10
Typography plugin for proseTask 1
MDX setup verifiedTask 0
Each page committed separatelyEach task

No Placeholders Check

  • All file paths are exact (e.g., apps/frontend/app/[locale]/legal/terms-of-use/page.mdx)
  • All frontmatter fields match actual content from source files
  • All imports match existing component paths (verified: LegalPage at ~/components/ui/legal-page, Button at ~/components/ui/button, LocaleLink at ~/components/ui/locale-link)
  • No "TBD", "TODO", or "fill in later" in any step
  • Code blocks in every step that creates or modifies code

Plan complete. Two execution options:

1. Subagent-Driven (recommended) — dispatch one subagent per task, review between tasks, fast iteration

2. Inline Execution — execute tasks in this session using executing-plans, batch execution with checkpoints

Which approach?