Skip to content

fix(render): measure page height from content, not the body box - #131

Merged
andylizf merged 1 commit into
mainfrom
fix/page-height-viewport-bounded-body
Jul 31, 2026
Merged

fix(render): measure page height from content, not the body box#131
andylizf merged 1 commit into
mainfrom
fix/page-height-viewport-bounded-body

Conversation

@andylizf

Copy link
Copy Markdown
Contributor

What breaks

pixelshot clamps the measured page height to body.getBoundingClientRect().bottom.
That bound holds only when the page lets the body size to its content. Wikipedia's
Vector 2022 skin pins html, body to height: 100% and lets the article overflow
visibly, so a 19,951px page measures 1,568px and captures one tile — with
complete: true set regardless (#124).

The same expression also reads a viewport-relative rect as a document coordinate.
A URL with a #fragment loads already scrolled to the anchor, where the bottom edge
is negative and Math.max(bottom, 1) floors the entire page to 1px.

Fix

Measure the lowest edge among the body and its element children, and add the scroll
offset back. On a self-sizing body this reads the same as before, so the clamp still
drops the blank tail an inflated documentElement.scrollHeight would otherwise buy
(root padding, trailing margin).

Both capture paths carried their own copy of the probe. The snippet now lives in
page_metrics.py so they can't drift.

Verification

Standard and turbo probes, stock Chrome and the patched headless_shell:

page before after
wikipedia (#124), standard 1568 19951
wikipedia (#124), turbo 1553 19679
wikipedia #Type_systems 1 19951
simonwillison.net 18252 18252
ourworldindata.org 8713 8713
root padding (clamp case) 26 26

The 1553 is the number from the issue report — that path needs the patched
headless_shell, which is linux-x64 only.

tests/test_render.py gains a regression case: a page whose body is pinned to the
viewport must tile in full.

Closes #124

pixelshot clamped the page height to `body.getBoundingClientRect().bottom`,
which only bounds the document when the page lets the body size to its
content. Sites that pin it to the viewport — `html, body { height: 100% }`,
as Wikipedia's Vector 2022 skin does — leave the content overflowing a
one-viewport box, so a 19,951px article measured 1,568px and captured a
single tile with `complete: true` (issue #124).

The rect was also read as a document coordinate while being
viewport-relative. A URL with a `#fragment` loads already scrolled, where
the bottom edge is negative and `Math.max(bottom, 1)` floors the whole page
to 1px.

Measure the lowest edge among the body and its element children instead, and
add the scroll offset back. That reads the same as before on a self-sizing
body, so the clamp still drops the blank tail an inflated
`documentElement.scrollHeight` would otherwise buy (root padding, trailing
margin), while surviving a pinned body.

Both capture paths carried their own copy of the probe; the snippet now
lives in one module so they can't drift apart.

Verified in stock Chrome, standard and turbo probes agreeing on every page:

    page                  before    after   scrollHeight
    wikipedia (#124)        1568    19951          19951
    wikipedia #fragment        1    19951          19951
    simonwillison.net      18252    18252          18252
    ourworldindata.org      8713     8713           8713

Closes #124
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
web Ready Ready Preview Jul 30, 2026 4:28pm

@andylizf

Copy link
Copy Markdown
Contributor Author

I verified the core #124 regression on rulin.

What I ran:

.venv/bin/python -m pytest tests/test_render.py::test_page_taller_than_a_viewport_bounded_body_is_fully_tiled -q --tb=short

Result: 1 passed in 4.37s.

That test exercises the important failure mode: html, body { height: 100% } with visible overflowing content. It asserts the measured page_height is above one viewport and that multiple tiles are written, so the old “one tile with complete: true” truncation is covered.

One caveat: the full tests/ run through collect_facts.sh hit its 300s timeout on rulin, so I do not have a complete full-suite result for this PR. The targeted regression is verified; the broad test run remains incomplete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pixelshot reports page_height:1553 on 18,609px Wikipedia page — returns only 1 tile

1 participant