How to Detect and Extract Layered Image Compositions in ai-website-cloner-template

Run the browser-side discovery script from .windsurf/workflows/clone-website.md to capture every <img> element's parent context, sibling count, and CSS positioning data, then flag containers with siblingCount > 1 or background-image declarations as layered compositions requiring multi-asset extraction.

The ai-website-cloner-template treats layered images as first-class extraction targets to ensure cloned websites remain pixel-perfect. Unlike single raster files, these compositions consist of multiple stacked assets—background gradients, foreground illustrations, and overlay icons—that must be detected and reconstructed individually. This guide walks through the repository's built-in detection logic and provides the exact JavaScript implementation used to identify these complex visual stacks.

What Are Layered Image Compositions?

Layered image compositions are UI sections that appear as single pictures but are actually constructed from multiple raster or SVG assets stacked via CSS positioning. A typical example combines a background watercolor texture, a foreground UI mock-up, and small icon overlays. According to the docs/research/INSPECTION_GUIDE.md, missing any layer during extraction results in visually incomplete clones, making detection critical for fidelity.

Where Detection Logic Lives

The repository centralizes its layered-asset detection guidance across three key locations:

Component File Path Purpose
Workflow guidance .windsurf/workflows/clone-website.md Narrative explaining that sections may contain multiple layers; requires enumerating all <img> and background-image elements. Contains the extraction script at lines 88-124.
CLI reference .opencode/commands/clone-website.md Duplicates the layered-asset requirements for command-line agent usage.
Inspection checklist docs/research/INSPECTION_GUIDE.md High-level audit checklist explicitly flagging "Z-index layers" and "Layered assets matter" as critical inspection points.

Step-by-Step Detection Process

Running the Browser Discovery Script

Detection executes within the target page's browser context using Chrome MCP, Playwright MCP, or any supported browser tool. The script gathers comprehensive metadata for every image element to reveal stacking relationships.

The process iterates over all <img> elements and extracts:

  • src, alt, and natural dimensions
  • parentClasses – CSS classes of the direct container (img.parentElement?.className)
  • siblingCount – Number of sibling <img> elements in the same parent ([...img.parentElement.querySelectorAll('img')].length)
  • position and zIndex – Computed CSS values to identify absolutely-positioned overlays

Additionally, the script scans every DOM node for non-none backgroundImage values and records the element's tag and class.

Identifying Stacked Layers

A container qualifies as a layered composition when the returned JSON shows either:

  • siblingCount > 1 inside the same parent element
  • Presence of both a background-image declaration and child <img> elements within the same container

These patterns indicate stacked visual assets requiring separate extraction.

Code Implementation

Browser Extraction Script

Execute this script in the browser console (as implemented in .windsurf/workflows/clone-website.md lines 88-124) to generate the layer map:

// Run via browser MCP (Chrome DevTools or Playwright)
const layers = (() => {
  // Gather <img> elements with parent context
  const imgs = [...document.querySelectorAll('img')].map(img => ({
    src: img.src || img.currentSrc,
    alt: img.alt,
    width: img.naturalWidth,
    height: img.naturalHeight,
    parentClasses: img.parentElement?.className ?? '',
    siblingCount:
      img.parentElement
        ? [...img.parentElement.querySelectorAll('img')].length
        : 0,
    position: getComputedStyle(img).position,
    zIndex: getComputedStyle(img).zIndex,
  }));

  // Gather background-image declarations
  const bgs = [...document.querySelectorAll('*')]
    .filter(el => {
      const bg = getComputedStyle(el).backgroundImage;
      return bg && bg !== 'none';
    })
    .map(el => ({
      url: getComputedStyle(el).backgroundImage,
      element: `${el.tagName.toLowerCase()}.${el.className.split(' ')[0] || ''}`,
    }));

  return JSON.stringify({ imgs, bgs }, null, 2);
})();

console.log(layers);

Interpreting the Output

Save the JSON output to identify layered compositions. This example shows a detected stack:

{
  "imgs": [
    {
      "src": "/images/hero-bg.jpg",
      "alt": "",
      "width": 1920,
      "height": 1080,
      "parentClasses": "hero-section",
      "siblingCount": 2,
      "position": "relative",
      "zIndex": "auto"
    },
    {
      "src": "/images/hero-illustration.png",
      "alt": "",
      "width": 400,
      "height": 350,
      "parentClasses": "hero-section",
      "siblingCount": 2,
      "position": "absolute",
      "zIndex": "10"
    }
  ],
  "bgs": [
    {
      "url": "url(\"/images/hero-gradient.png\")",
      "element": "section.hero-section"
    }
  ]
}

