$npx -y skills add google-labs-code/stitch-skills --skill extract-static-htmlExtract self-contained static HTML from a built web application or React components by inlining CSS and images. Use this skill whenever you need to capture a specific UI state, share a static version of a page, or prepare assets for Stitch upload, even if the user just asks to 's
| 1 | # Extract Static HTML |
| 2 | |
| 3 | Extract a self-contained static HTML file from any web application. |
| 4 | |
| 5 | ## Which Strategy to Use |
| 6 | |
| 7 | You MUST ask the user to choose which strategy to use before proceeding. Present the options clearly, **recommend Strategy A** as the preferred default, and **provide a brief pros/cons summary** for each option to help them make an informed decision. |
| 8 | |
| 9 | | | Strategy A (Puppeteer) | Strategy B (Browser Subagent) | |
| 10 | | :--- | :--- | :--- | |
| 11 | | **When** | App runs locally, no auth wall | Need to interact with page first (click, fill forms) | |
| 12 | | **Fidelity** | **Highest — computed styles resolved** | High — rendered DOM | |
| 13 | | **Setup** | **Zero — no mock needed** | Zero — no mock needed | |
| 14 | | **Framework** | **Any** | Any | |
| 15 | | **Output** | **Writes to file — no size limit** | May truncate in agent context | |
| 16 | |
| 17 | > [!WARNING] |
| 18 | > **Checkpoint — User Confirmation Required.** |
| 19 | > You **MUST** ask the user which strategy they prefer before proceeding. |
| 20 | > Present the comparison table above, recommend Strategy A as the default, and |
| 21 | > wait for explicit approval. Do **NOT** make the decision yourself or proceed |
| 22 | > until the user confirms. |
| 23 | |
| 24 | *** |
| 25 | |
| 26 | ## Strategy A: Puppeteer Snapshot (Recommended) |
| 27 | |
| 28 | Launches headless Chrome, captures the fully rendered DOM, and produces a self-contained HTML file with all CSS inlined and images as base64. Works with **any framework** — no MockPage.jsx needed. |
| 29 | |
| 30 | ### Prerequisites |
| 31 | |
| 32 | - App running locally (e.g., `npm run dev`) |
| 33 | - Node.js with `puppeteer` available (check: `node -e "require('puppeteer')"`) |
| 34 | |
| 35 | ### Workflow |
| 36 | |
| 37 | 1. **Start the App** and note the port. |
| 38 | |
| 39 | > [!WARNING] |
| 40 | > **Checkpoint — User Confirmation Required.** |
| 41 | > After starting the local server, you **MUST** pause and ask the user for |
| 42 | > confirmation before running the snapshot script or launching a browser |
| 43 | > subagent. Report the URL and port to the user so they can verify the app |
| 44 | > is running and rendering correctly. Do **NOT** proceed to the snapshot |
| 45 | > step until the user confirms. |
| 46 | |
| 47 | 2. **Run the Snapshot Script**: |
| 48 | ```bash |
| 49 | npx tsx <SKILL_DIR>/scripts/snapshot.ts \ |
| 50 | --url http://localhost:5173 \ |
| 51 | --output .stitch/home.html \ |
| 52 | --wait 2000 |
| 53 | ``` |
| 54 | |
| 55 | 3. **Multiple pages** — run once per route: |
| 56 | ```bash |
| 57 | npx tsx <SKILL_DIR>/scripts/snapshot.ts \ |
| 58 | --url http://localhost:5173 --output .stitch/home.html --wait 2000 |
| 59 | npx tsx <SKILL_DIR>/scripts/snapshot.ts \ |
| 60 | --url http://localhost:5173/pricing --output .stitch/pricing.html --wait 2000 |
| 61 | npx tsx <SKILL_DIR>/scripts/snapshot.ts \ |
| 62 | --url http://localhost:5173/dashboard --output .stitch/dashboard.html --wait 2000 --html-class dark |
| 63 | ``` |
| 64 | |
| 65 | ### Script Flags |
| 66 | |
| 67 | | Flag | Default | Description | |
| 68 | | :--- | :--- | :--- | |
| 69 | | `--url` | *(required)* | URL to capture | |
| 70 | | `--output` | *(required)* | Output file path | |
| 71 | | `--wait` | `1000` | Extra wait (ms) after network idle. Increase for lazy-loading apps. | |
| 72 | | `--viewport` | `1280x800` | Viewport size as `WIDTHxHEIGHT` | |
| 73 | | `--html-class` | — | Class(es) for `<html>` element (e.g., `dark`) | |
| 74 | | `--remove-fixed` | `false` | Remove fixed/sticky elements (cookie banners, chat widgets) | |
| 75 | | `--full-height` | `false` | Resize viewport to full scroll height | |
| 76 | | `--title` | — | Override page title | |
| 77 | |
| 78 | ### What It Does Automatically |
| 79 | |
| 80 | - Inlines all `<link rel="stylesheet">` → `<style>` blocks |
| 81 | - Converts `<img>` `src` **and `srcset`** → base64 data URIs (skips fonts) |
| 82 | - Inlines `<source srcset>` URLs as base64 |
| 83 | - Removes failed/dead `srcset` entries so the browser falls back to the inlined `src` |
| 84 | - Removes `<script>` tags, Vite overlay, Next.js dev indicators |
| 85 | - Resolves relative CSS `url()` paths before inlining |
| 86 | |
| 87 | ### Framework Notes |
| 88 | |
| 89 | | Framework | Notes | |
| 90 | | :--- | :--- | |
| 91 | | **React + Vite** | Works out of the box. `--wait 1000`. | |
| 92 | | **Next.js** | `--wait 3000` for SSR hydration. URL: `http://localhost:3000`. `<img srcset>` from `/_next/image` is auto-inlined as base64. | |
| 93 | | **Vue / Nuxt** | Works out of the box. | |
| 94 | | **Svelte / SvelteKit** | Works out of the box. | |
| 95 | | **Storybook** | Use story URL: `--url http://localhost:6006/?path=/story/...` | |
| 96 | | **SSR (Webpack)** | May need longer `--wait`. | |
| 97 | |
| 98 | ### Troubleshooting |
| 99 | |
| 100 | | Issue | Solution | |
| 101 | | :--- | :--- | |
| 102 | | Images missing | Increase `--wait` | |
| 103 | | Images show as broken after server stops | Verify `srcset` was inlined — check log for "Inlined N images". If `srcset` URLs failed, they are auto-removed so `src` (inlined) is used. | |
| 104 | | Next.js `/_next/image` not inlined | Ensure the dev server is running when snapshot runs — the |