| name | pixel-perfect |
| description | Orchestrate a complete visual QA workflow to make a dev/Storybook page match a baseline URL pixel-for-pixel. Combines the visual-diff skill (screenshot comparison) with the computed-styles skill (CSS property extraction) in a loop until the pages match. Use when replicating a production page, matching a design reference, or achieving pixel perfection on a component. |
Skill: Pixel-Perfect Orchestration
Drive a page or component to pixel-perfect fidelity with a baseline by orchestrating visual-diff (screenshot comparison) and computed-styles (CSS property extraction) in a converging loop.
When to Use
- Replicating a production page's look in the local codebase
- Matching a Storybook component to a live reference
- Any task where the user says "make it match" or "pixel perfect"
When NOT to Use
- Building new designs from scratch (no baseline exists)
- Content-only changes with no visual target
- Figma-based design work (use the Figma MCP skill instead)
Prerequisites
- All prerequisites from the
visual-diff and computed-styles skills are met
- The baseline URL and current URL are known
- The dev server or Storybook is running
Design System
All fixes must use the Bitovi design token system defined in src/styles/bitovi-theme.css. Prefer token-based Tailwind classes over arbitrary values. Only use [arbitrary] syntax when no matching token exists in the theme.
Workflow Overview
┌─────────────────────────────────────┐
│ For each breakpoint: │
│ desktop → tablet → mobile │
│ │
│ 1. VISUAL DIFF │
│ Screenshot both pages │
│ Run pixel diff │
│ Identify problem regions │
│ diffPercent < 2%? ──── YES ──── next breakpoint (or DONE if all 3 pass)
│ │ │
│ NO │
│ ▼ │
│ 2. COMPUTED STYLES │
│ Extract CSS from both pages │
│ Build property diff table │
│ Fix mismatches with Tailwind │
│ Verify fixes took effect │
│ │ │
│ ▼ │
│ 3. RE-DIFF │
│ Re-screenshot current page │
│ Run pixel diff again │
│ diffPercent < 2%? ──── YES ──── next breakpoint
│ │ │
│ NO │
│ ▼ │
│ Go to step 2 with new regions │
└─────────────────────────────────────┘
Detailed Steps
Step 1: Initial Visual Diff
Load the visual-diff skill and run the full workflow:
- Screenshot the baseline at desktop breakpoint (1280x720)
- Screenshot the current page at the same breakpoint
- Run
node scripts/visual-diff.mjs to produce the diff image and JSON stats
- View the diff image through the HTTP server + Playwright vision
- Record the
diffPercent and identify which regions (top, middle, bottom) have red clusters
If diffPercent < 2%, the pages are effectively matching. Report success and stop.
If diffPercent >= 2%, proceed to Step 2.
Interpreting the Diff Image
Map red clusters to specific DOM regions:
| Red cluster location | Likely DOM region | Example |
|---|
| Top band | Navigation bar | Height, padding, logo, nav items |
| Below nav | Hero section | Font sizes, spacing, background |
| Middle grid | Card layout | Card sizing, gaps, typography |
| Bottom | Footer | Link colors, spacing, background |
| Full-width bands | Container width | Max-width, padding differences |
| Scattered dots only | Font rendering | Usually acceptable, not actionable |
Step 2: Computed Style Extraction
Load the computed-styles skill. Based on the regions identified in Step 1:
- Plan extraction targets: Map each red region to specific DOM elements and CSS properties
- Extract from baseline: Use
mcp_playwright_browser_evaluate on the baseline URL
- Extract from current: Use
mcp_playwright_browser_evaluate on the current URL (remember the Storybook iframe pattern if applicable)
- Build diff table: Compare every property, flag mismatches
- Fix mismatches: Apply Tailwind classes — always resolve computed values to design tokens from
src/styles/bitovi-theme.css first (see the Design System section above). Only use [arbitrary] values when no token matches.
- Verify each fix: Re-extract from current to confirm the computed value now matches
Prioritization
Fix differences in this order (highest visual impact first):
- Layout: width, height, maxWidth, padding, margin, display, flexDirection
- Typography: fontSize, fontWeight, fontFamily, lineHeight, letterSpacing
- Colors: color, backgroundColor, borderColor
- Decoration: borderRadius, boxShadow, opacity
- Fine-tuning: textTransform, gap, overflow
Step 3: Re-Diff
After fixing a batch of computed style differences:
- Re-screenshot only the current page (baseline hasn't changed)
- Re-run the pixel diff
- View the new diff image
Evaluate progress:
| New diffPercent | Action |
|---|
| < 2% | Done. Report final stats. |
| Decreased significantly | Good progress. If > 2%, identify remaining red regions and loop back to Step 2. |
| Unchanged or increased | Something went wrong. Check that fixes actually applied (Tailwind theme overrides, specificity conflicts). Use computed-styles to verify. |
Step 4: Convergence
Continue the loop (Steps 2-3) until diffPercent < 2% or the remaining differences are confirmed to be not worth further pursuit.
Stop and flag for manual review — do NOT keep iterating on these:
- Sub-pixel font rendering (different OS/browser/font-smoothing)
- Anti-aliasing artifacts (icon edges, curved borders)
- Pixel-level positioning offsets (elements within 1–3px of correct position)
- Shadow or gradient rendering variance
- Third-party embedded content (reCAPTCHA, chat widgets, analytics overlays)
- Dynamic content (timestamps, user-specific data, live chat, cookie banners)
- Differences that require non-Tailwind workarounds or browser-specific hacks
When stopping for one of these reasons, clearly state:
- What the remaining diffPercent is
- Which specific element(s) are causing it
- Why it is not worth automating further
- What the user could do manually to address it
Using Subagents
For complex pages with many regions to fix, use subagents to parallelize investigation:
Investigation Subagent
Use the Explore agent to research element structures before extracting styles:
"Explore the DOM structure of [baseline URL]. Find all elements in the
[region] section. Report element tags, class names, and nesting structure.
Thoroughness: medium."
Extraction Pattern
When extracting styles, do both pages in sequence (not parallel, since Playwright MCP shares one browser):
- Navigate to baseline → extract → store results
- Navigate to current → extract → store results
- Compare in conversation context
Breakpoint Strategy
Always run the pixel-perfect loop at all three breakpoints:
| Breakpoint | Width | Height | Order |
|---|
| Desktop | 1280 | 720 | 1st — fix base layout here first |
| Tablet | 768 | 1024 | 2nd — fix responsive breakpoint issues |
| Mobile | 375 | 667 | 3rd — fix mobile-specific issues |
Workflow:
- Run the full visual-diff + computed-styles loop at desktop until
diffPercent < 2%
- Then run at tablet — resize viewport, re-screenshot both sides, diff, fix, re-diff
- Then run at mobile — same process
Fix desktop first so you're not chasing responsive issues against a broken base layout. Each breakpoint has its own screenshot files (e.g. temp/vdiff-baseline-tablet.png, temp/vdiff-current-tablet.png, temp/vdiff-diff-tablet.png).
Component-scoped comparison at smaller breakpoints: Re-measure the component height at each breakpoint (it will change) and resize the viewport accordingly before screenshotting.
Session Notes
Keep a running log of what was fixed and verified. This helps avoid re-checking properties and provides a summary at the end.
Use session memory (/memories/session/) to store:
- The diff table with baseline vs current values
- Which properties have been fixed and verified
- Current diffPercent after each round
Success Criteria
| Level | diffPercent | Description |
|---|
| Pixel perfect | < 1% | Only sub-pixel rendering differences remain |
| Excellent match | 1–2% | Minor font rendering, anti-aliasing, or shadow differences — stop here |
| Good match | 2-5% | Small spacing or color differences worth fixing |
| Needs work | > 5% | Significant visual differences still present |
The default target is < 2%. Stop and report as soon as this threshold is met — do not keep iterating to squeeze from 1% to 0% unless the user explicitly asks. Flag remaining issues that fall into the "hard to align" bucket (font rendering, sub-pixel spacing, third-party overlays) and let the user decide whether to fix them manually.
Example Session
User: "Make the nav component match production"
Agent:
1. Load pixel-perfect skill
2. Load visual-diff skill → screenshot production nav + Storybook nav
3. Run diff → diffPercent: 12.3%
4. View diff → red clusters at: nav height, logo area, nav item spacing, CTA button
5. Load computed-styles skill
6. Extract nav properties from production:
nav.height = 80px, nav.padding = 0px 128px, logo.width = 108px ...
7. Extract same from Storybook:
nav.height = 60px, nav.padding = 0px 32px, logo.width = 80px ...
8. Build diff table, fix mismatches with Tailwind — resolve to design tokens from `src/styles/bitovi-theme.css` (e.g. `bg-brand-orange`, `text-teal-500`, `font-semibold`, `p-md`)
9. Verify fixes with re-extraction
10. Re-screenshot + re-diff → diffPercent: 3.1%
11. View diff → remaining red at CTA button border-radius
12. Extract CTA styles, fix borderRadius
13. Re-diff → diffPercent: 0.8%
14. Done! Report final comparison table.
Cleanup
After the session:
rm -f temp/vdiff-*.png
All computed style data lives in conversation context and session memory only.