mcpField
C
clone-website

clone-website

github.com/julianromli设计与 UI2102.1k 次安装2 天前更新
代码仓库

关于 clone-website

Clone Website 是一项技能,用于检查网站并协调其在 Next.js 项目中的视觉重建。它会将页面外观、素材、响应式布局和交互记录到组件规格中,供构建 Agent 使用。

主要功能

  1. 01通过浏览器自动化检查网站
  2. 02捕获桌面和移动端截图
  3. 03提取样式、文本与素材
  4. 04记录交互状态和行为
  5. 05为构建 Agent 编写组件规格
  6. 06验证 TypeScript 和项目构建

适用场景

  1. 01根据线上 URL 重建页面
  2. 02记录响应式布局与交互
  3. 03准备可审查的组件实现说明

MCP 相关

该技能要求通过 Chrome MCP 或 Playwright MCP 等浏览器 MCP 工具检查目标页面。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/julianromli/ai-skills --skill clone-website

安装后在 Claude Code 中输入/clone-website确认已加载。

详细介绍

克隆网站

你即将对 $ARGUMENTS 进行逆向工程,并将其重建为像素级精确的克隆。

提供多个 URL 时,请分别处理,并尽可能并行执行,同时将每个网站的提取产物隔离在专属文件夹中(例如 docs/research/<hostname>/)。

这不是一个分为两个阶段(检查后再构建)的流程。你是一名在施工现场巡视的工头——检查页面的每个版块时,都要将详细规格写入文件,然后将该文件交给专业构建 Agent,并提供其所需的一切。提取与构建并行进行,但提取必须一丝不苟,并生成可审查的产物。

默认范围

目标是 $ARGUMENTS 所指向的页面。请精确克隆该 URL 上可见的内容。除非用户另有说明,否则采用以下默认设置:

  • 还原级别: 像素级精确——颜色、间距、排版和动画均需准确匹配
  • 范围内: 视觉布局与样式、组件结构与交互、响应式设计、用于演示的模拟数据
  • 范围外: 真实后端/数据库、身份验证、实时功能、SEO 优化、无障碍审查
  • 自定义: 无——纯粹仿制

如果用户提供了额外说明(特定还原级别、自定义要求、补充背景),请优先遵循这些说明,而非默认设置。

前置检查

  1. 必须使用浏览器自动化。 检查可用的浏览器 MCP 工具(Chrome MCP、Playwright MCP、Browserbase MCP、Puppeteer MCP 等)。使用任一可用工具——如果有多个,优先使用 Chrome MCP。如果没有检测到任何工具,请询问用户有哪些浏览器工具,以及如何连接。这项技能离不开浏览器自动化。
  2. 将 $ARGUMENTS 解析为一个或多个 URL。规范化并验证每个 URL;如有无效 URL,请先让用户更正。对于每个有效 URL,使用浏览器 MCP 工具确认其可访问。
  3. 确认基础项目可以构建:npm run build。Next.js + shadcn/ui + Tailwind v4 脚手架应已就绪。如果尚未就绪,请告知用户先完成设置。
  4. 如果输出目录不存在,则创建它们:docs/research/、docs/research/components/、docs/design-references/、scripts/。若需克隆多个网站,也要准备各网站专属文件夹,例如 docs/research/<hostname>/ 和 docs/design-references/<hostname>/。
  5. 在一条命令中处理多个网站时,可先确认是并行执行(若资源允许,推荐采用)还是顺序执行,以避免资源过载。

指导原则

以下原则能让成功克隆与“差不多就行”的拼凑品区分开来。请牢记这些原则,并以此指导每一个决策。

1. 完整性胜过速度

每个构建 Agent 都必须获得完美完成任务所需的一切:截图、精确的 CSS 值、附带本地路径的下载资源、真实文本内容、组件结构。如果构建 Agent 还需要猜测任何内容——颜色、字号、内边距数值——就说明提取工作没有做好。多花一分钟再提取一个属性,也不要交付不完整的任务说明。

2. 小任务,完美结果

当 Agent 收到“构建整个功能区”的任务时,往往会忽略细节——大致估算间距、猜测字号,最后做出的东西“差不多”,但显然不对。如果它只收到一个专注的组件,并拿到精确的 CSS 值,每次都能完美完成。

逐个查看区块并判断其复杂度。一个包含标题和按钮的简单横幅?一个 Agent。一个包含 3 种不同卡片样式、每种都有独特悬停状态和内部布局的复杂区块?每种卡片样式分配一个 Agent,再为区块外层分配一个 Agent。拿不准时,就拆得更小。

**复杂度预算规则:**如果构建器提示词中的规格内容超过约 150 行,这个区块对单个 Agent 来说就太复杂了。将其拆成更小的部分。这是一项机械检查——不要以“但这些内容都相关”为由忽略它。

3. 真实内容,真实素材

从线上网站提取实际文本、图像、视频和 SVG。这是一个克隆,不是模型图。使用 element.textContent,下载每个 <img> 和 <video>,将内联 <svg> 提取为 React 组件。只有当内容明显由服务器生成且每个会话都不同时,才生成内容。

**分层素材很重要。**看起来像一张图片的区块,往往由多个图层组成——背景水彩效果/渐变、前景 UI 模型 PNG、叠加图标。检查每个容器的完整 DOM 树,并列出其中所有 <img> 元素和背景图片,包括绝对定位的叠加元素。即使背景正确,漏掉一张叠加图片也会让克隆看起来空荡荡的。

4. 先打好基础

基础就绪之前,什么都无法构建:需要包含目标网站设计令牌(颜色、字体、间距)的全局 CSS、内容结构的 TypeScript 类型,以及全局素材(字体、favicon)。这一过程必须按顺序进行,不容妥协。之后的工作都可以并行处理。

5. 提取外观和行为

网站不是一张截图,而是一个鲜活的整体。元素会响应滚动、悬停、点击、调整大小和时间而移动、变化、出现和消失。如果你只提取每个元素的静态 CSS,那么克隆在截图中看起来可能正确,但实际使用起来会死气沉沉。

对每个元素,都要提取它的外观(通过 getComputedStyle() 获取精确的计算后 CSS)和行为(什么会发生变化、什么会触发变化,以及变化如何过渡)。不要说“看起来像 16px”——要提取实际的计算值。不要说“导航栏会在滚动时变化”——要记录精确的触发条件(滚动位置、IntersectionObserver 阈值、视口相交情况)、变化前后的状态(两组 CSS 值),以及过渡方式(时长、缓动、CSS transition、JS 驱动还是 CSS animation-timeline)。

以下是需要留意的行为示例——仅供说明,并非穷举。页面可能还会出现列表中没有的行为,你也必须捕捉到:

  • 导航栏在滚过某个阈值后缩小、改变背景或增加阴影
  • 元素进入视口时以动画方式显现(向上淡入、滑入、错开延迟)
  • 区块在滚动时吸附到位(scroll-snap-type)
  • 视差图层以不同于滚动的速度移动
  • 会产生动画的悬停状态(不只是变化——过渡时长和缓动方式也很重要)
  • 带有进入/退出动画的下拉菜单、模态框、手风琴
  • 滚动驱动的进度指示器或透明度过渡
  • 自动播放的轮播或循环切换的内容
  • 页面区块之间从深色到浅色(或任何主题)的过渡
  • 循环切换的标签页/胶囊式内容——通过过渡切换可见卡片组的按钮
  • 滚动驱动的标签页/手风琴切换——侧边栏中的活动项会随着内容滚过而自动变化(通过 IntersectionObserver,而非点击处理程序)
  • 平滑滚动库(Lenis、Locomotive Scroll)——检查是否存在 .lenis 类或滚动容器包装元素
6. 构建前先识别交互模式

克隆时最容易造成高昂代价的错误,就是原页面由滚动驱动,你却构建了基于点击的 UI,反之亦然。在为交互式区块编写任何构建提示词之前,你必须明确回答:此区块是由点击、滚动、悬停、时间驱动,还是由其中多种方式共同驱动?

