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
transparentorrgba(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
!importantoverriding!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: hiddennot clipping intended contentopacity> 0.1 on informational elementsvisibility: visible(nothiddenorcollapse)displaynotnoneon intended elementsz-indexstacking shows elements in correct order- Images render with
widthandheight> 0 - SVGs render with
fill/strokeset - Fonts loaded (rendered
font-familymatches 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
| Symptom | Likely cause | Fix |
|---|---|---|
| White-on-white text | Token defined in wrong layer/format | Check framework version, register token correctly |
| Elements missing | display: none or zero dimensions | Check computed display, width, height |
| Colors all the same | Tokens resolve to same value | Audit computed values, check theme definition |
| Borders invisible | Border color matches background | Set explicit border color different from background |
| Icons missing | SVG fill is none or font not loaded | Check computed fill, verify font loading |
| Layout broken after deploy | CSS load order changed | Verify 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
colordiffers from computedbackground-color - Every interactive element: computed width and height ≥ 44px (thresholds)
- CTA
background-colorappears 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:
- What is the single most prominent element? Is it the value prop or CTA?
- Can you identify what this page offers in 5 seconds?
- Is there visual clutter competing for attention?
- Does the content hierarchy make sense at this width?
- 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.
- Who is this for? A specific person — not "everyone."
- What do they need to know? One essential piece — not five.
- What do they need to do? One clear action — not a menu.
- Why should they trust us? Specific proof — not adjectives.
- 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.
| Severity | Definition | Action |
|---|---|---|
| CRITICAL | Blocks core user task (broken CTA, invisible text, no keyboard nav) | Fix before shipping |
| HIGH | Degrades experience significantly (poor contrast, tiny targets) | Fix before shipping |
| MEDIUM | Noticeable quality issue (inconsistent spacing, missing states) | Fix in next iteration |
| LOW | Polish (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.
| Evaluator | Focus |
|---|---|
| Design Critic | Visual hierarchy, CTA placement, mobile, trust signals |
| Copywriter | Headlines, value props, emotional triggers, awareness level |
| SEO Specialist | Meta tags, heading structure, performance |
| Growth Strategist | Synthesizes 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.
| Dimension | Weight | Score 1 | Score 3 | Score 5 |
|---|---|---|---|---|
| Value clarity | 20 | Cannot identify offer in 10s | Offer clear, benefit vague | Offer + benefit + audience clear in 5s |
| Visual hierarchy | 15 | Multiple competing focal points | Clear hierarchy, CTA buried | Single focal point per viewport, CTA dominant |
| CTA design | 15 | CTA below fold, same style as body | CTA visible, color not reserved | CTA above fold, unique color, 44px+ target, centered |
| Social proof | 10 | None | Generic ("trusted by thousands") | Specific numbers + recognizable names |
| Mobile experience | 10 | Broken at 375px | Functional but cramped | Full experience preserved, touch-optimized |
| Load performance | 10 | LCP > 4s | LCP 2.5–4s | LCP < 2.5s |
| Accessibility | 10 | Fails keyboard nav or contrast | Passes basic contrast, partial keyboard | Full WCAG 2.2 AA |
| Copy quality | 5 | Generic or AI-sounding | Clear but not compelling | Specific, benefit-led, addresses objection |
| Trust signals | 3 | No indicators | Basic (copyright, privacy link) | Security badges, social proof, money-back |
| Page weight | 2 | > 3 MB | 1–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
| Phase | What happens | Time |
|---|---|---|
| Solo first pass | Each reviewer walks the artifact alone, lists findings by severity | 15 min |
| Synthesis | Reviewers share findings; conflicts surface; one prioritised list emerges | 20 min |
| Decision | Author accepts/defers/disputes each finding with reasoning | 15 min |
| Re-review | Author fixes accepted findings; one reviewer re-walks the artifact | 20 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
- Product Design hub — situation router + Hard Thresholds Table
- Design Language — tokens, primitives, component hierarchy
- Interaction + Accessibility — WCAG 2.2 deep dive
- Process Quality Assurance — Deming's 14 points