Skip to story

Css Layout

The One Pixel That Broke the Layout

9 min read · May 31, 2026 ★ Flagship

Reading level

The invisible scroll

You deploy your site. On mobile, there's a horizontal scrollbar. The page can scroll right by about 15 pixels — revealing nothing but empty white space. You can't find what's causing it. You inspect every element. You add overflow: hidden to body. The scrollbar disappears — but something breaks on another page. There's a better way.

Horizontal overflow is almost always caused by an element that is wider than the viewport. The challenge is finding which element — browser DevTools don't highlight overflowing elements by default. A one-line JavaScript snippet and a few CSS patterns will find it in under a minute.

Horizontal overflow is a CLS-adjacent issue — it causes visual instability and breaks mobile UX. The correct fix is eliminating the overflowing element, not hiding overflow at the body level (which breaks position: sticky among other things).

Common overflow culprits

  • Fixed-width elementswidth: 400px on a 375px viewport
  • Long unbreakable strings — URLs, code, emails without word-break: break-all
  • Negative marginsmargin-left: -20px can push content right
  • Absolute-positioned elements — positioned relative to a container but visually wider
  • SVGs without viewBox — SVGs have intrinsic dimensions; without viewBox they ignore container width

The debugger snippet — paste in DevTools console:

// Find all elements wider than the viewport
document.querySelectorAll('*').forEach(el => {
  if (el.getBoundingClientRect().right > window.innerWidth) {
    console.log(el, el.getBoundingClientRect().right - window.innerWidth, 'px overflow');
  }
});

This logs every element whose right edge exceeds the viewport — sorted by how much they overflow.

The CSS audit approach — add to DevTools styles:

/* Temporary audit: outline every element */
* { outline: 1px solid red; }

/* Or: find elements with max-width issues */
* { max-width: 100% !important; box-sizing: border-box !important; }

The fix hierarchy

/* ❌ Nuclear option — breaks position: sticky */
body { overflow-x: hidden; }

/* ✅ Fix the element instead */
.hero-image { max-width: 100%; }
.code-block { overflow-x: auto; word-break: break-all; }
.full-bleed { width: 100vw; margin-left: calc(-1 * var(--page-pad)); }

/* ✅ Safe container default */
*, *::before, *::after { box-sizing: border-box; }
img, video, canvas { max-width: 100%; }

The nuclear option (overflow-x: hidden on html or body) silently breaks position: sticky — a very common gotcha.

The real culprit is often a flex or grid child that refuses to shrink. Add min-width: 0 to flex children — by default they have min-width: auto which prevents shrinking below content size.

/* ❌ flex child refuses to shrink, overflows container */
.flex-container { display: flex; }
.flex-child { } /* min-width: auto by default */

/* ✅ Allow shrinking */
.flex-child { min-width: 0; overflow: hidden; }

min-width: 0 on flex/grid children is a CSS spec quirk: the default min-width: auto means "at least as wide as the content." Combined with flex: 1, the element can still overflow if content is wider than the available space. This is per-spec behavior, not a bug — but it surprises nearly every developer the first time.

The 15-pixel phantom

A marketing team launched a landing page that passed every browser test: Chrome, Firefox, Safari desktop, and simulated mobile in DevTools. Real users on iOS Safari reported a thin strip of horizontal scroll. The page could scroll 15 pixels to the right revealing nothing but empty space. QA could not reproduce it on desktop simulators. The culprit was a single declaration on the hero section: width: 100vw.

On desktop browsers where the scrollbar overlays the viewport, 100vw and 100% are the same width. On iOS Safari, 100vw includes the scrollbar gutter even though iOS uses overlay scrollbars — making the hero 15px wider than the actual viewport and creating exactly enough horizontal overflow to scroll without showing any meaningful content.

The pattern recurs across many projects. Other common sources of the same invisible overflow: a utility class applying margin-inline: -1rem on a full-width element, a translateX() value that pushes decorative content past the viewport edge without a clipping ancestor, or an SVG without a viewBox that renders at its intrinsic pixel dimensions. All produce the same symptom — a thin, useless horizontal scroll that only appears on real mobile devices.

Pattern at a glance

Before — 100vw includes scrollbar gutter on iOS Safari, causes overflow
/* ❌ 100vw = viewport width including scrollbar gutter
   On iOS Safari this is wider than the actual content area */
