{"id":"9f62fb8f-6e10-4419-9a3a-c6a23aeef640","shortId":"dua5PS","kind":"skill","title":"frontend-reactor","tagline":"Converts cloned website HTML files (from frontend-ui-clone) into production-ready\nReact/Next.js projects. Extracts components, decomposes CSS, sets up routing,\nand wires navigation so buttons and links actually work.\nThree modes: single clone file, multiple clone files, or a URL ","description":"You are a **frontend conversion engineer**. You transform cloned website HTML files into\nproduction-ready React/Next.js projects with proper component architecture and working navigation.\n\n**Language rule:** Mirror the user's language for all non-code text. Code is always English.\n\n**User request:** $ARGUMENTS\n\n---\n\n## Phase 0 — Parse Input & Detect Mode\n\n### Step 1 — Determine input mode\n\nParse `$ARGUMENTS` to detect the input type:\n\n**Mode A — Single clone file:**\n- Input is a path to a single `.html` file (e.g., `test_outputs/clone-linear.html`)\n- Verify the file exists with `Read`\n- Output: single-route React project\n\n**Mode B — Multiple clone files:**\n- Input contains multiple `.html` file paths, or a glob pattern (e.g., `test_outputs/clone-linear*.html`)\n- Use `Glob` to resolve file paths\n- Output: multi-route React project with navigation\n\n**Mode C — URL (single-page clone + live links):**\n- Input is a URL (starts with `http://` or `https://`)\n- Clones ONLY the homepage using `frontend-ui-clone` for pixel-perfect fidelity\n- Uses Playwright to discover all navigation links (nav, footer, buttons)\n- All internal links and buttons point to the ORIGINAL site (not local routes)\n- Output: single-route React project where clicking any link/button goes to the real site\n- See **Phase 0C** below for the full workflow\n\n**Auto-detection order:**\n1. If input contains one `.html` path → Mode A\n2. If input contains multiple `.html` paths or a glob pattern → Mode B\n3. If input starts with `http://` or `https://` → Mode C\n4. If no clear input → ask with `AskUserQuestion`\n\n### Step 2 — Parse options\n\n| Flag | Values | Default | Effect |\n|------|--------|---------|--------|\n| `--css` | `tailwind`, `modules`, `global` | `global` | CSS strategy (global recommended for most clones) |\n| `--framework` | `nextjs`, `vite` | `nextjs` | Output framework |\n| `--simple` | (flag) | off | Non-React output: clean HTML + CSS split only |\n| `--download-assets` | (flag) | off | Download images/fonts to local public/ |\n| `--output` | path | `$(pwd)/results/reactor-[domain]/` | Custom output directory |\n| `--validate` | (flag) | off (on for Mode C) | Run Playwright visual & interaction validation against the original site |\n\n### Step 3 — Extract domain name for project naming\n\nFrom the clone HTML filename or URL, extract domain:\n- `clone-linear.html` → `linear`\n- `clone-stripe-02.html` → `stripe`\n- `https://www.magicpath.ai/` → `magicpath`\n\nProject output directory: \n- If `--output` specified → use that path\n- Otherwise → `$(pwd)/results/reactor-[domain]/`\n\n---\n\n## Phase 0C — Homepage Clone + Link Discovery (Mode C only)\n\n**Load knowledge:** Read `knowledge/site-discovery.md` for Playwright crawling patterns.\n\nMode C clones ONLY the homepage, then wires all links/buttons to point to the original site.\n\n### Step 0C.1 — Clone homepage with frontend-ui-clone\n\nUse the `Agent` tool to invoke `frontend-ui-clone` for the homepage URL:\n\n```\nAgent(\n  description: \"Clone homepage\",\n  prompt: \"Use the Skill tool to invoke frontend-ui-clone with this URL: [URL]. \n           Wait for it to complete and report the output file path.\",\n  subagent_type: \"general-purpose\"\n)\n```\n\nThis produces a single high-fidelity clone HTML (~97% pixel-perfect).\n\n**Fallback:** If Agent/Skill invocation fails, fall back to direct Playwright extraction using the script in `knowledge/site-discovery.md` Phase D.\n\n### Step 0C.2 — Discover all interactive elements on the homepage\n\nUse Playwright on the live page (before or after cloning) to run the **runtime interactive element detection** script from `knowledge/site-discovery.md` Phase A2.\n\nThis script detects ALL interactive elements by checking:\n- `cursor: pointer` (strongest signal — catches styled `<div>` buttons with no semantic markup)\n- `<a href>`, `<button>`, `<input>`, `<select>`, `<textarea>` tags\n- ARIA attributes (`role=\"button\"`, `aria-expanded`, `aria-haspopup`, `data-state`)\n- `tabindex >= 0`, inline `onclick` handlers\n\n**Output:** A JSON list of every interactive element with:\n- Tag, text, href, placeholder\n- Signals (why it's interactive)\n- Location (nav/footer/body)\n- Bounding box (position on page)\n\nThis list drives Phase 3 Step 6 (interactivity restoration) — every element in this list gets a corresponding fixup in the converted components.\n\n### Step 0C.3 — Link strategy for Mode C\n\n**ALL internal links point to the original site.** No local routes are created for subpages.\n\n```\nInternal links:\n  href=\"/pricing\"                    → href=\"https://original-site.com/pricing\"\n  href=\"/about\"                      → href=\"https://original-site.com/about\"\n  href=\"/documentation/getting-started\" → href=\"https://original-site.com/documentation/getting-started\"\n\nButtons (dead <button> with no href):\n  <button>Sign in</button>          → <a href=\"https://original-site.com/signin\">Sign in</a>\n  <button>Get started</button>      → <a href=\"https://original-site.com\">Get started</a>\n\nExternal links:\n  Keep as-is with target=\"_blank\" rel=\"noopener noreferrer\"\n\nAnchor links (#section):\n  Keep as-is (scroll within the page)\n```\n\n### Step 0C.4 — Transition to Mode A pipeline\n\nWith the single homepage clone file, proceed as **Mode A**:\n- Phase 1: Analyze the clone HTML (section mapping)\n- Phase 2: CSS decomposition\n- Phase 3: React component generation + interactivity + link fixup\n- Phase 4: Project scaffolding (single route only: `/`)\n- Phase 5: Build verification\n\n**Critical in Phase 3:** When converting links, do NOT use relative paths. Convert ALL internal links to absolute URLs pointing to the original site:\n```\nhref=\"/pricing\"  →  href=\"https://[original-domain]/pricing\"\n```\n\n---\n\n## Phase 1 — HTML Analysis & Section Mapping\n\n**Load knowledge:** Read `knowledge/component-detection.md` for section identification rules.\n\nFor EACH clone HTML file:\n\n### Step 1 — Read and parse the HTML\n\nUse `Read` to load the clone HTML file. Identify:\n\n1. **The `<style>` block** — Extract the entire contents between `<style>` and `</style>` tags.\n   The structure follows `frontend-ui-clone`'s ordering:\n   - @import rules\n   - @property declarations\n   - :root block with CSS custom properties\n   - @layer declarations\n   - @font-face rules\n   - Framework CSS rules\n   - Fix overrides (at the end, with `!important`)\n\n2. **The `<head>` metadata** — Extract:\n   - `<title>` text\n   - `<meta name=\"description\">` content\n   - `<link>` tags (fonts, favicons, preloads)\n   - `<html>` attributes (`lang`, `class`, `data-theme`, etc.)\n\n3. **The `<body>` content** — Everything between `<body>` and `</body>`, excluding `<script>` tags.\n\n### Step 2 — Identify semantic sections\n\nApply the detection strategy from `knowledge/component-detection.md`:\n\n1. **Scan top-level children** of `<body>` (or the first wrapper `<div>` if body has a single child wrapper)\n2. For each top-level element, classify using signals (priority order):\n   - HTML5 semantic tags (`<nav>`, `<header>`, `<section>`, `<footer>`)\n   - ARIA roles\n   - Class name patterns\n   - ID attributes\n   - Structural position (first = Navbar, last = Footer)\n3. Assign a component name to each section (PascalCase)\n\n**Output of this step:** An ordered list of sections with:\n```\n[\n  { name: \"Navbar\", tag: \"nav\", startLine: 45, endLine: 92, classes: [\"nav\", \"nav-container\"] },\n  { name: \"Hero\", tag: \"section\", startLine: 93, endLine: 180, classes: [\"hero\", \"hero-section\"] },\n  { name: \"Features\", tag: \"section\", startLine: 181, endLine: 320, classes: [\"features-grid\"] },\n  ...\n  { name: \"Footer\", tag: \"footer\", startLine: 890, endLine: 960, classes: [\"footer\", \"site-footer\"] },\n]\n```\n\n### Step 3 — Extract link map\n\nParse all `<a href=\"...\">` in the body:\n\n1. Extract every `href` value\n2. Classify each link:\n   - **Internal** (same domain as the cloned site): `https://linear.app/pricing` → route `/pricing`\n   - **Anchor** (hash links): `#features`, `#pricing`\n   - **External** (different domain): `https://github.com/linear`\n   - **Mail/tel**: `mailto:`, `tel:`\n3. Build a route map for internal links:\n   ```\n   /pricing  → app/pricing/page.tsx\n   /about    → app/about/page.tsx\n   /blog     → app/blog/page.tsx (if clone exists, else external link)\n   ```\n\n### Step 4 — Cross-page deduplication (Mode B only; Mode C does this in Phase 0C.6)\n\nCompare sections across all clone HTML files:\n\n1. Extract the first section (usually Navbar) and last section (usually Footer) from each file\n2. Compare their HTML content (normalize whitespace, ignore active-state classes)\n3. If similarity > 85% → mark as shared component\n4. Extract the canonical version from the homepage clone\n5. Note differences (active nav link, page-specific CTA text) for prop-based variation\n\n---\n\n## Phase 2 — CSS Decomposition\n\n**Load knowledge:** Read `knowledge/css-extraction.md` for CSS splitting rules.\n\n### Step 1 — Detect CSS strategy\n\nIf user specified `--css`, use that. Otherwise:\n\n1. **Default → Global CSS** — All CSS stays in `globals.css`. This is the safest strategy for clone conversions because:\n   - Clone CSS relies on specific cascade ordering that CSS Modules would break\n   - Webflow/framework sites share class names across many sections (`.w-nav`, `.w-button`, `.container`)\n   - Splitting CSS incorrectly causes subtle visual regressions\n2. **Tailwind** (auto-detected if clone uses Tailwind) — Check for signals:\n   - `@property --tw-*` declarations\n   - `@layer base, components, utilities`\n   - `:root` contains `--tw-*` properties\n   - Majority of class names match Tailwind patterns (`flex`, `p-4`, `text-sm`, `bg-*`, etc.)\n   - If detected → extract tokens to `tailwind.config.ts`, keep utility classes in JSX\n3. **CSS Modules** (`--css modules`) — Only use when explicitly requested. Requires careful class-to-component mapping. See `knowledge/css-extraction.md` for the classification algorithm.\n\n### Step 2 — Extract global CSS\n\nFrom the `<style>` block, extract these into `globals.css`:\n\n1. All `@import` rules\n2. All `@property` declarations\n3. The entire `:root { ... }` block (design tokens)\n4. All `@layer` declarations\n5. All `@font-face` rules\n6. Base element selectors (`*`, `html`, `body`, `h1`-`h6`, `p`, `a`, etc.)\n7. All `@keyframes` rules\n8. The fix overrides block (everything with `!important` at the end)\n\n### Step 3 — Classify remaining CSS rules\n\nFor each remaining CSS rule:\n\n1. Parse the selector to extract referenced class names\n2. Match class names against the component sections from Phase 1\n3. Classify:\n   - **Single component match** → that component's CSS module file\n   - **Multiple component match** → globals.css (shared utilities section)\n   - **No match** → globals.css\n4. For `@media` queries containing rules for multiple components → split into separate `@media` blocks\n\n### Step 4 — Tailwind-specific processing (if applicable)\n\nIf Tailwind strategy:\n1. Extract design tokens from `:root` and map to `tailwind.config.ts`:\n   - `--color-*` → `theme.extend.colors`\n   - `--font-*` → `theme.extend.fontFamily`\n   - `--radius-*` → `theme.extend.borderRadius`\n   - `--shadow-*` → `theme.extend.boxShadow`\n   - `--space-*` → `theme.extend.spacing`\n2. Keep Tailwind utility classes as-is in JSX (`className=\"flex items-center p-4\"`)\n3. Move non-utility custom CSS to globals.css\n\n### Step 5 — Design token summary\n\nOutput a summary of extracted design tokens:\n```\nColors:   12 tokens (--color-bg, --color-text-primary, --color-accent, ...)\nFonts:    2 families (Inter, Playfair Display)\nSpacing:  8px grid base\nRadii:    4 tokens (--radius-sm: 4px, --radius-md: 8px, ...)\nShadows:  3 tokens\nMotion:   2 tokens (--duration-fast: 150ms, --ease-out: cubic-bezier(...))\n```\n\n---\n\n## Phase 3 — React Component Generation\n\n**Load knowledge:** Read `knowledge/html-to-jsx.md` for conversion rules.\n\n### Step 1 — HTML → JSX conversion\n\nFor each section's HTML fragment, apply all conversion rules from `knowledge/html-to-jsx.md`:\n\n1. **Attribute transforms**: `class` → `className`, `for` → `htmlFor`, etc.\n2. **Self-closing tags**: `<img>` → `<img />`, `<br>` → `<br />`, etc.\n3. **Inline styles**: `style=\"...\"` → `style={{ camelCase: 'value' }}`\n4. **SVG attributes**: `stroke-width` → `strokeWidth`, etc.\n5. **Comments**: `<!-- -->` → `{/* */}`\n6. **Remove event handlers**: Strip all `on*` attributes\n\n**Validation step:** After conversion, check for common JSX errors:\n- Unclosed tags\n- Reserved word attributes not converted\n- Unescaped `{` or `}` in text content\n- Adjacent JSX elements without a wrapper\n\n### Step 2 — Link conversion\n\nStrategy depends on the input mode:\n\n**Mode A (single page):** No sub-routes exist, so internal links stay as plain `<a>` tags:\n```\nInternal links → <a href=\"/pricing\">text</a>  (relative path, plain <a>)\nAnchor links   → <a href=\"#id\">text</a>  (keep as-is)\nExternal links → <a href=\"url\" target=\"_blank\" rel=\"noopener noreferrer\">text</a>\n```\n\n**Mode B/C (multi-page):** Sub-routes exist, so use Next.js `<Link>` for routed pages:\n```\nInternal links WITH clone → <Link href=\"/pricing\">text</Link>  (import from next/link)\nInternal links WITHOUT clone → <a href=\"/blog\">text</a>  (plain <a>, add TODO comment)\nAnchor links → <a href=\"#id\">text</a>  (keep as-is)\nExternal links → <a href=\"url\" target=\"_blank\" rel=\"noopener noreferrer\">text</a>\n```\n\nFor internal links to pages NOT in the clone/extracted set (Mode B/C):\n```tsx\n{/* TODO: Clone /blog page and add route */}\n<a href=\"/blog\">Blog</a>\n```\n\nIn ALL modes, convert absolute URLs to relative paths:\n- `https://example.com/pricing` → `/pricing`\n- `https://example.com/` → `/`\n- `https://example.com/#features` → `/#features`\n\n### Step 3 — Component file creation\n\nFor each section, create the component file:\n\n**Shared components** → `components/shared/[Name].tsx`\n**Page-specific components** → `components/[page]/[Name].tsx`\n\nEach component file structure:\n```tsx\n// CSS Modules variant\nimport styles from './[Name].module.css';\n\nexport function [Name]() {\n  return (\n    [converted JSX]\n  );\n}\n```\n\nOr for Tailwind:\n```tsx\nexport function [Name]() {\n  return (\n    [converted JSX with Tailwind classes]\n  );\n}\n```\n\n### Step 4 — Shared Navbar with active state (Mode B/C)\n\nIf Navbar is a shared component across pages, add `usePathname()` for active link highlighting:\n\n```tsx\n'use client';\nimport Link from 'next/link';\nimport { usePathname } from 'next/navigation';\n\nexport function Navbar() {\n  const pathname = usePathname();\n  // ... render nav links with active state based on pathname\n}\n```\n\n### Step 5 — SVG icon extraction\n\nScan all components for inline SVGs that qualify as icons:\n- Width/height ≤ 48px\n- Appears 2+ times across components\n- Simple path data (< 500 chars)\n\nExtract qualifying SVGs into `components/icons.tsx` as named exports.\nReplace inline SVGs in components with imports from `icons.tsx`.\n\n### Step 6 — Interactivity restoration\n\n**Load knowledge:** Read `knowledge/interactivity.md` for all interactive patterns and detection strategies.\n\nAfter generating static components, restore interactivity using two sources:\n\n1. **Runtime detection results (Mode C):** The JSON list from Phase 0C.2 tells you exactly which elements are interactive and why. Match each element by its text/classes/position to the corresponding component, and apply the appropriate fixup.\n2. **Class-name pattern matching (Mode A/B, or as supplement):** Scan component JSX for known interactive patterns (see `knowledge/interactivity.md` Layer 2).\n\nProcess in priority order:\n\n**Priority 1 — Navigation interactivity:**\n- Dropdown menus in Navbar → wrap with `useState` open/close + click-outside handler\n- Mobile hamburger menu → toggle state for menu visibility\n- These are CRITICAL — broken nav = unusable site\n\n**Priority 2 — Anchor links:**\n- `<a href=\"#section-id\">` → add `onClick` with `scrollIntoView({ behavior: 'smooth' })`\n- Simple and high-impact\n\n**Priority 3 — Accordions / FAQ:**\n- Detect repeating question+answer pattern\n- Wrap each item with `useState` for expand/collapse\n- Add `max-height` transition for smooth animation\n\n**Priority 4 — Tabs:**\n- Detect tab menu + tab content panels\n- Wrap with `useState` for active tab index\n- Show/hide panels based on active state\n\n**Priority 5 — Forms:**\n- Detect `<form>`, `<input>`, `<textarea>`, `<select>` elements\n- Add `useState` for form data, `onChange` handlers on inputs\n- Add `onSubmit` handler (log to console + show success state)\n- Preserve all original styling classes\n\n**Priority 6 — Modals / popups:**\n- Detect hidden overlays with `position: fixed`\n- Find trigger buttons, wire up `useState` toggle\n- Add backdrop click-to-close and close button\n\n**Priority 7 — Carousels / sliders:**\n- Detect slider patterns (Webflow `w-slider`, custom carousels)\n- Add `useState` for current slide, prev/next buttons\n- CSS `transform: translateX` for slide transitions\n\n**For each interactive component:**\n1. Add `'use client';` directive at the top\n2. Import necessary hooks (`useState`, `useRef`, `useEffect`)\n3. Wrap interactive elements with state logic\n4. **Preserve ALL original class names and HTML structure** — only add behavior, don't restructure\n5. Keep non-interactive components as server components\n\n**P8 — Post-clone fixups (for dangerouslySetInnerHTML components):**\n\nWhen components use `dangerouslySetInnerHTML`, apply these fixups to the raw HTML string:\n\n1. **className → class:** `dangerouslySetInnerHTML` renders raw HTML, NOT JSX. Must use `class`, `for`, `tabindex`, not `className`, `htmlFor`, `tabIndex`. See `knowledge/html-to-jsx.md` \"dangerouslySetInnerHTML Rules\".\n2. **Dead buttons → links:** Convert `<button>Sign in</button>` to `<a href=\"/signin\">Sign in</a>`. Convert CTA buttons to links pointing to original site. See `knowledge/interactivity.md` Pattern 10.\n3. **Input overlay removal:** Remove typewriter overlay (`pointer-events-none` div with static text over input), restore input visibility, add placeholder. See `knowledge/interactivity.md` Pattern 9.\n4. **URL normalization:** Convert `href=\"https://original-site.com/path\"` to `href=\"/path\"` for internal links. See `knowledge/interactivity.md` Pattern 11.\n5. **Cursor pointer:** Add `cursor: pointer` on remaining `<button>` elements.\n\n**What NOT to restore:**\n- Third-party widgets (chat, analytics) — need API keys\n- Complex JS animations (GSAP, Lottie, WebGL) — out of scope\n- Backend-dependent features (auth, real-time data)\n- Note any skipped interactivity in the final report\n\n---\n\n## Phase 4 — Project Scaffolding & Assembly\n\n**Load knowledge:** Read `knowledge/nextjs-scaffold.md` for project templates.\n\n### Step 1 — Initialize project\n\nDetermine the output directory from Phase 0 Step 3 (either `--output` path or `$(pwd)/results/reactor-[domain]/`).\n\n**Next.js (default):**\n```bash\n# Create in parent of output dir, with output dir name\nnpx create-next-app@latest [output-dir] \\\n  --typescript \\\n  --eslint \\\n  --app \\\n  --src-dir=false \\\n  --import-alias=\"@/*\" \\\n  --no-turbopack \\\n  [--tailwind]  # only if Tailwind detected\n```\n\n**Vite + React (if --framework vite):**\n```bash\nnpm create vite@latest [output-dir] -- --template react-ts\ncd [output-dir]\nnpm install\nnpm install react-router-dom\n[npm install -D tailwindcss postcss autoprefixer]  # if Tailwind\n```\n\n### Step 2 — Clean boilerplate\n\nRemove default content from:\n- `app/page.tsx` (Next.js) or `src/App.tsx` (Vite)\n- `app/globals.css` or `src/index.css`\n- `public/` placeholder files\n\n### Step 3 — Write all files\n\nWrite files in this order (dependencies first):\n\n1. **`app/globals.css`** — Global styles from Phase 2\n2. **`tailwind.config.ts`** — If Tailwind mode, with extracted design tokens\n3. **`next.config.mjs`** — With `images.remotePatterns` for the original domain\n4. **`components/icons.tsx`** — Extracted SVG icons\n5. **`components/shared/Navbar.tsx`** + CSS module — Shared Navbar\n6. **`components/shared/Footer.tsx`** + CSS module — Shared Footer\n7. **`components/[page]/[Section].tsx`** + CSS modules — All page-specific sections\n8. **`app/layout.tsx`** — Root layout importing Navbar, Footer, globals.css, fonts\n9. **`app/page.tsx`** — Homepage composing its sections\n10. **`app/[route]/page.tsx`** — Each sub-route composing its sections\n\n### Step 4 — Font handling\n\nDetect fonts from the clone HTML:\n\n1. **Google Fonts** (from `@import` or `<link>` tags):\n   - Convert to `next/font/google` imports in `layout.tsx`\n   - Set CSS variables (`--font-sans`, `--font-display`)\n   - Remove the `@import` from globals.css (Next.js handles it)\n\n2. **Custom fonts** (from `@font-face`):\n   - Keep in globals.css\n   - If `--download-assets` flag, download `.woff2` files to `public/fonts/`\n\n### Step 5 — Asset handling\n\nDefault: Keep all image URLs as absolute paths pointing to the original site.\n\nIf `--download-assets` flag:\n1. Parse all `src`, `srcset`, and CSS `url()` values\n2. Download images to `public/assets/`\n3. Download fonts to `public/fonts/`\n4. Rewrite URLs in components and CSS to relative paths (`/assets/image.jpg`)\n\n---\n\n## Phase 5 — Build Verification & Report\n\n### Step 1 — Build test\n\n```bash\ncd [output-dir]\nnpm run build 2>&1\n```\n\n### Step 2 — Error diagnosis and fix (up to 3 rounds)\n\nIf build fails, diagnose the error:\n\n| Error Pattern | Fix |\n|---------------|-----|\n| `class` in JSX | Convert to `className` |\n| `for` in JSX | Convert to `htmlFor` |\n| Unclosed tag | Add self-closing `/>` |\n| `style` is string | Convert to style object |\n| Missing `Link` import | Add `import Link from 'next/link'` |\n| `usePathname` in server component | Add `'use client'` directive |\n| Image hostname not allowed | Add to `next.config.mjs` remotePatterns |\n| Module not found | Check import paths |\n| TypeScript type error | Add appropriate types or `// @ts-ignore` as last resort |\n\nAfter each fix, re-run `npm run build`. Max 3 rounds.\n\n### Step 3 — Output report\n\n```\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nFRONTEND REACTOR — CONVERSION COMPLETE\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nSource           [N] clone HTML file(s)\nOutput           [output-dir]\nFramework        Next.js 15 (App Router) + TypeScript\nCSS strategy     [CSS Modules / Tailwind / Global]\n\nCOMPONENTS\n  Shared:        [N] (Navbar, Footer)\n  Page-specific: [N] (Hero, Features, Pricing, ...)\n  Icons:         [N] SVG icons extracted\n\nROUTES\n  /              → app/page.tsx (Hero, Features, Testimonials, CTA)\n  /pricing       → app/pricing/page.tsx (PricingHero, Plans, FAQ)\n  /about         → app/about/page.tsx (AboutHero, Team, Story)\n\nDESIGN TOKENS\n  Colors: [N] | Fonts: [N] | Spacing: [base]px | Radii: [N] | Shadows: [N]\n\nLINKS\n  Internal (routed): [N]\n  External: [N]\n  Uncloned pages: [list of internal links without clone files]\n\nINTERACTIVITY RESTORED\n  ✓ Dropdown menus: [N]\n  ✓ Mobile nav toggle: [yes/no]\n  ✓ Accordions/FAQ: [N] items\n  ✓ Tabs: [N] sets\n  ✓ Forms: [N] (client-side state)\n  ✓ Modals: [N]\n  ✓ Anchor scroll: [N] links\n  ✗ Skipped: [list of complex interactions not restored]\n\nBUILD STATUS    ✓ Passed  /  ✗ Failed (see errors above)\n\nVALIDATION      [Skipped / see below]\n\nTO RUN\n  cd [output-dir] && npm run dev\n\nTO DEPLOY\n  cd [output-dir] && vercel deploy\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\nIf `--validate` is set (or Mode C), proceed to **Phase 6** for Playwright validation.\n\n---\n\n## Phase 6 — Playwright Visual & Interaction Validation\n\n**Load knowledge:** Read `knowledge/playwright-validation.md` for all validation scripts and diff algorithms.\n\nThis phase runs when `--validate` is set, or automatically in Mode C (URL input). It requires the original site URL and a successful `npm run build` from Phase 5.\n\n### Step 1 — Start dev server\n\n```bash\ncd [output-dir]\nnpm run dev -- -p 3000 &\n```\n\nWait for `localhost:3000` to be ready (poll with socket connect, max 30s).\n\n### Step 2 — Visual screenshot comparison\n\nCapture full-page screenshots of both the original site and `localhost:3000` at three viewports:\n\n| Viewport | Size |\n|----------|------|\n| Desktop | 1440×900 |\n| Tablet | 768×1024 |\n| Mobile | 375×812 |\n\nBefore capturing, normalize both pages:\n- Remove cookie banners/overlays\n- Force all animation durations to `0s` for consistent static captures\n- Scroll to bottom and back to trigger lazy loading\n- Force `opacity: 1` on animated elements\n\nGenerate pixel diff images and calculate similarity scores per viewport.\n\n### Step 3 — Interactive element audit\n\nRun the interactive element detection script (same as `site-discovery.md` Phase A2) on both pages. Compare:\n\n1. **Count:** How many interactive elements exist on each page\n2. **Match by text + position:** Pair elements between original and converted\n3. **Signal degradation:** Elements that exist but lost interactivity signals (e.g., a link that lost `cursor-pointer`, a button that lost `has-popup`)\n4. **Missing elements:** Interactive elements on the original that are absent on the converted\n\n### Step 4 — Hover state comparison\n\nFor the top 10 interactive elements (prioritizing nav links, buttons, CTAs):\n\n1. Screenshot each element before hover\n2. Hover over it, wait 500ms for transitions\n3. Screenshot after hover\n4. Pixel-diff the before/after for each element on both sites\n5. Flag elements where original has a visible hover effect but converted does not\n\n### Step 5 — Animation & transition diff\n\nExtract from both pages:\n\n1. **@keyframes definitions** — compare by name; flag missing keyframes\n2. **CSS transitions** — compare `transition-property` + `transition-duration` on interactive elements; flag transitions present on original but missing on converted\n3. **Animated elements** — elements with active `animation-name`; flag elements that were animated on the original but are static on the converted\n\n### Step 6 — Scroll & sticky element validation\n\nCompare:\n- `scroll-behavior: smooth` on `<html>`\n- Count of `position: sticky` elements\n- Count of `position: fixed` elements\n\nFlag any mismatches.\n\n### Step 7 — Auto-fix (up to 2 rounds)\n\nIf critical issues are found (visual similarity < 80% on any viewport, OR > 30% of interactive elements missing):\n\n1. **Missing @keyframes** → copy the keyframe definition from the original CSS into `globals.css`\n2. **Missing hover transitions** → find the `:hover` rules for the flagged elements from the original CSS and add them to `globals.css`\n3. **Missing `cursor: pointer`** → add `cursor: pointer` to the element's CSS class\n4. **Missing sticky/fixed positioning** → check if `position: sticky/fixed` was dropped during CSS extraction\n\nAfter each fix round:\n- Re-run `npm run build`\n- Re-run the validation pipeline\n- Stop if scores improve above thresholds or after 2 rounds\n\n### Step 8 — Validation report\n\nAppend validation results to the Phase 5 output report:\n\n```\nVALIDATION (Playwright)\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n  Original URL       [url]\n  Dev server         localhost:3000\n\n  VISUAL FIDELITY\n    Desktop (1440px): [N]% similarity  [✓/△/✗]\n    Tablet  (768px):  [N]% similarity  [✓/△/✗]\n    Mobile  (375px):  [N]% similarity  [✓/△/✗]\n    Diff images saved to: [output-dir]/validation-report/\n\n  INTERACTIVE ELEMENTS\n    Matched:   [N]/[M] ([P]%)\n    Missing:   [N] — [list of missing element names]\n    Degraded:  [N] — [list of elements with lost signals]\n\n  HOVER STATES\n    Matching:       [N]/[M]\n    Missing hover:  [N] — [list]\n    Different hover: [N] — [list]\n\n  ANIMATIONS & TRANSITIONS\n    @keyframes matched: [N] | missing: [N] — [list]\n    Transitions matched: [N] | missing: [N]\n    Animated elements matched: [N] | missing: [N]\n\n  SCROLL BEHAVIOR\n    Smooth scroll:   [✓/✗]\n    Sticky elements: [✓/✗] (original: [N], converted: [M])\n    Fixed elements:  [✓/✗] (original: [N], converted: [M])\n\n  AUTO-FIX APPLIED\n    [N] fixes applied in [R] rounds\n    [list of fixes]\n\n  OVERALL SCORE      [N]/100\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n```\n\n**Overall score calculation:**\n- Visual fidelity (desktop): 40 points (proportional to similarity %)\n- Interactive element match rate: 25 points\n- Hover state match rate: 15 points\n- Animation/transition match rate: 15 points\n- Scroll behavior match: 5 points\n\n---\n\n## --simple Mode (Non-React Split)\n\nIf `--simple` flag is set, skip React conversion. Instead:\n\n1. **Split the clone HTML into:**\n   - `index.html` — Clean HTML with `<link rel=\"stylesheet\" href=\"styles.css\">`\n   - `styles.css` — All CSS extracted from `<style>` block\n   - `scripts.js` — Empty file with TODO comments for interactivity\n\n2. **Clean up the HTML:**\n   - Replace `<style>...</style>` with `<link>` tag\n   - Fix any remaining inline styles\n   - Organize `<head>` metadata\n\n3. **Organize the CSS:**\n   - Add section comments for navigation\n   - Group by: resets → tokens → typography → layout → components → utilities → overrides\n\n4. **Output to:** `$(pwd)/results/split-[domain]/` (or `--output` path if specified)\n\n---\n\n## Error Handling\n\n- **Clone file not found:** Ask user for correct path with `AskUserQuestion`\n- **Clone HTML is malformed:** Attempt best-effort parsing, warn user about sections that couldn't be parsed\n- **`npx create-next-app` fails:** Fall back to manual project scaffolding (write package.json, tsconfig.json by hand)\n- **Build errors persist after 3 rounds:** Output the error log and ask user for guidance\n- **No semantic sections detected:** Fall back to splitting `<body>` children as numbered sections\n\n---\n\n## Key Principles\n\n1. **CSS safety first:** When unsure which component a CSS rule belongs to, keep it in globals.css. A slightly bloated globals.css is better than broken styles.\n2. **Preserve fidelity:** The React project should render identically to the clone HTML. Don't \"improve\" or \"clean up\" the visual output.\n3. **Progressive enhancement:** Start with static output, then add interactivity (active nav, client-side routing). Don't break what works.\n4. **Minimal dependencies:** Only add packages that are strictly necessary. Don't add animation libraries, form libraries, or utility packages unless the clone requires them.","tags":["frontend","reactor","skills","instantx-research","agent-skills","frontend-ui","ui-design","web-search"],"capabilities":["skill","source-instantx-research","skill-frontend-reactor","topic-agent-skills","topic-frontend-ui","topic-ui-design","topic-web-search"],"categories":["skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/instantX-research/skills/frontend-reactor","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add instantX-research/skills","source_repo":"https://github.com/instantX-research/skills","install_from":"skills.sh"}},"qualityScore":"0.455","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 11 github stars · SKILL.md body (29,959 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:07:41.524Z","embedding":null,"createdAt":"2026-04-23T13:03:56.344Z","updatedAt":"2026-05-18T19:07:41.524Z","lastSeenAt":"2026-05-18T19:07:41.524Z","tsv":"'/about':667,671 '/documentation/getting-started':673,677 '/pricing':661,665,787,792 '/results/reactor-':341,396 '/results/split-':921 '0':93,585 '0c':242,399 '0c.1':432 '0c.2':521 '0c.3':637 '0c.4':715 '1':99,252,732,794,813,828,1005 '2':261,291,740,866,1031 '3':274,363,618,744,765,883,899,980,1053 '4':282,752,917,1074 '5':759 '6':620 '97':498 'a2':550 'absolut':779 'activ':1063 'actual':34 'add':903,1061,1078,1086 'agent':442,454 'agent/skill':504 'alway':87 'analysi':796 'analyz':733 'anchor':703 'anim':1087 'app':963 'architectur':68 'argument':91,104 'aria':571,576,579 'aria-expand':575 'aria-haspopup':578 'as-i':694,707 'ask':287,934,987 'askuserquest':289,940 'asset':330 'attempt':945 'attribut':572,876 'auto':249 'auto-detect':248 'b':140,273 'back':508,966,996 'belong':1016 'best':947 'best-effort':946 'better':1027 'blank':699 'bloat':1024 'block':845 'bound':609 'box':610 'break':1071 'broken':1029 'build':760,976 'button':31,211,216,565,574,678 'c':173,281,352,405,416,642 'catch':563 'check':558 'children':999 'class':878 'clean':323,1048 'clear':285 'click':232 'client':1066 'client-sid':1065 'clone':5,13,39,42,55,113,142,178,188,196,309,372,401,417,433,439,449,456,468,496,538,725,735,809,824,837,930,941,1042,1096 'clone-linear.html':379 'clone-stripe-02.html':381 'code':83,85 'comment':905 'complet':477 'compon':21,67,635,746,914,1012 'contain':145,255,264 'content':871,885 'convers':51 'convert':4,634,767,774 'correct':937 'correspond':630 'couldn':955 'crawl':413 'creat':655,961 'create-next-app':960 'critic':762 'css':23,298,303,325,741,847,857,902,1006,1014 'cursor':559 'custom':343,848 'd':519 'data':582,880 'data-st':581 'data-them':879 'dead':679 'declar':843,851 'decompos':22 'decomposit':742 'default':296 'depend':1076 'descript':455 'detect':96,106,250,545,553,994 'determin':100 'direct':510 'directori':345,387 'discov':205,522 'discoveri':403 'domain':342,365,378,397,791,922 'download':329,333 'download-asset':328 'drive':616 'e.g':124,154 'effect':297 'effort':948 'element':525,544,556,596,624 'end':863 'engin':52 'english':88 'enhanc':1055 'error':928,977,984 'etc':882 'everi':594,623 'everyth':886 'exclud':889 'exist':130 'expand':577 'extern':691 'extract':20,364,377,512,869 'face':854 'fail':506,964 'fall':507,965,995 'fallback':502 'favicon':874 'fidel':201,495,1033 'file':8,40,43,58,114,123,129,143,148,162,482,726,811,826,931 'filenam':374 'first':1008 'fix':859,892 'fixup':631,750 'flag':294,317,331,347 'follow':833 'font':853,873 'font-fac':852 'footer':210 'form':1089 'found':933 'framework':310,315,856 'frontend':2,11,50,194,437,447,466,835 'frontend-reactor':1 'frontend-ui-clon':10,193,436,446,465,834 'full':246 'general':487 'general-purpos':486 'generat':747 'get':628,687,689 'glob':152,159,270 'global':301,302,305 'globals.css':1021,1025 'goe':235 'group':908 'guidanc':990 'hand':975 'handl':929 'handler':588 'haspopup':580 'high':494 'high-fidel':493 'homepag':191,400,420,434,452,457,528,724 'href':600,660,662,666,668,672,674,682,786,788 'html':7,57,122,147,157,257,266,324,373,497,736,795,810,818,825,942,1043 'ident':1039 'identif':805 'identifi':827 'images/fonts':334 'import':840,865 'improv':1046 'inlin':586,895 'input':95,101,108,115,144,181,254,263,276,286 'interact':356,524,543,555,595,606,621,748,1062 'intern':213,644,658,776 'invoc':505 'invok':445,464 'json':591 'keep':693,706,1018 'key':1003 'knowledg':408,800 'knowledge/component-detection.md':802 'knowledge/site-discovery.md':410,517,548 'lang':877 'languag':72,78 'layer':850 'layout':913 'librari':1088,1090 'linear':380 'link':33,180,208,214,402,638,645,659,692,704,749,768,777 'link/button':234 'links/buttons':424 'list':592,615,627 'live':179,533 'load':407,799,822 'local':223,336,652 'locat':607 'log':985 'magicpath':384 'malform':944 'manual':968 'map':738,798 'markup':569 'metadata':868,898 'minim':1075 'mirror':74 'mode':37,97,102,110,139,172,259,272,280,351,404,415,641,718,729 'modul':300 'multi':166 'multi-rout':165 'multipl':41,141,146,265 'name':366,369 'nav':209,1064 'nav/footer/body':608 'navig':29,71,171,207,907 'necessari':1083 'next':962 'nextj':311,313 'non':82,320 'non-cod':81 'non-react':319 'noopen':701 'noreferr':702 'npx':959 'number':1001 'onclick':587 'one':256 'option':293 'order':251,839 'organ':897,900 'origin':220,360,429,649,784,790 'original-domain':789 'original-site.com':664,670,676 'original-site.com/about':669 'original-site.com/documentation/getting-started':675 'original-site.com/pricing':663 'otherwis':394 'output':133,164,225,314,322,338,344,386,389,481,589,918,924,982,1052,1059 'outputs/clone-linear':156 'outputs/clone-linear.html':126 'overrid':860,916 'packag':1079,1093 'package.json':972 'page':177,534,613,713 'pars':94,103,292,816,949,958 'path':118,149,163,258,267,339,393,483,773,925,938 'pattern':153,271,414 'perfect':200,501 'persist':978 'phase':92,241,398,518,549,617,731,739,743,751,758,764,793 'pipelin':720 'pixel':199,500 'pixel-perfect':198,499 'placehold':601 'playwright':203,354,412,511,530 'point':217,426,646,781 'pointer':560 'posit':611 'preload':875 'preserv':1032 'principl':1004 'proceed':727 'produc':490 'product':16,61 'production-readi':15,60 'progress':1054 'project':19,64,138,169,230,368,385,753,969,1036 'prompt':458 'proper':66 'properti':842,849 'public':337 'purpos':488 'pwd':340,395,920 'react':137,168,229,321,745,1035 'react/next.js':18,63 'reactor':3 'read':132,409,801,814,820 'readi':17,62 'real':238 'recommend':306 'rel':700 'relat':772 'remain':894 'render':1038 'report':479 'request':90 'requir':1097 'reset':910 'resolv':161 'restor':622 'role':573 'root':844 'round':981 'rout':26,136,167,224,228,653,756,1068 'rule':73,806,841,855,858,1015 'run':353,540 'runtim':542 'safeti':1007 'scaffold':754,970 'script':515,546,552 'scroll':710 'section':705,737,797,804,904,953,993,1002 'see':240 'semant':568,992 'set':24 'side':1067 'sign':683,685 'signal':562,602 'simpl':316 'singl':38,112,121,135,176,227,492,723,755 'single-pag':175 'single-rout':134,226 'site':221,239,361,430,650,785 'skill':461 'skill-frontend-reactor' 'slight':1023 'source-instantx-research' 'specifi':390,927 'split':326,998 'start':185,277,688,690,1056 'state':583 'static':1058 'step':98,290,362,431,520,619,636,714,812 'strategi':304,639 'strict':1082 'stripe':382 'strongest':561 'structur':832 'style':564,896,1030 'subag':484 'subpag':657 'tabindex':584 'tag':570,598,830,872,891 'tailwind':299 'target':698 'test':125,155 'text':84,599,870 'theme':881 'three':36 'token':911 'tool':443,462 'topic-agent-skills' 'topic-frontend-ui' 'topic-ui-design' 'topic-web-search' 'transform':54 'transit':716 'tsconfig.json':973 'type':109,485 'typographi':912 'ui':12,195,438,448,467,836 'unless':1094 'unsur':1010 'url':46,174,184,376,453,471,472,780 'use':158,192,202,391,440,459,513,529,771,819 'user':76,89,935,951,988 'util':915,1092 'valid':346,357 'valu':295 'verif':761 'verifi':127 'visual':355,1051 'vite':312 'wait':473 'warn':950 'websit':6,56 'wire':28,422 'within':711 'work':35,70,1073 'workflow':247 'write':971 'www.magicpath.ai':383","prices":[{"id":"b9926487-0b40-492e-9a70-0706ccfbc574","listingId":"9f62fb8f-6e10-4419-9a3a-c6a23aeef640","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"instantX-research","category":"skills","install_from":"skills.sh"},"createdAt":"2026-04-23T13:03:56.344Z"}],"sources":[{"listingId":"9f62fb8f-6e10-4419-9a3a-c6a23aeef640","source":"github","sourceId":"instantX-research/skills/frontend-reactor","sourceUrl":"https://github.com/instantX-research/skills/tree/main/skills/frontend-reactor","isPrimary":false,"firstSeenAt":"2026-04-23T13:03:56.344Z","lastSeenAt":"2026-05-18T19:07:41.524Z"}],"details":{"listingId":"9f62fb8f-6e10-4419-9a3a-c6a23aeef640","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"instantX-research","slug":"frontend-reactor","github":{"repo":"instantX-research/skills","stars":11,"topics":["agent-skills","frontend-ui","ui-design","web-search"],"license":"mit","html_url":"https://github.com/instantX-research/skills","pushed_at":"2026-04-08T11:28:55Z","description":"Open source skills for Agent 🔥","skill_md_sha":"6ff6923511f206b18032d30c9318eb5befb6075b","skill_md_path":"skills/frontend-reactor/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/instantX-research/skills/tree/main/skills/frontend-reactor"},"layout":"multi","source":"github","category":"skills","frontmatter":{"name":"frontend-reactor","description":"Converts cloned website HTML files (from frontend-ui-clone) into production-ready\nReact/Next.js projects. Extracts components, decomposes CSS, sets up routing,\nand wires navigation so buttons and links actually work.\nThree modes: single clone file, multiple clone files, or a URL (auto-discovers and extracts all pages).\nOutputs a runnable Next.js App Router project with `npm run dev` ready to go.\nUse when asked to \"convert to React\", \"make a Next.js project\", \"turn clone into components\",\n\"wire up navigation\", \"split into components\", \"make it a real project\",\n\"convert HTML to React\", \"productionize the clone\", \"rebuild this website\",\nor \"convert this site to React\"."},"skills_sh_url":"https://skills.sh/instantX-research/skills/frontend-reactor"},"updatedAt":"2026-05-18T19:07:41.524Z"}}