From fdfce6495ce2fb9d7322deb26a73937d4f155105 Mon Sep 17 00:00:00 2001 From: Dmitry Verkhoturov Date: Tue, 18 Aug 2026 22:33:44 +0100 Subject: [PATCH] Remove the widget body padding and the surplus reported height Previously the widget document had `padding: 6px` on the body, so every embedded widget sat 6px inside its container and could not align flush with the host layout. `updateIframeHeight` then reported `document.body.offsetHeight + 12`, but the body is `box-sizing: border-box` and `offsetHeight` already includes padding, so the addition double-counted it. Measured against the deployed widget: the content needs 20610px, the body reported 20622px with the padding, and the parent was told 20634px, leaving 24px of empty space below every embed on top of the horizontal inset. Removing the padding does not clip anything. With it at zero, offsetHeight, body scrollHeight and documentElement scrollHeight all agree, and the last child carries no bottom margin, so no margin collapses through the body edge. Resolves #1487. --- frontend/apps/remark42/app/styles/global.css | 2 +- .../remark42/app/utils/post-message.spec.ts | 54 +++++++++++++++++++ .../apps/remark42/app/utils/post-message.ts | 4 +- 3 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 frontend/apps/remark42/app/utils/post-message.spec.ts diff --git a/frontend/apps/remark42/app/styles/global.css b/frontend/apps/remark42/app/styles/global.css index d3cfd400..2b548833 100644 --- a/frontend/apps/remark42/app/styles/global.css +++ b/frontend/apps/remark42/app/styles/global.css @@ -2,7 +2,7 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; margin: 0; - padding: 6px; + padding: 0; font-family: system-ui; font-size: 14px; color: rgb(var(--primary-text-color)); diff --git a/frontend/apps/remark42/app/utils/post-message.spec.ts b/frontend/apps/remark42/app/utils/post-message.spec.ts new file mode 100644 index 00000000..9d200011 --- /dev/null +++ b/frontend/apps/remark42/app/utils/post-message.spec.ts @@ -0,0 +1,54 @@ +import { updateIframeHeight } from './post-message'; + +describe('updateIframeHeight', () => { + const postMessage = jest.fn(); + let originalParent: Window; + + beforeAll(() => { + originalParent = window.parent; + // postMessageToParent bails out when window.parent is window, which is the case in jsdom + Object.defineProperty(window, 'parent', { value: { postMessage }, writable: true, configurable: true }); + }); + + afterAll(() => { + Object.defineProperty(window, 'parent', { value: originalParent, writable: true, configurable: true }); + }); + + beforeEach(() => { + postMessage.mockClear(); + jest.spyOn(document.body, 'offsetHeight', 'get').mockReturnValue(500); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should report the document height without adding to it', () => { + updateIframeHeight(); + + expect(postMessage).toHaveBeenCalledWith({ height: 500 }, '*'); + }); + + it('should report the dropdown height when it exceeds the document height', () => { + const dropdown = document.createElement('div'); + + jest.spyOn(dropdown, 'getBoundingClientRect').mockReturnValue({ top: 100 } as DOMRect); + jest.spyOn(dropdown, 'scrollHeight', 'get').mockReturnValue(600); + + updateIframeHeight(dropdown); + + // 20px allowance for the shadow under the dropdown + expect(postMessage).toHaveBeenCalledWith({ height: 720 }, '*'); + }); + + it('should report the document height when it exceeds the dropdown height', () => { + const dropdown = document.createElement('div'); + + jest.spyOn(dropdown, 'getBoundingClientRect').mockReturnValue({ top: 10 } as DOMRect); + jest.spyOn(dropdown, 'scrollHeight', 'get').mockReturnValue(50); + + updateIframeHeight(dropdown); + + expect(postMessage).toHaveBeenCalledWith({ height: 500 }, '*'); + }); +}); diff --git a/frontend/apps/remark42/app/utils/post-message.ts b/frontend/apps/remark42/app/utils/post-message.ts index 3ec62ad3..bee61e21 100644 --- a/frontend/apps/remark42/app/utils/post-message.ts +++ b/frontend/apps/remark42/app/utils/post-message.ts @@ -78,8 +78,8 @@ export function updateIframeHeight(dropdown?: HTMLElement) { scrollHeight = window.scrollY + Math.abs(top) + dropdown.scrollHeight + 20; } - // The size of vertical padding on body is 12px - const bodyHeight = document.body.offsetHeight + 12; + // offsetHeight already covers padding and border, so it is the full document height + const bodyHeight = document.body.offsetHeight; postMessageToParent({ height: Math.max(scrollHeight, bodyHeight) }); }