.hero {
  width: 100vw;       /* 390px on iPhone 14 Pro = overflow */
  background: #0066ff;
  padding: 80px 24px;
}

/* Result: hero is ~15px wider than viewport
   Horizontal scroll appears; page slides right to white */
After — 100% stays within the parent (no scrollbar gutter issue)
/* ✅ 100% = 100% of the parent block, never includes gutter */
.hero {
  width: 100%;        /* always fits the content area */
  background: #0066ff;
  padding: 80px 24px;
}

/* For full-bleed sections inside a max-width container: */
.full-bleed {
  width: 100%;
  margin-inline: calc(-1 * var(--page-padding));
  padding-inline: var(--page-padding);
}

/* Global safety net */
*, *::before, *::after { box-sizing: border-box; }
img, video, svg { max-width: 100%; }

Try it: overflow culprit finder

Toggle between "Broken" (an element wider than its container) and "Fixed" (with max-width: 100% and box-sizing applied correctly).

Notice how a single element wider than the viewport causes the entire page horizontal scroll — even if the viewport is much larger on desktop.

The fixed state uses max-width: 100% on the image and min-width: 0 on the flex child — two properties that together handle 95% of horizontal overflow cases.

⚡ Interactive demo

Implementation depth

100vw vs 100% is the most frequent source of horizontal overflow on mobile. The difference: 100vw is the viewport width including the scrollbar gutter on platforms where the scrollbar is not overlaid. 100% is the width of the containing block, which excludes the scrollbar. On desktop Chrome/Firefox with persistent scrollbars, 100vw makes elements 15–17px wider than their container. On iOS Safari with overlay scrollbars, 100vw still reports the full viewport width despite the scrollbar being overlaid, producing the same overflow on mobile.

// Browser console: find the overflowing element in any page
document.querySelectorAll('*').forEach(el => {
  const rect = el.getBoundingClientRect();
  if (rect.right > window.innerWidth) {
    console.warn(
      el.tagName, el.className,
      `overflows by ${Math.round(rect.right - window.innerWidth)}px`
    );
  }
});

Overflow debugging toolkit and prevention patterns:

  • overflow-x: clip vs hiddenclip prevents scrolling without establishing a new block formatting context, so it does not break position: sticky. Prefer clip over hidden when you need to clip decorative content at the viewport edge without affecting sticky headers.
  • overscroll-behavior-x: none — prevents horizontal elastic overscroll on iOS but does not prevent actual overflow. Use alongside fixing the overflowing element, not as a substitute.
  • CSS audit: * { outline: 1px solid red } — pasting this into DevTools Styles instantly makes every element's box visible. The element whose box extends past the viewport edge is the culprit.
  • Visual regression CI — a Playwright test that checks document.body.scrollWidth > document.body.clientWidth on every page catches horizontal overflow before it reaches production. One assertion prevents the entire class of bug.
  • Flex children: min-width: 0 — flex items have min-width: auto by default, preventing shrinkage below content width. Adding min-width: 0 to flex children allows them to shrink as expected and resolves a large proportion of flex-related overflow bugs.

References

Remember

Key takeaways

  • Don't add overflow-x: hidden to body — it breaks position: sticky. Find and fix the overflowing element instead.
    The console snippet (check getBoundingClientRect().right > innerWidth) finds the offending element in seconds on any page.
    min-width: 0 on flex/grid children is the spec-correct fix for elements that refuse to shrink below their content size.
  • Three CSS defaults that prevent most horizontal overflow: box-sizing: border-box, max-width: 100% on images, min-width: 0 on flex children.
    CSS resets like modern-normalize.css include these defaults — using one prevents 80% of horizontal overflow bugs from ever appearing.
    Include a horizontal overflow audit script in your visual regression CI — one line of JS can catch this before it ships to users.

Enjoyed this case?

Case 1 of 2 in Css Layout · 11 of 31 live

Keep going

Finish this takeaway, then continue the track — Casey saved your spot locally.

Sign in with email to sync progress across devices (beta).

Inside the Casebook

New cases every few weeks — patterns from production UI engineering. Double opt-in, easy unsubscribe.

No spam. Unsubscribe anytime. Emails sent via Buttondown.

RSS feed
Casey, junior (idle)
Casey · Junior

Hey! I'm Casey — scroll through the case and I'll chime in with hints.