Port the visual theme and styling from a live website to a React/Tailwind CSS project. Extracts colours, typography, spacing, and component styles — via agent-browser automation, manual inspection, curl/wget, or direct source reading — writes structured documentation and all artifacts under .context/artifacts/{website}/ with timestamps, applies findings as Tailwind v4 CSS tokens, then verifies by visually diffing the original site against the local or deployed version. Use when cloning a brand, replicating a design system, matching a reference site, migrating visual identity, copying a style guide, or porting a theme from any live URL into a React codebase.
95
94%
Does it follow best practices?
Impact
98%
1.44xAverage score across 5 eval scenarios
Advisory
Suggest reviewing before use
How to interpret visual diffs and fix mismatches when comparing the original site to your local or deployed build.
# Start local dev server first
bun run dev# Screenshot both sides
agent-browser --session source open <TARGET_URL> && \
agent-browser --session source wait --load networkidle && \
agent-browser --session source screenshot --full "${ARTIFACTS}/verify-source.png"
agent-browser --session local open http://localhost:5173 && \
agent-browser --session local wait --load networkidle && \
agent-browser --session local screenshot --full "${ARTIFACTS}/verify-local.png"
# Side-by-side pixel diff
agent-browser diff url <TARGET_URL> http://localhost:5173 \
--screenshot "${ARTIFACTS}/diff-homepage.png"With mcp-playwright, omit filename to receive screenshots inline in context — the agent can read and reason about the images directly without saving files or running a diff tool.
playwright_browser_navigate(url: "<TARGET_URL>")
playwright_browser_wait_for(time: 2)
playwright_browser_take_screenshot(fullPage: true) # source — visible inline
playwright_browser_navigate(url: "http://localhost:5173")
playwright_browser_wait_for(time: 2)
playwright_browser_take_screenshot(fullPage: true) # local — visible inlineBoth images appear in the agent's context. Describe observed differences (colour, font, spacing, layout) directly — no pixel-diff tooling required.
agent-browser diff url produces:
There is no mismatch percentage. Instead, visually inspect both screenshots in context and note:
| Percentage | Interpretation |
|---|---|
| < 5% | Acceptable — minor rendering differences |
| 5–20% | Review needed — likely font, spacing, or colour drift |
| 20–50% | Significant — section layout or component structure differs |
| > 50% | Major — probably wrong page, wrong viewport, or missing content |
Symptom: Text looks wider/narrower, slightly different weight.
Cause: Font not loaded, fallback font used, or font-display: swap delay.
Fix:
<link> in index.html includes the Google Fonts URL with display=swap--font-sans in @theme inline matches the extracted font family name exactlywght@400;600;700)<!-- Correct -->
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />Symptom: Buttons, backgrounds, or text appear in the wrong shade.
Cause: HSL conversion was off, or wrong token assigned.
Fix:
references/extraction.mdrgb() value to hex using the HSL converter--primary is really the CTA colour, not a backgroundQuick browser check:
agent-browser:
agent-browser eval --stdin <<'JS'
const btn = document.querySelector('button, [class*="btn"]');
getComputedStyle(btn).backgroundColor
JSmcp-playwright:
playwright_browser_evaluate(function: '() => { const btn = document.querySelector("button, [class*=\\"btn\\"]"); return getComputedStyle(btn).backgroundColor; }')Symptom: Sections have wrong padding/margin compared to original.
Cause: Extracted spacing values weren't translated to the correct Tailwind scale.
Fix:
py-[72px], then define a tokenSymptom: A section visible on the original doesn't appear locally.
Cause: Unimplemented component, wrong route, or different page structure.
Fix: This is a content/structure issue, not a theme issue. Skip for the theme porting phase — focus on visual fidelity of what is present.
Symptom: Buttons/cards are more or less rounded.
Cause: --radius token set to wrong value.
Fix: Extract the exact value:
agent-browser:
agent-browser eval 'getComputedStyle(document.querySelector("button")).borderRadius'mcp-playwright:
playwright_browser_evaluate(function: '() => getComputedStyle(document.querySelector("button")).borderRadius')Then update --radius in :root.
Symptom: Cards look flat or over-shadowed.
Cause: Tailwind's built-in shadow scale doesn't match.
Fix: Extract the exact box-shadow:
agent-browser:
agent-browser eval --stdin <<'JS'
const card = document.querySelector('[class*="card"], article');
card ? getComputedStyle(card).boxShadow : 'not found'
JSmcp-playwright:
playwright_browser_evaluate(function: '() => { const card = document.querySelector("[class*=\\"card\\"], article"); return card ? getComputedStyle(card).boxShadow : "not found"; }')Then define a custom token:
@theme inline {
--shadow-card: 0 1px 3px rgb(0 0 0 / 0.08);
}Symptom: Logo or icons are bigger/smaller than original.
Cause: Missing width/height constraints, or SVG viewBox differences.
Fix: This is a component-level issue. Note the pixel dimensions from the source screenshot and apply them directly in the component.
Check each route independently.
agent-browser:
PAGES=("" "/about" "/services" "/pricing" "/contact")
for PAGE in "${PAGES[@]}"; do
SLUG="${PAGE//\//-}"
SLUG="${SLUG:-home}"
agent-browser diff url "<TARGET_URL>${PAGE}" "http://localhost:5173${PAGE}" \
--screenshot "${ARTIFACTS}/diff${SLUG}.png"
donemcp-playwright (inline — navigate to each page pair in sequence):
# For each page: screenshot source, then local — inspect inline
playwright_browser_navigate(url: "<TARGET_URL>")
playwright_browser_take_screenshot(fullPage: true)
playwright_browser_navigate(url: "http://localhost:5173")
playwright_browser_take_screenshot(fullPage: true)
# Repeat for /about, /services, etc.agent-browser:
# Source at 375px
agent-browser --session source set viewport 375 812
agent-browser --session source open <TARGET_URL>
agent-browser --session source wait --load networkidle
agent-browser --session source screenshot --full "${ARTIFACTS}/verify-source-mobile.png"
# Local at 375px
agent-browser --session local set viewport 375 812
agent-browser --session local open http://localhost:5173
agent-browser --session local wait --load networkidle
agent-browser --session local screenshot --full "${ARTIFACTS}/verify-local-mobile.png"
# Diff
agent-browser diff screenshot \
--baseline "${ARTIFACTS}/verify-source-mobile.png" \
"${ARTIFACTS}/verify-local-mobile.png"mcp-playwright:
playwright_browser_resize(width: 375, height: 812)
playwright_browser_navigate(url: "<TARGET_URL>")
playwright_browser_wait_for(time: 2)
playwright_browser_take_screenshot(fullPage: true) # source mobile — inline
playwright_browser_navigate(url: "http://localhost:5173")
playwright_browser_wait_for(time: 2)
playwright_browser_take_screenshot(fullPage: true) # local mobile — inlineWhen a preview URL is available.
agent-browser:
PREVIEW_URL="https://your-project-abc123.vercel.app"
agent-browser diff url <TARGET_URL> "${PREVIEW_URL}" \
--screenshot "${ARTIFACTS}/diff-deployed-homepage.png"mcp-playwright:
playwright_browser_navigate(url: "<TARGET_URL>")
playwright_browser_wait_for(time: 2)
playwright_browser_take_screenshot(fullPage: true) # source — inline
playwright_browser_navigate(url: "https://your-project-abc123.vercel.app")
playwright_browser_wait_for(time: 2)
playwright_browser_take_screenshot(fullPage: true) # deployed — inlinesrc/index.css or the affected componentbun run build && bun run previewAim for < 10% mismatch on primary pages (home, about) before considering the port complete.