Why Your CSS Grid Breaks Only on iPhone: Fixing Layout Overlap in Mobile Safari

Meta Description: Debug CSS Grid layout overlap bugs in mobile Safari with real fixes for implicit rows, minmax, flex-basis conflicts, and viewport units.

Introduction

The first time I saw it, I genuinely thought I was losing my mind. A three-column CSS Grid layout that looked flawless in Chrome DevTools’ device emulator, tested clean in Firefox responsive mode, and then completely fell apart the moment a teammate opened it on an actual iPhone. Cards overlapping, text bleeding into the column next to it, images stacked on top of buttons.

I spent an embarrassing number of hours convinced it was a caching issue before I accepted the truth: mobile Safari has its own set of quirks with CSS Grid, and device emulators don’t always catch them because they’re rendering with the desktop engine, not WebKit’s actual mobile rendering path.

This guide walks through the real causes of CSS Grid overlap bugs specifically in mobile Safari, how I debug them on an actual device, and the fixes that have consistently worked for me across several production projects.

Why Does CSS Grid Overlap in Mobile Safari?

CSS Grid overlap in mobile Safari usually happens because of implicit row sizing defaulting to auto combined with content that doesn’t shrink (like fixed-width images or long unbreakable text), unsupported or inconsistently interpreted minmax() behavior, or dynamic viewport unit quirks unique to WebKit’s mobile engine. Unlike Chrome, Safari is stricter about how it resolves intrinsic sizing in grid tracks.

Why You Can’t Fully Trust Desktop DevTools for This

Before diving into fixes, I want to flag something that cost me real debugging time: Chrome’s “iPhone” device emulation in DevTools simulates the viewport size, but it still renders with Chrome’s Blink engine, not WebKit. Safari’s actual mobile rendering engine has different default behaviors around:

  • Implicit grid track sizing
  • How minmax() resolves against available space
  • Dynamic viewport units (dvh, svh, lvh) and even the older vh
  • Flexbox/Grid interaction when nested
  • overflow behavior on grid items with intrinsic content

In my experience, if a layout bug only reproduces on real iOS devices (or Safari’s Responsive Design Mode specifically, not Chrome’s emulator), it’s almost always one of these WebKit-specific quirks — not a “bug in your CSS” in the general sense.

Step 1: Reproduce It Properly

Before touching a single line of CSS, make sure you’re actually looking at the real bug. Here’s my checklist:

  1. Test on an actual iPhone if you have access to one — not just Chrome’s device toolbar.
  2. If you don’t have a physical device, use Safari’s Responsive Design Mode (Develop menu → Enter Responsive Design Mode) on a Mac, which uses the real WebKit engine.
  3. For remote debugging on an actual iPhone from a Mac: connect the device, enable Settings → Safari → Advanced → Web Inspector, then open Safari → Develop → [Your Device] → [Your Page] on the Mac. This gives you real WebKit DevTools attached to the real device render.
  4. Check whether the bug reproduces at specific breakpoints or across all sizes — grid overlap bugs are often breakpoint-specific.

Pro tip: BrowserStack and similar cloud device services are a solid fallback if you don’t own an iPhone, but I’ve occasionally seen subtle differences between their virtualized devices and a real physical phone for rendering edge cases like this. When a bug report seems highly specific to “one user’s iPhone,” don’t rule out testing on real hardware if you can borrow one.

Step 2: The Most Common Cause — Implicit Row Height with Unshrinkable Content

This is the bug I run into most often. Here’s a simplified version of a layout that broke for me:

.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

.card {
  background: #fff;
  border-radius: 8px;
  padding: 16px;
}

.card img {
  width: 100%;
}

On desktop, this rendered fine. On mobile Safari, cards with longer image alt content or slightly different intrinsic image dimensions caused rows to collapse into each other, with content from one row bleeding into the next.

The root cause: when grid rows use the default auto sizing and one row’s content doesn’t have a clearly resolved height early enough in Safari’s layout pass, WebKit can render before some content (particularly images without explicit dimensions) has finished being measured, and it doesn’t always trigger a clean re-layout the way Chrome does.

The fix that worked for me:

.card-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: minmax(min-content, auto);
  gap: 16px;
}

