About screenshot-to-html
screenshot-to-html is a skill for turning UI screenshots into self-contained HTML pages. It guides developers through inferring semantic layout and design tokens, then rendering and comparing iterations to refine visual fidelity.
Key features
- 01Screenshot-to-HTML workflow
- 02Semantic HTML and design tokens
- 03Render-and-compare iteration
- 04Responsive layout guidance
- 05Interactive states and accessibility
- 06Optional motion guidance
Use cases
- 01Recreating a web page from a screenshot
- 02Refining layout through visual comparisons
- 03Adding functional controls to a static-looking replica
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ sevzq/ screenshot-to-html --skill screenshot-to-htmlAfter installing, type/screenshot-to-htmlin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
.gitignoreREADME.zh-CN.mdREADME.mdLICENSE.nojekyllAGENTS.mdpackage-lock.jsonpackage.jsonindex.htmlreference.mdexamples/app-elevenlabs/output.htmlexamples/app-spotify/output.htmlexamples/dashboard-stripe/output.htmlexamples/landing-clay/output.htmlexamples/landing-cloudflare/output.htmlexamples/landing-linear/output.htmlexamples/landing-modal/output.htmlexamples/landing-stripe/output.htmlexamples/mobile-airbnb/output.htmlexamples/mobile-tesla/output.htmlreferences/animation.mdinteraction.mdscripts/crop.mjsshot.mjsDetails
From the project READMEscreenshot-to-html — Screenshot to High-Fidelity HTML
Turn a UI image into a clean, self-contained HTML page whose rendered output matches the target image. Respond to the user in their language (they often write Chinese); keep chat replies short and let the work happen in files.
Core principle: replicate the RENDERED state, not the COMPUTED state
The target image is the ground truth of the final rendered pixels. Reproduce that, not a mechanical transcription of per-element values.
- Computed-state cloning (do NOT do this). Eyedropper every hex, "measure"
pixel coordinates/sizes, and paste
getComputedStyle-style numbers into absolute-positioned divs. Result: magic-number soup, non-semantic, not responsive, brittle — and small extraction errors compound so it looks worse. - Rendered-state cloning (DO this). Read the design like a developer: infer
semantic structure, a small set of design tokens (palette, type scale,
spacing rhythm, radii, shadows), and layout intent (flex/grid). Build it,
then render your code, screenshot it, and compare against the target, and
iterate. Optimize "does my render look like the target," goal:
render(code) ≈ target.
This is why the workflow is a feedback loop, not a one-shot dump.
Workflow
Copy this checklist and track progress:
- [ ] Phase 0 — Setup (inputs, stack, target width, deps, assets)
- [ ] Phase 1 — Read the design (global -> components -> exact text)
- [ ] Phase 2 — Build the first draft (semantic + tokens, self-contained)
- [ ] Phase 3 — Rendered-state loop (render, compare, fix; repeat 2-4x)
- [ ] Phase 4 — Interactivity & states (affordances + behavior, then verify)
- [ ] Phase 5 — Final checks
- [ ] Phase 6 — Enhance with motion (optional)
Phase 0 — Setup
- Confirm inputs: the target image path(s) and the desired output file path.
- Assets are automatic (no questions by default). Fill each slot with what gives the
sharpest, most faithful result — not "crop everything": user-supplied assets win; real logos as
inline SVG / official files; specific unique content cropped from the source
(
scripts/crop.mjs) when that region is sharp; generic photos from Unsplash /picsum; ahttps://placehold.coblock as last resort. Quality gate: never ship a blurry upscaled crop — swap a soft crop for a sharp stock photo. Only ask once (non-blocking) if an asset is brand-critical AND no layer can approximate it. See Images & assets in reference.md. - Pick the stack (ask only if unclear):
vanilla(HTML + plain CSS, self-contained, default) ortailwind(HTML + Tailwind CDN). See Stacks. - Understand the canvas & scale FIRST — before writing any CSS. Read the pixel
size (
sips -g pixelWidth -g pixelHeight <image>), then decide a design width in CSS px, not the raw pixel count:- Screenshots are usually retina (
@2x/@3x), so the file is 2-3x the CSS width. Do not just divide and build at that size — that ships a shrunken page with~10pxnav text that looks tiny at 100% zoom. Map to a real breakpoint: web ->1280or1440; mobile ->375/390/393. - Build at that width with real-world type sizes (hero ~48-72px, body ~16-18px)
and keep it responsive/fluid (
clamp(),max-width,%), so it opens at a normal size and adapts — never a fixed miniature. - Run the render/compare loop at this design width;
--scale 2only sharpens the diff, it does not mean "author at half size."
- Screenshots are usually retina (
- Ensure render deps once (no browser download):
npm i -D playwright-core.
Phase 1 — Read the design (global first, then local)
Look at the image carefully and write a short build spec. Global before local:
- Layout: page max-width, column grid, section order, alignment.
- Color tokens: name 4-8 colors (bg, surface, ink, muted, brand, accent, line). Account for gradients/overlays/opacity — never trust one pixel.
- Type: families (serif vs sans, geometric vs humanist -> closest Google Font), the heading->body scale, weights, letter-spacing, line-height.
- Spacing rhythm (base 4/8px), border-radii, shadow depth, borders/dividers.
- Components: nav, hero, feature/bento, stats, cards, testimonials, CTA, footer — note structure and repetition (build one, loop the data).
- Content: transcribe the exact visible text (do not invent or translate it).
Do NOT record pixel coordinates. Record relationships and tokens. For the full rubric see reference.md.
Phase 2 — Build the first draft
- One self-contained file (inline
<style>, minimal inline JS only if needed). - Semantic HTML (
header/nav/main/section/article/footer, headings in order). - Put tokens in
:rootCSS variables; build layout with flex/grid, fluid sizing (clamp,min()), not fixed pixel frames. - Use the exact text from the image.
- Images: fill each slot with the best layer for that slot (see Phase 0) — crop specific,
unique content from the source when it's sharp (
node scripts/crop.mjs --in <target> --rect x,y,w,h --out assets/<name>.png), use a sharp Unsplash/picsumphoto for generic imagery (a fuzzy crop is worse than a real photo), elsehttps://placehold.co/<w>x<h>with a<!-- asset: ... -->note. Icons: inline SVG (avoid emoji-as-icon). - Match the image's breakpoint first; add sensible responsive behavior without inventing layouts you cannot see.
Phase 3 — Rendered-state loop (the important part)
- Render your draft to a screenshot:
node scripts/shot.mjs \ --in <output.html> --out tmp/screenshot-to-html/v1.png --width <DESIGN_WIDTH> - View both the target image and your render. Compare region by region and list concrete diffs, prioritized: layout/structure > sizing/spacing > color/contrast > typography > polish (shadows, radii, gradients).
- Fix the top issues with semantic edits (adjust tokens/layout, not magic
numbers). Re-render to
v2.png,v3.png, ... - Repeat until differences are cosmetic (usually 2-4 rounds). Each round must reduce the visual gap; if a change makes it worse, revert it.
Comparison tips and common illusions are in reference.md.
Phase 4 — Interactivity & states (make it a page, not an image)
A screenshot is one frozen frame — it can't show hover, focus, or what's clickable. Matching it alone ships a "webpage that's secretly an image." Make the replica behave like a real page:
- Real elements. Anything that looks clickable is a
<button>or<a href>(orrole="button"+tabindex="0"), never an inert<div>/<span>; inputs are real<input>. - Affordances by default (always). Every control gets
cursor: pointer, a visible:hoverchange, a:focus-visiblering (keyboard), and an:activepress, with a shorttransition(~120-180ms). These don't alter the resting look — they add feedback. - Functional behavior when the image implies it (minimal inline JS): tab/segmented controls switch the visible panel; a multi-screen mobile app navigates inside its device frame (bottom-nav / cards swap screens); modals/menus/dropdowns/accordions open and close (Esc / backdrop to dismiss); in-page nav smooth-scrolls. Don't invent flows you cannot infer.
- A11y & motion. Logical heading order,
alttext, visible focus, decent contrast; honorprefers-reduced-motion. - Verify, don't assume:
node scripts/shot.mjs --in <file> --verifyflags clickable-looking elements that are inert or missing a hover/focus state;--statescaptures hover/focus/open frames. Fix every dead control. Full detail in reference.md. - Tactile depth (zero-dependency). For the controls the screenshot implies — swipeable galleries, sliders, a heart that pops, drag-to-snap decks — make them feel hand-built with plain JS + CSS (pointer drag, velocity snap, press/scale). No motion library needed for any of it. Patterns and copy-paste snippets in references/interaction.md.
Phase 5 — Final checks
- Side-by-side render vs target reads as "the same page."
- Exact text; correct color/contrast; type scale and weights match.
- Responsive sanity: render at a second width (e.g.
--width 390) — nothing overlaps or overflows. - Interactivity: every clickable-looking element is real and shows hover/focus;
--verifypasses. - Self-contained and valid; no leftover probe/temp files (keep
tmp/screenshot-to-html/out of the deliverable). Report the final file path + final render.
Phase 6 — Enhance with motion (optional, only on request)
Interactivity (Phase 4) ships by default; GSAP motion/animation does not. Rich tactile
feedback — drag, snap, like-pop — needs no library (references/interaction.md);
GSAP is only for heavier, explicitly-requested choreography. Do not add motion
on your own, and never ask about it — only enhance when the user explicitly requests animation ("加动效",
"make it animated"). Then add restrained GSAP animation — high-impact moments,
not AI-slop everywhere. Read references/animation.md for
the patterns (load-in stagger, scroll-triggered reveal, hover micro-interactions),
CDN setup, and pitfalls. Keep the HTML self-contained (GSAP via CDN) and honor
prefers-reduced-motion; verify a settled frame with node scripts/shot.mjs --motion.
This skill is self-contained; for deeper choreography you may optionally also install
the official GSAP skills (npx skills add github.com/greensock/gsap-skills) — not required.
Scripts (paths relative to this skill directory)
scripts/shot.mjs — render a local HTML file with installed Google Chrome via
playwright-core (no browser download); the render step of the loop.
# full-page (default), crisp 2x:
node scripts/shot.mjs --in replica.html --out tmp/screenshot-to-html/v1.png --width 1280
# exact-pixel overlay: add --scale 1 ; above-the-fold only: --viewport-only ; keep motion: --motion
If Chrome is missing it falls back to bundled chromium (npx playwright install chromium).
scripts/crop.mjs — crop a rectangle out of the source screenshot to reuse real
imagery (asset layer 2). Uses sharp if installed, else macOS sips.
node scripts/crop.mjs --in <target.png> --rect x,y,w,h --out assets/hero.png
Stacks
Pick per request; both produce a single self-contained file.
vanilla (default) — HTML + plain CSS in one file. Tokens in :root. Best for
matching a bespoke visual identity and staying dependency-free.
tailwind — add <script src="https://cdn.tailwindcss.com"></script> and build
with utilities; map the palette/scale via the tailwind.config inline object.
Both stacks:
- Fonts: Google Fonts
<link>(pick the closest match to the image). - Icons: inline SVG, or Font Awesome
https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css. - Missing images: layered fallback (crop from source -> Unsplash/picsum ->
placehold.co).
Output & temp conventions
- Final HTML: the path the user asks for. Default
index.html; if a file already exists there and is unrelated, do not overwrite it — confirm or write toreplica.html. - Screenshots/intermediates:
tmp/screenshot-to-html/(safe to delete). Create it if missing.
Quality expectations
- Semantic, readable, maintainable; design tokens over magic numbers.
- No absolute-position-everything layouts; no "measured" pixel coordinates.
- Fidelity to the image: layout, exact text, color, type, spacing, radii, shadows.
- Multiple screenshots = multiple pages/tabs: link or scaffold them sensibly.
Additional resources
- reference.md — analysis rubric, comparison methodology, computed-state traps, fonts/colors/assets, the four-layer asset strategy.
- references/interaction.md — zero-dependency tactile-polish standard (Phase 4): pointer-drag primitive, swipe/carousel snap, like-pop, toggles, reduced-motion.
- references/animation.md — optional GSAP motion patterns (Phase 6): CDN setup, load-in/scroll/hover recipes, reduced-motion, pitfalls.