Writing Visual Audit Reports That Claude Code Can Execute
The quality of visual QA depends on how you communicate findings to Claude Code — learn the structured audit report format that turns observations into one-shot fixes.
Every visual QA method in this track — the Chrome extension, screenshot upload, your own eyes — produces findings. The findings are only as useful as the instructions you write from them.
"It looks wrong" is not an instruction. "The modal overlay is covered by the dropdown menu because of a z-index stacking context conflict in the sticky navbar" is an instruction. One of these produces a one-shot fix. The other produces a diagnostic conversation that takes eight turns and ends with Claude Code asking you to clarify what you meant.
This lesson teaches the audit report format that production operators use: a structured template with severity classification, file path hints, expected vs. actual descriptions, and the closing prefix that constrains Claude Code to fix exactly what you identified without touching anything else.
The Audit Report Template
Every finding in a visual audit report follows this structure:
## Visual Audit: [Page Name]
**URL**: /path-to-page
**Viewport**: 1440 x 900
**Date**: 2026-04-03
### Finding 1: [Short description]
- **Severity**: P0 / P1 / P2
- **Location**: Component name, CSS selector, or page region
- **File**: Best guess at the source file path
- **Expected**: What it should look like
- **Actual**: What it actually looks like right now
- **Suggested fix**: The specific CSS property, Tailwind class, or structural change
### Finding 2: [Short description]
...
Every field has a purpose. Remove one and the report degrades.
Page Name and URL give Claude Code the routing context. If it needs to find the page component, it starts with the URL.
Viewport is essential for responsive work. An issue at 1440px desktop may not be reproducible at 375px mobile. An issue at 375px mobile may not exist at desktop. The viewport tells Claude Code whether to look for responsive-specific logic (media queries, conditional classes, responsive variants like md:flex-row).
Severity drives the fix order. Claude Code should fix P0s before P1s before P2s — stating this explicitly in the closing instruction is even more important than the per-finding severity labels.
Location is how Claude Code finds the issue in the DOM. The more specific, the better. A component name like PricingCard is better than "the pricing section." A CSS selector like .dashboard-stats-row .stat-value is better than "the stat numbers." A page region like "the footer, third column" at minimum tells Claude Code where to look.
File is the highest-leverage field because it eliminates the search step. Claude Code does not need to grep for which file renders the PricingCard — you are telling it. Approximate paths are fine. If you write src/components/Pricing* and the actual file is src/components/PricingTable.tsx, Claude Code will find it. Even a directory hint — "somewhere in src/components/" — reduces the search time.
Expected vs. Actual is the diff. Expected describes the design intent. Actual describes the current broken reality. This framing forces you to be precise: you cannot write "expected" without knowing what correct looks like, and you cannot write "actual" without having looked at the rendered output. The discipline of completing both fields catches vague findings before they become vague fix requests.
Suggested fix is your hypothesis about the code change. It does not need to be correct — Claude Code will verify and potentially improve it — but having a hypothesis signals that you have thought about the root cause. "Change font-weight from 300 to 400" is more useful than nothing, even if the actual fix turns out to be removing a font-thin class instead.
Severity Classification
Getting severity right is how you prioritize correctly under time pressure.
P0 — Broken or unusable. The feature does not work at all, or is blocked in a way that prevents users from completing a core action. Examples:
- A modal is visually hidden behind another element, so the close button is inaccessible
- A form submit button is completely invisible (transparent text on white background)
- A navigation link points to a 404
- Content is cut off by overflow:hidden and the page has no way to reveal it
P0s ship nothing until fixed. If you find a P0 in a visual audit, it goes to the top of the fixing queue and nothing else merges until it is resolved.
P1 — Visually wrong. The feature works but looks incorrect relative to the design intent or established patterns. Examples:
- A heading using font-weight: 300 on a dark background (technically readable, visually degraded)
- Card padding is 12px on three sides and 20px on one side (inconsistent, clearly off)
- Image aspect ratio is wrong (a 16:9 container rendering a 4:3 image with distortion)
- A grid of three columns where one column is visually narrower
- Button labels truncated with ellipsis when there is sufficient space
P1s should be fixed before shipping a feature, but they do not block unrelated work.
P2 — Polish. The implementation is functionally correct and within acceptable visual range, but refinement would elevate it. Examples:
- Slightly inconsistent spacing (24px vs. 28px in two places where 24px was intended)
- A hover transition that could be 200ms instead of the current 150ms for a smoother feel
- An icon that is 1-2px off-center in its container
- Line-height that is technically acceptable but could be improved
P2s accumulate into a polish pass at the end of a sprint or before a launch. They are real but not urgent.
The File Path Guess Technique
The "file" field works even when you do not know the exact path. Here is the mental model for making useful guesses:
From URL to file path (Next.js App Router):
/pricing→src/app/pricing/page.tsx/dashboard/settings→src/app/dashboard/settings/page.tsx/blog/[slug]→src/app/blog/[slug]/page.tsx
From component name to file path:
PricingCard→src/components/PricingCard.tsxorsrc/components/pricing/PricingCard.tsxDashboardStats→src/components/dashboard/DashboardStats.tsx
When you have no idea:
- "Somewhere in src/components/" is still useful
- "The pricing page, probably in src/app/pricing/" is still useful
- A wrong guess that gets Claude Code to the right directory is more useful than no guess
The goal is not accuracy — it is reducing search time. Claude Code can search for the right file, but every search step adds latency to the fix loop.
Batching Findings by File
Sending one finding per Claude Code turn produces worse results than batching findings from the same file into one turn. Here is why:
Each Claude Code turn involves reading the file, understanding the context, making changes, and writing the file. If you send five findings one at a time and they all touch PricingTable.tsx, Claude Code reads that file five times, makes five separate edits, and you get five reload-and-verify cycles.
If you batch all five findings from PricingTable.tsx into one turn, Claude Code reads the file once, makes all five changes in a single coherent pass, and you get one reload-and-verify cycle. The result is higher quality because Claude Code can see how the changes interact with each other — fixing the padding and the font-weight and the grid alignment simultaneously, with awareness of each change's effect on the others.
The batching discipline in your audit report:
## Visual Audit: Dashboard
**URL**: /dashboard
**Viewport**: 1440 x 900
---
### Findings for src/components/DashboardCard.tsx
#### Finding 1: Card padding inconsistent
- **Severity**: P1
- **Location**: DashboardCard component inner container
- **File**: src/components/DashboardCard.tsx
- **Expected**: 24px padding on all four sides
- **Actual**: Top and bottom padding appears to be 24px, left and right appears to be 16px
- **Suggested fix**: Ensure the container uses `p-6` (24px all sides) not `py-6 px-4`
#### Finding 2: Card title font weight too light
- **Severity**: P1
- **Location**: DashboardCard title element
- **File**: src/components/DashboardCard.tsx
- **Expected**: font-weight 600 (semibold) for dark background readability
- **Actual**: font-weight appears to be 300 or 400 — title reads weakly on dark bg
- **Suggested fix**: Add `font-semibold` class to the title element
---
### Findings for src/components/StatsRow.tsx
#### Finding 3: Stats row not vertically centered
...
Grouping by file makes the report more readable for you and more executable for Claude Code. When you paste the "Findings for StatsRow.tsx" section, Claude Code goes directly to that file.
A Complete Realistic Audit Report
Here is a full audit report for a fictional dashboard page. Read it as the standard you are aiming for — specific, actionable, batched by file, with clear expected vs. actual diffs.
## Visual Audit: Admin Dashboard
**URL**: /admin/dashboard
**Viewport**: 1440 x 900
**Date**: 2026-04-03
---
### Findings for src/app/admin/dashboard/page.tsx
#### Finding 1: Page max-width too wide
- **Severity**: P1
- **Location**: Page root container
- **File**: src/app/admin/dashboard/page.tsx
- **Expected**: Content constrained to max-w-7xl (1280px) centered
- **Actual**: Content stretches to full viewport width — on a 1440px monitor
it looks unpinned and overly wide
- **Suggested fix**: Wrap page content in a `max-w-7xl mx-auto px-6` container
---
### Findings for src/components/dashboard/StatsRow.tsx
#### Finding 2: Revenue stat value truncated
- **Severity**: P0
- **Location**: StatsRow, first card ("Total Revenue")
- **File**: src/components/dashboard/StatsRow.tsx
- **Expected**: Full dollar value visible, e.g., "$128,450"
- **Actual**: Value truncates to "$128..." with ellipsis — the card is too narrow
or the font size is too large for the container
- **Suggested fix**: Either increase the card min-width or reduce the stat value
font-size from text-3xl to text-2xl on this component
#### Finding 3: Stat label color too dim
- **Severity**: P1
- **Location**: StatsRow, label text beneath each stat value
- **File**: src/components/dashboard/StatsRow.tsx
- **Expected**: Labels readable at text-slate-400 or brighter
- **Actual**: Labels appear to be text-slate-600 on a dark background — very
difficult to read without leaning forward
- **Suggested fix**: Change stat label color class from `text-slate-600` to
`text-slate-400`
---
### Findings for src/components/dashboard/ActivityFeed.tsx
#### Finding 4: Activity feed items missing left border accent
- **Severity**: P2
- **Location**: ActivityFeed, individual feed items
- **File**: src/components/dashboard/ActivityFeed.tsx
- **Expected**: Each activity item has a 2px left border in the relevant status
color (green for success, yellow for warning, red for error)
- **Actual**: All items render with no left border — they look identical
regardless of status
- **Suggested fix**: Add `border-l-2` with a conditional color class based on
the `status` prop: `border-green-500`, `border-yellow-500`, `border-red-500`
#### Finding 5: Feed timestamp not right-aligned
- **Severity**: P2
- **Location**: ActivityFeed, timestamp text in each feed item
- **File**: src/components/dashboard/ActivityFeed.tsx
- **Expected**: Timestamp right-aligned in the feed item row, consistent with
the design reference
- **Actual**: Timestamp sits immediately after the activity text with no
right-alignment — the right edge of the row is empty
- **Suggested fix**: The feed item row needs `flex justify-between` and the
timestamp should be in its own span
---
Fix these visual issues in order of severity. Do not change anything else.
This report produces a one-shot fix on the first attempt in the majority of cases. Notice what makes it work: every finding has enough information that Claude Code can go from report to edit without asking a clarifying question.
The Closing Prefix
Every audit report sent to Claude Code must end with:
Fix these visual issues in order of severity. Do not change anything else.
This single line does two things that matter:
"In order of severity" tells Claude Code to prioritize P0s first. Without this, Claude Code may address findings in the order they appear in the report, which may not match severity order if you wrote them in discovery order rather than priority order.
"Do not change anything else" is a constraint that prevents scope creep. Without it, Claude Code may "helpfully" refactor the component while fixing the padding issue, rename variables for clarity, or adjust styles that were not in the report but that it noticed while reading the file. These unrequested changes introduce risk — they may break things that were working, or alter behavior you wanted to preserve. The constraint keeps the diff minimal and reviewable.
This is the Surgical Iteration Rule applied to visual QA: fix only what was identified, verify those specific things resolved, move on.
The First-Attempt Fix Rate
The metric that tells you whether your audit reports are good is first-attempt fix rate: the percentage of findings in a report that are correctly resolved by Claude Code on the first try, requiring no follow-up clarification or additional turns.
A poorly written report (vague descriptions, no file paths, no expected/actual) produces a first-attempt fix rate around 30-40%. Most findings require at least one clarification turn.
A well-written report using the template above produces a first-attempt fix rate of 80-90%. One-shot, reload, verified.
The 50-60 percentage point difference is entirely in the quality of the report. Claude Code's capability is constant — the input determines the output.
The Exercise
- Choose a page in a project you are actively developing. Alternatively, use any page you can visually inspect and hypothetically assign to file paths.
- Look at the page with fresh eyes. Identify at least three visual issues — one P0 or P1, and two others.
- Write a full audit report using the template from this lesson. Complete every field: severity, location, file (even if approximate), expected, actual, suggested fix. Group findings by file.
- End the report with the closing prefix.
- Paste the report into Claude Code. Read the fix. Reload the browser. Measure how many of your findings were correctly resolved on the first attempt.
- For any finding that was not resolved correctly, review your report. Was the expected/actual clear enough? Was the suggested fix misleading? Revise the finding and retry.
Your goal is to debug your own report-writing until your first-attempt fix rate exceeds 80%.
Common Mistakes
Writing "Expected: looks correct" and "Actual: looks wrong." This communicates nothing. Expected and actual must describe specific visual properties: dimensions, colors, font weights, alignment, spacing in pixels or Tailwind class equivalents.
Sending one massive report for an entire site. If your report has 30 findings across 15 files, Claude Code will try to fix all of them in one turn and produce a large, hard-to-review diff. Limit reports to one logical section at a time — one page, or one component family. Five to eight findings per report is the practical upper bound.
Skipping the "Do not change anything else" closing. Without this constraint, every report is an invitation for Claude Code to make unrequested improvements. You will eventually reload the browser, find the visual issue fixed, and then discover that some other part of the page changed in a way you did not want. The constraint is non-optional.
Using P0/P1/P2 as emotional labels rather than functional ones. "This really bothers me" is not P0. P0 is "the user cannot complete the action." Keep the severity classification functional. It determines fix priority and should be consistent across every audit you produce.
Summary
Visual audit reports are the translation layer between human perception and Claude Code execution. The template — severity, location, file path, expected, actual, suggested fix — gives Claude Code everything it needs to find the problem and apply the right change without a clarifying conversation. Batching findings by file produces higher-quality fixes in fewer turns. The closing prefix "Fix these visual issues in order of severity. Do not change anything else" keeps diffs minimal and reviewable. The metric that tells you whether your reports are working is first-attempt fix rate — aim for 80% or higher.
What's Next
You now understand why Claude Code is blind to pixels, how the Chrome extension closes that gap for live single-page inspection, how screenshot upload covers multi-page and design-comparison workflows, and how to write audit reports that produce one-shot fixes. The next lesson, The Full Loop: Build, See, Fix, Verify, ties these pieces together into a single repeatable rhythm — turning the build-see-fix-verify loop into reflex rather than deliberate effort — before the track moves on to responsive QA, Computer-Use MCP, and visual testing in CI.