pengelbrecht/skills · Archived

clone-website

Reverse-engineer and clone any website into production-ready Astro 6 code. Extracts assets, exact CSS, content, and behaviors section-by-section, writes component specs, then dispatches parallel builder agents in worktrees. Use when user asks to: clone website, vibe clone, replicate landing page, copy website design, rebuild this site, recreate this page, clone specific sections (hero, pricing, footer, etc). "rebuild this site in Astro", "clone the hero section from [url]", "copy this design".

First seen Mar 26, 2026

Installation

$ npx skills add pengelbrecht/skills --skill clone-website

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from pengelbrecht/skills · top by installs.

npx skills add pengelbrecht/skills

Browse all from pengelbrecht/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 1
License LICENSE
Default branch main
Open issues 0
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 24,709 B
  • docs SUMMARY.md 602 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 5 installs

SKILL.md

Clone Website Skill

Reverse-engineer any website into production-ready Astro 6 code using agent-browser for headless scraping. Extraction and construction happen in parallel — as each section is inspected, a detailed spec is written and handed to a builder agent working in an isolated git worktree.

Guiding Principles

  1. Completeness Beats Speed — Every builder must have everything: screenshot, exact CSS values, downloaded assets with local paths, real text, component structure. Extract the extra property rather than ship an incomplete brief.
  2. Small Tasks, Perfect Results — Single-component sections get one builder agent; multi-variant sections get one agent per variant. If a builder prompt exceeds ~150 lines of spec, split the section.
  3. Real Content, Real Assets — Extract actual text via element.textContent, download every <img> and <video>, convert inline <svg> to components. Check for layered compositions (background + foreground + overlay).
  4. Foundation First — Global CSS tokens, fonts, TypeScript types, and global assets must exist before any section building. Sequential, non-negotiable. Everything after is parallel.
  5. Extract How It Looks AND How It Behaves — Document appearance (exact computed CSS via getComputedStyle()) AND behavior (what changes, trigger, transition). Not "looks like text-lg" but the actual computed value.
  6. Identify Interaction Model Before Building — Answer definitively: Is this click-driven, scroll-driven, hover-driven, time-driven, or mixed? Scroll first before clicking. If things change as you scroll, extract the mechanism. If not, then click/hover test. This is the #1 most expensive mistake — wrong interaction model requires a complete rewrite.
  7. Extract Every State, Not Just Default — Click each tab/button, extract content per state. For scroll-dependent elements: capture computed styles at position 0, scroll past trigger, capture again, diff them.
  8. Spec Files Are Source of Truth — Every component gets a spec file BEFORE dispatch. The spec is the contract between extraction and builder. Non-optional.
  9. Build Must Always Compile — Every builder verifies the build passes before finishing. Broken build is never acceptable, even temporarily.

Prerequisites

Before starting, verify tools are installed:

agent-browser --version   # Required: headless scraping
node --version             # Required: Astro project
npm --version              # Required: package management
curl --version             # Optional: downloading images

If agent-browser is not installed:

npm install -g agent-browser && agent-browser install

If any required tool is missing, stop and inform the user with install instructions before proceeding.

Workflow

Execute these 5 phases in order. Never skip Phase 2 or Phase 5.

Phase 1: Reconnaissance

IMPORTANT: Run all agent-browser commands sequentially, one at a time. They share a single browser session. Do NOT run them as parallel tool calls — if one fails, parallel siblings get cancelled.

IMPORTANT: Shell escaping with agent-browser eval. Complex JS with nested quotes, arrow functions, or template literals often breaks due to shell escaping. Prefer these approaches:

  • Use agent-browser get text, get html, get attr built-in commands instead of eval when possible
  • For eval, keep JS simple — avoid nested quotes and callbacks
  • For complex JS, pipe via stdin: echo 'your code here' | agent-browser eval --stdin
  • Or use base64: agent-browser eval -b "$(echo 'your code' | base64)"

Step 1: Open & Screenshot

agent-browser open [TARGET_URL]
agent-browser wait --load networkidle

