#301: sort the reflow report so the culprit cannot be buried
Some checks failed
CI / Build container images (pull_request) Successful in 1m13s
CI / Auth e2e pack (pull_request) Failing after 8m18s
CI / Import/export fidelity gate (pull_request) Has been skipped
CI / Lint, typecheck, test (pull_request) Successful in 6m23s

CI still reports 737 while the local stack is now clean, and the box list
was capped at 15 entries — all of them nav links clipped by their own
scroller. Whatever pushes the page in CI sits past that cap.

The list is now sorted by reach, marks each entry as either clipped by a
fitting scroller or actually pushing the page, and shows 40.
This commit is contained in:
Claude Opus 5 2026-08-01 11:15:36 +02:00
parent 27038a1f27
commit 0420f97c42

View File

@ -141,20 +141,36 @@ async function expectNoHorizontalScroll(page: Page, label: string): Promise<void
}
}
// Raw: every box reaching past the viewport, no filtering at all.
const past: string[] = [];
for (const el of Array.from(document.querySelectorAll('body *'))) {
const rect = el.getBoundingClientRect();
if (rect.width > 0 && rect.right > limit + 1) {
past.push(`${describe(el)} right=${Math.round(rect.right)} w=${Math.round(rect.width)}`);
/** Content inside a scroll container may exceed the viewport that is
* the remedy. But only when the CONTAINER fits: a scroller that is
* itself too wide still pushes the page. */
const insideFittingScroller = (el: Element): boolean => {
for (let node = el.parentElement; node && node !== doc; node = node.parentElement) {
const ox = getComputedStyle(node).overflowX;
if (ox === 'auto' || ox === 'scroll' || ox === 'hidden') {
return node.getBoundingClientRect().right <= limit + 1;
}
}
}
return false;
};
// Widest reach first, so a long tail of clipped children cannot bury the
// one box that actually pushes the page.
const past = Array.from(document.querySelectorAll('body *'))
.map((el) => ({ el, rect: el.getBoundingClientRect() }))
.filter(({ rect }) => rect.width > 0 && rect.right > limit + 1)
.sort((a, b) => b.rect.right - a.rect.right)
.map(
({ el, rect }) =>
`${describe(el)} right=${Math.round(rect.right)} w=${Math.round(rect.width)}` +
`${insideFittingScroller(el) ? ' [in fitting scroller]' : ' <-- pushes page'}`,
);
return {
overflowBy: doc.scrollWidth - limit,
viewport: `html client=${limit} scroll=${doc.scrollWidth} | body client=${document.body.clientWidth} scroll=${document.body.scrollWidth} rect=${Math.round(document.body.getBoundingClientRect().width)}`,
overflowing: overflowing.slice(0, 15),
past: past.slice(0, 15),
past: past.slice(0, 40),
};
});
const diagnosis = [