Here, the hero-section container contains two sibling images (siblingCount: 2) plus a background-image, confirming a three-layer composition.

Extracting and Storing Assets

Downloading Identified Layers

Once detected, download each asset using curl, wget, or a Node.js script. Store them under public/images/ with descriptive prefixes indicating their layer order:


# Example downloads for the hero composition

curl -o public/images/hero-gradient.png https://example.com/images/hero-gradient.png
curl -o public/images/hero-bg.jpg https://example.com/images/hero-bg.jpg
curl -o public/images/hero-illustration.png https://example.com/images/hero-illustration.png

Recording in Component Specs

Document the extracted layers in the component specification's "Assets" block (as defined in the workflow template):


## Assets

- Background image: `public/images/hero-gradient.png`
- Base photo: `public/images/hero-bg.jpg`
- Overlay illustration: `public/images/hero-illustration.png`

Reconstructing in React Components

Assemble the layers in your React component using absolute positioning or Tailwind utilities to preserve the original stacking:

// src/components/HeroSection.tsx
export default function HeroSection() {
  return (
    <section className="relative overflow-hidden">
      {/* Background gradient layer */}
      <div 
        className="absolute inset-0 z-0"
        style={{ backgroundImage: 'url(/images/hero-gradient.png)' }}
      />
      
      {/* Base photo layer */}
      <img
        src="/images/hero-bg.jpg"
        alt=""
        className="relative z-10 w-full h-auto object-cover"
      />
      
      {/* Overlay illustration layer */}
      <img
        src="/images/hero-illustration.png"
        alt=""
        className="absolute inset-0 z-20 m-auto w-48 h-48"
      />
    </section>
  );
}

Full Pipeline Checklist

Follow this workflow derived from .windsurf/workflows/clone-website.md to ensure complete extraction:

  • Run the layer detection script in the target page's browser context
  • Verify that any container with siblingCount > 1 or a background-image is logged
  • Download every identified asset into public/images/
  • Add the assets to the component spec under the Assets heading
  • Build the React component using stacked <img> elements or background-image CSS with proper z-index values
  • Run npm run build followed by npx tsc --noEmit to verify compilation

Summary

  • Layered compositions are UI sections built from multiple stacked images that appear as single visuals.
  • The detection logic resides in .windsurf/workflows/clone-website.md (lines 88-124) and relies on analyzing siblingCount, parentClasses, and zIndex values.
  • Browser-side execution via MCP tools captures both <img> elements and CSS background-image declarations.
  • Containers with multiple sibling images or combined img/background assets require multi-file extraction.
  • Store extracted assets in public/images/ and reconstruct layers usingabsolute positioning or Tailwind z-index utilities in React components.

Frequently Asked Questions

How do I distinguish between a single image and a layered composition?

A single image exists in isolation within its parent container, showing siblingCount: 1 and no background-image on the parent. A layered composition shows siblingCount > 1 (multiple <img> elements sharing a parent) or a combination of background-image CSS and child images. The detection script in .windsurf/workflows/clone-website.md automatically flags these patterns based on DOM structure analysis.

Can I automate the download process for detected layers?

Yes. Pipe the JSON output from the browser script into a Node.js utility script (such as scripts/download-assets.mjs) that iterates over the src and url fields. Use fetch() or https.get() to stream each asset into public/images/ while preserving the original filenames or applying descriptive prefixes like hero-bg and hero-overlay.

What should I do if an image uses CSS background-image instead of an <img> tag?

The detection script specifically queries computed styles for backgroundImage properties across all DOM nodes. These appear in the bgs array of the output JSON. When reconstructing the component, apply these as background-image CSS properties on <div> elements or section containers rather than <img> tags, ensuring you match the original z-index and positioning values captured during detection.

Where does the ai-website-cloner-template expect me to store extracted layered assets?

The template convention places all extracted visual assets in the public/images/ directory. Reference these files in your component specs using relative paths starting with public/images/, and import them in React components using absolute paths like /images/hero-bg.jpg. This ensures Next.js or similar frameworks correctly serve the files as static assets.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →