launchframe 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/clone-website/SKILL.md +473 -564
- package/.clinerules +147 -285
- package/.codex/skills/clone-website/SKILL.md +473 -564
- package/.continue/commands/clone-website.md +475 -566
- package/.continue/rules/project.md +151 -285
- package/.cursor/commands/clone-website.md +470 -561
- package/.cursor/rules/project.mdc +7 -22
- package/.gemini/commands/clone-website.toml +476 -567
- package/.github/copilot-instructions.md +147 -281
- package/.github/skills/clone-website/SKILL.md +473 -564
- package/.gitignore +49 -0
- package/.opencode/commands/clone-website.md +473 -564
- package/.windsurf/workflows/clone-website.md +470 -561
- package/AGENTS.md +65 -160
- package/README.md +162 -121
- package/bin/launchframe.mjs +343 -0
- package/docs/research/INSPECTION_GUIDE.md +80 -124
- package/package.json +97 -54
- package/src/app/globals.css +1 -93
- package/src/app/layout.tsx +16 -5
- package/src/app/page.tsx +37 -2
- package/src/lib/launchframe-config.ts +8 -0
- package/.amazonq/cli-agents/clone-website.json +0 -9
- package/.amazonq/rules/project.md +0 -281
- package/.augment/commands/clone-website.md +0 -565
- package/.claude/skills/marketing-social-proof-motion/SKILL.md +0 -47
- package/.cursor/commands/marketing-social-proof-motion.md +0 -42
- package/.nvmrc +0 -1
- package/CHANGELOG.md +0 -80
- package/START_HERE.md +0 -15
- package/docs/design-references/playwright-example.com-1440px.png +0 -0
- package/docs/design-references/playwright-example.com-390px.png +0 -0
- package/launchframe.config.json +0 -14
- package/public/images/.gitkeep +0 -0
- package/public/seo/.gitkeep +0 -0
- package/public/videos/.gitkeep +0 -0
- package/scripts/recon-playwright.mjs +0 -396
- package/src/components/marketing/scribewise-landing.tsx +0 -34
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Scaffold a Launchframe project: clone reference URL via AI workflow + SaaS landing copy.
|
|
4
|
+
* Usage: npx launchframe@latest <url> "<saas-idea>" [--dir=name] [--skip-install]
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { cp, mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const PKG_ROOT = resolve(__dirname, "..");
|
|
14
|
+
|
|
15
|
+
const SKIP_DIR_NAMES = new Set(["bin", "node_modules", ".git", ".next", "dist", "out"]);
|
|
16
|
+
|
|
17
|
+
/** Root dot-directories we ship for agent tooling */
|
|
18
|
+
const ALLOW_DOT_DIRS = new Set([
|
|
19
|
+
".cursor",
|
|
20
|
+
".claude",
|
|
21
|
+
".codex",
|
|
22
|
+
".continue",
|
|
23
|
+
".gemini",
|
|
24
|
+
".opencode",
|
|
25
|
+
".windsurf",
|
|
26
|
+
".github",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const SKIP_ROOT_FILES = new Set(["package-lock.json"]);
|
|
30
|
+
|
|
31
|
+
function printHelp() {
|
|
32
|
+
console.log(`
|
|
33
|
+
launchframe — scaffold a Next.js app for cloning a reference site + your SaaS idea
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
npx launchframe@latest <url> "<saas-idea>" [options]
|
|
37
|
+
|
|
38
|
+
Arguments:
|
|
39
|
+
<url> HTTPS URL of the site to reverse-engineer (visual reference)
|
|
40
|
+
"<saas-idea>" Short pitch / positioning that should appear on the landing page
|
|
41
|
+
|
|
42
|
+
Options:
|
|
43
|
+
--dir, -o Output folder (default: first label of hostname + "-launchframe")
|
|
44
|
+
--skip-install Do not run npm install after scaffolding
|
|
45
|
+
-h, --help Show this message
|
|
46
|
+
|
|
47
|
+
Note:
|
|
48
|
+
The output folder must not live inside the Launchframe package directory (the folder that contains this CLI).
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
npx launchframe@latest https://stripe.com "AI invoicing for freelancers"
|
|
52
|
+
`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseArgs(argv) {
|
|
56
|
+
const out = {
|
|
57
|
+
url: null,
|
|
58
|
+
idea: null,
|
|
59
|
+
dir: null,
|
|
60
|
+
skipInstall: false,
|
|
61
|
+
help: false,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const positional = [];
|
|
65
|
+
for (let i = 0; i < argv.length; i++) {
|
|
66
|
+
const a = argv[i];
|
|
67
|
+
if (a === "-h" || a === "--help") {
|
|
68
|
+
out.help = true;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (a === "--skip-install") {
|
|
72
|
+
out.skipInstall = true;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (a === "--dir" || a === "-o") {
|
|
76
|
+
out.dir = argv[++i];
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (a.startsWith("--dir=")) {
|
|
80
|
+
out.dir = a.slice("--dir=".length);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (a.startsWith("-o=")) {
|
|
84
|
+
out.dir = a.slice("-o=".length);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
positional.push(a);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (positional[0]) out.url = positional[0];
|
|
91
|
+
if (positional[1]) out.idea = positional[1];
|
|
92
|
+
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function validateUrl(raw) {
|
|
97
|
+
let u;
|
|
98
|
+
try {
|
|
99
|
+
u = new URL(raw);
|
|
100
|
+
} catch {
|
|
101
|
+
throw new Error(`Invalid URL: ${raw}`);
|
|
102
|
+
}
|
|
103
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") {
|
|
104
|
+
throw new Error("URL must start with http:// or https://");
|
|
105
|
+
}
|
|
106
|
+
return u.href;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function defaultDirName(urlStr) {
|
|
110
|
+
try {
|
|
111
|
+
const host = new URL(urlStr).hostname.replace(/^www\./i, "");
|
|
112
|
+
const label = host.split(".")[0] || "site";
|
|
113
|
+
const safe = label.replace(/[^a-zA-Z0-9-_]/g, "-").toLowerCase();
|
|
114
|
+
return `${safe || "site"}-launchframe`;
|
|
115
|
+
} catch {
|
|
116
|
+
return "launchframe-app";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function slugFromDir(dir) {
|
|
122
|
+
const base = dir.replace(/\\/g, "/").split("/").filter(Boolean).pop() ?? "launchframe-app";
|
|
123
|
+
return base
|
|
124
|
+
.toLowerCase()
|
|
125
|
+
.replace(/[^a-z0-9-_]/g, "-")
|
|
126
|
+
.replace(/-+/g, "-")
|
|
127
|
+
.replace(/^-|-$/g, "") || "launchframe-app";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function tsStringLiteral(value) {
|
|
131
|
+
return JSON.stringify(value);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function shouldCopyRootEntry(baseName, isDirectory) {
|
|
135
|
+
if (SKIP_DIR_NAMES.has(baseName)) return false;
|
|
136
|
+
if (isDirectory && baseName.startsWith(".")) {
|
|
137
|
+
if (!ALLOW_DOT_DIRS.has(baseName)) return false;
|
|
138
|
+
}
|
|
139
|
+
if (!isDirectory && SKIP_ROOT_FILES.has(baseName)) return false;
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Output cannot be inside the template package tree (would recurse while copying). */
|
|
144
|
+
function isForbiddenOutput(packageRootAbs, destRootAbs) {
|
|
145
|
+
const pkg = resolve(packageRootAbs);
|
|
146
|
+
const dest = resolve(destRootAbs);
|
|
147
|
+
if (dest === pkg) return true;
|
|
148
|
+
const rel = relative(pkg, dest);
|
|
149
|
+
return Boolean(rel) && !rel.startsWith("..") && !isAbsolute(rel);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function copyTemplateTree(sourceRoot, destRootAbs) {
|
|
153
|
+
await mkdir(destRootAbs, { recursive: true });
|
|
154
|
+
const entries = await readdir(sourceRoot, { withFileTypes: true });
|
|
155
|
+
const destResolved = resolve(destRootAbs);
|
|
156
|
+
for (const ent of entries) {
|
|
157
|
+
const from = join(sourceRoot, ent.name);
|
|
158
|
+
if (resolve(from) === destResolved) continue;
|
|
159
|
+
if (!shouldCopyRootEntry(ent.name, ent.isDirectory())) continue;
|
|
160
|
+
const to = join(destRootAbs, ent.name);
|
|
161
|
+
await cp(from, to, { recursive: true });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function writeGeneratedPackageJson(destRoot, npmPackageName) {
|
|
166
|
+
const raw = await readFile(join(PKG_ROOT, "package.json"), "utf8");
|
|
167
|
+
const pkg = JSON.parse(raw);
|
|
168
|
+
|
|
169
|
+
const nextPkg = {
|
|
170
|
+
name: npmPackageName,
|
|
171
|
+
version: "0.1.0",
|
|
172
|
+
private: true,
|
|
173
|
+
description: pkg.description,
|
|
174
|
+
license: pkg.license,
|
|
175
|
+
engines: pkg.engines,
|
|
176
|
+
scripts: pkg.scripts,
|
|
177
|
+
dependencies: pkg.dependencies,
|
|
178
|
+
devDependencies: pkg.devDependencies,
|
|
179
|
+
keywords: pkg.keywords,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
await writeFile(
|
|
183
|
+
join(destRoot, "package.json"),
|
|
184
|
+
`${JSON.stringify(nextPkg, null, 2)}\n`,
|
|
185
|
+
"utf8",
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function writeLaunchframeArtifacts(destRoot, url, idea) {
|
|
190
|
+
const configTs = `/**
|
|
191
|
+
* Written by Launchframe CLI — edit freely after scaffolding.
|
|
192
|
+
*/
|
|
193
|
+
export const LAUNCHFRAME_SOURCE_URL = ${tsStringLiteral(url)} as const;
|
|
194
|
+
|
|
195
|
+
export const LAUNCHFRAME_SAAS_IDEA = ${tsStringLiteral(idea)} as const;
|
|
196
|
+
`;
|
|
197
|
+
|
|
198
|
+
await writeFile(join(destRoot, "src", "lib", "launchframe-config.ts"), configTs, "utf8");
|
|
199
|
+
|
|
200
|
+
const ctx = {
|
|
201
|
+
sourceUrl: url,
|
|
202
|
+
saasIdea: idea,
|
|
203
|
+
notes:
|
|
204
|
+
"Use /clone-website with sourceUrl for pixel-perfect extraction. Align landing copy with saasIdea.",
|
|
205
|
+
};
|
|
206
|
+
await writeFile(
|
|
207
|
+
join(destRoot, "launchframe.context.json"),
|
|
208
|
+
`${JSON.stringify(ctx, null, 2)}\n`,
|
|
209
|
+
"utf8",
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const md = `# Launchframe context
|
|
213
|
+
|
|
214
|
+
## Reference URL (clone target)
|
|
215
|
+
|
|
216
|
+
${url}
|
|
217
|
+
|
|
218
|
+
## SaaS idea (landing copy)
|
|
219
|
+
|
|
220
|
+
${idea}
|
|
221
|
+
|
|
222
|
+
When running \`/clone-website\`, pass the reference URL above. After structural cloning, rewrite headings and hero copy so they clearly communicate the SaaS idea while respecting attribution and copyright for third-party brands.
|
|
223
|
+
`;
|
|
224
|
+
|
|
225
|
+
await mkdir(join(destRoot, "docs", "research"), { recursive: true });
|
|
226
|
+
await writeFile(join(destRoot, "docs", "research", "LAUNCHFRAME.md"), md, "utf8");
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async function writeReadme(destRoot, npmPackageName, url, idea) {
|
|
230
|
+
const body = `# ${npmPackageName}
|
|
231
|
+
|
|
232
|
+
Created with [\`launchframe\`](https://www.npmjs.com/package/launchframe).
|
|
233
|
+
|
|
234
|
+
## Inputs
|
|
235
|
+
|
|
236
|
+
- **Reference site:** ${url}
|
|
237
|
+
- **SaaS idea:** ${idea}
|
|
238
|
+
|
|
239
|
+
## Quick start
|
|
240
|
+
|
|
241
|
+
\`\`\`bash
|
|
242
|
+
npm install
|
|
243
|
+
npm run dev
|
|
244
|
+
\`\`\`
|
|
245
|
+
|
|
246
|
+
Open your AI agent (Cursor, Claude Code, etc.) and run:
|
|
247
|
+
|
|
248
|
+
\`\`\`
|
|
249
|
+
/clone-website ${url}
|
|
250
|
+
\`\`\`
|
|
251
|
+
|
|
252
|
+
Keep the SaaS positioning from \`launchframe.context.json\` / \`src/lib/launchframe-config.ts\` when adapting cloned sections.
|
|
253
|
+
|
|
254
|
+
See \`AGENTS.md\` for full agent instructions.
|
|
255
|
+
`;
|
|
256
|
+
|
|
257
|
+
await writeFile(join(destRoot, "README.md"), body, "utf8");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function runNpmInstall(cwd) {
|
|
261
|
+
return new Promise((resolvePromise, reject) => {
|
|
262
|
+
const child = spawn("npm", ["install"], {
|
|
263
|
+
cwd,
|
|
264
|
+
stdio: "inherit",
|
|
265
|
+
shell: process.platform === "win32",
|
|
266
|
+
});
|
|
267
|
+
child.on("exit", (code) => {
|
|
268
|
+
if (code === 0) resolvePromise();
|
|
269
|
+
else reject(new Error(`npm install exited with code ${code}`));
|
|
270
|
+
});
|
|
271
|
+
child.on("error", reject);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function main() {
|
|
276
|
+
const args = parseArgs(process.argv.slice(2));
|
|
277
|
+
if (args.help) {
|
|
278
|
+
printHelp();
|
|
279
|
+
process.exit(0);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (!args.url || !args.idea) {
|
|
283
|
+
console.error("Error: missing URL or SaaS idea.\n");
|
|
284
|
+
printHelp();
|
|
285
|
+
process.exit(1);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
let url;
|
|
289
|
+
try {
|
|
290
|
+
url = validateUrl(args.url);
|
|
291
|
+
} catch (e) {
|
|
292
|
+
console.error(String(e.message));
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const idea = args.idea.trim();
|
|
297
|
+
if (!idea) {
|
|
298
|
+
console.error("Error: SaaS idea cannot be empty.");
|
|
299
|
+
process.exit(1);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const dirName = args.dir?.trim() || defaultDirName(url);
|
|
303
|
+
const destRoot = resolve(process.cwd(), dirName);
|
|
304
|
+
const npmPackageName = slugFromDir(dirName);
|
|
305
|
+
|
|
306
|
+
if (isForbiddenOutput(PKG_ROOT, destRoot)) {
|
|
307
|
+
console.error(
|
|
308
|
+
"Output folder cannot be inside the Launchframe package directory. Run from a parent folder or choose another path.",
|
|
309
|
+
);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
await mkdir(destRoot, { recursive: true });
|
|
314
|
+
|
|
315
|
+
await copyTemplateTree(PKG_ROOT, destRoot);
|
|
316
|
+
|
|
317
|
+
await writeGeneratedPackageJson(destRoot, npmPackageName);
|
|
318
|
+
await writeLaunchframeArtifacts(destRoot, url, idea);
|
|
319
|
+
await writeReadme(destRoot, npmPackageName, url, idea);
|
|
320
|
+
|
|
321
|
+
console.log(`\nCreated Launchframe project at ${destRoot}`);
|
|
322
|
+
console.log(` Reference URL: ${url}`);
|
|
323
|
+
console.log(` SaaS idea: ${idea}\n`);
|
|
324
|
+
|
|
325
|
+
if (!args.skipInstall) {
|
|
326
|
+
console.log("Running npm install...\n");
|
|
327
|
+
try {
|
|
328
|
+
await runNpmInstall(destRoot);
|
|
329
|
+
console.log("\nDone. Next: cd " + JSON.stringify(dirName) + " && npm run dev\n");
|
|
330
|
+
} catch (e) {
|
|
331
|
+
console.error(String(e.message));
|
|
332
|
+
console.error("\nDependencies were not installed. Run npm install inside the folder manually.\n");
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
} else {
|
|
336
|
+
console.log("Skipped npm install (--skip-install). Run npm install inside the folder.\n");
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
main().catch((err) => {
|
|
341
|
+
console.error(err);
|
|
342
|
+
process.exit(1);
|
|
343
|
+
});
|
|
@@ -1,124 +1,80 @@
|
|
|
1
|
-
# Website Inspection Guide
|
|
2
|
-
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- [ ]
|
|
11
|
-
- [ ]
|
|
12
|
-
- [ ]
|
|
13
|
-
- [ ]
|
|
14
|
-
- [ ]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
###
|
|
19
|
-
|
|
20
|
-
- [ ] **
|
|
21
|
-
- [ ] **
|
|
22
|
-
- [ ] **
|
|
23
|
-
- [ ] **
|
|
24
|
-
- [ ] **
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- [ ]
|
|
57
|
-
- [ ] **
|
|
58
|
-
- [ ] **
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- [ ] **
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- [ ] **
|
|
66
|
-
- [ ] **
|
|
67
|
-
- [ ] **
|
|
68
|
-
- [ ] **
|
|
69
|
-
- [ ] **
|
|
70
|
-
- [ ] **
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
7. **Animations** — transitions, entrance/exit, micro-interactions — **`framer-motion` vs CSS** and exact timing
|
|
82
|
-
|
|
83
|
-
### Common Components to Look For
|
|
84
|
-
- Navigation (top bar, sidebar, bottom bar)
|
|
85
|
-
- Cards / list items
|
|
86
|
-
- Buttons and links
|
|
87
|
-
- Forms and inputs
|
|
88
|
-
- Modals and dialogs
|
|
89
|
-
- Dropdowns and menus
|
|
90
|
-
- Tabs and segmented controls
|
|
91
|
-
- Avatars and user badges
|
|
92
|
-
- Loading skeletons
|
|
93
|
-
- Toast notifications
|
|
94
|
-
- Tooltips and popovers
|
|
95
|
-
- **Video / Lottie / canvas** blocks (do not substitute with static mockups without documenting why)
|
|
96
|
-
|
|
97
|
-
## Phase 3: Layout Architecture
|
|
98
|
-
|
|
99
|
-
- [ ] **Grid system** — CSS Grid? Flexbox? Fixed widths?
|
|
100
|
-
- [ ] **Column layout** — how many columns at each breakpoint?
|
|
101
|
-
- [ ] **Max-width** — main content area max-width
|
|
102
|
-
- [ ] **Sticky elements** — header, sidebar, floating buttons
|
|
103
|
-
- [ ] **Z-index layers** — navigation, modals, tooltips, overlays
|
|
104
|
-
- [ ] **Scroll behavior** — infinite scroll, pagination, virtual scrolling
|
|
105
|
-
|
|
106
|
-
## Phase 4: Technical Stack Analysis
|
|
107
|
-
|
|
108
|
-
- [ ] **Framework** — React? Vue? Angular? Check `__NEXT_DATA__`, `__NUXT__`, `ng-version`
|
|
109
|
-
- [ ] **CSS approach** — Tailwind (utility classes), CSS Modules, Styled Components, Emotion, vanilla CSS
|
|
110
|
-
- [ ] **State management** — Redux (check DevTools), React Query, Zustand, Pinia
|
|
111
|
-
- [ ] **API patterns** — REST, GraphQL (check network tab for `/graphql` requests)
|
|
112
|
-
- [ ] **Font loading** — Google Fonts, self-hosted, system fonts
|
|
113
|
-
- [ ] **Image strategy** — CDN, lazy loading, srcset, WebP/AVIF — **mirror URLs you are allowed to fetch**
|
|
114
|
-
- [ ] **Animation library** — site may use GSAP, Lottie, Rive, or CSS only — **in the Next.js clone, default to Framer Motion** unless a different lib is required for parity
|
|
115
|
-
|
|
116
|
-
## Phase 5: Documentation Output
|
|
117
|
-
|
|
118
|
-
After inspection, create these files in `docs/research/`:
|
|
119
|
-
1. `DESIGN_TOKENS.md` — All extracted colors, typography, spacing
|
|
120
|
-
2. `COMPONENT_INVENTORY.md` — Every component with structure notes
|
|
121
|
-
3. **`MEDIA_MANIFEST.md`** — (recommended) Table of every image/video/poster URL → local `public/` path or “blocked”
|
|
122
|
-
4. `LAYOUT_ARCHITECTURE.md` — Page layouts, grid system, responsive behavior
|
|
123
|
-
5. `INTERACTION_PATTERNS.md` — Animations: **CSS vs Framer Motion**, transitions, hover states
|
|
124
|
-
6. `TECH_STACK_ANALYSIS.md` — What the site uses and our chosen equivalents (Framer Motion for React animation)
|
|
1
|
+
# Website Inspection Guide
|
|
2
|
+
|
|
3
|
+
## How to Reverse-Engineer Any Website
|
|
4
|
+
|
|
5
|
+
This guide outlines what to capture when inspecting a target website via Chrome MCP or browser DevTools.
|
|
6
|
+
|
|
7
|
+
## Phase 1: Visual Audit
|
|
8
|
+
|
|
9
|
+
### Screenshots to Capture
|
|
10
|
+
- [ ] Every distinct page — desktop, tablet, mobile
|
|
11
|
+
- [ ] Dark mode variants (if applicable)
|
|
12
|
+
- [ ] Light mode variants (if applicable)
|
|
13
|
+
- [ ] Key interaction states (hover, active, open menus, modals)
|
|
14
|
+
- [ ] Loading/skeleton states
|
|
15
|
+
- [ ] Empty states
|
|
16
|
+
- [ ] Error states
|
|
17
|
+
|
|
18
|
+
### Design Tokens to Extract
|
|
19
|
+
- [ ] **Colors** — background, text (primary/secondary/muted), accent, border, hover, error, success, warning
|
|
20
|
+
- [ ] **Typography** — font family, sizes (h1-h6, body, caption, label), weights, line heights, letter spacing
|
|
21
|
+
- [ ] **Spacing** — padding/margin patterns (look for a scale: 4px, 8px, 12px, 16px, 24px, 32px, etc.)
|
|
22
|
+
- [ ] **Border radius** — buttons, cards, avatars, inputs
|
|
23
|
+
- [ ] **Shadows/elevation** — card shadows, dropdown shadows, modal overlay
|
|
24
|
+
- [ ] **Breakpoints** — when does the layout shift? (inspect with DevTools responsive mode)
|
|
25
|
+
- [ ] **Icons** — which icon library? custom SVGs? sizes?
|
|
26
|
+
- [ ] **Avatars** — sizes, shapes, fallback behavior
|
|
27
|
+
- [ ] **Buttons** — all variants (primary, secondary, ghost, icon-only, danger)
|
|
28
|
+
- [ ] **Inputs** — text fields, textareas, selects, checkboxes, toggles
|
|
29
|
+
|
|
30
|
+
## Phase 2: Component Inventory
|
|
31
|
+
|
|
32
|
+
For each distinct UI component, document:
|
|
33
|
+
1. **Name** — what would you call this component?
|
|
34
|
+
2. **Structure** — what HTML elements / child components does it contain?
|
|
35
|
+
3. **Variants** — does it have different sizes, colors, or states?
|
|
36
|
+
4. **States** — default, hover, active, disabled, loading, error, empty
|
|
37
|
+
5. **Responsive behavior** — how does it change at different breakpoints?
|
|
38
|
+
6. **Interactions** — click, hover, focus, keyboard navigation
|
|
39
|
+
7. **Animations** — transitions, entrance/exit animations, micro-interactions
|
|
40
|
+
|
|
41
|
+
### Common Components to Look For
|
|
42
|
+
- Navigation (top bar, sidebar, bottom bar)
|
|
43
|
+
- Cards / list items
|
|
44
|
+
- Buttons and links
|
|
45
|
+
- Forms and inputs
|
|
46
|
+
- Modals and dialogs
|
|
47
|
+
- Dropdowns and menus
|
|
48
|
+
- Tabs and segmented controls
|
|
49
|
+
- Avatars and user badges
|
|
50
|
+
- Loading skeletons
|
|
51
|
+
- Toast notifications
|
|
52
|
+
- Tooltips and popovers
|
|
53
|
+
|
|
54
|
+
## Phase 3: Layout Architecture
|
|
55
|
+
|
|
56
|
+
- [ ] **Grid system** — CSS Grid? Flexbox? Fixed widths?
|
|
57
|
+
- [ ] **Column layout** — how many columns at each breakpoint?
|
|
58
|
+
- [ ] **Max-width** — main content area max-width
|
|
59
|
+
- [ ] **Sticky elements** — header, sidebar, floating buttons
|
|
60
|
+
- [ ] **Z-index layers** — navigation, modals, tooltips, overlays
|
|
61
|
+
- [ ] **Scroll behavior** — infinite scroll, pagination, virtual scrolling
|
|
62
|
+
|
|
63
|
+
## Phase 4: Technical Stack Analysis
|
|
64
|
+
|
|
65
|
+
- [ ] **Framework** — React? Vue? Angular? Check `__NEXT_DATA__`, `__NUXT__`, `ng-version`
|
|
66
|
+
- [ ] **CSS approach** — Tailwind (utility classes), CSS Modules, Styled Components, Emotion, vanilla CSS
|
|
67
|
+
- [ ] **State management** — Redux (check DevTools), React Query, Zustand, Pinia
|
|
68
|
+
- [ ] **API patterns** — REST, GraphQL (check network tab for `/graphql` requests)
|
|
69
|
+
- [ ] **Font loading** — Google Fonts, self-hosted, system fonts
|
|
70
|
+
- [ ] **Image strategy** — CDN, lazy loading, srcset, WebP/AVIF
|
|
71
|
+
- [ ] **Animation library** — Framer Motion, GSAP, CSS transitions only
|
|
72
|
+
|
|
73
|
+
## Phase 5: Documentation Output
|
|
74
|
+
|
|
75
|
+
After inspection, create these files in `docs/research/`:
|
|
76
|
+
1. `DESIGN_TOKENS.md` — All extracted colors, typography, spacing
|
|
77
|
+
2. `COMPONENT_INVENTORY.md` — Every component with structure notes
|
|
78
|
+
3. `LAYOUT_ARCHITECTURE.md` — Page layouts, grid system, responsive behavior
|
|
79
|
+
4. `INTERACTION_PATTERNS.md` — Animations, transitions, hover states
|
|
80
|
+
5. `TECH_STACK_ANALYSIS.md` — What the site uses and our chosen equivalents
|