确定方法:

  1. 不要先点击。 缓慢滚动浏览该区块,观察内容是否会随着滚动自行变化。
  2. 如果会,它就是由滚动驱动的。提取其机制:IntersectionObserver、scroll-snap、position: sticky、animation-timeline 或 JS 滚动监听器。
  3. 如果滚动时没有任何变化,再通过点击或悬停测试交互是否由点击或悬停驱动。
  4. 在组件规范中明确记录交互模式:“交互模式:由 IntersectionObserver 驱动的滚动交互”或“交互模式:点击切换,并使用不透明度过渡”。

带有粘性侧边栏和滚动内容面板的区块,与点击切换内容的选项卡界面有本质区别。弄错就意味着必须彻底重写,而不是微调 CSS。

7. 提取所有状态,而不只是默认状态

许多组件有多种视觉状态——选项卡栏会根据当前选项卡显示不同的卡片,页头在滚动位置为 0 和 100 时外观不同,卡片也可能带有悬停效果。你必须提取所有状态,而不只是页面加载时可见的状态。

对于选项卡式/有状态的内容:

  • 通过浏览器 MCP 点击每个选项卡/按钮
  • 提取每个状态对应的内容、图片和卡片数据
  • 记录每项内容所属的状态
  • 记录状态之间的过渡动画(不透明度、滑动、淡入淡出等)

对于依赖滚动的元素:

  • 捕获滚动位置为 0 时的计算样式(初始状态)
  • 滚过触发阈值后,再次捕获计算样式(滚动后状态)
  • 对比两者,准确找出发生变化的 CSS 属性
  • 记录过渡 CSS(时长、缓动、属性)
  • 记录确切的触发阈值(以 px 表示的滚动位置,或视口交叉比例)
8. 规范文件是唯一事实来源

在分派任何构建 Agent 之前,每个组件都必须先在 docs/research/components/ 中创建一份规范文件。此文件是提取工作与构建 Agent 之间的约定。构建 Agent 会在提示词中收到规范文件的内联内容;该文件也会作为可审计的产物保留下来,供用户(或你)在发现问题时审查。

规范文件不是可选项,也不是锦上添花。如果你没有先编写规范文件就分派构建 Agent,就等于根据自己对浏览器 MCP 会话的记忆来提交不完整的说明,构建 Agent 只能自行猜测并填补遗漏。

9. 构建必须始终通过编译

每个构建 Agent 在结束前都必须验证 npx tsc --noEmit 能够通过。合并工作树后,你必须验证 npm run build 能够通过。绝不能接受构建失败,哪怕只是暂时的。

阶段 1:侦察

使用浏览器 MCP 导航至目标 URL。

截图
  • 在桌面端(1440px)和移动端(390px)视口下拍摄整页截图
  • 使用描述性名称保存到 docs/design-references/
  • 这些截图是主要参考;之后构建 Agent 会收到按区块截取的截图
全局提取

在进行任何操作之前,先从页面中提取以下信息:

字体 — 检查 <link> 标签,查找 Google Fonts 或自托管字体。检查关键元素(标题、正文、代码、标签)的计算后 font-family。记录实际使用的每种字体系列、字重和样式。使用 next/font/google 或 next/font/local 在 src/app/layout.tsx 中配置。

颜色 — 从页面各处的计算样式中提取网站的调色板。使用目标网站的实际颜色更新 src/app/globals.css 中的 :root 和 .dark CSS 变量块。将颜色映射到 shadcn 的令牌名称(background、foreground、primary、muted 等)。对于无法映射到 shadcn 令牌的颜色,添加自定义属性。

Favicon 和元数据 — 将 favicon、apple-touch-icon、OG 图片和 webmanifest 下载到 public/seo/。更新 layout.tsx 中的元数据。

全局 UI 模式 — 识别全站通用的 CSS 或 JS:自定义滚动条隐藏、页面容器上的 scroll-snap、全局关键帧动画、背景滤镜、用作叠加层的渐变、平滑滚动库(Lenis、Locomotive Scroll——检查 .lenis、.locomotive-scroll 或自定义滚动容器类)。将这些添加到 globals.css,并注明需要安装的库。

