1 ---
2 name: website-cloning
3 description: Clone any website as a pixel-perfect React + Vite app using Playwright extraction.
4 ---
5
6 # Clone Website — Pixel-Perfect Methodology
7
8 Reverse-engineer and rebuild a target website as an exact replica React + Vite clone. Every font, color, icon, image, section, background, transition, and interaction must match the original. Zero guessing, zero placeholders.
9
10 ## Cardinal Rules
11
12 1. **Raw HTML is the source of truth.** Before building ANY section, read the corresponding portion of `raw.html`. Never build from memory, screenshots alone, or guessed structure.
13 2. **One component per visual pattern.** Never reuse a component designed for one layout (e.g., product cards with prices) for a structurally different layout (e.g., category cards with just names). If two sections look different, they get different components or distinct CSS classes.
14
15 3. **All assets downloaded before building starts.** Every image, font, SVG, and video must be local in `public/` before any component code is written. No mid-build downloads.
16 4. **Build all sections, then verify the full page.** Building and screenshotting section-by-section is too slow. Build all components from raw HTML, assemble in App.tsx, then take a full-page screenshot and fix any discrepancies. This is 3-5x faster than per-section verification loops.
17
18 5. **No fabricated content.** Every heading, subtitle, button label, badge, price, and link must come from the source HTML. Never invent text that doesn't exist on the original page.
19 6. **Replace the scaffolded CSS entirely.** The `createArtifact`scaffold includes Tailwind/shadcn boilerplate. Replace`index.css` completely with plain CSS — a Google Font import, CSS reset, CSS variables for design tokens, and nothing else. Clone pages don't use component libraries.
20
21 ## Anti-Patterns (Common Mistakes to Avoid)
22
23 | Mistake | Correct Approach |
24
25 |---------|-----------------|
26
27 | Outlined/bordered buttons when original uses filled/solid | Check `raw.html`for button classes and extract`background-color`, not`border` |
28
29 | Center-aligned text when original is left-aligned | Extract `text-align` from computed styles |
30
31 | Adding badges/labels that don't exist in original | Only add elements that exist in `raw.html` |
32
33 | Skipping sections or changing their order | Follow the section inventory checklist exactly |
34
35 | Using placeholder images | Download all images in Phase 1 before building |
36
37 | Reusing `ProductCard` for category grids | Each visually distinct card type gets its own component |
38
39 | Guessing font sizes, colors, spacing | Extract exact computed values; never approximate |
40
41 | Building from screenshot interpretation alone | Always cross-reference `raw.html` for structure and content |
42
43 | Using an SVG `<text>`element for the logo | Extract the real SVG logo paths from`raw.html` |
44
45 | Running per-section screenshot QA loops | Build all sections, then do one full-page verify pass |
46
47 | Keeping Tailwind/shadcn/Radix in a clone | Replace index.css with plain CSS; remove unused deps |
48
49 | Translating/anglicizing text from a non-English page | Clone must use the EXACT language shown on the target page |
50
51 | Guessing the announcement bar color | Extract computed `background-color` from the banner element |
52
53 | Centering the logo when it's left-aligned | Take a header screenshot and compare logo position |
54
55 | Omitting the account/rewards bar text | Extract ALL header elements including loyalty/rewards UI |
56
57 ## Legitimate Use Policy
58
59 Before cloning, confirm the user's intent is legitimate. Ask:
60
61 1. "Is this your own website or your client's website?"
62 2. "What is this clone for?"
63
64 Acceptable: rebuilding your own site, design reference/learning, staging copy, platform migration.
65
66 **Refuse** if: impersonation, phishing, traffic theft, trademark infringement, or deception.
67
68 For non-owned sites (design inspiration), remind the user to replace logos, brand names, trademarks, product data, and contact info with their own.
69
70 ## Prerequisites
71
72 ```bash
73
74 pip install playwright
75
76 CHROMIUM_PATH=$(find /nix/store -maxdepth 4 -name "chromium" -type f 2>/dev/null | head -1)
77
78 echo "Chromium at: $CHROMIUM_PATH"
79
80 ```
81
82 ### Critical Playwright settings
83
84 - Always use `--no-sandbox` args
85 - Use `wait_until="domcontentloaded"`(not`"networkidle"`)
86
87 - Add `page.wait_for_timeout(5000)` after navigation
88 - Set `timeout=60000`on all`page.goto()` calls
89
90 ---
91
92 ## Phase 1: Reconnaissance & Extraction
93
94 All extraction happens before any building. See `extraction.md` for complete Python scripts.
95
96 ### 1.1 Save Raw HTML (THE SOURCE OF TRUTH)
97
98 ```python
99
100 raw_html = page.content()
101
102 with open(f"{OUT_DIR}/raw.html", "w", encoding="utf-8") as f:
103
104 f.write(raw_html)
105
106 ```
107
108 This file is the authoritative reference for ALL section structure, content, class names, element ordering, and text content. Computed styles supplement it but never replace it.
109
110 ### 1.2 Language & Locale Detection (CRITICAL)
111
112 If the target URL contains a locale path (e.g., `/es-do`,`/fr`,`/de`,`/ja`), the clone MUST be in that language. However, server-side rendering may return English even for locale URLs — the localization often happens via client-side JavaScript after page load.
113
114 #### Detection steps
115
116 1. After `page.wait_for_timeout(8000)` (extra wait for JS locale loading), extract all visible text from key areas:
117
118 ```python
119
120 locale_info = page.evaluate("""
121
122 () => ({
123
124 bannerText: document.querySelector('[class*="banner"], [class*="announcement"]')?.innerText?.trim(),
125
126 navLinks: [...document.querySelectorAll('nav a, .main-nav a')].map(a => a.innerText.trim()).filter(t => t).slice(0, 8),
127
128 loyaltyText: document.querySelector('[class*="loyalty"], [class*="rewards"]')?.innerText?.trim(),
129
130 headerText: document.querySelector('header')?.innerText?.trim()?.slice(0, 500),
131
132 htmlLang: document.documentElement.lang,
133
134 url: window.location.href
135
136 })
137
138 """)
139
140 ```
141
142 1. If the URL locale doesn't match the extracted text language, the page probably needs more time for JS to run, or the locale is cookie-based.
143 2. **When in doubt, use the language implied by the URL locale.** If `/es-do` shows English text in the raw HTML, translate all user-facing text to Spanish when building. The URL locale is the user's intent.
144
145 **Brand terms stay in the original language.** Product names (e.g., "ALO Runner"), color names (e.g., "SUNSHINE"), brand names (e.g., "ALO Wellness Club") should NOT be translated — the real site keeps these in English even on localized pages.
146
147 ### 1.3 Screenshots (Desktop only for initial build)
148
149 Take a full-page screenshot at 1440px. This becomes the primary visual reference. Tablet and mobile screenshots are only needed if the user specifically requests responsive behavior.
150
151 **Take a separate header-only screenshot** at this stage — crop to just the top 150px. This will be your reference for logo placement, nav layout, banner color, and account/rewards UI. Header issues are the most common mistakes.
152
153 ### 1.4 Section Inventory
154
155 Parse the raw HTML to produce a complete ordered checklist. For each section, record:
156
157 - Section index and DOM selector (tag, id, classes)
158 - Exact heading text and subheading text
159
160 - Button labels
161 - Image count
162
163 - Background color (if non-transparent)
164
165 Save as `clone-data/inventory.json`. This becomes the build checklist.
166
167 ### 1.5 Design Tokens
168
169 Extract CSS custom properties, body font-family, heading font-family, primary colors. Save to `clone-data/tokens.json`.
170
171 ### 1.6 Font Handling
172
173 #### Priority order
174
175 1. **Download actual font files** — Check `@font-face`rules for`.woff2`/`.woff`URLs. Download to`public/fonts/`and declare`@font-face`in`index.css`.
176 2. **Use Google Fonts if available** — If the site uses Google Fonts, add the `@import`or`<link>` tag.
177
178 3. **Map to closest equivalent** — Only as a last resort:
179
180 | Proprietary Font | Google Fonts Equivalent |
181
182 |-----------------|----------------------|
183
184 | Proxima Nova | DM Sans |
185
186 | Geograph | DM Sans |
187
188 | Self Modern | DM Serif Text |
189
190 | Graphik | Inter |
191
192 | Circular | DM Sans |
193
194 | GT Walsheim | Plus Jakarta Sans |
195
196 | Tiempos | Playfair Display |
197
198 | Apercu | Source Sans Pro |
199
200 | Founders Grotesk | Space Grotesk |
201
202 | National | DM Sans |
203
204 | Futura | Jost |
205
206 | Avenir | Nunito Sans |
207
208 | Gotham | Montserrat |
209
210 | Brandon Grotesque | Raleway |
211
212 ### 1.7 SVG Logo Extraction (CRITICAL)
213
214 The site's logo is almost always an inline SVG in the `raw.html`, NOT just text. Search for it:
215
216 ```bash
217
218 # Search raw HTML for SVG near logo references
219
220 python3 -c "
221
222 with open('clone-data/raw.html') as f:
223
224 html = f.read()
225
226 # Search around 'logo' class references
227
228 import re
229
230 for m in re.finditer(r'logo', html[:15000], re.IGNORECASE):
231
232 idx = m.start()
233
234 # Look for SVG nearby
235
236 svg_start = html.find('<svg', max(0, idx-200))
237
238 if svg_start != -1 and svg_start < idx + 500:
239
240 svg_end = html.find('</svg>', svg_start) + 6
241
242 print(html[svg_start:svg_end])
243
244 break
245
246 "
247
248 ```
249
250 **Never use an SVG `<text>`element as a logo substitute.** Extract the real SVG`<path>` elements from the source HTML. The logo is the most recognizable element on the page — getting it wrong immediately signals "fake."
251
252 ### 1.8 Asset Download (ALL assets, ALL at once)
253
254 Download every image, video, SVG, background image, and font file before building starts. See `extraction.md` for the complete download script.
255
256 **CDN URL upscaling** (increase resolution before downloading):
257
258 - **Shopify `_small`suffix**:`_small.jpg`→`_1200x.jpg` (very common pattern)
259 - **Shopify query params**: `?width=X`→`?width=1200`
260
261 - **Sanity**: `?w=X`→`?w=1200`
262 - **Cloudinary**: `w_X`→`w_1200`
263
264 - **Contentful**: `?w=X`→`?w=1200`
265
266 **Verification:** After downloading, verify every file exists and is >100 bytes. The download script includes automatic retry with fallback User-Agent strings.
267
268 ### 1.9 Header Deep Extraction (CRITICAL)
269
270 The header is the most error-prone section. Extract detailed information beyond the basic inventory:
271
272 ```python
273
274 header_info = page.evaluate("""
275
276 () => {
277
278 const header = document.querySelector('header');
279
280 if (!header) return null;
281
282 // Banner/announcement bar
283
284 const banner = document.querySelector('[class*="banner"], [class*="announcement"], [class*="uni-banner"]');
285
286 const bannerBg = banner ? getComputedStyle(banner.querySelector('[class*="col"], div') || banner).backgroundColor : null;
287
288 // Logo position
289
290 const logo = header.querySelector('svg, [class*="logo"] img, [class*="logo"] svg');
291
292 const logoRect = logo?.getBoundingClientRect();
293
294 const headerRect = header.getBoundingClientRect();
295
296 // Nav links
297
298 const navLinks = [...header.querySelectorAll('nav a, [class*="nav"] a')].map(a => a.innerText.trim()).filter(t => t && t.length < 30);
299
300 // Right-side elements (account, rewards, search, cart, wishlist)
301
302 const rightElements = [...header.querySelectorAll('[class*="loyalty"], [class*="rewards"], [class*="account"], [class*="cart"], [class*="wishlist"]')];
303
304 return {
305
306 bannerText: banner?.innerText?.trim(),
307
308 bannerBgColor: bannerBg,
309
310 bannerTextColor: banner ? getComputedStyle(banner).color : null,
311
312 logoPosition: logoRect ? (logoRect.left < headerRect.width / 3 ? 'left' : logoRect.left < headerRect.width * 2/3 ? 'center' : 'right') : 'unknown',
313
314 navLinks: navLinks.slice(0, 10),
315
316 rightSideText: rightElements.map(el => el.innerText?.trim()).filter(t => t),
317
318 rightSideHTML: rightElements.map(el => el.innerHTML?.slice(0, 300)),
319
320 };
321
322 }
323
324 """)
325
326 ```
327
328 This prevents the three most common header mistakes: wrong banner color, wrong logo position, missing account/rewards text.
329
330 ### 1.10 Footer Link Extraction
331
332 Extract all footer links separately — they're needed for the footer component:
333
334 ```python
335
336 footer_data = page.evaluate("""
337
338 () => {
339
340 const footer = document.querySelector('footer');
341
342 if (!footer) return null;
343
344 return {
345
346 text: footer.innerText,
347
348 bgColor: getComputedStyle(footer).backgroundColor,
349
350 links: [...footer.querySelectorAll('a')].map(a => ({
351
352 text: a.innerText.trim(), href: a.getAttribute('href')
353
354 })).filter(l => l.text)
355
356 };
357
358 }
359
360 """)
361
362 ```
363
364 ---
365
366 ## Phase 2: Foundation Build
367
368 Sequential — do this yourself, not delegated.
369
370 1. **Create artifact** via `createArtifact()`with type`react-vite`
371 2. **Replace `index.css` entirely** — Remove ALL Tailwind/shadcn boilerplate. Write plain CSS:
372
373 - Google Fonts `@import`(or`@font-face` for self-hosted)
374 - Universal reset (`*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }`)
375
376 - CSS variables for design tokens (font families, colors)
377 - Basic body styles (font-family, color, background, antialiasing)
378
379 - Reset styles for `a`,`button`,`img`,`ul/ol`
380
381 1. **Replace `App.tsx`** — Remove all router/query/toast boilerplate. A clone is a single static page.
382 2. **Organize assets** in `public/images/`
383
384 ---
385
386 ## Phase 3: Build All Sections
387
388 Build all components from the section inventory, referencing `raw.html` for exact content. Use inline styles or CSS modules — not Tailwind.
389
390 ### For EACH section in the inventory
391
392 **Step 1: Read the raw HTML** for that section's exact structure, text, and element hierarchy.
393
394 **Step 2: Build the component** using:
395
396 - Exact text content from raw HTML (copy-paste headings, button labels)
397 - Local image paths from `public/images/`
398
399 - Real SVG paths for logos/icons extracted from raw HTML
400 - Inline styles for layout (position, display, flex, grid, padding, colors, fonts)
401
402 - Hover interactions via `onMouseEnter`/`onMouseLeave` inline handlers
403 - For carousels: `useRef`+`scrollBy`with`overflow-x: auto; scrollbar-width: none`
404
405 **Step 3: Use `import.meta.env.BASE_URL`prefix** for all image`src` attributes so they resolve correctly under the artifact's preview path.
406
407 ### Build tips
408
409 - **Build ALL sections before verifying.** Don't stop to screenshot after each one.
410 - **Use inline styles** — Simpler than CSS files for clones, and avoids naming/scoping issues.
411
412 - **Reusable components are okay when the visual pattern is truly identical** (e.g., two hero banners that differ only in image/button text can share a `HeroBanner` component with props).
413 - **`href="#"` is fine** — For a visual clone, real link targets are a nice-to-have, not a requirement.
414
415 - **Remove unused scaffolded dependencies** — The `package.json`from`createArtifact` includes 40+ shadcn/Radix packages. These are dead weight for a clone.
416
417 ---
418
419 ## Phase 4: Page Assembly & Verification
420
421 1. Import all components into `App.tsx` in exact DOM order from the inventory
422 2. Start the dev server and take a full-page screenshot at 1280px
423
424 3. Compare against the original screenshot from Phase 1
425 4. Fix discrepancies section by section
426
427 5. Run e2e test to verify all sections render (use `runTest()`)
428
429 ### Verification checklist
430
431 - [ ] All sections present in correct order
432 - [ ] Logo is the real SVG (not text substitute)
433
434 - [ ] Logo position matches (left/center/right)
435 - [ ] All images load (no broken images in console)
436
437 - [ ] Heading text matches exactly
438 - [ ] All text is in the correct language (match URL locale)
439
440 - [ ] Button styles match (filled vs outlined, correct colors)
441 - [ ] Background colors match for sections with colored backgrounds
442
443 - [ ] Announcement bar has correct background color AND text
444 - [ ] Account/rewards/loyalty text is present in header (if original has it)
445
446 - [ ] Carousels scroll properly
447 - [ ] Hover states work on interactive elements
448
449 - [ ] Footer has correct columns and content
450
451 ---
452
453 ## Component Specification Format
454
455 For complex sections dispatched to subagents, write specs at `docs/research/components/<name>.md`:
456
457 ```markdown
458
459 # <ComponentName> Specification
460
461 ## Overview
462
463 - Target file: `src/components/<ComponentName>.tsx`
464 - Interaction model: <static | click | scroll | time>
465
466 ## DOM Structure (from raw.html)
467
468 <Exact element hierarchy with tag names, classes, nesting>
469
470 ## Computed Styles (exact values)
471
472 ### Container
473
474 - display: flex; flex-direction: row; gap: 24px; padding: 60px 80px;
475
476 ### Heading
477
478 - font-size: 48px; font-weight: 400; color: \#230d0d;
479
480 ### Button
481
482 - background-color: \#f195a7; border-radius: 999px; padding: 12px 32px;
483
484 ## Text Content (verbatim from raw.html)
485
486 <Every heading, paragraph, button label — copy-pasted exactly>
487
488 ## Assets (local paths)
489
490 - /images/products/charm-1.webp
491
492 ## States & Behaviors
493
494 ### Hover on card
495
496 - transform: none → scale(1.02)
497 - transition: transform 0.3s ease
498
499 ```
500
501 ---
502
503 ## Quick Reference: Full Workflow
504
505 ```text
506
507 1. pip install playwright; find Chromium path
508 2. Navigate to target URL with Playwright
509
510 3. Save raw.html (page.content()) — THIS IS THE SOURCE OF TRUTH
511 4. Detect locale/language from URL path (e.g., /es-do = Spanish)
512
513 5. Take full-page desktop screenshot + header-only screenshot (top 150px)
514 6. Build section inventory from raw.html → clone-data/inventory.json
515
516 7. Extract design tokens → clone-data/tokens.json
517 8. Extract SVG logo from raw.html (search for <svg near 'logo' classes)
518
519 9. Extract header details: banner color, logo position, nav links, rewards/loyalty text
520 10. Extract fonts (download .woff2 files or map to Google Fonts)
521
522 11. Download ALL images/videos/SVGs to public/images/ (batch with retry)
523 12. Extract footer links → clone-data/footer.json
524
525 13. createArtifact("react-vite", ...)
526 14. Replace index.css (plain CSS reset + design tokens — NO Tailwind/shadcn)
527
528 15. Replace App.tsx (remove router/query boilerplate — single page)
529 16. Build ALL section components (referencing raw.html, using correct language)
530
531 17. Assemble page in App.tsx (exact DOM order from inventory)
532 18. Start dev server, take full-page screenshot, compare vs original
533
534 19. Fix discrepancies (check header first — most common mistake area)
535 20. Run e2e test to verify all sections render
536
537 21. Present artifact
538
539 ```
540
541 ## Reference Files
542
543 - `extraction.md` — Complete Python extraction scripts (Playwright)
544 - `pitfalls.md` — Detailed common pitfalls and solutions