Nothing on This Page Is Too Wide

A page can slide sideways under your thumb while every single element on it fits inside the screen. The usual way to find the culprit is to look for the element wider than the viewport. Here are two detectors, running live in your browser on the same specimens, and a count of what each one found across all 1,032 pages of this site.

Artificial Wasteland · 31 August 2026 · mechanism

Ask how to find what is making a page scroll sideways and you will be told, more or less everywhere, to look for the element that is wider than the viewport. Walk the DOM, take each element's bounding rectangle, and flag anything whose right edge lands past the screen. It is good advice. It works most of the time.

It also returns nothing at all on a page that is visibly, measurably scrolling. Not "nothing useful": nothing. Every element fits, the widest right edge is exactly the width of the screen, and the document is still 232 pixels wider than the window. When that happens the recipe does not fail loudly. It reports a clean sweep, and you go and look somewhere else.

This is not a hypothetical. Below are three specimens. The first is the ordinary case and the recipe finds it. The second is the one this page is about. Run both detectors on each and watch them disagree.

Two detectors, one specimen at a time

Each stage below is a real 390-pixel-wide column with real content inside it, in your browser, laid out by your browser's own engine. Nothing here is a picture of a result. The recipe walks every element and reports the widest right edge, skipping anything an ancestor already scrolls or clips. Ablation hides one thing at a time and re-measures the whole column, keeping only what actually made it narrower.

Specimen 1 A block with a fixed width of 900px. The ordinary case.

Some ordinary prose above it.

And some below.

Ready.

Specimen 2 The same column, overflowing by a similar amount. This time every element fits.

Provenance line, of the kind this site prints under a figure:

source SHA-256 45ea884f4b47ea8f96e9a5a32c764a65b35525277aab40272b1bbff4a4a75af5

Nothing else unusual.

Ready.

Specimen 3 Two hashes, stacked. In a monospace font they are exactly the same width, so each one alone is enough, and neither shows up when you remove it.

45ea884f4b47ea8f96e9a5a32c764a65b35525277aab40272b1bbff4a4a75af5

1f8a660589ad181575b622e671cdc8bc6cdcefed617c52314ba982080d71c1de

Ready.

Why the recipe cannot see specimen 2

Because a bounding rectangle describes a box, and CSS lets ink land outside the box. The paragraph holding that hash is exactly as wide as its column. Its rectangle fits. What does not fit is its content: scrollWidth is far larger than clientWidth, overflow-x is visible, so the characters are painted straight out past the edge of the element that contains them. Ask every element how wide it is and every element answers honestly, and the answer is not the question you needed.

There are two more cases a rectangle sweep cannot reach. A bare text node has no element and therefore no rectangle at all; you have to put a Range around it to measure it. And a pseudo-element is not in the DOM you are walking.

Ablation does not care which of these it is, and that is the whole of its advantage. It never asks how wide anything is. It removes something, measures the document, and puts it back. If the document got narrower, that thing is part of the reason it was wide. That is not a heuristic about widths; it is the definition of the thing being looked for.

One pass is not an account

Remove-one-at-a-time has a failure of its own, which specimen 3 is built to show. Those two hashes are the same length, so in a monospace font they are exactly the same width. Remove either one and the box does not get one pixel narrower, because the other is still holding it open. A single remove-one pass therefore concludes that neither is to blame, which is the correct answer to the question it asked and the wrong answer to the question you have. Ablation gets past it by hiding every candidate and then revealing them one at a time against that empty background, which tests each one on its own rather than in the presence of its rivals. Watch the readout say so.

The same problem shows up along the other axis, in sequence rather than in parallel: a small cause standing behind a large one is invisible until the large one is gone. So the search runs in rounds, keeps what it has found hidden, and looks again at what is left. Then it hides everything it found at once and reports what is still unaccounted for, so that a partial answer is never mistaken for a complete one. On the real page described below, the hash accounted for 98 of 111 pixels and the remaining 13 were simply standing in its shadow.

What happened when both were run over this whole site

On 31 August 2026 a real browser was driven at a 390-pixel viewport over every built page of this site: 1,032 pages, three OG card templates excluded because they exist to be photographed at 1200 pixels. Nothing in the build chain had ever looked.

38 pages were wider than the phone they were being read on. They split into two defects that a width measurement alone cannot tell apart:

What the reader getsPages
the document scrolls sideways, usually to blank space29
content is clipped off instead: the page looks fine and part of it is unreachable9

The second group sets overflow-x: hidden on the body, which propagates to the viewport. Nothing scrolls and something is simply gone. A check that measures scrollWidth and stops has been counting both of those as one defect.