# Full-page screenshots at desktop and mobile
agent-browser screenshot --full ./docs/design-references/desktop-full.png
agent-browser eval "window.innerWidth = 390; window.innerHeight = 844; void(0)"
agent-browser screenshot --full ./docs/design-references/mobile-full.png
agent-browser eval "window.innerWidth = 1440; window.innerHeight = 900; void(0)"

Step 2: Extract Page Content & Meta

agent-browser eval "document.documentElement.outerHTML"
agent-browser get text "body"
agent-browser eval "document.title"
agent-browser snapshot

echo 'JSON.stringify(Array.from(document.querySelectorAll("meta")).map(function(m) { return { name: m.name || m.getAttribute("property"), content: m.content } }))' | agent-browser eval --stdin

Step 3: Comprehensive Asset Discovery

Run this single script to find all images, videos, background images, SVGs, fonts, and favicons:

cat <<'JSEOF' | agent-browser eval --stdin
JSON.stringify({
  images: Array.from(document.querySelectorAll("img")).map(function(img) {
    return {
      src: img.src || img.currentSrc, alt: img.alt,
      naturalWidth: img.naturalWidth, naturalHeight: img.naturalHeight,
      parentClasses: img.parentElement ? img.parentElement.className : "",
      position: getComputedStyle(img).position,
      zIndex: getComputedStyle(img).zIndex
    };
  }),
  videos: Array.from(document.querySelectorAll("video, video source, iframe[src*=vimeo], iframe[src*=youtube], iframe[src*=wistia]")).map(function(el) {
    return {
      tag: el.tagName, src: el.src || el.getAttribute("src"),
      poster: el.poster || null, autoplay: el.autoplay || null,
      loop: el.loop || null, muted: el.muted || null
    };
  }),
  backgroundImages: Array.from(document.querySelectorAll("*")).filter(function(el) {
    var bg = getComputedStyle(el).backgroundImage;
    return bg && bg !== "none";
  }).map(function(el) {
    return {
      url: getComputedStyle(el).backgroundImage,
      element: el.tagName + "." + (el.className || "").toString().split(" ")[0]
    };
  }),
  svgCount: document.querySelectorAll("svg").length,
  favicons: Array.from(document.querySelectorAll("link[rel*=icon]")).map(function(l) {
    return { href: l.href, sizes: l.sizes ? l.sizes.toString() : null };
  })
});
JSEOF

Step 4: Font Detection & Extraction

Detect all fonts — Google Fonts, self-hosted/commercial, and system fonts:

cat <<'JSEOF' | agent-browser eval --stdin
var fontLinks = Array.from(document.querySelectorAll("link[href*='fonts.googleapis.com'], link[href*='fonts.gstatic.com'], link[href*='use.typekit.net']")).map(function(l) {
  return { href: l.href, type: "hosted-service" };
});
var fontFaces = Array.from(document.styleSheets).flatMap(function(s) {
  try { return Array.from(s.cssRules); } catch(e) { return []; }
}).filter(function(r) { return r instanceof CSSFontFaceRule; }).map(function(r) {
  var src = r.style.getPropertyValue("src");
  return {
    family: r.style.getPropertyValue("font-family").replace(/['"]/g, ""),
    weight: r.style.getPropertyValue("font-weight"),
    style: r.style.getPropertyValue("font-style"),
    src: src,
    isGoogle: src.indexOf("fonts.gstatic.com") > -1,
    urls: (src.match(/url\(["']?([^"')]+)["']?\)/g) || []).map(function(u) {
      return u.replace(/url\(["']?|["']?\)/g, "");
    })
  };
});
var usedFamilies = Array.from(new Set(
  Array.from(document.querySelectorAll("*")).slice(0, 300).map(function(el) {
    return getComputedStyle(el).fontFamily;
  })
));
JSON.stringify({ fontLinks: fontLinks, fontFaces: fontFaces, usedFamilies: usedFamilies });
JSEOF

Font handling strategy:

  • Google Fonts: Use the detected Google Fonts URL or configure via @fontsource
  • Commercial/self-hosted fonts: Ask user "I detected commercial font [name]. Do you have a license? (y/n)". If yes: download the .woff2 files from the extracted URLs and configure via @font-face in global.css. If no: suggest the closest Google Font alternative.
  • System fonts: Use as-is in the font stack

Step 5: Deep CSS Extraction (getComputedStyle Walker)

Extract exact computed styles for any element and its children (up to 4 levels deep). Use this per-section during Phase 3 spec writing:

# Replace SELECTOR with actual CSS selector (e.g., "section.hero", "#pricing", "nav")
cat <<'JSEOF' | agent-browser eval --stdin
(function(selector) {
  var el = document.querySelector(selector);
  if (!el) return JSON.stringify({ error: "Element not found: " + selector });
  var props = [
    "fontSize","fontWeight","fontFamily","lineHeight","letterSpacing","color",
    "textTransform","textDecoration","backgroundColor","background",
    "padding","paddingTop","paddingRight","paddingBottom","paddingLeft",
    "margin","marginTop","marginRight","marginBottom","marginLeft",
    "width","height","maxWidth","minWidth","maxHeight","minHeight",
    "display","flexDirection","justifyContent","alignItems","gap",
    "gridTemplateColumns","gridTemplateRows",
    "borderRadius","border","borderTop","borderBottom","borderLeft","borderRight",
    "boxShadow","overflow","overflowX","overflowY",
    "position","top","right","bottom","left","zIndex",
    "opacity","transform","transition","cursor",
    "objectFit","objectPosition","mixBlendMode","filter","backdropFilter",
    "whiteSpace","textOverflow","WebkitLineClamp"
  ];
  function extractStyles(element) {
    var cs = getComputedStyle(element);
    var styles = {};
    props.forEach(function(p) {
      var v = cs[p];
      if (v && v !== "none" && v !== "normal" && v !== "auto" && v !== "0px" && v !== "rgba(0, 0, 0, 0)")
        styles[p] = v;
    });
    return styles;
  }
  function walk(element, depth) {
    if (depth > 4) return null;
    var children = Array.from(element.children);
    return {
      tag: element.tagName.toLowerCase(),
      classes: (element.className || "").toString().split(" ").slice(0, 5).join(" "),
      text: element.childNodes.length === 1 && element.childNodes[0].nodeType === 3
        ? element.textContent.trim().slice(0, 200) : null,
      styles: extractStyles(element),
      images: element.tagName === "IMG"
        ? { src: element.src, alt: element.alt, naturalWidth: element.naturalWidth, naturalHeight: element.naturalHeight }
        : null,
      childCount: children.length,
      children: children.slice(0, 20).map(function(c) { return walk(c, depth + 1); }).filter(Boolean)
    };
  }
  return JSON.stringify(walk(el, 0), null, 2);
})("SELECTOR");
JSEOF

Step 6: Design Token Extraction

# Extract CSS custom properties
cat <<'JSEOF' | agent-browser eval --stdin
var props = Array.from(document.styleSheets)
  .flatMap(function(s) { try { return Array.from(s.cssRules); } catch(e) { return []; } })
  .filter(function(r) { return r.selectorText === ":root"; })
  .flatMap(function(r) { return r.cssText.match(/--[^:]+:[^;]+/g) || []; });
JSON.stringify(props);
JSEOF

# Extract key colors from semantic elements
cat <<'JSEOF' | agent-browser eval --stdin
var sels = ["h1","h2","h3","p","a","button","nav","footer","header","section"];
var result = sels.map(function(sel) {
  var el = document.querySelector(sel);
  if (!el) return null;
  var s = getComputedStyle(el);
  return { el: sel, color: s.color, bg: s.backgroundColor, font: s.fontFamily, size: s.fontSize, weight: s.fontWeight, lineHeight: s.lineHeight };
}).filter(function(x) { return x !== null; });
JSON.stringify(result);
JSEOF

Step 7: Interaction Sweep

Mandatory. Observe behaviors before building — wrong interaction model requires a complete rewrite.

# 1. Slow scroll top-to-bottom — observe what changes
agent-browser eval "window.scrollTo(0, 0)"
agent-browser screenshot ./docs/design-references/scroll-top.png
agent-browser eval "window.scrollTo(0, 300)"
agent-browser screenshot ./docs/design-references/scroll-300.png
agent-browser eval "window.scrollTo(0, document.body.scrollHeight)"
agent-browser screenshot ./docs/design-references/scroll-bottom.png
agent-browser eval "window.scrollTo(0, 0)"

While scrolling, note:

  • Does the header shrink, gain a shadow, or change background?
  • Do elements fade/slide in as they enter the viewport?
  • Is there scroll-snap behavior?
  • Are there parallax layers?
  • Do tabs/accordions auto-switch based on scroll position?
# 2. Detect smooth scroll libraries (Lenis, Locomotive Scroll)
cat <<'JSEOF' | agent-browser eval --stdin
JSON.stringify({
  hasLenis: !!document.querySelector(".lenis") || !!document.querySelector("[data-lenis]") || typeof window.lenis !== "undefined",
  hasLocomotiveScroll: !!document.querySelector("[data-scroll-container]") || !!document.querySelector(".locomotive-scroll"),
  hasScrollSnap: Array.from(document.querySelectorAll("*")).some(function(el) {
    var ss = getComputedStyle(el).scrollSnapType;
    return ss && ss !== "none";
  }),
  hasStickyElements: Array.from(document.querySelectorAll("*")).filter(function(el) {
    return getComputedStyle(el).position === "sticky";
  }).map(function(el) { return el.tagName + "." + (el.className || "").toString().split(" ")[0]; })
});
JSEOF
# 3. Click interactive elements — observe state changes
# For each tab, button, accordion trigger: click and screenshot
agent-browser click "[data-tab]:first-child"
agent-browser screenshot ./docs/design-references/tab-state-1.png
agent-browser click "[data-tab]:nth-child(2)"
agent-browser screenshot ./docs/design-references/tab-state-2.png
# 4. Responsive check at key breakpoints
agent-browser eval "window.innerWidth = 768; void(0)"
agent-browser screenshot --full ./docs/design-references/tablet-full.png
agent-browser eval "window.innerWidth = 390; void(0)"
agent-browser screenshot --full ./docs/design-references/mobile-full.png
agent-browser eval "window.innerWidth = 1440; void(0)"

Step 8: Multi-State CSS Extraction

For any element that changes state (scroll-triggered, hover, click), capture before/after:

# Example: Header before/after scroll
# State A: at top
agent-browser eval "window.scrollTo(0, 0)"
cat <<'JSEOF' | agent-browser eval --stdin
var el = document.querySelector("header");
var cs = getComputedStyle(el);
JSON.stringify({
  state: "A-top", height: cs.height, padding: cs.padding,
  backgroundColor: cs.backgroundColor, boxShadow: cs.boxShadow,
  backdropFilter: cs.backdropFilter, transition: cs.transition
});
JSEOF

# State B: scrolled
agent-browser eval "window.scrollTo(0, 200)"
cat <<'JSEOF' | agent-browser eval --stdin
var el = document.querySelector("header");
var cs = getComputedStyle(el);
JSON.stringify({
  state: "B-scrolled", height: cs.height, padding: cs.padding,
  backgroundColor: cs.backgroundColor, boxShadow: cs.boxShadow,
  backdropFilter: cs.backdropFilter, transition: cs.transition
});
JSEOF

Then diff: "Property X: VALUEA → VALUEB, triggered by scroll > 200px, transition: all 0.3s ease"

Step 9: Page Topology

Document the page structure to docs/research/PAGE_TOPOLOGY.md:

  • Section names and visual order
  • Fixed vs. flow content
  • Layout structure (single column, sidebar, etc.)
  • Dependencies between sections
  • Interaction model per section (static / click-driven / scroll-driven / hover-driven / time-driven)

Partial Cloning

If a specific section is requested, scope extraction:

agent-browser get html "#hero"
agent-browser get html "section.pricing"
agent-browser snapshot -s "#hero"

Step 10: Close Browser

agent-browser close

Phase 2: Analysis (MANDATORY)

STOP. Present analysis to user before ANY code generation.

Read [references/analysis-template.md](references/analysis-template.md) and fill out the template with:

  • Detected sections and component breakdown
  • Extracted design tokens (colors, typography, spacing)
  • Font inventory: Google Fonts vs commercial (with license question) vs system
  • Image inventory with download/fallback status
  • Behaviors detected (scroll-triggered, hover states, animations)
  • Interaction model per section
  • Proposed file structure

Ask user: "Ready to proceed? (y/n or request modifications)"

Do not generate code until user confirms.

Phase 3: Foundation Build (Sequential — Not Delegated)

These must be done sequentially before any parallel builder work:

  1. src/styles/global.css — Design tokens as CSS variables + Tailwind import
  2. Font setup — Google Fonts via @fontsource or commercial fonts via @font-face in global.css
  3. src/layouts/Layout.astro — Base layout with SEO metadata and font references
  4. Download all assets to public/images/, public/videos/, public/fonts/
  5. Extract inline SVGs as components if reused across sections
  6. astro.config.mjs + package.json — Astro config with integrations
  7. Verify: npm run build passes before proceeding

Reference [references/tech-stack.md](references/tech-stack.md) for Astro 6 conventions.

Phase 4: Component Specification & Parallel Build

For each section of the page, run this loop:

Step 1: Extract (Per-Section)

  • Screenshot the section in isolation
  • Run the deep CSS extraction script (Step 5 from Phase 1) on the section's container
  • For multi-state elements: capture styles at State A, trigger change, capture State B, diff
  • Extract all text content verbatim using agent-browser get text
  • For tabbed content: click each tab, extract content per state
  • Identify all assets (including layered/overlapping images)

Step 2: Write Component Spec File

Write a spec to docs/research/components/<component-name>.spec.md using the template in [references/component-spec-template.md](references/component-spec-template.md).

Pre-dispatch checklist:

  • Spec file has ALL sections filled out
  • Every CSS value is from getComputedStyle(), not estimated
  • Interaction model identified and documented
  • For stateful components: every state's content and styles captured
  • For scroll-driven: trigger threshold, before/after styles, transition recorded
  • For hover: before/after values and transition timing recorded
  • All images identified (including overlays, layered compositions)
  • Responsive behavior documented for desktop, tablet, and mobile
  • Text content verbatim, not paraphrased

Step 3: Dispatch Builder Agent (Worktree)

Use the Agent tool with isolation: "worktree" to dispatch a builder for each component:

  • Simple section (1–2 sub-components): one builder agent
  • Complex section (3+ sub-components): one agent per sub-component + one for the wrapper

Each builder agent receives:

  • The full component spec inline (from the spec file)
  • Screenshot path for visual reference
  • Shared imports: design tokens from global.css, any shared SVG components
  • Target file path (e.g., src/components/landing/Hero.astro)
  • Instruction to verify build: npm run build before finishing

Do NOT wait for builders to complete before extracting the next section. Dispatch and move on.

Reference [references/component-patterns.md](references/component-patterns.md) for Astro component structure.

Step 4: Merge & Verify

As builder agents complete:

  • Their worktree branches will be available for merging
  • After each merge, verify npm run build passes
  • Fix type errors or conflicts immediately

Step 5: Page Assembly

After all builders complete:

  1. Import all section components into src/pages/index.astro
  2. Implement page-level layout from topology doc (scroll containers, sticky positioning, z-index)
  3. Implement page-level behaviors: scroll-snap, scroll-driven animations, smooth scroll (Lenis if detected)
  4. Verify: npm run build passes clean

Phase 5: Visual QA

Do not skip this phase.

  1. Take side-by-side screenshots: original vs. clone at 1440px and 390px
  2. Compare section by section, top to bottom
  3. For each discrepancy:

- Check if the spec was accurate - Re-extract from the original if needed - Update the component

  1. Test all interactions: scroll behaviors, click states, hover effects, animations
  2. Verify responsive behavior at 1440px, 768px, and 390px

Report to user:

  • Total sections built
  • Total components created
  • Total spec files written
  • Total assets downloaded
  • Build result (npm run build)
  • Any remaining visual discrepancies or known gaps

Tech Stack (Fixed)

Layer Technology
Framework Astro 6 (static + islands)
Language TypeScript (strict)
Styling Tailwind CSS v4
Interactive Islands React (via @astrojs/react, only when needed)
Icons Lucide (astro-icon or inline SVG)
Fonts Google Fonts via @fontsource, or commercial fonts via local @font-face (with user license confirmation), or system fonts

When to Use Islands

Most cloned landing pages are static and need zero client-side JS. Use Astro's default: render everything as static HTML.

Only create interactive React islands (client:* directives) when the cloned site has:

  • Mobile hamburger menu (toggle open/close) → client:idle
  • Tabs, accordions, carousels → client:visible
  • Form validation with live feedback → client:load
  • Animated counters or scroll-triggered animations → client:visible

Static alternatives preferred when possible:

  • CSS-only hamburger via <details>/<summary> or :target
  • CSS-only accordions via <details> elements
  • CSS scroll-snap carousels

Image Handling

  1. Extract all image URLs using the asset discovery script (Phase 1, Step 3)
  2. Check for layered/composite images — a section may have background + foreground + overlay
  3. Attempt download via curl or agent-browser
  4. On failure, use Unsplash fallback:

- Hero: https://images.unsplash.com/photo-[id]?w=1920&h=1080 - Avatars: https://images.unsplash.com/photo-[id]?w=100&h=100 - Features: Prefer Lucide icons or inline SVGs over images

  1. Save to public/images/ with descriptive kebab-case names
  2. For optimized images, store in src/images/ and use <Image /> from astro:assets

Partial Cloning

Parse user request for section filters:

Request Action
"clone the hero from X" Generate only Hero.astro
"clone pricing and footer" Generate Pricing.astro + Footer.astro
"clone X" (no filter) Full page clone

Code Standards

  • Mobile-first responsive design
  • Use Tailwind arbitrary values for pixel-perfection: w-[347px]
  • Extract repeated colors to CSS variables in global.css
  • Use class:list for conditional classes in .astro files
  • Add brief comments only for non-obvious patterns
  • Prefer gap-* over margins for flex/grid spacing
  • Prefer static .astro components; only use .tsx islands when interactivity is required
  • Use <Image /> from astro:assets for optimized images in src/
  • Use standard <img> for images in public/

What NOT to Do

  • Don't build click-based when original is scroll-driven (or vice versa). Determine interaction model FIRST by scrolling before clicking. This is the #1 most expensive mistake — requires a complete rewrite, not a CSS fix.
  • Don't extract only the default state — click each tab, scroll past triggers, capture every state's content and CSS
  • Don't miss overlay/layered images — check every container's DOM for multiple <img> elements and positioned overlays
  • Don't build mockup components for sections that are actually videos/animations — check for <video>, Lottie, canvas first
  • Don't approximate CSS values — "looks like text-lg" is wrong if computed is 18px with different line-height; use exact getComputedStyle() values
  • Don't dispatch builders without spec files — the spec forces exhaustive extraction and creates an auditable artifact
  • Don't scope builder agents too broadly — if the prompt exceeds ~150 lines of spec, split the section
  • Don't bundle unrelated sections (CTA + footer) — treat as separate components
  • Don't skip responsive extraction — test at 1440, 768, 390 during reconnaissance
  • Don't forget smooth scroll libraries (Lenis, Locomotive Scroll) — default scroll feels noticeably different
  • Don't reference external docs in builder prompts — inline the spec's CSS values directly