| name | pixel-perfect-design |
| description | Designer-grade UI implementation from mockup images. Systematically analyzes visual hierarchy, spacing rhythm, typography, color, shadows, and micro-interactions. Extracts precise specs from images when no Figma/design tokens exist. Drives a build-compare-refine loop via FlowDeck screenshots until implementation is indistinguishable from the original design. Use when implementing UI from any visual reference or when the user requests pixel-perfect fidelity. |
Pixel-Perfect Design Implementation
MANDATORY TRIGGER
Activate this skill when ANY of these conditions are true:
Explicit signals (user provides a design reference):
- User attaches an image file (PNG, JPG, screenshot, mockup, exported comp)
- User says "build this", "create this screen", "implement this design", "make it look like this"
- User says "pixel perfect", "match the design", "design fidelity"
Implicit signals (user is describing a UI to build):
- User describes a specific screen layout with visual details (colors, spacing, typography)
- User references a design system, brand guidelines, or specific visual treatment
- User provides a sketch or wireframe (even hand-drawn)
- User asks to "recreate" or "clone" an existing app's UI from a screenshot
During implementation (mid-task triggers):
- You just implemented a UI view and haven't visually verified it yet — run the validation loop
- User says "does it look right?", "check the UI", "how does it look?"
- User reports the UI "doesn't match", "looks off", "spacing is wrong"
- You made changes to a view's layout, colors, typography, or effects — re-validate
Figma links: If the user provides a Figma URL (e.g., figma.com/design/..., figma.com/file/...), use the Figma MCP server to fetch exact design tokens, measurements, and specs. With Figma MCP data, skip Phase 0 and Phase 1 (measurement) and go directly to Phase 2 (Implementation). The rest of the workflow — layered implementation, optical corrections, and the FlowDeck validation loop — is the same regardless of design source.
Core Philosophy
Think like a designer, implement like an engineer.
A pixel-perfect UI is not just correct measurements — it is correct perception. A mathematically centered element can look off-center. A color that matches hex values can feel wrong in context. A shadow with correct parameters can look heavy or flat depending on surrounding elements. This skill trains you to see and correct for these perceptual gaps.
Phase 0: Design Reading (Before Touching Code)
Before measuring a single pixel, read the design. Understand what it communicates.
Step 1: Identify Visual Hierarchy
Look at the mockup and answer:
- What is the primary focal point? — The element the eye hits first (largest text, brightest color, most contrast)
- What is the secondary information? — Supporting text, icons, metadata
- What is tertiary/ambient? — Backgrounds, dividers, subtle borders
- What is the user's intended action? — The primary CTA (call-to-action), its visual weight
Document this as a comment block before any code:
Step 2: Identify the Design System
Scan the mockup for repeating patterns:
- Spacing rhythm: Are gaps consistently 8pt, 12pt, 16pt, 24pt? (Most designs use a 4pt or 8pt base grid)
- Color palette: Count distinct colors. Usually 2-3 primary + 2-3 neutral + 1-2 accent
- Typography scale: Count distinct text sizes/weights. Usually 3-5 levels
- Corner radius pattern: One or two standard radii used everywhere
- Component repetition: Cards, buttons, list items — same component styled consistently
Step 3: Identify the Layout Strategy
Determine the structural approach:
- Full-bleed vs. inset: Does content go edge-to-edge or have margins?
- Scroll vs. fixed: Is this a scrollable list or a fixed layout?
- Safe area behavior: Does content respect safe areas or extend behind them?
- Gravity: Is content top-anchored, centered, or bottom-anchored?
- Negative space: Where is deliberate whitespace? It is as important as content.
Phase 1: Precise Measurement from Images
When working from an image (not Figma), you must extract specs visually. Here is the systematic approach.
Step 1: Establish Reference Dimensions
The mockup image may not be at 1x scale. Establish a baseline:
- Identify a known element — Status bar height (54pt on modern iPhones), navigation bar (44pt), tab bar (49pt + safe area), or standard button height (44-50pt)
- Calculate the scale factor — If the status bar measures 108px in the image, the scale is 2x
- Apply the scale factor to all subsequent measurements
Step 2: Measure the Spatial Grid
Work outside-in:
- Screen margins — Distance from screen edges to outermost content
- Section spacing — Gaps between major content blocks
- Element spacing — Gaps between items within a section
- Internal padding — Space inside containers (cards, buttons, bubbles)
Document with precision:
Step 3: Extract Typography
For each distinct text style:
- Estimate size — Compare against known references (body text is typically 15-17pt)
- Identify weight — Regular (thin strokes), Medium (slightly thicker), Semibold/Bold (noticeably thicker)
- Check line height — Measure baseline-to-baseline for multi-line text
- Note color — Is it pure black? Dark gray? A tinted color?
- Note alignment and case — Centered? Leading? Uppercase?
Step 4: Extract Colors
Systematically catalog every color:
- Backgrounds — Screen bg, card bg, input bg, overlay bg
- Text — Primary, secondary, tertiary, placeholder, link
- Interactive — Button fill, button text, selection, focus ring
- Semantic — Success (green), warning (amber), error (red), info (blue)
- Decorative — Gradients, illustrations, divider lines
When extracting from images, describe colors precisely:
Step 5: Extract Effects
For each visual effect:
- Shadows — Estimate offset direction, blur radius, color/opacity
- Corner radii — Measure against element size (small ~8pt, medium ~12-16pt, large ~20-28pt, pill ~height/2)
- Borders — Width (hairline 0.5pt, thin 1pt, medium 2pt), color, opacity
- Blurs/Overlays — Background blur, tinted overlays, gradients
Phase 2: Implementation — Structure First, Style Second
Rule: Build in Layers
Implement in this exact order. Do not skip ahead.
Layer 1: Skeleton Layout (No Styling)
Build the spatial structure with placeholder content. Get every element positioned correctly with exact spacing. Use spacing: 0 on all stacks and control gaps explicitly.
VStack(spacing: 0) {
heroContent
Spacer().frame(height: 32)
formContent
Spacer().frame(height: 24)
actionContent
}
.padding(.horizontal, 20)
Key patterns:
.padding()
.padding(.horizontal, 20)
.padding(.top, 16)
.padding(.bottom, 12)
.padding(EdgeInsets(top: 20, leading: 16, bottom: 12, trailing: 16))
Layer 2: Typography
Apply all text styles. Match size, weight, color, alignment, and line height.
Text("Welcome Back")
.font(.system(size: 28, weight: .semibold))
.foregroundStyle(Color(hex: "#1A1A1A"))
.multilineTextAlignment(.center)
Text("Sign in to continue where you left off")
.font(.system(size: 15, weight: .regular))
.foregroundStyle(Color(hex: "#6B7280"))
.lineSpacing(4)
.multilineTextAlignment(.center)
Line height in SwiftUI: SwiftUI doesn't have a direct lineHeight property. Use .lineSpacing() which adds space between lines. To match a design's line height:
.lineSpacing(4)
Tracking/letter spacing:
.tracking(0.5)
.tracking(-0.3)
Layer 3: Colors and Backgrounds
Apply all background colors, text colors, and tints.
enum DesignColors {
static let background = Color(hex: "#F8F8FA")
static let cardBackground = Color.white
static let textPrimary = Color(hex: "#1A1A1A")
static let textSecondary = Color(hex: "#6B7280")
static let accent = Color(hex: "#007AFF")
}
Layer 4: Shapes, Borders, and Radii
Apply corner radii, borders, and shape clipping.
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: 16, style: .continuous)
.stroke(Color(hex: "#E5E7EB"), lineWidth: 1)
)
.clipShape(Capsule())
Important: Always use style: .continuous for rounded rectangles unless the design clearly shows geometric (non-smooth) corners. Apple's design language uses continuous (squircle) corners everywhere.
Layer 5: Shadows and Effects
Apply shadows, blurs, and overlays last — they depend on correct shapes being in place.
.shadow(color: Color.black.opacity(0.08), radius: 8, x: 0, y: 2)
.shadow(color: Color.black.opacity(0.04), radius: 2, x: 0, y: 1)
.shadow(color: Color.black.opacity(0.08), radius: 16, x: 0, y: 4)
.background(.ultraThinMaterial)
Multiple shadows: Real-world shadows typically need two layers — a tight shadow for definition and a larger ambient shadow for depth. Compare your single .shadow() against the design; if it looks flat, add a second layer.
Phase 3: Optical Corrections
These are the details that separate engineer-quality from designer-quality UI.
Optical Centering
Mathematical center ≠ visual center. Elements with visual weight at the bottom (like text descenders or icons with a base) need to shift slightly upward to appear centered.
Text("A")
.font(.system(size: 20, weight: .semibold))
.frame(width: 40, height: 40)
.offset(y: -1)
.background(Circle().fill(Color.blue))
Optical Spacing
Equal mathematical spacing between elements of different sizes looks uneven. Larger elements need more space around them.
Text("Settings")
.font(.system(size: 34, weight: .bold))
Spacer().frame(height: 24)
Text("Notifications")
.font(.system(size: 17, weight: .semibold))
Spacer().frame(height: 8)
Icon-Text Alignment
Icons and text don't naturally align to the same baseline. Adjust with small offsets:
HStack(spacing: 8) {
Image(systemName: "bell.fill")
.font(.system(size: 16))
.offset(y: -0.5)
Text("Notifications")
.font(.system(size: 17))
}
Touch Targets
Minimum touch target is 44x44pt (Apple HIG). If a design shows a small icon button, extend the tap area:
Button(action: { }) {
Image(systemName: "xmark")
.font(.system(size: 14, weight: .medium))
.frame(width: 44, height: 44)
}
Color Perception in Context
Colors appear different depending on surroundings:
- A gray that looks right on white may look too light on a light-gray background
- Dark text on a vibrant background needs more weight to maintain readability
- Shadows on colored backgrounds should use the background's hue, not pure black
.shadow(color: Color.blue.opacity(0.3), radius: 8, x: 0, y: 4)
Phase 4: Build, Compare, Refine (The Loop)
This is the critical phase. You will iterate until the implementation is indistinguishable from the design.
Step 1: Build and Capture
flowdeck run
flowdeck ui simulator session start -S "<simulator>" --json
Then use the Read tool on latest_screenshot to see the current UI.
Step 2: Side-by-Side Comparison
With both the original design image and the implementation screenshot visible:
- Squint test — Blur your vision. Do the two images have the same weight distribution? Same light/dark balance? Same visual rhythm?
- Scan top-to-bottom — Check each element in order: position, size, color, typography
- Check edges — Are margins consistent? Do elements align to the same grid?
- Check whitespace — Is the breathing room between sections correct? Whitespace is as important as content.
- Check color temperature — Does the overall tone match? Warm vs. cool, saturated vs. muted?
Step 3: Document Discrepancies
Be specific. Not "spacing looks off" — instead:
// DISCREPANCIES FOUND:
// 1. Title top margin: implementation 52pt, design ~60pt → increase by 8pt
// 2. Card shadow: too heavy — reduce opacity from 0.12 to 0.08
// 3. Subtitle color: too dark — change from #555555 to #6B7280
// 4. Button corner radius: using circular, design shows continuous (squircle)
// 5. Section spacing between form and button: 16pt, design shows ~24pt
Step 4: Fix One Thing at a Time
Make a single correction, rebuild, and re-check. Do not batch fixes — you need to see the effect of each change in isolation.
flowdeck run
Step 5: Repeat Until Done
The loop ends when you cannot identify any visible difference between the design and the implementation at normal viewing distance (arm's length from screen).
Phase 5: Responsive Verification
A pixel-perfect design on one device must remain proportionally correct across screen sizes.
Strategy
Not everything should scale. Categorize each value:
| Category | Behavior | Example |
|---|
| Fixed | Same on all devices | Button height (50pt), icon size (24pt), border width (1pt) |
| Proportional | Scales with screen width | Horizontal margins, card width |
| Adaptive | Changes at breakpoints | Grid columns (2 on SE, 3 on Pro Max), layout direction |
| Content-driven | Determined by content | Text wrapping, list item height |
Verify on Key Sizes
Build and screenshot on at least:
- Smallest — iPhone SE (375pt wide) — does content fit? Any overflow?
- Standard — iPhone 16 (393pt wide) — the design target
- Largest — iPhone 16 Pro Max (430pt wide) — does it stretch gracefully?
flowdeck run -S "iPhone SE (3rd generation)"
flowdeck run -S "iPhone 16"
flowdeck run -S "iPhone 16 Pro Max"
Common Responsive Issues
.frame(width: 350)
.frame(maxWidth: .infinity)
.padding(.horizontal, 20)
.frame(height: 100)
.padding(.vertical, 16)
Phase 6: Dark Mode and Appearance
If the design includes dark mode variants, verify both. If not provided, at minimum ensure the implementation doesn't break in dark mode.
Color Adaptation
extension Color {
static let cardBackground = Color(light: .white, dark: Color(hex: "#1C1C1E"))
static let textPrimary = Color(light: Color(hex: "#1A1A1A"), dark: .white)
static let divider = Color(light: Color(hex: "#E5E7EB"), dark: Color(hex: "#38383A"))
}
Shadow Adaptation
Shadows are invisible on dark backgrounds. In dark mode, use lighter backgrounds or borders instead:
.shadow(color: colorScheme == .dark
? Color.clear
: Color.black.opacity(0.08),
radius: 8, x: 0, y: 2)
Anti-Patterns (Never Do This)
Layout Anti-Patterns
.padding()
.padding(.horizontal, 20)
GeometryReader { geo in
content.frame(width: geo.size.width - 40)
}
content.padding(.horizontal, 20)
VStack { item1; Spacer(); item2 }
VStack(spacing: 0) { item1; Spacer().frame(height: 16); item2 }
.offset(y: -37)
.offset(y: -37)
Color Anti-Patterns
.foregroundStyle(.gray)
.foregroundStyle(Color(hex: "#6B7280"))
.foregroundStyle(.black)
.foregroundStyle(Color(hex: "#1A1A1A"))
Typography Anti-Patterns
.font(.title)
.font(.system(size: 28, weight: .semibold))
Verification Checklist
Before declaring implementation complete, verify each category:
Spatial Accuracy (within 1pt tolerance)
Typography Accuracy
Color Accuracy
Shape and Effect Accuracy
Perceptual Accuracy
Responsive Behavior
Hex Color Helper
If the project doesn't have a hex color initializer, add one:
extension Color {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet(charactersIn: "#"))
let scanner = Scanner(string: hex)
var rgbValue: UInt64 = 0
scanner.scanHexInt64(&rgbValue)
let r = Double((rgbValue & 0xFF0000) >> 16) / 255.0
let g = Double((rgbValue & 0x00FF00) >> 8) / 255.0
let b = Double((rgbValue & 0x0000FF)) / 255.0
self.init(red: r, green: g, blue: b)
}
}
Integration with FlowDeck Workflow
This skill works hand-in-hand with FlowDeck:
flowdeck run — Build and launch to see the implementation
flowdeck ui simulator session start — Continuous screenshots for comparison
- Read
latest_screenshot — Your eyes on the UI after every change
- Read
latest_tree — Verify accessibility labels and element structure
- Iterate — Change code, rebuild, re-read screenshot, compare with design
flowdeck run -S "<device>" — Test on different screen sizes
The build-compare-refine loop should be tight: change one thing, verify immediately, move to the next discrepancy. Never batch multiple changes before checking.