.card img {
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9; /* reserves space before the image loads */
  display: block;
}

Two changes matter here:

  1. grid-auto-rows: minmax(min-content, auto) gives Safari an explicit sizing strategy instead of relying purely on auto, which reduces the chance of a mis-measured row.
  2. aspect-ratio on the image reserves layout space before the image finishes loading, which prevents the exact “content shifts after paint” behavior that causes visible overlap on a slower mobile connection.

I learned the aspect-ratio fix the hard way after realizing the bug only happened on cellular connections and almost never on fast Wi-Fi in the office — a dead giveaway that image load timing was part of the problem.

Step 3: The minmax() Overflow Trap

Another one I’ve hit repeatedly: using minmax() with a fixed minimum that doesn’t account for padding, border, or gap, causing Safari to overflow the track and visually overlap adjacent columns.

/* Problematic */
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 12px;
}

On a narrow mobile viewport (say, 320px wide on an SE), minmax(250px, 1fr) combined with gap: 12px and any padding on the container can push the computed track size just past the available space. Chrome tends to be more forgiving about clipping the overflow quietly; in my testing, Safari is more likely to let the item visually overflow its track and overlap the next column.

Fix — always account for gap and padding, and add a safety clamp:

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr));
  gap: 12px;
  padding: 12px;
  box-sizing: border-box;
}

Wrapping the minimum in min(250px, 100%) tells the browser “never demand more than 100% of the available space,” which prevents the overflow-driven overlap entirely, even on very narrow viewports. This single change has fixed more mobile Safari grid bugs for me than anything else in this article.

Step 4: Dynamic Viewport Units (vh, dvh, svh) Causing Overlap

If your grid container height is set using vh and your layout includes Safari’s collapsing address bar, you’ve probably seen elements overlap or get cut off as the viewport height changes when the user scrolls.

/* Old approach — problematic on mobile Safari */
.page-grid {
  display: grid;
  height: 100vh;
  grid-template-rows: auto 1fr auto;
}

Safari’s 100vh historically included the space behind the collapsible browser chrome, so 100vh didn’t match the actually visible viewport, and elements pinned to the bottom row could overlap content or get clipped as the address bar showed/hid.

Fix — use dynamic viewport units with a fallback:

.page-grid {
  display: grid;
  height: 100vh; /* fallback for older browsers */
  height: 100dvh; /* dynamic viewport height, accounts for Safari's UI chrome */
  grid-template-rows: auto 1fr auto;
}

dvh is supported in Safari 15.4+, which covers the overwhelming majority of real-world traffic today, but always keep the 100vh fallback line above it since CSS gracefully ignores unsupported units and falls back to the last valid declaration.

Browser Compatibility Table

FeatureSafari iOSChrome AndroidFirefox AndroidNotes
display: gridFull supportFull supportFull supportCore Grid is solid everywhere
minmax()Supported, stricter overflow handlingSupported, more forgiving on overflowSupportedWrap with min() for safety
aspect-ratioSupported (15+)Full supportFull supportUse to prevent layout shift
dvh / svh / lvhSupported (15.4+)Supported (108+)Supported (101+)Always keep a vh fallback
gap in GridFull supportFull supportFull supportNot the usual culprit
subgridSupported (16+)Supported (117+)Full supportNewer, check target audience support

Debugging Workflow I Use Every Time

  1. Confirm the bug is real WebKit behavior using Safari’s Responsive Design Mode or a physical device — not Chrome’s emulator.
  2. Inspect computed grid track sizes in Safari Web Inspector’s Layout panel, which visually overlays grid lines on the page.
  3. Check whether any grid item has intrinsic content (unloaded images, long unbroken text/URLs, embedded iframes) that could be resolving late.
  4. Look for vh-based sizing anywhere in the affected component tree, even in a parent container the bug doesn’t seem directly related to.
  5. Check minmax() usage against your smallest supported viewport width, not just your design system’s “mobile” breakpoint.
  6. Isolate the component in a minimal reproduction — strip everything else away. This alone has solved several “impossible” bugs for me by revealing an unrelated parent style bleeding in.

