D
P
0
← All articles Baca dalam Bahasa Indonesia

Elementor: Under the Page Builder's Hood

683px of Overflow Appears After Removing `body { overflow-x: hidden }`? A Flex Cell With No `width`

· · 6 min read
683px of Overflow Appears After Removing `body { overflow-x: hidden }`? A Flex Cell With No `width`

The client's ask was a single sentence: build the site fully in Elementor, without any hacks. What gave that sentence teeth was the list of things that ended up counting as a hack. The Custom CSS block in page settings counted, because that is how the client's requirement read, and an identical block sat on all sixteen pages.

Before it came out, the block looked harmless enough. Every rule inside it was scoped as .elementor-<page-id> .elementor-element-<element-id>, and after the legacy template was applied, the element IDs on the other 15 pages had changed. So on one exception page the hack did work and the visual was correct, while on the remaining 15 the rules were still stored in page settings but rendered nothing at all. Dead code along for the ride.

One part did not follow that pattern. On lines 13 to 15 of the same block sat an overflow rule that named no element ID:

html, body {
  overflow-x: hidden;
}

The project notes flagged this part differently from its neighbors: it is site-wide, so it cannot be handled per page. On 18 May the custom_css block was stripped from the 15 non-exception pages, and close to 29 thousand characters went with it.

One page out of fifteen

After that I swept the fifteen stripped pages again at a 1440px viewport. Fourteen came back clean. One did not.

The page that was not clean had 683px of horizontal overflow. That means the document measured 1440 + 683 = 2123px while the window stayed at 1440, and anyone opening it could drag the page sideways by 683 pixels.

The cell that was never given a width

The culprit was one flex cell in that page's three-pill row. It had no explicit width, and it had no _flex_size: none either. Its two siblings in the same row had widths of 377 and 339. The data looked roughly like this before the fix:

[
  { "settings": { "width": { "unit": "px", "size": 377 } } },
  { "settings": { "width": { "unit": "px", "size": 339 } } },
  { "settings": { } }
]

In Elementor, flex children have no auto-sizing you can lean on. By default they stretch to full width. So the third cell expanded until it filled its parent's inner width, 1182px. The arithmetic is straightforward and anyone can repeat it: the 1440 viewport minus 129 of padding on the left and 129 on the right, so 1440 - 258 = 1182.

The source notes read this as a latent layout bug that overflow-x: hidden had been quietly masking the whole time, meaning the bug predated the strip by a long while. I am writing that down as a reading, not a verdict. What is actually measured is this: once the block was gone, that page suddenly carried 683px of overflow at 1440, while fourteen other pages stripped the exact same way did not.

The fix uses native controls

What ended up on the broken cell was two settings: a width of 480 and _flex_size: none.

{
  "width": { "unit": "px", "size": 480 },
  "_flex_size": "none"
}

_flex_size: none is not stray CSS smuggled in from outside. It is a native Elementor control, sitting under Advanced then Flex then Size then None, and its job is to stop a flex child from shrinking. On a project whose requirement is no hacks, that difference matters: the fix plants no new debt in a spot that was just cleaned out.

The rule I took from this is short. Every flex child needs one of two things, an explicit width or _flex_size: 'none'. Either one is enough; what is not allowed is having neither. The broken cell had neither, and its two siblings had one.

The same symptom was already on record twice

That cell was not the first appearance of this behavior, and the project's recipe notes already held two earlier cases.

In another section's pill row, the default content_width: full made each pill stretch across the full row width until they stacked vertically. The fix was an explicit width per pill, 260, 440, 360, and 290, following the pattern of the pills in the very row this case came from, which use 298. A later note forbids leaving that same row's pill containers at the default content_width: full, because the result is equal distribution across the row. Widths get set per pill from the design instead, the first three narrower at 244 and the last one 308 when its text runs longer. Without explicit widths, pills auto-stretch and the text does not wrap right.

The mirror image of the same behavior once went after icons. Pill icons rendered at 11 to 17 by 20 pixels instead of 20 by 20, because the SVGs carried a 20 by 20 viewBox with no width or height attributes, so the img width collapsed on its own, and then as a flex child it got shrunk further whenever the neighboring heading ran long. A global script fixed 385 icons across 9 pages on the same date as the custom_css strip, 18 May. Stretching and shrinking are two directions of one behavior, and the same setting stops both.

The check that now runs after any overflow rule is removed

The process rule that came out of this: after removing any overflow-x: hidden style, sweep every page with a document.scrollWidth check through a headless browser at the target viewport, so whatever was being hidden comes out. That rule went into the project's DO NOT list, with this cell as its worked example.

The check itself is simple. Puppeteer opens the page and compares document.documentElement.scrollWidth against the viewport width at 1440:

const pg = await browser.newPage();
await pg.setViewport({ width: 1440, height: 900 });
await pg.goto(pageUrl, { waitUntil: 'networkidle0' });
 
const scrollWidth = await pg.evaluate(() => document.documentElement.scrollWidth);
console.log(scrollWidth === 1440 ? 'clean' : `overflow ${scrollWidth - 1440}px`);

The script this project runs uses puppeteer-core with a browser already installed on the machine as its executable, a 1440 by 900 viewport, and pages served from a local server. Once that cell was fixed, all sixteen pages were confirmed to have no horizontal overflow.

What I took from it

A site-wide rule installed to tidy up a symptom keeps working quietly until somebody pulls it out, and what surfaces at that moment is usually not a new bug but an old bill coming due. Which is why pulling it out has to be followed by a measurement rather than a glance.

What I am not claiming: that the overflow-x: hidden rule is now definitely unnecessary. It was only verified at 1440, and smaller viewports may still need it. The rule has not actually left either, since it still rides inside the custom_css block remaining on the one exception page. Right after the strip that remainder stood at 1927 characters, down from a total of 30,832, so what genuinely disappeared at that point was 30,832 - 1927 = 28,905 characters.

The rest is still open. The page that broke now records zero HTML widgets and zero custom_css characters, but it still holds 47 inline style attributes, and across all sixteen pages the total is 549, higher than the earlier estimate. Whether those inline attributes count as a hack or as something Elementor is allowed to do is not my call to make; it waits on the client to clarify.