#301: the real culprit was the jump nav, not the wide content

The first attempt fixed plausible suspects. CI measured the actual page
and named something else: six `.settings-nav__link` buttons, 417px of
page-level overflow at 320px.

`.settings-nav` already had `overflow-x: auto`, but as a flex child it
also had the default `min-width: auto` — the min-content width of the
whole jump strip. That forced the column wider than the viewport, so its
own overflow rule never had anything to scroll. `min-width: 0` is exactly
the case CLAUDE.md warns about under Reflow.

The guard now ignores elements that sit inside a scroll container. Such
content is *meant* to be wider than the viewport — reporting it buried
the one finding that mattered under twelve lines of noise, and the cap
truncated the list before it could show anything else.

The table wrapper and the wrapping settings rows from the first commit
stay. Neither was the cause here, but a table cannot shrink below its
min-content width and those rows cannot wrap on their own, so both are
hardening that holds regardless of content.
This commit is contained in:
Claude Opus 5 2026-08-01 07:37:05 +02:00 committed by Claude Fable 5
parent f938ee9880
commit 18c2ed0bfe
2 changed files with 19 additions and 2 deletions

View File

@ -120,11 +120,22 @@ async function expectNoHorizontalScroll(page: Page, label: string): Promise<void
const report = await page.evaluate(() => {
const doc = document.documentElement;
const limit = doc.clientWidth;
/** Content inside its own scroll container is allowed to be wider than
* the viewport that is the prescribed remedy, not the defect. Only
* what pushes the PAGE is a finding. */
const insideScroller = (el: HTMLElement): boolean => {
for (let node = el.parentElement; node && node !== doc; node = node.parentElement) {
const overflowX = getComputedStyle(node).overflowX;
if (overflowX === 'auto' || overflowX === 'scroll' || overflowX === 'hidden') return true;
}
return false;
};
const offenders: string[] = [];
for (const el of Array.from(document.querySelectorAll<HTMLElement>('body *'))) {
const rect = el.getBoundingClientRect();
// 1 px Toleranz gegen Subpixel-Rundung.
if (rect.width > 0 && rect.right > limit + 1) {
if (rect.width > 0 && rect.right > limit + 1 && !insideScroller(el)) {
const cls =
el.className && typeof el.className === 'string'
? `.${el.className.trim().split(/\s+/).join('.')}`
@ -134,7 +145,7 @@ async function expectNoHorizontalScroll(page: Page, label: string): Promise<void
);
}
}
return { scrollWidth: doc.scrollWidth, clientWidth: limit, offenders: offenders.slice(0, 12) };
return { scrollWidth: doc.scrollWidth, clientWidth: limit, offenders: offenders.slice(0, 20) };
});
expect(
{ overflowBy: report.scrollWidth - report.clientWidth, offenders: report.offenders },

View File

@ -4014,6 +4014,12 @@ ul[data-type='task_list'] li p:last-of-type {
position: static;
width: 100%;
overflow-x: auto;
/* THE reflow fix (issue #301). As a flex child the nav defaults to
`min-width: auto`, i.e. the min-content width of the whole jump
strip so it pushed the column wider than the viewport and its own
`overflow-x: auto` never got the chance to scroll anything. Measured
at 320px: 417px of page-level overflow, all of it these links. */
min-width: 0;
}
.settings-nav ul {