The Problem
Flexbox gap is super handy, but it’s a relatively recent addition to browsers, and there’s not a great way to do feature detection using @supports
. @supports (gap: 1rem)
will be a false positive if the browser supports gap with grid layouts, and adding @supports (flex-gap: 1rem)
wasn’t in the cards.
You can run a bit of Javascript and add a class like in the old Modernizr days, but that feels so kludgy after getting used to the elegance of @supports
.
The Trick
@supports selector()
entered browsers at roughly the same point as flexbox gap, which means it can be used as a rough proxy for support for flex gap. Define your gap value[1] and add it to both a flex container with gap
and half of it to the container’s children as margin, which you remove inside an @supports selector()
[2]:
$gap: 1rem;
.flex {
display: flex;
flex-wrap: wrap;
gap: $gap;
> * {
margin: $gap * 0.5;
@supports selector(:first-child) {
margin: 0;
}
}
}
The Catch
Browsers that support flex gap and do not support @supports selector()
will apply both the gap and the fallback margin, doubling up the amount of spacing. This will happen in at least 0.23% of browsers and at most 2.41%[3].
According to caniuse, it will definitely be a false negative in: Amount of definite false negatives: 0.235% It might be a false negative in this much larger percentage of browsers, as their support for Amount of possible false negatives: 2.175% Largest possible amount of false negatives: 2.41%More browser support information
@supports selector()
is marked as unknown:
The Solution
A bit of space between elements is not the worst thing in the world, but if it’s really undesirable for your design it might make sense to also wrap your gap declaration inside @supports selector()
:
$gap: 1rem;
.flex {
display: flex;
flex-wrap: wrap;
> * {
margin: $gap * 0.5;
}
@supports selector(:first-child) {
gap: $gap;
> * {
margin: 0;
}
}
}
As a Sass mixin
@mixin flex-gap($gap: 1.25em) {
$gap-half: calc(#{$gap} * 0.5);
@if type-of($gap) == "number" {
$gap-half: $gap * 0.5;
} @else if type-of($gap) == "list" {
$gap-half: ();
@each $value in $gap {
$value-half: calc(#{$value} * 0.5);
@if type-of($value) == "number" {
$value-half: $value * 0.5;
}
$gap-half: append($gap-half, $value-half);
}
}
> * {
margin: $gap-half;
}
@supports selector(:first-child) {
gap: $gap;
> * {
margin: 0;
}
}
}
.flex-gap-test-cases {
@include flex-gap(1em);
@include flex-gap(var(--gap));
@include flex-gap(1em 0.5em);
@include flex-gap(var(--gap-sm) 1.5em);
}
-
You could use a CSS custom property, but using a Sass variable or regular number will cover more old browsers. ↩︎
-
I went with
:first-child
, but the selector doesn’t really matter as long as it’s well-supported. ↩︎ -
Feature support information and browser usage statistics taken from caniuse’s usage table on May 11, 2022. ↩︎
Top comments (0)