Write code, verify it visually in browser or iOS simulator, and iterate based on visual feedback until implementation matches expectations.
From claude-toolkitnpx claudepluginhub johwer/marketplace --plugin claude-toolkitThis skill uses the workspace's default tool permissions.
resources/ITERATION_PATTERNS.mdresources/PLATFORM_GUIDELINES.mdscripts/README.mdscripts/compare_screenshots.pySearches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
This Skill provides a structured workflow for visual development tasks. Claude will write or modify code, verify the result visually using browser screenshots or iOS simulator, compare against design requirements, and iterate until the implementation matches expectations.
When to invoke this Skill:
IMPORTANT: Before implementing changes, ask the user which platform(s) to test on using the AskUserQuestion tool:
Note: If the user is not on macOS, automatically default to web testing only and inform them that iOS simulator is only available on macOS.
⚠️ IMPORTANT: Pre-Verification Check
Before running verification tools, ALWAYS verify the page renders correctly first:
playwright-cli console or dev server logs)Only proceed with verification tools after confirming the page renders correctly.
Always verify changes visually based on the chosen platform(s):
Playwright CLI is the default verification method. It's headless, token-efficient, and auto-generates test code from interactions. Check dtf-config.json verification field — if set to "applescript", use Option B instead.
# Detect the dev server port (worktrees use VITE_DEV_PORT from .env.local, main uses 3000)
# S3 translations work on all 3xxx ports (CORS: http://localhost:3*)
PORT=$(grep VITE_DEV_PORT apps/web/.env.local 2>/dev/null | cut -d= -f2 || echo 3000)
# Open browser and navigate (use --headed to watch)
playwright-cli open http://localhost:$PORT/[relevant-path]
# Take a DOM snapshot to get element refs
playwright-cli snapshot
# Interact using refs from snapshot output
playwright-cli click e5
playwright-cli fill e3 "test value"
playwright-cli select e9 "option-value"
# Take screenshot for visual verification
playwright-cli screenshot --filename=verify-feature.png
# Check for console errors
playwright-cli console
# Close when done
playwright-cli close
Multi-agent sessions — use named sessions to avoid conflicts:
playwright-cli -s=frontend open http://localhost:$PORT --headed
playwright-cli -s=frontend snapshot
playwright-cli -s=frontend screenshot
Video recording for PR evidence:
playwright-cli video-start
# ... perform interactions ...
playwright-cli video-stop feature-demo.webm
Test generation — every CLI interaction outputs Playwright TypeScript code. Collect the generated code and write it as a test file:
# Each command outputs code like:
# await page.getByRole('button', { name: 'Submit' }).click();
# Collect these into a .spec.ts file
Use this if Playwright CLI is not installed or dtf-config.json has "verification": "applescript". See ~/.claude/projects/-Users-username-Documents/memory/visual-testing.md for the full AppleScript workflow.
# Navigate via Chrome
osascript -e 'tell application "Google Chrome" to set URL of active tab of first window to "http://localhost:<PORT>/..."'
# Wait + capture
sleep 4 && osascript -e 'tell application "Google Chrome" to activate' && sleep 0.5 && screencapture -x ~/Downloads/screenshot.png
# List available simulators
mcp__ios-simulator__list_simulators
# Start a simulator
mcp__ios-simulator__start_simulator
# Device: iPhone 15 Pro (or user's choice)
# Take screenshot
mcp__ios-simulator__take_screenshot
# Interact with the app
mcp__ios-simulator__tap
mcp__ios-simulator__swipe
Test on web first (Playwright CLI), then iOS simulator, comparing behavior and appearance across both.
If issues are found:
Once visually satisfied, run standard checks:
npm run writePrettier # Format code
npm run lint # Check linting
npm run type-check # Verify types
npm run build # Ensure builds
When the user approves the result, commit the changes following the git workflow.
User: Here's a design for the new login page [provides screenshot]
Claude:
1. Asks about platform preference (Web/iOS/Both)
2. Analyzes the design mock
3. Implements the login page component
4. Takes screenshot of the implementation
5. Compares: "The layout matches, but the button color is slightly off and spacing needs adjustment"
6. Adjusts the code
7. Takes new screenshot
8. Compares: "Now matches the design. The button color and spacing are correct."
9. Runs quality checks
10. Ready for commit
User: The header looks broken on mobile
Claude:
1. Asks about platform preference
2. Connects to browser via Puppeteer
3. Navigates to the page
4. Takes screenshot at mobile viewport
5. Identifies: "The logo is overlapping the menu button"
6. Fixes the CSS flexbox layout
7. Takes new screenshot
8. Verifies: "Layout now works correctly on mobile"
9. Tests at different breakpoints
10. Runs quality checks
npm install -g @playwright/cli@latest — headless browser automationnpm start)VITE_DEV_PORT in .env.local, defaults to 3000)See the resources folder for:
❌ DO NOT use \n to submit forms
\n (newline) in iOS Safari adds literal characters instead of submitting❌ Avoid triggering iOS autofill
Use this step-by-step pattern for iOS Safari form automation:
Navigate to login page
http:// if needed to avoid search)\n)Fill username field
Fill password field
Submit form
\nWait and verify
// Good example of iOS Safari login automation
// Navigate to login page
ui_tap(address_bar)
ui_type("http://localhost:<PORT>/login/userAndPass")
// Tap suggestion instead of typing \n
ui_tap(first_suggestion)
sleep(3)
// Fill username
ui_tap(username_field)
ui_type("gunner")
// Fill password (be careful not to trigger autofill)
ui_tap(password_field)
ui_type("tolvan")
// Submit by tapping button
ui_tap(login_button)
sleep(3)
// Verify
ui_view()
Problem: Forms submit to Google instead of navigating
\n to submit or didn't include http:// in URLProblem: iPhone passcode prompt appears
Problem: Typed in wrong field
Problem: Button tap doesn't work
ui_view() to verify state before and after actions