Skip to main content

Audit Loop

How do you make sure a page that compiles also renders, reads, and converts?

"Present in the DOM" is not "visible to a human." "Visible" is not "understood." "Understood" is not "acted on." Each gap needs its own check. This page is the canonical loop that runs every check in order — same loop whether a human or an AI is driving.

For every threshold cited below, see the Hard Thresholds Table. Numbers live there, the loop lives here.

The Loop

GENERATE → Build the artifact against a concrete spec
CRITIQUE → Score against measurable thresholds (rendering → visual → 5-question)
REFINE → Fix each failure; quote the old line and the new
REPEAT → Re-run critique. Zero failures = stop. Max 3 iterations.

Source: Madaan et al. 2023 — 20% average quality improvement; 30% code-error reduction (Google Research 2025).

What makes it work:

  • The critique step needs a concrete rubric — vague feedback produces identical output each iteration
  • "No issues found" on first critique means the rubric is too soft. Look harder.
  • Rotate rubric focus per iteration (rendering → visual → semantic) to break score plateaus
  • Costs roughly 3× tokens. Use only for high-stakes outputs — landing pages, hero sections, CTA blocks, prompt-deck slides

Layer 1 — Rendering Verification

Run after deploying, after changing CSS tokens, after framework upgrades, or when something looks wrong but you cannot pinpoint why.

Token resolution

Every CSS custom property and design token must resolve to a concrete value.

  • Every var(--token) in use has a defined value in the active theme
  • Background colors produce visible values (not transparent or rgba(0,0,0,0))
  • Border colors render and differ from background
  • Text colors differ from backgrounds (DevTools Computed tab)
  • No fallback values masking missing definitions (var(--missing, transparent))

CSS cascade

Framework defaults, resets, and utility classes silently override each other.

  • Utility classes win over framework defaults (DevTools Styles panel — utility not struck through)
  • Zero instances of !important overriding !important
  • Reset does not zero your intended values
  • Scoped styles do not leak into or out of components

Visual rendering

Elements exist in the DOM but produce no visual output more often than expected.

  • No zero-height containers hiding content
  • overflow: hidden not clipping intended content
  • opacity > 0.1 on informational elements
  • visibility: visible (not hidden or collapse)
  • display not none on intended elements
  • z-index stacking shows elements in correct order
  • Images render with width and height > 0
  • SVGs render with fill/stroke set
  • Fonts loaded (rendered font-family matches intended)

Automated rendering audit

Paste in browser console to catch the most common failures:

// 1. Invisible text (color === background-color)
document.querySelectorAll("p, h1, h2, h3, h4, span, a, li, td, th, label, button").forEach((el) => {
const s = getComputedStyle(el);
if (s.color === s.backgroundColor && el.textContent.trim()) {
console.warn("Invisible text:", el.textContent.slice(0, 50), el);
}
});

// 2. Zero-dimension containers with children
document.querySelectorAll("div, section, article, main, aside, nav").forEach((el) => {
const r = el.getBoundingClientRect();
if ((r.width === 0 || r.height === 0) && el.children.length > 0) {
console.warn("Zero-dim container with children:", el);
}
});

// 3. Unresolved CSS variables
document.querySelectorAll("*").forEach((el) => {
const s = getComputedStyle(el);
for (const prop of s) {
const v = s.getPropertyValue(prop);
if (v.includes("var(") && v.includes(")")) {
console.warn("Unresolved CSS variable on", el, prop, v);
}
}
});

Quick diagnosis

SymptomLikely causeFix
White-on-white textToken defined in wrong layer/formatCheck framework version, register token correctly
Elements missingdisplay: none or zero dimensionsCheck computed display, width, height
Colors all the sameTokens resolve to same valueAudit computed values, check theme definition
Borders invisibleBorder color matches backgroundSet explicit border color different from background
Icons missingSVG fill is none or font not loadedCheck computed fill, verify font loading
Layout broken after deployCSS load order changedVerify stylesheet order in build output

Layer 2 — Browser Verification

Three sub-layers from cheapest to richest.

Accessibility tree

2–5 KB vs 100+ KB for screenshots. Deterministic, fast. Check:

  • All interactive elements have accessible names
  • Heading hierarchy (H1 → H2 → H3, no skips)
  • Landmark regions present (main, nav, footer)
  • All images have alt text

