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.
Files changed (38) hide show
  1. package/.claude/skills/clone-website/SKILL.md +473 -564
  2. package/.clinerules +147 -285
  3. package/.codex/skills/clone-website/SKILL.md +473 -564
  4. package/.continue/commands/clone-website.md +475 -566
  5. package/.continue/rules/project.md +151 -285
  6. package/.cursor/commands/clone-website.md +470 -561
  7. package/.cursor/rules/project.mdc +7 -22
  8. package/.gemini/commands/clone-website.toml +476 -567
  9. package/.github/copilot-instructions.md +147 -281
  10. package/.github/skills/clone-website/SKILL.md +473 -564
  11. package/.gitignore +49 -0
  12. package/.opencode/commands/clone-website.md +473 -564
  13. package/.windsurf/workflows/clone-website.md +470 -561
  14. package/AGENTS.md +65 -160
  15. package/README.md +162 -121
  16. package/bin/launchframe.mjs +343 -0
  17. package/docs/research/INSPECTION_GUIDE.md +80 -124
  18. package/package.json +97 -54
  19. package/src/app/globals.css +1 -93
  20. package/src/app/layout.tsx +16 -5
  21. package/src/app/page.tsx +37 -2
  22. package/src/lib/launchframe-config.ts +8 -0
  23. package/.amazonq/cli-agents/clone-website.json +0 -9
  24. package/.amazonq/rules/project.md +0 -281
  25. package/.augment/commands/clone-website.md +0 -565
  26. package/.claude/skills/marketing-social-proof-motion/SKILL.md +0 -47
  27. package/.cursor/commands/marketing-social-proof-motion.md +0 -42
  28. package/.nvmrc +0 -1
  29. package/CHANGELOG.md +0 -80
  30. package/START_HERE.md +0 -15
  31. package/docs/design-references/playwright-example.com-1440px.png +0 -0
  32. package/docs/design-references/playwright-example.com-390px.png +0 -0
  33. package/launchframe.config.json +0 -14
  34. package/public/images/.gitkeep +0 -0
  35. package/public/seo/.gitkeep +0 -0
  36. package/public/videos/.gitkeep +0 -0
  37. package/scripts/recon-playwright.mjs +0 -396
  38. 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
- ## Priority (read first): media & motion
4
-
5
- Launchframe clones live pages for a **visual** result. Two things most often separate a convincing build from a hollow one:
6
-
7
- ### 1. Images & video (do this before obsessing over utility classes)
8
-
9
- - [ ] **Every `<img>`** — `src` / `srcset` / `currentSrc`, `sizes`, `loading`, `decoding`, `alt`, intrinsic dimensions
10
- - [ ] **`<picture>` / `<source>`**resolution switches, art direction, `type` (WebP/AVIF)
11
- - [ ] **Every `<video>`** `src` + nested `<source>`, **poster**, `autoplay`, `loop`, `muted`, `playsinline`, `controls`
12
- - [ ] **Background images** `background-image` on ancestors (hero stacks are often **layers** of img + gradient + PNG mockup)
13
- - [ ] **Lazy / below-fold** scroll the page once before asset discovery so `data-src` / lazy-loaded URLs resolve if the site uses them
14
- - [ ] **Download** — mirror into `public/images/` and `public/videos/` with stable paths; list failures in `docs/research/EXTRACTION_LIMITATIONS.md`
15
-
16
- If automation hits a bot wall, **do not pretend extraction succeeded** — capture what you can from successful fetches and document gaps.
17
-
18
- ### 2. Motion (prefer Framer Motion in this repo)
19
-
20
- - [ ] **Entrance** — fade/slide/scale on mount or on **scroll into view** (note threshold / `margin`)
21
- - [ ] **Stagger** — children animating in sequence (hero bullets, card grids)
22
- - [ ] **Scroll-linked** — progress, parallax, pinned sections (may combine with CSS `animation-timeline` or libs)
23
- - [ ] **Gestures** — drag, pan, hover follow (often Framer Motion)
24
- - [ ] **Implementation rule** — use **`framer-motion`** for anything beyond trivial single-property CSS `transition`. Record **duration, easing, delay, stagger**, and **trigger** (scroll, hover, tap) in specs.
25
-
26
- ### 3. Idea-tailored illustration & motifs *(production landings)*
27
-
28
- When the reference page is **sparse** (mostly type + gray boxes) or after a **rebrand** the UI still reads generic, illustration is required — and it must be **unique to the SaaS idea** (`launchframe.config.json#idea`), not reusable wallpaper.
29
-
30
- - [ ] **Metaphor list** — 3–6 nouns/verbs derived from `idea`; every bespoke asset maps to ≥ one entry
31
- - [ ] **Uniqueness check** — If the scene works unchanged for another product category, revise
32
- - [ ] **Inline SVG** — hero shapes, dividers, card mini-scenes; note `viewBox`, **`currentColor`** vs fixed fills, **Idea tie-in** sentence per asset
33
- - [ ] **Pixel art / sprites** — only when character reinforces the metaphor; palette hex table; `imageRendering` / grid discipline
34
- - [ ] **Motif thread** — recurring element echoing hero + OG + favicon **for this product**, not a random geometric pattern
35
- - [ ] **Accent tokens** — primary/secondary roles aligned with idea personality (extract from reference or define in `:root`)
36
- - [ ] **Motion tiers A–E** — document with **reduced-motion** fallback (`prefers-reduced-motion`)
37
-
38
- See **`AGENTS.md` → Production polish for marketing landings** for tier definitions and folders (`src/components/marketing/art/`, `public/images/marketing/`).
39
-
40
- ---
41
-
42
- ## How to Reverse-Engineer Any Website
43
-
44
- This guide outlines what to capture when inspecting a target website via Chrome MCP or browser DevTools.
45
-
46
- ## Phase 1: Visual Audit
47
-
48
- ### Screenshots to Capture
49
- - [ ] **Single-page landing — full scroll depth** — slow-scroll until lazy sections stabilize; **`fullPage`** / stitched capture so footer and below-fold bands are included (not viewport-only hero grabs)
50
- - [ ] Every distinct page — desktop, tablet, mobile
51
- - [ ] Dark mode variants (if applicable)
52
- - [ ] Light mode variants (if applicable)
53
- - [ ] Key interaction states (hover, active, open menus, modals)
54
- - [ ] Loading/skeleton states
55
- - [ ] Empty states
56
- - [ ] Error states
57
- - [ ] **Video frames** — capture a frame mid-play for reference if motion is subtle
58
- - [ ] **Hero / full-bleed** — wide crops where raster layers are easy to miss
59
-
60
- ### Design Tokens to Extract
61
- - [ ] **Colors** — background, text (primary/secondary/muted), accent, border, hover, error, success, warning
62
- - [ ] **Typography** — font family, sizes (h1-h6, body, caption, label), weights, line heights, letter spacing
63
- - [ ] **Spacing** — padding/margin patterns (look for a scale: 4px, 8px, 12px, 16px, 24px, 32px, etc.)
64
- - [ ] **Border radius** — buttons, cards, avatars, inputs
65
- - [ ] **Shadows/elevation** — card shadows, dropdown shadows, modal overlay
66
- - [ ] **Breakpoints** — when does the layout shift? (inspect with DevTools responsive mode)
67
- - [ ] **Icons** — which icon library? custom SVGs? sizes?
68
- - [ ] **Avatars** — sizes, shapes, fallback behavior
69
- - [ ] **Buttons** — all variants (primary, secondary, ghost, icon-only, danger)
70
- - [ ] **Inputs** — text fields, textareas, selects, checkboxes, toggles
71
-
72
- ## Phase 2: Component Inventory
73
-
74
- For each distinct UI component, document:
75
- 1. **Name** what would you call this component?
76
- 2. **Structure**what HTML elements / child components does it contain?
77
- 3. **Variants**does it have different sizes, colors, or states?
78
- 4. **States**default, hover, active, disabled, loading, error, empty
79
- 5. **Responsive behavior** how does it change at different breakpoints?
80
- 6. **Interactions**click, hover, focus, keyboard navigation
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 pagedesktop, 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