Install
$ agentstack add skill-lukedj78-dev-flow-screenshot-to-page ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
screenshot-to-page — screenshot → working route
This skill takes one screenshot and one route-name from the user, and produces a working page in the existing scaffolded app. The route renders close to the visual reference, uses only components and tokens already defined by the project's design system, and is reachable in pnpm dev.
What this skill targets
Pixel-perfect on what's reproducible: layout, typography, color, spacing, radii, component states. The skill iterates render → screenshot → pixel-diff → fix until the visual delta is below threshold (default /`.
What this skill is NOT
- Not a Figma-to-code service. The input is a static raster, not a vector.
- Not a layout designer — if the screenshot is ambiguous (e.g., two interpretations of a section break), ask the user.
When this skill applies
- A
/exists (phase >= scaffolded). .workflow/screenshots/contains at least one image.- A
.workflow/DESIGN.mdexists with valid tokens. - The user names a route to build (
/,/pricing,/dashboard, etc.) or points at a specific screenshot.
Contract
This skill follows the dev-flow contract — see references/contracts.md. Key facts:
- Reads
/.workflow/screenshots/,/.workflow/DESIGN.md,/.workflow/meta.json#stack. - Writes the route into the codebase at the project root, at the canonical path for the framework:
- Next App Router:
/app//page.tsx - Vite + React: registers a route in
/src/router.tsx(or whatever the scaffold uses) plus a component file/src/pages/.tsx. - Astro:
/src/pages/.astro. - Sets
phase = "page_generated"(only if current phase is earlier in the enum).
Workflow
Step 1 — Pick the screenshot and route
If the user named a screenshot, use it. Otherwise list .workflow/screenshots/*.png and ask which one.
Ask for the route name (e.g., /, /journal, /pricing). Validate: must start with /, must be a valid URL path, must not collide with an existing route in /. If there's a collision, ask: replace, or pick a different route.
Step 2 — Read the screenshot
Use vision to inspect the screenshot. Identify, in this order:
- Page-level layout: full-bleed sections vs. centered max-width column? Single column or grid? Sticky header? Footer?
- Sections, top to bottom. For each section:
- Type (hero, feature grid, testimonial, pricing table, footer, ...)
- Vertical extent (rough height as fraction of viewport)
- Content (text + imagery + CTAs)
- Components per section: buttons, cards, inputs, navigation, list items, etc.
- Typography hierarchy: which h-level for hero, which for section titles, which for body.
- Color usage: where does primary appear? Where surfaces? Backgrounds?
Don't try to count pixels. Estimate, sanity-check against the design tokens.
Step 3 — Detect repeated patterns and plan components
Before writing any JSX, scan the screenshot for repeated visual patterns. The output of this step is a small plan, not code.
For each section identified in Step 2, ask: "is there a sub-element that appears 2 or more times with the same structure?" Common patterns:
- Card grid — 3+ cards with identical structure (image + title + body + meta) → extract `
,`, etc. - Nav items — 4+ links in a header → extract `` and map over an array.
- Pricing tiers — 2–4 columns with identical structure → extract ``.
- Testimonials / logos / metrics — list of similar items → extract `
//`. - Form fields — repeated label+input → extract ``.
- Sidebar / nav menu items with icon+label → extract ``.
For each detected pattern, decide:
- Component name (PascalCase, descriptive of what it shows, not what it looks like —
ArticleCardnotRoundedBox). - Props the component takes (the variable bits — title, image, href, ...).
- File path — typically
app//.tsxfor one-shot use orcomponents/.tsxif the pattern is likely reusable across pages.
Don't over-componentize singletons. A unique hero section is fine inline in page.tsx. Extract only when there's reuse (the same shape ≥2 times) or when the section is over ~80 lines of JSX (readability).
State the plan to the user briefly: "Estraggo ArticleCard (4 occorrenze nella griglia 'Latest articles') e NavLink (5 occorrenze nell'header). Hero, footer, e signup-form restano inline. Procedo?" The user can override before any code is written.
Step 4 — Map to the design system
Read .workflow/meta.json#stack.ui to know which UI library the project already uses — don't guess or mix libraries into an existing app.
For each component you identified in Step 2, decide:
- Use an existing primitive first. Prefer this over hand-rolling. What "existing primitive" means depends on
stack.ui: "shadcn"— copy-pasted source incomponents/ui/(e.g.Button,Card). Import from@/components/ui/."mui"— runtime-themed components from@mui/material(e.g.Card,Stack)."base-ui"(standalone Base UI, no shadcn CLI, nocomponents/ui/folder) — import headless primitives directly from@base-ui-components/react(e.g.@base-ui-components/react/dialog) and style them with the project's Tailwind classes/tokens. There is no pre-styled component to fetch — the styling is part of what you write."coss"— primitives live incomponents/ui/just like shadcn, but originate from the@coss/*registry (Cal.com design system on Base UI). Treat them exactly like shadcn primitives for import purposes (@/components/ui/); if a needed component/particle hasn't been pulled intocomponents/ui/yet, hand off to thecoss-uiskill to fetch it viapnpm dlx shadcn@latest add @coss/rather than hand-rolling it here.- Compose primitives if no exact match (e.g., a "stat card" =
Card+ headline typography + body). - Hand-roll a small custom component only if necessary, and only if it'll be reusable. One-off custom JSX in the page file is fine for unique sections.
Chat / conversation / rendered-markdown surfaces are NOT hand-rolled. If the screenshot shows a chat, an AI console, a support inbox, a comment thread — or any surface rendering markdown/AI output — compose the official shadcn chat primitives (MessageScroller / Message / Bubble / Marker, Jun 2026) + .typeset (Jul 2026) + streamdown. Follow design-md-to-app/references/chat-and-typeset.md. Never build chat with div bubbles + manual autoscroll, and never render model markdown as whitespace-pre-wrap (bold/lists/code leak as literal syntax).
For each color/spacing/radius decision, reference the token by name. Don't hardcode hex codes — use bg-primary, text-on-surface-variant, rounded-md (shadcn) or theme values (MUI). If the screenshot shows a color that's not in DESIGN.md, ask the user before adding — it's either an oversight in DESIGN.md or a one-off photo color that shouldn't become a token.
Step 5 — Write the route + extracted components
Generate the files at the canonical framework path (see Contract). Constraints:
- TypeScript for everything.
- Extracted components first, then the page that imports them. This keeps
page.tsxreadable. - Imagery placeholders: the screenshot likely has photography you don't have. Use a `
withbg-mutedand the dimensions of the original, plus an HTML comment{/ TODO: replace with hero image /}`. Don't link to external image services. - Copy: use the actual text visible in the screenshot. If text is illegible, write a one-line placeholder and flag it in the report.
- Responsive: read the DESIGN.md
## Layoutsection for breakpoint rules. If the system documents 3 breakpoints (Desktop/Tablet/Mobile), generate the page with appropriate Tailwind breakpoint classes (md:,lg:) or MUI breakpoint props.
Accessibility checklist (always apply)
A page that looks right but fails basic a11y checks is not done. Run through this list as you write — most items take seconds when done at write-time and hours when retrofitted:
- Semantic landmarks: one `
per page. Use,,,instead ofwhen the role applies. Avoid wrapping everything in nested`s. - Heading hierarchy: exactly one `
per page, then→without skipping levels. Visual size and heading level are independent — style with classes, not by demotingto`. - Alt text: every `
andneedsalt. For decorative-only imagery, usealt=""(empty string, not absent) so screen readers skip it. Photography placeholders inheritalt="TODO"` plus a comment. - Buttons vs. links: `
for actions inside the page,(or) for navigation. Don't style a` as a button — keyboard users can't reach it. - Focus visible: never
outline: nonewithout a replacement. shadcn shipsfocus-visible:ring-2 focus-visible:ring-ringby default — preserve it. If the design hides outlines for aesthetic reasons, add a custom:focus-visiblestyle that's visible against the background. - Form labels: every input has an associated `
(matched byhtmlFor+id`, or wrapping). Placeholder text is not a label substitute — it disappears on focus and many screen readers ignore it. - Color contrast: body text against its background needs ≥ 4.5:1 (WCAG AA). Brand colors that fail this on the surface they sit on are a DESIGN.md bug, not a screenshot-to-page bug — flag it back to the user instead of silently swapping the color.
- Interactive target size: tappable elements are ≥ 24×24px (WCAG 2.5.8). Tight icon-only buttons need
p-2minimum. aria-labelfor icon-only buttons: a `with only an icon inside is invisible to screen readers withoutaria-label="Toggle theme"` or equivalent.prefers-reduced-motion: handled globally byglobals.cssif the scaffold randesign-md-to-app. If you add a new long animation here, double-check it respects the global guard.
If any item can't be fixed in the screenshot-to-page run (e.g., the DESIGN.md mandates a low-contrast color), document it explicitly in the hand-off message under "Accessibility notes" — never silently ship an a11y violation.
Step 6 — Verification loop (two modes)
The work isn't done after the first write. Iterate until visual quality matches the threshold for the chosen mode. Required when a browser tool (Playwright / Chrome MCP / etc.) is available — without one, fall back to the sub-section "No browser available" below.
Pick the mode FIRST, before writing a single iteration
Two modes — they're not interchangeable, and over-applying pixel-tight is a real failure pattern:
structure-first (default for app routes — dashboards, forms, internal product pages)
- Target: token correctness + semantic correctness + visual delta ≤ 8%.
- Stop after 3 iterations, or when delta plateaus.
- The HTML is allowed to differ from the screenshot in pixel position by up to ~8 px on most edges, AS LONG AS:
- The colors used are the right tokens (no
bg-[#abc123], onlybg-primary/bg-card/ etc.). - The typography uses the declared scale (
text-display-lg, etc.), not freetext-[42px]. - The component structure is correct (a `
IS a, not a` faking it). - The layout is responsive and respects the breakpoint rules from DESIGN.md.
- Why this is the default: pixel-tight produces rigid HTML that imitates pixels instead of respecting tokens. For a dashboard / settings page / list view, that rigidity hurts more than it helps — the next developer can't tell which spacing was a "design choice" and which was an LLM matching one pixel.
pixel-tight (opt-in for marketing / landing pages / brand-critical surfaces)
- Target: visual delta ≤ 2% (the original threshold).
- Stop after 8 iterations, or earlier if delta plateaus.
- All
structure-firstrules still apply (still must use tokens, not arbitrary pixel values) — but on top of that, position, scale, and typographic detail must match the screenshot to within 2%. - When this is right: hero pages, pricing pages, signup flows, conversion-critical surfaces where the visual fidelity IS the value. A landing where the headline is 4px off feels broken; a dashboard where the table padding is 4px off is fine.
How to decide
The signal is the kind of route, not the user's preference. Ask the user only if the case is genuinely ambiguous.
- Route looks like
/,/pricing,/about,/blog/,/sign-up,/showcase→ pixel-tight. - Route looks like
/dashboard,/clienti/,/settings,/admin, anything CRUD → structure-first. - Mix on the same page (rare): pick
pixel-tightfor the hero, write the rest instructure-firststyle and call it good.
State the chosen mode in your hand-off message: "Iterated in structure-first mode (3 passes, final delta 6.4%) — token-correct, semantically clean. Switch to pixel-tight if you want closer fidelity for production."
The loop
- Build & start dev server:
``bash cd pnpm build # catch syntax/type errors before launching pnpm dev & # background dev server `` If build fails, fix the most likely cause once. If it still fails, stop and tell the user — don't loop on a broken build.
- Render and capture the route at the same viewport as the source screenshot (typical:
1440×900for desktop frames; read the source PNG dimensions and match):
``bash # via Playwright MCP, or equivalent browser_resize(width: , height: ) browser_navigate(url: "http://localhost:3000") browser_wait_for(time: 2) # let fonts + animations settle browser_take_screenshot(filename: ".workflow/screenshots/_render-iter-N.png") ``
- Diff the renders. Use the helper script:
``bash python3 scripts/visual_diff.py \ .workflow/screenshots/_render-iter-N.png `` Output: a delta percentage (0–100), and an annotated diff PNG showing where the renders differ. Threshold depends on the mode you picked:
structure-first(default for app routes): ≤ 8% delta is shippable. Concentrate on token correctness over pixel match.pixel-tight(landing/marketing): ≤ 2% delta. ⚠ Verifica visiva richiesta: nessun browser tool in questa sessione. Aprihttp://localhost:3000e confronta con.workflow/screenshots/. Il pixel-perfect loop non è stato eseguito; il risultato è quindi un primo draft, non un match verificato.
Don't claim convergence you didn't measure.
What the loop tries hard NOT to fix
- Photography content — if the source has a hero photo of someone holding a coffee, the placeholder div will always diff. Mark this as "expected delta — imagery placeholder" in the report.
- Custom fonts not loaded — if DESIGN.md references a font that isn't on Google Fonts and the user hasn't supplied a local file, the rendered font is a fallback and will diff. Flag and stop chasing.
- Animations / hover states / scroll behavior — static screenshots can't show motion. Don't hallucinate state.
Step 7 — Update state and report
Update .workflow/meta.json:
- if current phase is earlier than
page_generated, setphase = "page_generated" - bump
updated_at - append history:
``json { "skill": "screenshot-to-page", "ran_at": "", "inputs": {"screenshot": "screenshots/", "route": ""}, "outputs": ["app/"], "phase_before": "", "phase_after": "page_generated" } ``
Tell the user:
- Path to the new route in
app/(relative). - Components used (existing primitives + any custom ones added).
- Tokens referenced (prove the design system was applied, not bypassed).
- Things you couldn't replicate from the screenshot (e.g., specific photography, custom illustrations, pixel-perfect spacing) — flagged for hand-tuning.
- Next-step proposal: another
screenshot-to-pagerun for the next page, ormodule-addto wire features behind these routes.
Important constraints
- **Us
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: lukedj78
- Source: lukedj78/dev-flow
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.