the preview measures the page with its minimum height off

The site's shell is min-h-dvh so a short page puts its footer at the bottom of the
window. Measuring it then asks the frame how tall the frame is, and the answer is
whatever it already was.

So the minimum is neutralised by a stylesheet injected into the frame, and what is
left is the height of what is on the page.

The cost, stated: the preview does not show a footer pushed to the bottom of a
window, because it is not a window. It shows the page ending where it ends.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-02 16:23:05 +01:00
co-authored by Claude Opus 5
parent ab049f5ddc
commit 6098ae2285
+24 -7
View File
@@ -271,22 +271,36 @@ export const SitePreview = ({
measure guides running down through it. Nothing was broken; the frame was simply taller than the
document, and an iframe has no `height: fit-content`.
── The WRAPPER, not the body ──
── The WRAPPER, with its minimum height taken off ──
The site's `<body>` is `min-h-screen`, so its height is the FRAME's height and measuring it would
ask the frame how tall the frame is. Its one child is the page — header, main, footer — and that is
a real content height. It stopped being `min-h-dvh` on the same day this changed, for the same
reason: the stretch belonged to nothing anybody could see or set.
The site's `<body>` is `min-h-screen` and the page shell inside it is `min-h-dvh`, so measuring
either asks the frame how tall the frame is and the answer is whatever it already was. Both are
right on a real page — a short one should put its footer at the bottom of the window — and both
make this circular.
So the minimum is neutralised in the frame before measuring, by a stylesheet this injects. What is
left is the height of what is actually on the page, which is what the panel should be.
The cost, stated: the preview does not show the footer pushed to the bottom of a window, because it
is not a window. It shows the page ending where the page ends.
*/
const [contentHeight, setContentHeight] = useState(0);
useEffect(() => {
if (!loaded) return;
const frameDocument = frame.current?.contentDocument;
const page = frameDocument?.body.firstElementChild as HTMLElement | null;
const page = frameDocument?.querySelector<HTMLElement>('[data-page-shell]') ?? (frameDocument?.body.firstElementChild as HTMLElement | null);
const view = frameDocument?.defaultView;
if (!page || !view) return;
/*
Injected rather than set inline: `min-h-dvh` is a class, and an inline `min-height` would have to
win against it by specificity. A stylesheet of our own, last in the document, does.
*/
const relax = frameDocument!.createElement('style');
relax.textContent = '[data-page-shell]{min-height:0!important}body{min-height:0!important}';
frameDocument!.head.append(relax);
const measure = () => setContentHeight(Math.ceil(page.getBoundingClientRect().height));
measure();
@@ -299,7 +313,10 @@ export const SitePreview = ({
*/
const observer = new view.ResizeObserver(measure);
observer.observe(page);
return () => observer.disconnect();
return () => {
observer.disconnect();
relax.remove();
};
}, [loaded]);
/*