How often the recipe would have come up empty

Of the 38 broken pagesCount
the widest unclipped element fits inside the viewport: the recipe finds nothing at all11
the widest rectangle under-reads the document's real width14
fully attributed by ablation, residual zero37

The page that prompted all this was already known to be broken. Someone had measured it at 505 pixels wide in a 390 window, hunted for the cause, and stopped honestly at "the widest element outside any scroller measures only 392px", with 115 pixels unaccounted for. Ablation named it on the first run: a paragraph of provenance carrying a 64-character SHA-256 with overflow-wrap: normal, its content 483 pixels wide inside a 353-pixel box. The document goes 501, then 403 once that paragraph is hidden, then 390 once the three figures behind it are. Residual zero. That is the same specimen you ran above.

The defect turned out to be structural, not careless

36 of the 38 were standalone pages. The two that were not were both in the clipped group, held by a rule they inherit and the others do not. This site's Astro-rendered pages load a stylesheet that gives them overflow-x: hidden on the body and a horizontal scroller for wide tables. A standalone page in public/ never loads it. The same stylesheet also ships white-space: nowrap on the first cell of a table, next to the rule that rescues it, and the hazard travels by copy-paste into pages that have no .prose to rescue them. The defect tracked the stylesheet, not the author.

The fix, and what measuring it changed

The obvious guard is a blanket rule making every table on a narrow screen scroll inside its own box. Written, applied to the whole corpus and then measured, it fixed 25 pages and restyled 52 that had no problem, one of them by 29 per cent of its height, purely because a rule aimed at a defect it did not have reached it anyway. So the guard that shipped measures first: if the document already fits, it touches nothing.

Two more corrections came out of measuring rather than reasoning. Detection has to compare a table to its parent, not to the viewport, because a 382-pixel table in a 350-pixel column on a 390-pixel screen is real horizontal scroll and is not wider than the screen. And max-width: 100% is not always tighter than what the author already wrote: on one page it won the cascade against a deliberate 240-pixel cap, relaxed it, and pushed a page that had fit 74 pixels over. The rule now carries zero specificity, so anything the page itself says beats it.

Measured the same way, before and afterBeforeAfter
pages wider than a 390px viewport3815
…the reader can scroll2913
…content clipped off92
total overflow across the corpus3,730px1,449px
pages newly broken by the guardn/a0

23 pages fixed, none broken. Fifteen remain, each with a proven cause recorded, and shrinking that list is somebody's next evening.

The control that stopped this page making a false claim

Every page's height was recorded before and after, to catch a guard quietly rearranging pages it was not aimed at. 36 pages changed height. Then the same code was run twice and the two runs compared: 10 of those 36 do not survive it. The two largest apparent regressions, one of them 717 pixels, resolve to exactly zero. They are instrument pages whose height depends on what they happen to draw. Without that control this page would be reporting two regressions that do not exist. The 26 real changes top out at 150 pixels, 2.50 per cent of a page: a long word now breaks onto a second line, on lines that were already sticking out of their column.

The page that scrolls to nothing

One of the 38 is not accounted for, and it is the most interesting of them. The document scrolls 225 pixels, confirmed two independent ways. The strip it scrolls to is empty: ask the browser what is at that coordinate after scrolling and it names the root element, which is to say nothing. The body's own scrollWidth is 390 throughout, so nothing inside the body overflows. The page's own table scroller is working exactly as its author built it. Hiding the table it holds takes the document from 615 back to 390 anyway.

A real defect a reader can feel, on a page with no element too wide, no failing scroller, and no mechanism this run could name. It is recorded as an open question rather than closed with a plausible story. It is also the strongest case for ablation there is, since the recipe that looks for a wide element has, here, nothing whatever to look at.

The check

Everything above is reproducible offline from a fresh checkout. The verifier makes 60 assertions in three kinds, and the third kind is the one that matters: the central claim is re-derived in a browser on specimens built for it, with no reference to this site's corpus at all. It runs both detectors on a page carrying a long hash and requires that the recipe reports a widest right edge no larger than the viewport while the document is measurably wider, and that ablation then finds the cause and accounts for all of it. If every number on this page were wrong, that check would still stand.

node research/nothing-on-this-page-is-too-wide/verify.mjs

The gate that keeps it true is scripts/check-mobile-overflow.mjs, now in the build chain. Its --selftest proves it can go red, on the three causes the census actually found, while staying green on wide content correctly held inside a scroller. The full lab record, including three ways the instrument was wrong before it was right, is in research/nothing-on-this-page-is-too-wide/.

What this does not show