You shipped a flex layout with gap: 1rem, it looks perfect in Chrome and Firefox, and then a support ticket comes in: on an old iPad, every item in the row is jammed together with zero spacing. No console error, no warning — the property is just silently ignored. This is one of the more common "invisible" CSS bugs because everything about it looks like it should work.
Why Gap Breaks Specifically in Flexbox (Not Grid)
CSS gap (formerly grid-gap) was implemented for grid layouts long before it was implemented for flex layouts. Browsers rolled out support in two separate passes:
- Grid gap: broad support since roughly 2017-2018 across all major engines.
- Flexbox gap: Chrome and Edge added it in version 84 (2020), Firefox in 63, and Safari lagged until 14.1 (April 2021).
The practical trap is that @supports (gap: 1px) returns true in browsers that only support grid gap, because the property itself is recognized — the feature query has no way to scope itself to "gap when the container is display: flex." So a feature-detection approach that looks correct at first glance will falsely report support in a browser where flex gap doesn't actually work. Full version data is on caniuse.com.
Who Actually Hits This in 2026
The realistic audience is users on Safari below 14.1 — which today means older devices frozen on an outdated iOS/iPadOS build that can no longer update, plus any embedded WebViews or kiosk browsers pinned to an old engine. It's a shrinking slice of traffic, but for e-commerce or internal enterprise tools where a single device fleet is common, it can be a real percentage rather than a rounding error.
Fallback Pattern 1: Margin-Based Spacing with :not(:last-child)
The most robust fallback avoids feature detection entirely and just uses margins as the baseline, then overrides with gap where it's supported:
.flex-row {
display: flex;
}
.flex-row > * {
margin-right: 1rem;
}
.flex-row > *:last-child {
margin-right: 0;
}
@supports (display: flex) and (gap: 1px) {
.flex-row > * {
margin-right: 0;
}
.flex-row {
gap: 1rem;
}
}
This works everywhere because margin support predates every browser you'll ever ship to. The @supports block still has the "grid gap counts as flex gap" false-positive problem in theory, but in practice every browser released after grid-gap support also shipped flex-gap support within a version or two, so the window where this matters is effectively closed for anything currently in the support matrix.
Handling Wrapped Rows
Margin-only fallbacks get messy once flex-wrap: wrap is involved, because you also need vertical spacing between wrapped rows, and :last-child won't catch the last item in each row. The cleaner fix there is negative-margin trick on the container:
.flex-row {
display: flex;
flex-wrap: wrap;
margin: -0.5rem;
}
.flex-row > * {
margin: 0.5rem;
}
@supports (display: flex) and (gap: 1px) {
.flex-row {
margin: 0;
gap: 1rem;
}
.flex-row > * {
margin: 0;
}
}
This is the pattern that scales — it handles both axes without per-child selectors, and it's the same technique that was standard practice before gap existed at all.
Fallback Pattern 2: PostCSS Autoprefixer-Style Build Step
If your build pipeline already runs PostCSS, a plugin can generate the margin fallback automatically from a single gap declaration, so you write modern CSS once and let the tooling emit the compatibility layer. This is worth it only if you're already hitting this pattern repeatedly across a codebase — for one or two components, hand-writing the @supports block is less overhead than adding a build dependency.
Fallback Pattern 3: Just Don't Support It
For internal tools, admin dashboards, or products with published minimum browser requirements, the honest answer is often to not build a fallback at all. Check your real analytics for Safari version distribution before spending engineering time on a device population that might be a fraction of a percent of sessions. If you do decide to drop support, document the minimum Safari version in your project's browser support policy so the next developer doesn't "fix" a bug that's actually an intentional cutoff.
Grid Gap Doesn't Need Any of This
Worth calling out explicitly: if your layout can be expressed as CSS Grid instead of Flexbox, gap has had solid cross-browser support for grid containers for years, well before flex gap caught up. If you're building a new layout and spacing consistency across old Safari matters, grid is the path of least resistance — you don't need any fallback code at all. See the MDN gap reference for the full compatibility breakdown by layout mode.
Decision Guide
| Situation | Recommended approach |
|---|---|
| New project, no legacy Safari in analytics | Use gap directly, skip fallbacks |
| Existing product with measurable old-Safari traffic | Margin + @supports fallback (Pattern 1) |
| Large codebase, many flex components | PostCSS-generated fallback (Pattern 2) |
| Layout could reasonably be Grid instead | Switch to Grid, sidestep the issue |
| Internal tool with a documented browser policy | Do nothing, document the cutoff |
If you're tuning spacing values that need to respond to viewport size rather than just falling back for old browsers, the Clamp Calculator is useful for generating fluid gap and margin values without writing a media query per breakpoint. Test the fallback path directly in an old device or BrowserStack session rather than trusting DevTools' user-agent override — UA spoofing doesn't change the actual rendering engine's feature support.