Common Mistakes I’ve Seen (and Made Myself)

  • Trusting Chrome’s device emulator as equivalent to real Safari testing. It simulates screen size only, not the rendering engine.
  • Using 100vh for full-height mobile layouts without a dvh fallback.
  • Setting minmax() minimums without accounting for gap and padding on narrow viewports.
  • Not reserving space for images with aspect-ratio or explicit width/height attributes, which causes layout shift that can visually manifest as overlap during the load window.
  • Forgetting box-sizing: border-box, which changes how padding factors into the track’s available width calculation and can quietly cause overflow-driven overlap.
  • Debugging only in production build without source maps, making it painfully slow to trace which rule is actually responsible.

Performance Considerations

Fixing these overlap bugs often has a secondary performance benefit: reserving space with aspect-ratio and explicit sizing reduces Cumulative Layout Shift (CLS), which is a Core Web Vitals metric that directly affects Google’s page experience signals. In my experience, teams that fix Safari-specific grid overlap bugs frequently see a small but measurable CLS improvement as a side effect, simply because the fix forces more predictable, pre-reserved layout space.

Checklist for Fixing CSS Grid Overlap in Mobile Safari

  • [ ] Reproduce the bug in real Safari (device or Responsive Design Mode), not Chrome’s emulator
  • [ ] Check grid-auto-rows and grid-template-rows for reliance on unguarded auto
  • [ ] Wrap minmax() minimums with min(value, 100%) for narrow viewport safety
  • [ ] Replace 100vh with 100dvh (keeping a vh fallback) for full-height layouts
  • [ ] Add aspect-ratio or explicit dimensions to all grid-item images
  • [ ] Confirm box-sizing: border-box is applied consistently
  • [ ] Re-test on the smallest target viewport width, not just a mid-size phone
  • [ ] Validate the fix doesn’t regress the Chrome/Firefox desktop layout

Frequently Asked Questions

1. Why does my CSS Grid layout work on Chrome but overlap on mobile Safari? This almost always comes down to WebKit-specific handling of implicit row sizing, minmax() overflow, or dynamic viewport units. Chrome tends to be more forgiving about ambiguous sizing, while Safari more strictly resolves — or fails to resolve — the same rules, which surfaces as visible overlap.

2. Does Chrome DevTools’ mobile emulator accurately show Safari bugs? No. Chrome’s device toolbar only simulates screen dimensions; it still renders with the Blink engine, not WebKit. For accurate Safari testing, use Safari’s Responsive Design Mode on a Mac or test on a real iOS device.

3. How do I fix 100vh issues on mobile Safari? Use 100dvh (dynamic viewport height) instead of, or as a progressive enhancement alongside, 100vh. Keep the 100vh declaration first as a fallback for browsers that don’t support dynamic viewport units yet.

4. Why does minmax() cause overflow on narrow screens in Safari? When the fixed minimum in minmax() exceeds the available space after accounting for gap and padding, Safari can let the grid item overflow its track rather than shrinking it, which visually looks like overlap. Wrapping the minimum with min(value, 100%) prevents this.

5. Can lazy-loaded images cause CSS Grid overlap in Safari? Yes. If an image doesn’t have reserved space via aspect-ratio or explicit width/height, the grid row can resize once the image finishes loading, shifting or overlapping neighboring content — especially noticeable on slower mobile connections.

6. Is CSS Grid fully supported in mobile Safari? Yes, core CSS Grid features have been supported in Safari since version 10.1, including on iOS. The issues covered in this article aren’t about missing support — they’re about differences in how Safari resolves sizing and layout timing compared to Chromium-based browsers.

Final Thoughts

CSS Grid overlap bugs in mobile Safari are frustrating precisely because they’re invisible in the tools most of us reach for first. The fix is almost never “rewrite the grid” — it’s usually one of a handful of specific issues: unguarded implicit row sizing, an unclamped minmax(), or a vh unit that doesn’t account for Safari’s collapsing browser chrome. Once you know to check those three things specifically, this class of bug goes from a multi-hour mystery to a five-minute fix.

If this saved you a debugging session, check out more frontend and cross-browser troubleshooting guides on SpiritCode.blog — I write these up as I run into them in real projects, so there’s always something new worth a read.