Migrate an existing HTML/CSS prototype into Vue components — pixel-for-pixel. It preserves a finished design; it doesn't generate or "improve" one.
Agents migrating UI prototypes keep making the same moves: snap padding: 18px to p-4, swap in a UI library "because it looks similar", or ship "looks good" without ever opening a browser. The result compiles and drifts. This skill makes the prototype the visual + behavioral source of truth and a passing visual diff the acceptance gate — not a clean build.
Claude Code:
/plugin marketplace add thaolaptrinh/ui-prototype-migration
/plugin install ui-prototype-migration@ui-prototype-migrationCodex:
codex plugin marketplace add thaolaptrinh/ui-prototype-migration
codex plugin add ui-prototype-migration@ui-prototype-migrationThen just ask — the skill auto-activates:
Migrate examples/fixtures/prototype-dashboard into a Vue 3 app and prove it matches.
Or use the commands directly: /migrate-prototype <path>, then /verify-parity as the gate.
CI runs the verifier against itself on every push/PR (each fixture vs itself, including the multi-page per-view path) — see .github/workflows/parity.yml.
Inspect → Inventory → Port 1:1 → Verify (diff) → Iterate to 0 diffs → Refactor (after parity)
- Port 1:1 — templates + the prototype's CSS verbatim (exact values; no Tailwind/utility snapping; ids/classes kept).
- Co-locate styles per component (
<style scoped>); global only tokens + base + shell + shared atoms. Slotted content needs:deep(); no extra wrapper elements. - Verify = diff, not build — per view × theme × viewport, every interactive state: pixel diff (alarm) + computed-style/bbox diff (diagnosis) + axe a11y (no new violations). Done = 0 real differences, with evidence.
Full method: SKILL.md + references/.
- The migrated app renders identically to the prototype at every viewport and theme — same spacing, radii, colors, fonts, shadows (proven by a diff, not an eyeball).
- Hover/focus/open/selected/loading states all match; responsive breakpoints behave the same.
- CSS values were carried verbatim (no utility snapping); component styles are co-located, not dumped in one global file.
- You can show per-view×theme×viewport diff evidence — or honestly state what you couldn't verify and why.
Designing new UI from a spec/screenshot, Figma-to-code, or "make this UI better". This skill preserves an existing design — pointing it at something unfinished is the wrong tool.
/migrate-prototype <prototype-path> [--target <dir>]— runs the phased migration workflow./verify-parity --prototype <url> --target <url>— the "done" gate (reports once; you iterate until it exits 0).
skills/ui-prototype-migration/—SKILL.md+references/(css-preservation, componentization, visual-verification, framework/vue).commands/—migrate-prototype.md,verify-parity.md(logic lives in the skill).scripts/—verify-parity.mjs(unified Phase-4 gate: computed-style + bbox + pixel + axe, per view × theme × viewport, with optional interactive-state checks via--states hover,focus, multi-page{view}URL templates, and a single-filereport.htmlfor the iterate loop) andcompare-visuals.mjs(legacy resting-state subset).examples/fixtures/— sample prototypes to practice on (admin dashboard, marketing landing, multi-page)..claude-plugin/+.codex-plugin/— plugin manifests for Claude Code and Codex.CONTRIBUTING.md— validation gates, version discipline, PR conventions.
- Slotted icon collapses to 0px. A
<style scoped>rule like.btn svgdoes not match an SVG passed through<slot>(it gets the parent's scope attr). Use.btn :deep(svg). Symptom: a button ~16px narrower than the prototype. - Responsive rules lose to scoped specificity. A scoped base rule (
0,2,0) can beat a global@mediarule (0,1,0), so the layout doesn't collapse at the breakpoint. Co-locate the component's own@mediainside its scoped block, or verify at the breakpoint. - axe reports a "new" violation that isn't. Vue scoped attributes (
[data-v-xxx]) appear in axe node selectors, so target keys never match prototype keys.verify-parity.mjsstrips them; if writing your own diff, normalize both sides. - Geometry diff from smooth-scroll timing. With
html{scroll-behavior:smooth}, measuring right afterfocus()/hover()catches both pages mid-scroll at different offsets.window.scrollTo({top:0, behavior:'instant'})+ a settle wait before measuring.
- Only Vue 3 ships out of the box; other frameworks need a
references/framework/<name>.md. - Pixel diff is environment-sensitive (anti-aliasing/fonts) — treated as a signal; the computed-style diff + a11y carry the weight. Run VRT single-OS for determinism.
- Verification needs a browser/Playwright; if unavailable, fall back to structured manual inspection and report it — never claim parity unproven.
MIT.