How to Convert a Screenshot to HTML and CSS Code
Learn how to rebuild a screenshot as semantic HTML and CSS, compare renders, handle responsive behavior, and automate captures with ScreenshotNeo.
A screenshot contains pixels, not the original DOM, stylesheet, responsive rules, or interaction logic. Converting it to HTML and CSS is therefore a reconstruction task: inspect the image, infer its structure, implement a first pass, render it at the same viewport, and refine the differences.
The reliable workflow is:
- Measure and inspect the reference image.
- Map visible regions to semantic HTML.
- Build the large layout relationships with CSS Grid or Flexbox.
- Add real assets, fonts, colors, spacing, and details.
- Render at the reference viewport and compare.
- Check additional widths and interactive states.
1. Understand what the screenshot can and cannot tell you
A screenshot can show visible headings, paragraphs, images, links, forms, navigation, cards, colors, alignment, and approximate dimensions. It cannot prove the original markup, source assets, font files, breakpoints, hover states, keyboard behavior, data loading, or animation logic. Treat every hidden behavior as an implementation decision that needs review.
If you only have one viewport, reproduce that viewport first and describe other responsive behavior as an assumption. Do not copy the entire page into one background image when the goal is editable, accessible, responsive code.
2. Inspect and measure the reference
Record the screenshot’s pixel dimensions before writing code. Create an inventory of the page regions:
- Header and navigation
- Hero or page heading
- Main content and sidebar columns
- Cards, lists, forms, and repeated components
- Images and their crop ratios
- Footer and secondary links
Transcribe visible copy exactly where possible. Note the dominant colors, background surfaces, borders, shadows, corner radii, line lengths, image crop positions, and the distance between major blocks. Start with geometry: container width, columns, section heights, and alignment. Typography and decorative details come later.
3. Choose a reconstruction route
| Route | Input | Output | Best use |
|---|---|---|---|
| Manual reconstruction | Screenshot and available assets | HTML and CSS you control | Production pages, accessibility, and precise structure |
| Screenshot to editable design | Screenshot | Editable design layers | Recovering a design you want to refine before coding |
| Design to code | Design frame and its layers/styles | HTML/CSS or another code format | Projects where design-system context exists |
| Screenshot-to-code generator | Screenshot or mockup | Starter code | Fast first drafts that still require inspection |
Figma documents screenshot-to-design and design-to-code as separate workflows: its screenshot converter creates editable layers, while its code converter generates code from selected design frames. Its developer documentation also shows a prompt for generating a selection in plain HTML and CSS. These workflows provide a starting point; they do not reveal the original application’s hidden behavior or guarantee production-ready code. Figma screenshot-to-design, Figma design-to-code, and Figma tools and prompts.
The open-source screenshot-to-code project is another possible starting point and lists HTML and CSS among its outputs. Compare tools by their input requirements, whether they output editable layers or code, access to existing components and styles, supported formats, and the correction work you must do.
4. Create semantic HTML
Map visual regions to meaningful elements. A typical landing page might look like this:
<body>
<header class="site-header">
<a class="brand" href="/">Acme</a>
<nav aria-label="Primary">
<a href="/features">Features</a>
<a href="/pricing">Pricing</a>
<a href="/docs">Docs</a>
</nav>
<a class="button" href="/signup">Get started</a>
</header>
<main>
<section class="hero" aria-labelledby="hero-title">
<div class="hero-copy">
<p class="eyebrow">Analytics for teams</p>
<h1 id="hero-title">See what your customers do next.</h1>
<p>A concise description copied from the reference screenshot.</p>
<a class="button" href="/signup">Start free</a>
</div>
<figure class="hero-media">
<img src="dashboard.webp" alt="Analytics dashboard overview">
</figure>
</section>
<section class="features" aria-labelledby="features-title">
<h2 id="features-title">Everything your team needs</h2>
<div class="feature-grid">
<article class="feature-card">
<h3>Fast reports</h3>
<p>Reusable content for the first card.</p>
</article>
<article class="feature-card">
<h3>Shared context</h3>
<p>Reusable content for the second card.</p>
</article>
</div>
</section>
</main>
<footer class="site-footer">...</footer>
</body>
Use one h1, a logical heading order, real links and buttons, labels for form controls, and meaningful image alternative text. Keep repeated cards as repeated components rather than individually positioned rectangles.
5. Establish layout CSS before visual polish
:root {
--page-max: 1120px;
--gutter: clamp(1rem, 3vw, 3rem);
--space-1: .5rem;
--space-2: 1rem;
--space-3: 1.5rem;
--space-4: 3rem;
--ink: #172033;
--muted: #65708a;
--surface: #ffffff;
--accent: #4f46e5;
}
* { box-sizing: border-box; }
body {
margin: 0;
color: var(--ink);
background: var(--surface);
font-family: Inter, system-ui, sans-serif;
line-height: 1.5;
}
.site-header,
main,
.site-footer {
width: min(100% - 2 * var(--gutter), var(--page-max));
margin-inline: auto;
}
.site-header {
display: flex;
align-items: center;
gap: 2rem;
min-height: 72px;
}
.site-header nav { display: flex; gap: 1.25rem; margin-left: auto; }
.button {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 44px;
padding: .65rem 1rem;
border-radius: .5rem;
color: white;
background: var(--accent);
text-decoration: none;
}
.hero {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
align-items: center;
gap: clamp(2rem, 6vw, 6rem);
padding-block: clamp(4rem, 10vw, 9rem);
}
.hero h1 { max-width: 11ch; font-size: clamp(2.5rem, 7vw, 5.5rem); line-height: .98; }
.hero-media img { display: block; width: 100%; height: auto; border-radius: 1rem; }
.feature-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1rem; }
.feature-card { padding: 1.5rem; border: 1px solid #e4e7ef; border-radius: .75rem; }
@media (max-width: 720px) {
.site-header { flex-wrap: wrap; gap: 1rem; padding-block: 1rem; }
.site-header nav { order: 3; width: 100%; margin-left: 0; overflow-x: auto; }
.hero { grid-template-columns: 1fr; padding-block: 3rem; }
.hero h1 { max-width: none; }
.feature-grid { grid-template-columns: 1fr; }
}
Use Grid for two-dimensional relationships and Flexbox for one-dimensional rows or columns. Prefer constraints such as minmax(), clamp(), and max-width containers over dozens of absolute pixel offsets. Match the reference viewport first, then add breakpoints based on where the layout actually needs to change.
6. Match typography, assets, and details
- Load the actual font files if you have them; otherwise choose a close fallback and expect different line wrapping.
- Match font size, weight, line-height, and letter spacing together. A correct font size with the wrong line-height can shift an entire section.
- Use the original image assets where available. Match
object-fit, aspect ratio, and focal position before adding overlays. - Apply colors to surfaces, text, borders, and interactive states separately.
- Add shadows, radii, icons, and separators after the geometry is stable.
7. Render, compare, and iterate
Open the page at the screenshot’s exact viewport dimensions. Compare in this order:
- Overall content width and page edges
- Header height and hero position
- Column widths, section heights, and major alignment
- Text wrapping and image crops
- Colors, borders, radii, shadows, and icon spacing
A transparent overlay or image-difference view makes systematic offsets easier to see. Change one group of variables at a time. If every section is too far left, fix the container; if only a heading wraps differently, inspect the font, width, or letter spacing.
8. Validate responsive and interactive behavior
A static image cannot establish mobile behavior. Check at least one narrower viewport and one wider viewport. Verify that navigation remains usable, text does not overflow, images do not distort, focus indicators are visible, buttons have adequate target sizes, and content order still makes sense for keyboard and screen-reader users.
Also test states that are absent from the screenshot: hover, focus, disabled controls, validation errors, long labels, missing images, and loading states. Heavy JavaScript animation, canvas-rendered content, and virtualized lists can be difficult to represent as editable layers; review such areas manually. Figma’s capture limitations describe these issues for code-to-design capture.
9. Capture a reference page when you need a fresh screenshot
If the reference is an existing web page rather than a local image, capture it at a controlled viewport before reconstruction. A browser script gives you full control:
import { chromium } from "playwright";
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 1 });
await page.goto("https://example.com", { waitUntil: "networkidle" });
await page.screenshot({ path: "reference.png", fullPage: true });
await browser.close();
For pages that depend on authentication, consent, delayed content, or animations, configure the browser context and wait conditions explicitly. Keep the capture viewport, device scale, timezone, and state documented so later comparisons are reproducible.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. It accepts one GET request and returns PNG, JPEG, WebP, or PDF. Before capture it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for all options. This is a complete cURL example:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', buffer);
Useful options include full-page capture with lazy images loaded, CSS-selector element capture, dark mode, 12 device presets or any viewport, retina scale, PDF paper size and margins, custom CSS and JavaScript, clicks before capture, selector hiding, waits for a selector or delay or network idle, request and resource blocking, custom headers and cookies, user agent and Authorization, timezone and geolocation, transparent backgrounds, image resizing, cache TTL, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage API access, and an OpenAPI specification. Parameter names used by other screenshot APIs also work, which can simplify migration.
For AI-assisted reconstruction, its MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients. Plans include 1,000 free shots per month with no card; paid plans start at $5 for 3,000 shots. Yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account.
10. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Everything is horizontally shifted | Container width, margin, or box-sizing mismatch | Set box-sizing: border-box, inspect the max-width and gutters, and compare the outer edges first. |
| Text wraps on different lines | Different font, weight, width, or letter spacing | Load the intended font, match the text column width, then tune line-height and tracking. |
| Images look stretched | Missing aspect ratio or incorrect object-fit | Set an explicit aspect ratio and use object-fit: cover or contain as the reference requires. |
| Mobile layout breaks | Desktop-only fixed widths or absolute positioning | Replace offsets with Grid/Flexbox constraints and add a breakpoint where content needs it. |
| Screenshot capture shows a cookie banner | The capture context has not handled consent | Accept or remove the banner in browser code, or use ScreenshotNeo’s consent handling. |
| Capture is blank or times out | Page load failure, bot check, or content that never becomes ready | Check the URL and wait condition, inspect response verdict headers, and retry with an appropriate timeout. |
| Dynamic content differs between runs | Animation, time-dependent data, ads, or random content | Freeze animations, set timezone and data state, block unnecessary requests, and use a cache TTL where suitable. |
11. Performance, reliability, and cost
- Performance: capture only the element or viewport you need when a full page is unnecessary. Block ads, trackers, and unused resource types; wait for a specific selector instead of an indefinite network idle when the page has long-lived connections.
- Reliability: make renders deterministic by fixing viewport, device scale, timezone, geolocation, authentication state, and data. Retry transient failures with a bounded backoff and record verdict headers.
- Cost: cache stable references and choose a TTL. With ScreenshotNeo, cache hits and failed loads are not billed, and only clean shots are billed. Bulk capture can reduce request overhead for batches of up to 100 URLs.
- Security: keep API keys server-side, pass sensitive headers and cookies only when required, and avoid exposing signed credentials in client code.
12. A practical completion checklist
- Reference viewport dimensions are recorded.
- Major regions have semantic elements and a logical heading order.
- Layout uses reusable Grid or Flexbox relationships.
- Real assets, fonts, image crops, colors, and spacing are matched.
- The page is rendered and compared at the target viewport.
- At least one narrow and one wide viewport are checked.
- Keyboard focus, contrast, alt text, and interaction states are reviewed.
- Dynamic capture state is documented and repeatable.
FAQ
Can a screenshot reveal the original HTML?
No. It reveals rendered pixels. You must infer structure, content, and behavior.
Should I use absolute positioning?
Use it for genuinely positioned details, such as an overlay. Use Grid, Flexbox, and responsive constraints for page structure.
Is screenshot-to-code output production-ready?
Treat generated code as a draft. Review semantics, accessibility, responsiveness, assets, and interactions before shipping.
How do I reproduce a page at a different viewport?
Implement the known reference viewport first, then choose responsive rules based on content and test them at additional widths.