强制交互检查

这是一个专门的检查环节,必须在截图之后、其他操作之前进行。目的是发现页面上的所有行为——其中许多在静态截图中不可见。

滚动检查: 通过浏览器 MCP 从页面顶部缓慢滚动到底部。在每个区块暂停并观察:

  • 页眉的外观会改变吗?记录触发时的滚动位置。
  • 元素会在进入视口时播放动画吗?记录具体元素和动画类型。
  • 侧边栏或标签指示器会随滚动自动切换吗?记录其机制。
  • 是否存在滚动吸附点?记录所在容器。
  • 是否启用了平滑滚动库?检查是否存在非原生滚动行为。

点击检查: 点击所有看起来可交互的元素:

  • 每个按钮、标签、胶囊、链接和卡片
  • 记录发生的变化:内容会改变吗?会打开模态框吗?会显示下拉菜单吗?
  • 对于标签/胶囊:逐个点击并记录每种状态下显示的内容

悬停检查: 将指针悬停在所有可能带有悬停状态的元素上:

  • 按钮、卡片、链接、图片、导航项
  • 记录发生的变化:颜色、缩放、阴影、下划线、不透明度

响应式检查: 通过浏览器 MCP 测试 3 种视口宽度:

  • 桌面端:1440px
  • 平板端:768px
  • 移动端:390px
  • 在每种宽度下,记录布局发生变化的区块(单列变为堆叠、侧边栏消失等),以及大致的断点位置。

将所有发现保存到 docs/research/BEHAVIORS.md。这是行为参考手册;编写每个组件规范时都要参考它。

页面结构

从上到下梳理页面上每个独立区块,并为每个区块取一个临时名称。记录:

  • 区块的视觉顺序
  • 哪些是固定/粘性叠加层,哪些是流式内容
  • 页面整体布局(滚动容器、列结构、z-index 层级)
  • 区块之间的依赖关系(例如,覆盖所有内容的浮动导航)
  • 每个区块的交互模型(静态、点击驱动、滚动驱动、时间驱动)

将其保存为 docs/research/PAGE_TOPOLOGY.md,作为页面组装蓝图。

第 2 阶段:基础搭建

这是一个顺序执行的阶段。请亲自完成(不要委派给 Agent),因为这会涉及许多文件:

  1. 更新 layout.tsx 中的字体,使其与目标网站实际使用的字体一致
  2. 更新 globals.css,加入目标网站的颜色令牌、间距值、关键帧动画、工具类,以及所有全局滚动行为(Lenis、平滑滚动 CSS、body 上的 scroll-snap)
  3. 在 src/types/ 中为你观察到的内容结构创建 TypeScript 接口
  4. 提取 SVG 图标——找出页面上所有内联的 <svg> 元素,去重后将其保存为 src/components/icons.tsx 中带名称的 React 组件。根据视觉功能命名(例如 SearchIcon、ArrowRightIcon、LogoIcon)。
  5. 下载全局资源——编写并运行 Node.js 脚本(scripts/download-assets.mjs),将页面中的所有图片、视频及其他二进制资源下载到 public/。保留有意义的目录结构。
  6. 验证:npm run build 能够通过
资源发现脚本模式

使用浏览器 MCP 枚举页面上的所有资源:

// Run this via browser MCP to discover all assets
JSON.stringify({
  images: [...document.querySelectorAll('img')].map(img => ({
    src: img.src || img.currentSrc,
    alt: img.alt,
    width: img.naturalWidth,
    height: img.naturalHeight,
    // Include parent info to detect layered compositions
    parentClasses: img.parentElement?.className,
    siblings: img.parentElement ? [...img.parentElement.querySelectorAll('img')].length : 0,
    position: getComputedStyle(img).position,
    zIndex: getComputedStyle(img).zIndex
  })),
  videos: [...document.querySelectorAll('video')].map(v => ({
    src: v.src || v.querySelector('source')?.src,
    poster: v.poster,
    autoplay: v.autoplay,
    loop: v.loop,
    muted: v.muted
  })),
  backgroundImages: [...document.querySelectorAll('*')].filter(el => {
    const bg = getComputedStyle(el).backgroundImage;
    return bg && bg !== 'none';
  }).map(el => ({
    url: getComputedStyle(el).backgroundImage,
    element: el.tagName + '.' + el.className?.split(' ')[0]
  })),
  svgCount: document.querySelectorAll('svg').length,
  fonts: [...new Set([...document.querySelectorAll('*')].slice(0, 200).map(el => getComputedStyle(el).fontFamily))],
  favicons: [...document.querySelectorAll('link[rel*="icon"]')].map(l => ({ href: l.href, sizes: l.sizes?.toString() }))
});

然后编写下载脚本,将所有资源抓取到 public/。采用分批并行下载(每次 4 个),并妥善处理错误。

第 3 阶段:组件规格说明与分派

这是核心工作循环。对于页面拓扑中的每个区块(从上到下),你要做三件事:提取、编写规格说明文件,然后分派构建者。

第 1 步:提取

对于每个区块,使用浏览器 MCP 提取所有内容:

  1. 截取该区块的独立截图(滚动到该区块,并截取视口)。保存到 docs/design-references/。

  2. 提取区块中每个元素的 CSS。使用下面的提取脚本——不要手动测量单个属性。每个组件容器运行一次,并记录完整输出:

