Horizontal overflow at three viewports, with the mask lifted
OVERFLOW_CULPRITSThe failure it prevents
body { overflow-x: hidden } is in almost every stylesheet, and it makes every overflow check read clean forever. The page still scrolls sideways on a phone — the clip just hides the evidence from the audit. And the checks that do find something blame the marquee, which is 3000px wide on purpose and clipped by its own parent, so the team learns the check is wrong and stops reading it.
The prompt
Add a horizontal-overflow sweep to the browser harness that runs at every breakpoint, sees through the global clip, and does not blame elements that are legitimately clipped.
**The probe** — a single exported function serialized into the page:
```js
export const OVERFLOW_CULPRITS = () => {
const de = document.documentElement;
const prev = document.body.style.overflowX; // save
document.body.style.overflowX = "visible"; // lift the mask
void de.scrollWidth; // force reflow before measuring
const vw = de.clientWidth;
const overflow = de.scrollWidth - vw;
const culprits = [];
if (overflow > 1) {
for (const el of document.querySelectorAll("body *")) {
const r = el.getBoundingClientRect();
if (!r.width || !r.height) continue; // invisible
if (r.right <= vw + 1 && r.left >= -1) continue; // inside the viewport
let clipped = false;
for (let p = el.parentElement; p; p = p.parentElement) {
if (/hidden|clip|auto|scroll/.test(getComputedStyle(p).overflowX)) { clipped = true; break; }
}
if (clipped) continue; // a carousel doing its job, not the page being too wide
culprits.push({
tag: el.tagName.toLowerCase(),
cls: typeof el.className === "string" ? el.className.slice(0, 70) : "",
right: Math.round(r.right),
width: Math.round(r.width),
});
}
}
document.body.style.overflowX = prev; // restore — the page must be as you found it
return { overflow, culprits: culprits.slice(0, 5) };
};
```
Four things in there are not optional and each one is a bug you would otherwise ship:
- **Lifting `overflow-x` on body**, or the global clip masks every offender and the audit is decorative.
- **`void de.scrollWidth`** to force a reflow after the style change, or you measure the pre-clip layout.
- **The clipped-ancestor walk**, or the check cries wolf on every marquee, carousel and ticker, which is how checks get deleted.
- **The restore**, because later checks run on the same page and must see the app's real styles. Assert the restoration in the probe's self-test — a probe that mutates the page and forgets to undo it poisons every measurement after it.
**Sweep the viewports.** Default `[1440×900, 768×1024, 390×844]`, and **read the project's own breakpoints** out of the Tailwind config or the stylesheet's media queries and add one viewport just inside each. After `setViewport`, wait ~600ms before measuring: media queries, ResizeObservers and any JS-driven layout need a frame or several. Run the sweep on every route, not just the homepage — the marketing hero is rarely the thing that overflows.
**Fail per viewport**, naming the offenders:
```
FAIL overflow /features @ 390px
document is 214px wider than the viewport
div.grid.gap-8 — right edge 604px, width 604px
img.hero — right edge 512px, width 512px
```
The overflow amount alone is not actionable at 390px, where a dozen things could be responsible. Sort culprits by `right` descending so the worst offender is the first line.
**Do not** add the check by asserting `document.body.scrollWidth === window.innerWidth` and calling it done — that is the same number without the culprit list, and it will also read clean through the clip. **Do not** try to fix overflow by adding more `overflow-x: hidden`; if the check fires and someone's remedy is another clip, the check has made the codebase worse. Say this in the failure text.
**Hand back:** the probe, the sweep, and a run against this project at all viewports with the real numbers.What it produces
An OVERFLOW_CULPRITS probe (mask lifted, reflow forced, clipped descendants skipped, page restored) plus a per-route multi-viewport sweep that fails naming up to five offending elements with their right edges.
How you prove it works
Three fixtures, all with body{overflow-x:hidden} set exactly as the real app has it. (1) /overflow: a single width:1640px div at a 1440 viewport — the probe must report ~200px and name the div; if it reports 0, the mask is still on. (2) /marquee: a width:3000px child inside a overflow-x:hidden; width:100% parent — the probe must report exactly 0; anything else and it cries wolf on every carousel you own. (3) Run the probe on the overflow fixture, then read getComputedStyle(document.body).overflowX and assert it is back to hidden.