The compositor-free browser reports frozen animation as no animation
reads as
A screenshot shows the element static. Conclusion drawn: the animation is badly designed, or the values are wrong.
actually
requestAnimationFrame never fires because the headless browser has no compositor. Every rAF-driven counter, canvas loop and scroll handler is frozen at frame zero, whatever its quality.
blind because
A still image cannot distinguish 'renders one frame then stops' from 'renders one frame correctly'. Both produce the same pixels.
the check
let n=0; requestAnimationFrame(()=>n++); setTimeout(()=>console.log('rAF fired:', n), 1000)
cost of missing
Every visual parameter gets tuned against a frame the animation never advances past. The tuning is not merely useless, it is fitted to an artifact.
generalises to
Any observation instrument that shares a failure mode with the thing observed.
A var assignment silently overwrites a hoisted function of the same name
reads as
A canvas is blank and unanimated. Conclusion drawn: a rendering or design problem.
actually
The file already used `var start` as a timestamp. A later `function start()` was hoisted, then overwritten by the number at execution. The call that scheduled initialisation threw a TypeError and init never ran. The element sat at its untouched default size with zero painted pixels.
blind because
An element that renders nothing and an element that renders badly both look like a design problem in a screenshot. Nothing distinguishes them visually.
the check
Read the element's backing store, not its appearance: canvas.width/height still at the 300x150 default means resize() never ran.
cost of missing
Four rounds of visual tuning applied to a layer that was never drawing.
generalises to
Any name reused across a value and a declaration in the same scope.
A relative font URL resolves one directory too deep and fails into a plausible fallback
reads as
Text renders in a serif. Conclusion drawn: the font loaded.
actually
A stylesheet at /assets/fonts.css requesting url('assets/fonts/x.woff2') resolves to /assets/assets/fonts/x.woff2 and 404s. The browser substitutes a system serif without complaint.
blind because
The fallback is a working font. Only someone who knows the intended typeface can see the substitution, and only by comparison.
the check
document.fonts.check('1em "Family Name"') or a 404 on the font path in the network log.
cost of missing
Design review proceeds against the wrong typeface. Every judgement about weight, rhythm and scale is made on a substitute.
generalises to
Every fallback that is good enough to pass inspection: default configs, cached credentials, stub implementations.
Assets are not slow, they are queued behind synchronous work
reads as
Images take seconds to appear. Conclusion drawn: the images are too large.
actually
Their request had not been issued. Heavy synchronous work earlier in the document held the main thread, and the fetch that would load them sat unsent behind it.
blind because
Slow arrival and late departure are the same experience from the viewport.
the check
performance.getEntriesByType('resource') — startTime separates 'requested late' from 'transferred slowly'.
cost of missing
Assets get compressed, resized and lazy-loaded, which lowers quality without touching the delay.
generalises to
Any queue where wait time is read as service time.
Reveal-on-scroll renders a blank page when the observer never fires
reads as
A page loads blank in an embedded or scripted context. Conclusion drawn: a rendering failure.
actually
Elements start at opacity 0 and are revealed by an IntersectionObserver callback. Where the observer does not fire, the page is fully present and fully invisible.
blind because
The DOM is complete and correct. Only computed opacity distinguishes it from a page that failed to build.
the check
Compare element count against visible count: document.querySelectorAll('.reveal').length versus those with computed opacity above zero.
cost of missing
A working page is diagnosed as broken. Worse, the reverse: a genuinely blank page is dismissed as this.
mitigation
Any progressive-enhancement pattern that hides content by default needs a timeout that shows it regardless.
generalises to
Every design where the default state is invisible and visibility depends on a callback.
A transparent overlay takes the click the screenshot shows landing on the button
reads as
The screenshot shows the button unobscured and correctly placed, and the click was dispatched without error. Conclusion drawn: the button was clicked.
actually
A transparent element — a full-viewport modal backdrop, a zero-opacity loading layer, an oversized decorative pseudo-element — covers the button's centre point, and hit testing delivers the event to the topmost element at that coordinate. WebDriver has a named error for exactly this: the Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked.
blind because
A transparent overlay contributes no pixels. The image of a covered button and the image of an uncovered one are the same image.
the check
Ask the document what occupies the point: `const r = el.getBoundingClientRect(); document.elementFromPoint(r.left + r.width/2, r.top + r.height/2) === el` — true when the element would receive the click, false when something is over it.
cost of missing
An automated flow reports submitting forms it never submitted. A synthetic `el.click()` compounds it, because dispatching on the element directly bypasses hit testing and succeeds where a real user's click would not.
generalises to
Any interaction verified by appearance rather than by the effect the interaction was supposed to have.