// Per-component extraction — run via browser MCP
// Replace SELECTOR with the actual CSS selector for the component
(function(selector) {
  const el = document.querySelector(selector);
  if (!el) return JSON.stringify({ error: 'Element not found: ' + selector });
  const 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) {
    const cs = getComputedStyle(element);
    const styles = {};
    props.forEach(p => { const 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;
    const children = [...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(c => walk(c, depth + 1)).filter(Boolean)
    };
  }
  return JSON.stringify(walk(el, 0), null, 2);
})('SELECTOR');
  1. 提取多状态样式——对于具有多个状态的元素(滚动触发、悬停、活动标签页),同时捕获两种状态:
// State A: capture styles at current state (e.g., scroll position 0)
// Then trigger the state change (scroll, click, hover via browser MCP)
// State B: re-run the extraction script on the same element
// The diff between A and B IS the behavior specification

明确记录差异:“属性 X 从 VALUE_A 变为 VALUE_B,由 TRIGGER 触发,过渡效果为 TRANSITION_CSS。”

  1. 提取真实内容——包括所有文本、alt 属性、aria 标签和占位文本。对每个文本节点使用 element.textContent。对于带标签页或具有状态的内容,点击每个标签页,并提取各状态下的内容。

  2. 识别该区块使用的资源——包括 public/ 中下载的图片/视频,以及 icons.tsx 中的图标组件。检查是否存在分层图片(同一容器中堆叠的多个 <img> 或背景图片)。

  3. 评估复杂度——该区块包含多少个不同的子组件?不同的子组件指拥有独特样式、结构和行为的元素(例如卡片、导航项、搜索面板)。

第 2 步:编写组件规格说明文件

为每个区块(或子组件,如果你将其拆分)在 docs/research/components/ 中创建规格说明文件。这是必需的——每个构建者都必须有对应的规格说明文件。

文件路径: docs/research/components/<component-name>.spec.md

模板:

# <ComponentName> Specification

## Overview
- **Target file:** `src/components/<ComponentName>.tsx`
- **Screenshot:** `docs/design-references/<screenshot-name>.png`
- **Interaction model:** <static | click-driven | scroll-driven | time-driven>

## DOM Structure
<Describe the element hierarchy — what contains what>

## Computed Styles (exact values from getComputedStyle)

### Container
- display: ...
- padding: ...
- maxWidth: ...
- (every relevant property with exact values)

### <Child element 1>
- fontSize: ...
- color: ...
- (every relevant property)

### <Child element N>
...

## States & Behaviors

### <Behavior name, e.g., "Scroll-triggered floating mode">
- **Trigger:** <exact mechanism — scroll position 50px, IntersectionObserver rootMargin "-30% 0px", click on .tab-button, hover>
- **State A (before):** maxWidth: 100vw, boxShadow: none, borderRadius: 0
- **State B (after):** maxWidth: 1200px, boxShadow: 0 4px 20px rgba(0,0,0,0.1), borderRadius: 16px
- **Transition:** transition: all 0.3s ease
- **Implementation approach:** <CSS transition + scroll listener | IntersectionObserver | CSS animation-timeline | etc.>

### Hover states
- **<Element>:** <property>: <before> → <after>, transition: <value>

## Per-State Content (if applicable)

### State: "Featured"
- Title: "..."
- Subtitle: "..."
- Cards: [{ title, description, image, link }, ...]

### State: "Productivity"
- Title: "..."
- Cards: [...]

## Assets
- Background image: `public/images/<file>.webp`
- Overlay image: `public/images/<file>.png`
- Icons used: <ArrowIcon>, <SearchIcon> from icons.tsx

## Text Content (verbatim)
<All text content, copy-pasted from the live site>

## Responsive Behavior
- **Desktop (1440px):** <layout description>
- **Tablet (768px):** <what changes — e.g., "maintains 2-column, gap reduces to 16px">
- **Mobile (390px):** <what changes — e.g., "stacks to single column, images full-width">
- **Breakpoint:** layout switches at ~<N>px

填写每个部分。如果某个部分不适用(例如静态页脚没有状态),请写“N/A”——但在将“状态与行为”标为 N/A 前请再三考虑。即使是页脚,链接也可能有悬停状态。

第 3 步:分派构建者

根据复杂度,在 worktree 中分派构建者 Agent:

简单区块(1–2 个子组件):由一个构建者 Agent 负责整个区块。

复杂区块(3 个或更多不同子组件):拆分任务。每个子组件分配一个 Agent,另外安排一个 Agent 负责导入这些子组件的区块外层组件。先由子组件构建者开始,因为外层组件依赖它们。

每个构建者 Agent 都会收到:

  • 组件规范文件的完整内容(直接放在提示词中,不要说“去读取规范文件”)
  • docs/design-references/ 中区块截图的路径
  • 需要导入的共享组件(icons.tsx、cn()、shadcn 原语)
  • 目标文件路径(例如 src/components/HeroSection.tsx)
  • 完成前使用 npx tsc --noEmit 验证的指示
  • 关于响应式行为的信息:具体断点值以及对应的变化

不要等待。 为一个区块分派构建者后,立即开始提取下一个区块。构建者在各自的 worktree 中并行工作,你则继续提取。

第 4 步:合并

构建者 Agent 完成工作后:

  • 将其 worktree 分支合并到 main
  • 你充分了解每个 Agent 构建的内容,因此要妥善解决冲突
  • 每次合并后,验证构建仍然通过:npm run build
  • 如果合并引入了类型错误,立即修复

持续执行“提取 → 编写规范 → 分派 → 合并”循环,直到所有区块都构建完成。

第 4 阶段:页面组装

所有区块构建并合并后,在 src/app/page.tsx 中将它们连接起来:

  • 导入所有区块组件
  • 根据拓扑文档实现页面级布局(滚动容器、列结构、粘性定位、z-index 层叠)
  • 将实际内容连接到组件属性
  • 实现页面级行为:滚动吸附、滚动驱动动画、深色到浅色的过渡、交叉观察器、平滑滚动(Lenis 等)
  • 验证:npm run build 顺利通过

第 5 阶段:视觉 QA 对比

组装完成后,不要宣布克隆完成。并排截取对比截图:

  1. 并排打开原网站和克隆版本(或在相同视口宽度下截图)
  2. 在桌面端(1440px)从上到下逐个区块进行比较
  3. 在移动端(390px)再次比较
  4. 对发现的每处差异:
    • 检查组件规范文件——提取的值是否正确?
    • 如果规范有误:通过浏览器 MCP 重新提取,更新规范并修复组件
    • 如果规范正确但构建者实现有误:修复组件,使其符合规范
  5. 测试所有交互行为:滚动浏览页面、点击每个按钮和选项卡、将鼠标悬停在交互元素上
  6. 确认平滑滚动效果合适、页眉过渡正常、选项卡切换正常、动画能够播放

只有完成这轮视觉 QA 后,克隆才算完成。

分派前检查清单

分派任何构建者 Agent 前,确认每项都能勾选。如果不能,就返回继续提取信息。

  • 规范文件已写入 docs/research/components/<name>.spec.md,且所有部分均已填写
  • 规范中的每个 CSS 值均来自 getComputedStyle(),而非估算
  • 已识别并记录交互模型(静态 / 点击 / 滚动 / 时间)
  • 对于有状态的组件,已捕获每种状态的内容和样式
  • 对于滚动驱动的组件,已记录触发阈值、前后样式和过渡效果
  • 对于悬停状态,已记录前后取值和过渡时间
  • 已识别区块中的所有图片(包括叠加图层和分层组合)
  • 已记录至少桌面端和移动端的响应式行为
  • 文本内容与网站完全一致,未进行改写
  • 构建者提示词中的规范内容少于约 150 行;如果超出,则需要拆分区块

不要做的事

以下是之前克隆失败的教训——每一项都导致数小时的返工:

  • 原站由滚动驱动时,不要做基于点击的标签页(反之亦然)。 先滚动,再点击,以确定交互模式。这是代价最高的错误,必须彻底重写,不能只靠调整 CSS 修复。
  • 不要只提取默认状态。 如果加载时标签页显示“Featured”,请点击 Productivity、Creative、Lifestyle,并提取每个标签页的卡片和内容。如果页眉会在滚动时变化,请记录滚动位置为 0 和 100+ 时的样式。
  • 不要遗漏叠加/分层图片。 水彩背景 + 前景 UI 模型 = 2 张图片。检查每个容器的 DOM 树,确认是否包含多个 <img> 元素和定位叠加层。
  • 不要为实际是视频/动画的内容制作模型组件。 在制作复杂的 HTML 模型来复现视频内容前,先检查该区块是否使用了 <video>、Lottie 或 canvas。
  • 不要估算 CSS 类。 “看起来像 text-lg”是不对的:计算值可能是 18px,而 text-lg 是 18px/28px,但实际行高却是 24px。请提取精确值。
  • 不要把所有内容都塞进一个大型提交。 这套流程的核心就是逐步推进,并在每一步验证构建结果。
  • 不要在构建者的提示词中引用文档。 每位构建者都会在提示词中直接获得 CSS 规范——不要写“颜色请参阅 DESIGN_TOKENS.md”。构建者不应需要阅读外部文档。
  • 不要跳过资源提取。 没有真实图片、视频和字体,无论 CSS 多么完美,克隆看起来都会很假。
  • 不要给构建 Agent 太大的任务范围。 如果你编写的构建提示词因为区块复杂而变得很长,这就说明应该把任务拆分成更小的部分。
  • 不要把无关区块交给同一个 Agent。 CTA 区块和页脚是设计不同的独立组件——不要把它们都交给同一个 Agent 并寄希望于结果。
  • 不要跳过响应式提取。 如果只在桌面宽度下检查,克隆在平板和手机上就会出问题。提取时请在 1440、768 和 390 的宽度下测试。
  • 不要忘记平滑滚动库。 检查是否使用了 Lenis(.lenis 类)、Locomotive Scroll 或类似库。浏览器默认滚动的感觉明显不同,用户会立刻发现。
  • 没有规范文件,不要派发构建者。 规范文件能确保彻底提取信息,并生成可审计的产物。跳过它,就意味着构建者只能获得你凭记忆塞进提示词里的内容。

完成

完成后,请报告:

  • 构建的区块总数
  • 创建的组件总数
  • 编写的规范文件总数(应与组件数一致)
  • 下载的资源总数(图片、视频、SVG、字体)
  • 构建状态(npm run build 的结果)
  • 视觉 QA 结果(剩余的差异)
  • 已知的缺失项或限制

评论

支持 Markdown