Computed styles

Read actual rendered values from the browser — not what the code says, what the browser computed:

  • Every text element: computed color differs from computed background-color
  • Every interactive element: computed width and height ≥ 44px (thresholds)
  • CTA background-color appears on zero non-clickable elements
  • document.scrollWidth ≤ window.innerWidth (no horizontal overflow at 320 px)

Screenshot capture at 4 breakpoints

Capture at 375 / 768 / 1024 / 1440 px (canonical breakpoints — see Hard Thresholds Table row 41). Evaluate each:

  1. What is the single most prominent element? Is it the value prop or CTA?
  2. Can you identify what this page offers in 5 seconds?
  3. Is there visual clutter competing for attention?
  4. Does the content hierarchy make sense at this width?
  5. Are there obvious rendering failures (invisible text, broken layout)?

Compare 375 px vs 1440 px — is the information architecture preserved?

Layer 3 — The Five-Question Audit

Run upstream of every checklist. If the page fails any of these, the technical checks are decoration.

  1. Who is this for? A specific person — not "everyone."
  2. What do they need to know? One essential piece — not five.
  3. What do they need to do? One clear action — not a menu.
  4. Why should they trust us? Specific proof — not adjectives.
  5. What's stopping them? A real objection, named and answered.

If you cannot answer all five in plain prose before opening DevTools, no audit will save the page. The five-question audit is the gate — every other layer is the toolbox.

Five-second comprehension test

Show the page to 5 people. Each looks for 5 seconds. All 5 must answer:

  • Who is this for?
  • What do they offer?
  • What's the next action?

If 4 of 5 fail, the page has a comprehension problem the rest of the loop cannot fix. Re-architect the hero before re-running the loop.

Severity-Rated Audit

Evaluate against UX principles linters cannot catch. Rate every finding. Fix from the top.

SeverityDefinitionAction
CRITICALBlocks core user task (broken CTA, invisible text, no keyboard nav)Fix before shipping
HIGHDegrades experience significantly (poor contrast, tiny targets)Fix before shipping
MEDIUMNoticeable quality issue (inconsistent spacing, missing states)Fix in next iteration
LOWPolish (animation timing, micro-interactions)Backlog

UX principles to evaluate:

  • Loading states present
  • Visual hierarchy (not everything same weight)
  • Keyboard navigation complete
  • Spacing consistent
  • Error states handled
  • Empty states designed
  • Touch targets adequate
  • Color not sole status indicator

Four-Perspective Audit

Four specialised evaluators review the same page from different angles. Same artifact, different seat. Parallel — synthesis pass resolves conflicts and produces one prioritised action list.

EvaluatorFocus
Design CriticVisual hierarchy, CTA placement, mobile, trust signals
CopywriterHeadlines, value props, emotional triggers, awareness level
SEO SpecialistMeta tags, heading structure, performance
Growth StrategistSynthesizes findings, assigns A–F grade, prioritises actions

Design Critic scoring weights: Visual hierarchy 25 · CTA design 25 · Trust signals 20 · Mobile 15 · Spacing + typography 15.

Source: n8n workflow #9545 — four-agent landing page analysis. Full audit in under 90 seconds.

Scoring Rubric

100-point rubric → letter grade. Makes audits comparable across pages and over time.

DimensionWeightScore 1Score 3Score 5
Value clarity20Cannot identify offer in 10sOffer clear, benefit vagueOffer + benefit + audience clear in 5s
Visual hierarchy15Multiple competing focal pointsClear hierarchy, CTA buriedSingle focal point per viewport, CTA dominant
CTA design15CTA below fold, same style as bodyCTA visible, color not reservedCTA above fold, unique color, 44px+ target, centered
Social proof10NoneGeneric ("trusted by thousands")Specific numbers + recognizable names
Mobile experience10Broken at 375pxFunctional but crampedFull experience preserved, touch-optimized
Load performance10LCP > 4sLCP 2.5–4sLCP < 2.5s
Accessibility10Fails keyboard nav or contrastPasses basic contrast, partial keyboardFull WCAG 2.2 AA
Copy quality5Generic or AI-soundingClear but not compellingSpecific, benefit-led, addresses objection
Trust signals3No indicatorsBasic (copyright, privacy link)Security badges, social proof, money-back
Page weight2> 3 MB1–3 MB< 1 MB

