--- name: stitch-nextjs-components description: Converts a Stitch screen, a local HTML file, or a URL into production-ready Next.js 15 App Router components — Server vs Client split, dark mode via CSS variables, TypeScript strict, ARIA, and responsive mobile-first layout. Only the Stitch route needs an API key. allowed-tools: - "stitch*:*" - "Bash" - "Read" - "Write" --- # Stitch → Next.js 15 App Router Components You are a senior Next.js engineer. You convert HTML sources — Stitch screens, local files, or URLs — into clean, production-ready components that follow modern App Router conventions — not the Pages Router, not a Vite SPA. Every component ships with dark mode, responsive layout, and basic accessibility out of the box. ## When to use this skill Use this skill (not `react-components`) when: - The target project uses **Next.js 13+** with the **App Router** (`app/` directory) - The user mentions `next.js`, `app router`, `server components`, `server actions`, or `next-themes` - You see `app/layout.tsx`, `app/page.tsx`, or a `next.config.*` file in the project ## Prerequisites An HTML source. Any one of these works: - A **Stitch screen** — needs Stitch MCP access and a generated screen - A **local HTML file** — no Stitch account required - A **URL** — no Stitch account required Also: - Target project has `next-themes` installed for dark mode (or user approves adding it) ## Step 1: Resolve the source Everything downstream reads one file: `temp/source.html`. Get the HTML there by whichever route matches what the user gave you, then continue at Step 2 — the rest of this skill is identical regardless of where the markup came from. **From a Stitch screen:** 1. **Namespace discovery** — `list_tools` to find the Stitch MCP prefix 2. **Fetch metadata** — `[prefix]:get_screen` for the design JSON 3. **Download HTML** — GCS URLs need the reliable downloader: ```bash bash scripts/fetch-stitch.sh "[htmlCode.downloadUrl]" "temp/source.html" ``` 4. **Visual audit** — check `screenshot.downloadUrl` before rewriting. Append `=s0` to that URL for full resolution; the bare URL serves a 512px thumbnail regardless of the `width`/`height` the API reports. **From a local HTML file:** ```bash mkdir -p temp && cp "path/to/design.html" temp/source.html ``` **From a URL:** ```bash bash scripts/fetch-stitch.sh "https://example.com/page" "temp/source.html" ``` Despite the name, that script is a generic hardened downloader — follows redirects, retries transient failures, handles gzip, and fails loudly on an empty result. It does not care whether the URL points at Stitch. **From a screenshot:** there's no upload route — the Stitch MCP API has no image-upload tool. Either recreate the design from a text prompt via `stitch-mcp-generate-screen-from-text`, or hand-write the HTML and use the local-file route above. > Only the Stitch route needs an API key. Converting a local file or a URL works with no Google account at all. ## Step 2: Decide Server Component vs Client Component Apply this decision tree **per component**, not per file: | Has... | Use | |--------|-----| | `onClick`, `onChange`, `useState`, `useEffect`, animations | `'use client'` | | Only renders data, no interactivity | Server Component (no directive needed) | | Wraps a Client Component library | `'use client'` | | Form with Server Action | Server Component + `
` | **Default to Server Components.** Only add `'use client'` when required. This is the single most impactful App Router pattern. ## Step 3: Component architecture ### File structure ``` app/ ├── [route]/ │ ├── page.tsx ← Server Component (route entry) │ └── components/ │ ├── [Name].tsx ← Logic-heavy Client Component │ ├── [Name].module.css ← Scoped styles (optional) │ └── index.ts ← Re-exports src/ ├── components/ │ └── ui/ ← Reusable primitives ├── data/ │ └── mockData.ts ← Static content decoupled from components └── types/ └── index.ts ← Shared TypeScript types ``` ### Rules - **Props contract**: Every component has a `Readonly` interface at the top of the file. - **Data decoupling**: All static text, image URLs, and list data goes in `src/data/mockData.ts`. Components receive data via props. - **No hardcoded colors**: Use CSS custom property classes (`bg-[var(--color-primary)]`) or semantic Tailwind tokens. Never use arbitrary hex in JSX. - **No inline styles**: Exceptions only for truly dynamic values (e.g., width from JS calculation). ## Step 4: Dark mode with CSS variables This project uses a CSS variable approach that works with `next-themes`. Resolve colors from the source in this order, then map them to semantic tokens: 1. **Inline `tailwind.config`** in `` (what Stitch emits) — use it directly if present. 2. **CSS custom properties** already in the source (`:root { --color-primary: ... }`) — common in hand-written and templated HTML. 3. **A linked or inline stylesheet** — parse declared colors, font-families, radii, spacing. 4. **Last resort** — derive tokens from the most frequent computed values in the markup (dominant background, text color, accent, heading/body font, border radius), and tell the user what you inferred so they can correct it. The URL route only downloads the single HTML response — externally-linked stylesheets may not come along for the ride. If none of the above resolves a token, say so instead of inventing a palette. In `app/globals.css`: ```css :root { --color-background: #ffffff; --color-surface: #f4f4f5; --color-primary: /* dominant action color from the source */; --color-primary-foreground: #ffffff; --color-text: #09090b; --color-text-muted: #71717a; --color-border: #e4e4e7; } .dark { --color-background: #09090b; --color-surface: #18181b; --color-primary: /* same hue, lighter shade for dark bg */; --color-primary-foreground: #09090b; --color-text: #fafafa; --color-text-muted: #a1a1aa; --color-border: #27272a; } ``` In `app/layout.tsx`, wrap with `ThemeProvider` from `next-themes`: ```tsx import { ThemeProvider } from 'next-themes' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ) } ``` ## Step 5: Responsive layout All components must work at `sm` (640px), `md` (768px), `lg` (1024px), and `xl` (1280px) breakpoints. Apply these patterns from the source design: - **Navigation**: `hidden md:flex` for desktop nav, `flex md:hidden` for mobile hamburger - **Grid**: `grid-cols-1 sm:grid-cols-2 lg:grid-cols-3` — start single column - **Typography**: `text-2xl md:text-4xl` — scale up on larger screens - **Padding**: `px-4 md:px-8 lg:px-16` — breathe more at wider widths - **Images**: Always use `next/image` with `sizes` attribute to avoid CLS ## Step 6: Accessibility baseline Every component must include these without being asked: - **Semantic HTML**: `