Grade: A (90+) · B (80–89) · C (70–79) · D (60–69) · F (< 60).

Rule: Score 5 requires quoted evidence. Score 1 requires quoted evidence of failure. "Looks good" is not evidence.

Prompt Patterns

Five reusable critique patterns. Copy and adapt.

Rubric-anchored

Review [artifact] against this rubric. For each criterion:
1. Score 1–5
2. Quote specific evidence (line number, element, computed value)
3. State what change would raise the score by 1

[Insert rubric]

"No issues found" means the rubric is too soft. Look harder.

Perspective rotation

Evaluate from three perspectives. Same page, different seat.
FIRST-TIME VISITOR: Can you understand the offer in 5 seconds?
RETURN VISITOR: Can you find what you came back for in 2 clicks?
COMPETITOR: What would you copy? What would you attack?

List 3 observations per perspective with element references.

Inversion

You are trying to make this page FAIL. List the top 5 ways it could
lose a visitor. For each:
- The specific element or absence that causes the failure
- How a real user would experience it
- The fix (one sentence)

Threshold gatekeeper

You are a quality gate. This page must pass ALL of these to ship:
[List measurable thresholds from the Hard Thresholds Table]

For each threshold: PASS (quote evidence) or FAIL (quote violation + fix).
If ANY fail, output "HOLD — [count] failures" at the top.
If all pass, output "SHIP" at the top.

Progressive disclosure

Score on progressive disclosure:
1. HERO (0–2s): Does the visitor know WHAT + WHO + ACTION?
2. SCROLL 1 (2–10s): PROBLEM understood?
3. SCROLL 2 (10–30s): PROOF visible?
4. SCROLL 3 (30–60s): OBJECTION handled before the final CTA?
5. FINAL CTA: Same action as the hero CTA (consistency)?

Score 1–5 per stage. A page that scores 5 on 1–2 but 1 on 4–5 is a
funnel leak. Identify the leak.

Human Review

When two or more people review the same artifact, follow this workflow.

Phases + timings

PhaseWhat happensTime
Solo first passEach reviewer walks the artifact alone, lists findings by severity15 min
SynthesisReviewers share findings; conflicts surface; one prioritised list emerges20 min
DecisionAuthor accepts/defers/disputes each finding with reasoning15 min
Re-reviewAuthor fixes accepted findings; one reviewer re-walks the artifact20 min

Critique rules

  • Critique the artifact, not the author
  • Quote evidence: line number, element, computed value
  • Propose the fix, not just the problem
  • Severity before priority — fix CRITICAL before MEDIUM regardless of effort
  • "Looks good" is not feedback; either pass or quote what is good
  • Disputes resolve by re-running the threshold check, not by argument

Automated A11y Audit Script

Run in browser console for a quick accessibility check (does not replace axe DevTools or a full audit):

(function a11yAudit() {
const issues = [];

document.querySelectorAll("img:not([alt])").forEach((el) =>
issues.push({ type: "missing-alt", el, src: el.src }),
);

document.querySelectorAll('input:not([type="hidden"]), select, textarea').forEach((el) => {
const id = el.id;
const label = id ? document.querySelector(`label[for="${id}"]`) : null;
const ariaLabel = el.getAttribute("aria-label");
const ariaLabelledBy = el.getAttribute("aria-labelledby");
if (!label && !ariaLabel && !ariaLabelledBy) {
issues.push({ type: "missing-label", el });
}
});

let last = 0;
document.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach((h) => {
const lvl = parseInt(h.tagName[1], 10);
if (lvl > last + 1 && last !== 0) {
issues.push({ type: "skipped-heading", el: h, expected: last + 1, got: lvl });
}
last = lvl;
});

document.querySelectorAll("button, a[href]").forEach((el) => {
const name = el.textContent.trim() || el.getAttribute("aria-label") || el.getAttribute("title");
if (!name) issues.push({ type: "missing-accessible-name", el });
});

console.table(issues.map((i) => ({ type: i.type, el: i.el.outerHTML.slice(0, 80) })));
console.log(`Found ${issues.length} a11y issues`);
})();

Context

Source Material